如何在嵌入式ANSI C中制作锁定/关键部分?

时间:2019-07-06 23:24:37

标签: c99 microchip critical-section ansi-c xc8

我有这样的代码:(非常简化的代码)

// 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的一半将被中断处理程序重写,并且一切都搞砸了。

这不是RTOS,所以我没有内置锁。

我该怎么办?如何从头开始锁定?

我正在考虑像这样创建“关键部分”:

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 个答案:

答案 0 :(得分:1)

您可以编写一个用于保存参数副本的函数,该函数可以停止中断,执行复制,然后重新启动中断。

#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);