不同优化级别下AVR程序运行异常:-O3时PD6引脚LED不闪烁
问题:-O3优化级别下ATmega32M1程序PD6引脚无输出
问题背景
编写了一款用于熟悉ATmega32M1的测试程序,在除-O3外的所有编译优化级别下,PB5和PD6引脚的LED均按预期以8秒周期闪烁;但开启-O3优化后,仅PB5引脚的LED正常闪烁,PD6引脚始终保持低电平。
开发环境
- Microchip Studio(版本7.0.2594,原Atmel Studio)
- 编译器:avr-gcc 5.4.0
- 目标MCU:ATmega32M1
测试代码
#include <avr/io.h> #include <avr/interrupt.h> static uint64_t execution_time; //////////////////////////////////////////////////////////////////////////////// // Setup functions static inline void SetClock(void) { //Set clock to external crystal highest speed (8MHz): //(These instructions must be completed in 4 clock cycles or less) CLKPR = 0x80; //set CLKPCE, clear all else. CLKPR = 0x00; //Clear CLKPCE, set CLKPS[3:0] to 0. (Smallest prescale - fastest speed.) } static inline void SetTimer1(void) { //set up timer/counter 1 (CTC mode, TOP = OCR1A) TCCR1A = 0; //WGM0 & WGM1 = 0 TCCR1B = 0x0D; //set WGM[3:0] = 0,1,0,0 (CTC with OCR1A as TOP); CS1[2:0] = 1,0,1 (internal timer, prescale 1024) OCR1A = 0x7A12; //TOP = 31250; 4000.0 ms - This means an accurate count can be kept as well as the faster count. TIMSK1 = 1<<OCIE1A; //Interrupt on Timer 1 reaching OCR1A. //The Timer will run at 7.8125 ticks per ms. } void InitOutputs(void) { DDRB |= 0x7 << DDB5; //SPARE6, 7, 10 DDRC |= 0x3 << DDC6; //SPARE8, 9 DDRD |= 0x7 << DDD5 | 0x3 << DDD0; //SPARE11, 12, 13 && RELAY H & L } void Setup(void) { //Set up the various parts of the hardware. SetClock(); SetTimer1(); InitOutputs(); sei(); } //////////////////////////////////////////////////////////////////////////////// // Functional... functions void TestISR(void) { static uint64_t last_time; if (execution_time > last_time) { PIND = 1 << PORTD6; last_time = execution_time; } } int main(void) { Setup(); while (1) { TestISR(); } } ISR(TIMER1_COMPA_vect) { execution_time += 4000; PINB = 1 << PORTB5; }
问题分析
这不是编译器bug,而是代码中缺少对异步修改变量的volatile修饰,导致高优化级别下编译器错误地优化了逻辑:
execution_time变量在TIMER1_COMPA_vect中断服务程序中被异步修改,但主循环所在的非中断上下文没有声明该变量为volatile。- 在-O3优化级别下,avr-gcc会假设非中断上下文的变量不会被外部(如中断)修改,因此会将
TestISR()中的execution_time读取操作优化为仅读取一次,后续循环直接使用缓存的值。 - 由于初始时
execution_time和last_time均为0,编译器判定execution_time > last_time永远不成立,直接移除了PD6引脚的翻转代码;而PB5的翻转逻辑在中断中直接执行,不受该优化影响。
解决方案
将execution_time的声明修改为带volatile修饰,告诉编译器该变量可能被异步修改,必须每次都从内存读取最新值:
static volatile uint64_t execution_time;
修改后重新编译测试,无论在哪个优化级别下,PD6引脚的LED都会按预期闪烁。
补充说明
volatile关键字的核心作用是禁止编译器对变量的访问进行缓存优化,确保每次读取/写入都直接操作内存。对于在中断和主上下文之间共享的变量,必须添加该修饰,否则高优化级别下极大概率会出现逻辑异常。
内容的提问来源于stack exchange,提问作者Infinite_Maelstrom
相关产品推荐
相关产品推荐

