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

Global variables modified by main() and accessed by ISR()

这是我的 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 读取 -中断处理程序.
当 main 读取全局变量时,我得到了预期值,但在 ISR 中,我得到了首先分配给全局变量的值。
我知道这是一个优化问题,但我不明白是什么让编译器在 main 中看到正确的值,在 ISR 中看到初始值

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

ISR 没有正确的声明。您真的应该习惯使用原型样式的声明。使用 C99 或 C11 编译,您将收到警告。与主要相似:

void ISR(void)

int main(void)

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

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

表示,您缺少申报global_variablevolatile。你应该知道,因为你已经添加了标签。

编译器无法知道 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 可以是有符号或无符号的,并且有符号整数溢出会调用 undefined behavior C. 无符号溢出定义为简单的回绕(即:MAX+1 == 0)。更好的数据类型是 uint8_t,因为 C99(强烈推荐)明确声明您正在使用 8 位变量(char 不保证这一点)。

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

但是,如果增加计数器的大小,则必须在ISRmain中采取措施以确保读取值的一致性。这是因为 AVR 是一个 8 位机器,因此更新和读取不是原子的。

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

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

volatile char global_variable = 0;