这是我的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中正确读取它,但在主要部分我得到了初始值。
答案 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 (即更新了变量的全有或全无),因此也不需要更复杂的同步。
但,如果您增加了计数器的大小,则必须在ISR
或main
中进行规定,以确保一致地读取值。这是因为AVR是8位机器,因此更新和读取不是原子的。
注意:由于受欢迎的需求,您应检查目标是否实际执行写入原子。对于8位值也是如此。如果您不确定,请检查生成的汇编代码。但是,对于AVR,PIC,MSP430,ARM-Cortex-M( iff 总线和寄存器支持字节写入),除非使用DMA,否则上述代码是安全的其中一个变量。