Как сделать блокировку / критическую секцию во встроенном ANSI C? - PullRequest
0 голосов
/ 07 июля 2019

У меня есть такой код: (очень упрощенный код)

// This is code for Microchip 8-bit microcontroller, XC8 compiler (GCC based)

#define TIMER_COUNT 8;
volatile uint16_t timer_values[TIMER_COUNT];
volatile uint16_t timer_max_values[TIMER_COUNT];

// executes every 100ms
void timer1_interrupt_handler()
{
    for (uint8_t i = 0; i < TIMER_COUNT ; i++){
        timer_values[i];
    }
}

void main(){

    // (...) some initialization, peripherial and interrupt handler setup

    while(1){
        for (uint8_t i = 0; i < TIMER_COUNT ; i++) {

            // what if interrupt happens in the middle of comparison?
            if (timer_values[i] >= timer_max_values[i]) 
            {
                 timer_values[i] = 0;   // reset timer
                 executeTimerElapsedAction(i);
            }

        }
    }
}

Проблема в том, что это 8-битный микроконтроллер, и когда прерывание происходит в середине неатомарной операции с 16-битной переменной:

timer_values[i] >= timer_max_values[i]

или это:

timer_values[i] = 0; 

возможно, что половина uint16_t будет переписана обработчиком прерываний, и все испорчено.

Это не ОСРВ, поэтому у меня нет встроенных блокировок.

Что я могу сделать? Как сделать замок с нуля?

Я думал о создании "критической секции", подобной этой:

GlobalInterruptsDisable();     // enter critical section

if (timer_values[i] >= timer_max_values[i]) 
{
    timer_values[i] = 0;   // reset timer
    GlobalInterruptsEnable();     // exit critical section
    executeTimerElapsedAction(i);
}

но Боюсь, что пока что я пропущу некоторые прерывания (я использую 2 таймера, 2 прерывания UART и прерывания I2C), и что-то еще может испортиться.

Дополнительный вопрос:

Если я отключу прерывания примерно на 20-30 тактов процессора, а затем некоторые данные поступят в UART - я пропущу эти данные, или обработчик прерываний будет выполняться позже после включения прерываний?

1 Ответ

1 голос
/ 12 июля 2019

Вы можете написать функцию для копии параметра сохранения, которая останавливает прерывание, выполняет копирование и затем перезапускает прерывание.

#define protectedXfer(destination, source) protectedMemcpy(&(destination), \
                                                          &(source), \
                                                          sizeof(destination))

void protectedMemcpy(void *destination, const void *source, size_t num)
{
    int volatile oldIpl;
    oldIpl = SRbits.IPL;
    SRbits.IPL2 = 1; // If an Interrupt occurs here
    SRbits.IPL1 = 1; // the IPL bits are saved/restored
    SRbits.IPL0 = 1; // from the stack
    memcpy(destination, source, num);
    SRbits.IPL = oldIpl;
}

Знайте, что вы можете выполнить сохранение из значений таймера и проверить позже.

protectedXfer(destinaion, source);
...