为何需在主循环中调用millis()才能触发TIMER0_COMPA_vect中断服务程序?
定时器中断依赖millis()调用才能触发的问题
我正在复刻耳机的多功能按键功能——在一段时间内按下按键指定次数,耳机就会根据按压次数执行对应操作。但遇到一个奇怪的问题:必须在主循环里调用millis()函数,用于递增毫秒变量的TIMER0_COMPA_vect中断服务程序(ISR)才会触发,完全搞不懂为什么会这样。
以下是相关代码:
主代码
#include <mcu_init.h> #include <millis.h> #include <button.h> int main() { GPIO_init(); cli(); INT0_init(); TMR0_init(); sei(); while (1) { // 如果在这里调用millis(),TIMER0_COMPA_vect中断才会触发 handleButton(); } return 0; }
millis.c 实现
#include <millis.h> static volatile uint32_t miliseconds = 0; ISR (TIMER0_COMPA_vect) { miliseconds++; PORTA |= (1 << 1); } uint32_t millis() { uint32_t time; uint8_t oldSREG = SREG; cli(); time = miliseconds; SREG = oldSREG; return time; }
button.c 实现
#include <button.h> volatile uint8_t count = 0; volatile uint32_t lastPressTime = 0; ISR (INT0_vect) { if (millis() - lastPressTime >= 200) { count++; lastPressTime = millis(); } } void handleButton() { if (millis() - lastPressTime >= 1500) { lastPressTime = millis(); switch (count) { case 2: PORTA ^= (1 << 1); count = 0; break; case 3: PORTA ^= (1 << 3); count = 0; break; default: count = 0; } } return; }
millis.h 头文件
#ifndef MILLIS_H_ #define MILLIS_H_ #include <avr/interrupt.h> uint32_t millis(); #endif
button.h 头文件
#ifndef BUTTON_H_ #define BUTTON_H_ #include <millis.h> void handleButton(); #endif
问题根源分析
这个现象的核心原因是编译器优化:
- 你的
miliseconds变量是static volatile,但如果主循环中没有任何代码读取它(或通过millis()间接读取),编译器会认为定时器中断对miliseconds的修改没有实际意义,可能会优化掉定时器中断的触发逻辑,甚至忽略定时器初始化的部分代码。 - 虽然
handleButton()中调用了millis(),但如果编译器判断该函数的执行没有产生明确的“可见副作用”(比如某些分支下PORTA的修改被判定为非必要),可能会优化掉handleButton()的调用,间接导致millis()没被执行,最终让中断看似“不触发”。
解决方法
- 强制保留读取操作:在主循环中添加一行无意义但会触发
millis()调用的代码,阻止编译器优化:(void)millis(); // 强制读取miliseconds,确保编译器不会丢弃中断逻辑 - 明确保留副作用:确保中断和主循环中的操作有明确的硬件副作用,比如保留中断里的
PORTA |= (1 << 1);调试代码,让编译器认为这段逻辑必须执行。 - 检查定时器初始化:确认
TMR0_init()中正确开启了定时器比较匹配中断(设置OCIE0A位),并且时钟源配置正确。如果初始化代码有误,也可能导致中断仅在特定条件下触发。
额外注意事项
INT0_vect中断中调用millis()是安全的,但要确保临界区的处理正确(你当前的millis()实现已经通过保存和恢复SREG做到了这一点)。lastPressTime是32位volatile变量,在handleButton()中读取时,建议同样用临界区保护,避免因中断打断导致读取到不完整的数值。
内容的提问来源于stack exchange,提问作者Carlos Leonel Rivas Pimentel
相关产品推荐
相关产品推荐

