由main()修改并由ISR()访问的全局变量

时间:2015-08-29 12:32:25

标签: c embedded compiler-optimization volatile interrupt-handling

这是我的c代码

char global_variable = 0;
ISR(){
    PORTA = global_variable;
    toggle_led;//to make sure that the interrupt is triggered
}
int main(){
    while(1){
        _delay_ms(500);
        gobal_variable++;
        PORTB = global_variable;
    }
    return 0;
}

底线是我有一个由main函数修改的全局变量,并由main和ISR读取 - 中断处理程序
当主要读取全局变量时,我得到预期值,但在ISR中,我得到第一次分配给全局变量的值。
我知道这是一个优化问题,但我不明白是什么让编译器在主要中看到正确的值,在ISR中看到初始值

注意:当我在ISR中修改变量时,我在ISR中正确读取它,但在主要部分我得到了初始值。

2 个答案:

答案 0 :(得分:2)

在这种情况下,您应该插入一个内存屏障来断言所有写入将在您阅读之前完成。在用户空间中,您应该将此变量声明为 volatile

volatile char global_variable = 0;

答案 1 :(得分:0)

ISR没有正确的声明。你真的应该习惯使用原型风格的声明。使用C99或C11进行编译,您将收到警告。类似于main:

void ISR(void)

int main(void)

main的签名取决于您的环境,假设您使用的是裸机嵌入式系统,即独立环境

根据目标,您必须使用特定于编译器的属性将该函数标记为中断处理程序。

说,你遗漏了global_variable volatile。你应该知道,因为你已经添加了标签。

编译器无法知道无论如何都要调用ISR,并且该变量在其控制流之外被修改。因此它可以假定它具有默认值,即0。使用volatile限定符可以确切地告诉它它不能,并且变量实际上是在外部修改的。

请注意,由于所有这些,编译器不得优化对volatile个对象的加入。因此,您应该将对这些对象的访问限制到最小。在main中,最好使用辅助变量进行计数,然后将更新后的值一次写入volatile对象和计数器。这样就可以避免一次读取和一次写入:

volatile unsigned char global_variable;

...

int main(void)
{
    unsigned char counter;
    while ( 1 ) {
        _delay_ms(500);
        gobal_variable = counter++;
        PORTB = counter;
    }
    return 0;
}

请注意,我将类型更改为unsigned char这是至关重要的,因为char可以是有符号或无符号的,有符号整数溢出会在C中调用未定义的行为。无符号溢出是定义为简单换行(即:MAX + 1 == 0)。要使用的更好的数据类型是uint8_t,因为C99(强烈建议)明确声明您正在使用8位变量(char并不能保证这一点。)

注意:根据您在下面的评论,您使用的是AVR MCU。这是单核的,甚至不支持内存障碍。所以绝对没有必要这样做。此外,由于您有一个写入器并且写入是 atomic (即更新了变量的全有或全无),因此也不需要更复杂的同步。

,如果您增加了计数器的大小,则必须在ISRmain中进行规定,以确保一致地读取值。这是因为AVR是8位机器,因此更新和读取不是原子的。

注意:由于受欢迎的需求,您应检查目标是否实际执行写入原子。对于8位值也是如此。如果您不确定,请检查生成的汇编代码。但是,对于AVR,PIC,MSP430,ARM-Cortex-M( iff 总线寄存器支持字节写入),除非使用DMA,否则上述代码是安全的其中一个变量。