由main()修改并由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中,我得到第一次分配给全局变量的值。
我知道这是一个优化问题,但我不明白是什么让编译器看到主要的正确值和ISR中的初始值
注意:当我在ISR中修改变量时,我在ISR中读取它,但在主要中我得到了初始值。
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。 这是单核的,甚至不支持内存障碍。 所以绝对没有必要这样做。 此外,由于您有一个写入器并且写入是primefaces的 (即更新变量的全部或全部),因此也不需要更复杂的同步。
但是 ,如果增加计数器的大小,则必须采用ISR
或main
条款来确保一致地读取值。 这是因为AVR是8位机器,因此更新和读取不是primefaces的。
注意:由于受欢迎的需求,您应该检查目标是否实际执行写入primefaces。 对于8位值也是如此。 如果您不确定,请检查生成的汇编代码。 但是,对于AVR,PIC,MSP430,ARM-Cortex-M( 如果总线和寄存器支持字节写入),除非在其中一个变量上使用DMA,否则上述代码是安全的。
在这种情况下,您应插入一个内存屏障来断言所有写入将在您阅读之前完成。 在用户空间中,您应该将此变量声明为volatile 。
volatile char global_variable = 0;