为何中断模式下的Watchdog仍导致Arduino Mega 2560复位?
环境
我正在使用Arduino Mega 2560,以下是源代码:
#ifndef F_CPU #define F_CPU 16000000L #endif #include <avr/io.h> #include <avr/interrupt.h> #include <avr/wdt.h> void start_system_timer(void) { cli(); // 清除WDRF标志,避免超时复位循环 MCUSR &= ~(1<<WDRF); // 复位看门狗定时器 wdt_reset(); // 启用看门狗配置模式 WDTCSR |= 1 << WDCE | 1 << WDE; // 重置看门狗定时器寄存器 WDTCSR = 0; // 配置超时周期 wdt_enable(WDTO_1S); // 设置看门狗为中断模式 WDTCSR |= 1 << WDIE; sei(); } int main(void) { // 将PORTB7设为输出并点亮LED DDRB |= (1<<PB7); PORTB |= (1<<PB7); start_system_timer(); sei(); while (1) { } } ISR (WDT_vect, ISR_NAKED) { // 关闭LED PORTB &= ~(1<<PB7); // 重新启用看门狗中断以避免复位 WDTCSR |= 1 << WDIE; reti(); }
问题
我把看门狗定时器设置为中断模式,但不在中断处理函数中复位定时器时,设备还是会复位。根据数据手册,处于中断模式时不应该触发系统复位。我还试过在设置中断模式后清除WDIE位(WDTCSR &= ~(1<<WDIE);),但还是解决不了问题。
问题原因及解决方案
你的代码存在两个核心问题,导致看门狗触发了复位:
wdt_enable()的默认行为:调用wdt_enable(WDTO_1S)时,会同时启用看门狗的复位模式(WDE位被置1)。此时即使后续设置了WDIE位,看门狗实际处于「中断+复位」的组合模式——第一次超时触发中断,第二次超时就会触发系统复位。中断处理逻辑缺失:中断触发后,
WDIE位会被硬件自动清零,且你没有复位看门狗计数器。即使是中断模式,若不重置计数器,下一次超时依然会触发预设的动作(组合模式下就是复位)。
修正步骤:
切换为纯中断模式配置:放弃使用
wdt_enable(),直接手动配置WDTCSR寄存器,确保WDE位为0(禁用复位)、WDIE位为1(启用中断):
替换start_system_timer()中的wdt_enable(WDTO_1S);和WDTCSR |= 1 << WDIE;为以下代码:// 进入看门狗配置模式 WDTCSR = (1 << WDCE) | (1 << WDE); // 配置1秒超时、仅中断模式(WDP2|WDP1对应1秒分频,WDE=0禁用复位) WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1);在中断中复位看门狗:每次触发中断后,必须调用
wdt_reset()重置计数器,避免后续超时触发复位。修改中断函数:ISR (WDT_vect, ISR_NAKED) { wdt_reset(); // 复位看门狗计数器 PORTB &= ~(1<<PB7); // 关闭LED WDTCSR |= 1 << WDIE; // 重新启用中断 reti(); }删除冗余代码:
start_system_timer()末尾已调用sei(),main()中的重复调用可删除。
修正后的start_system_timer()函数:
void start_system_timer(void) { cli(); MCUSR &= ~(1<<WDRF); // 清除看门狗复位标志 wdt_reset(); // 初始复位看门狗 // 进入配置模式 WDTCSR = (1 << WDCE) | (1 << WDE); // 配置纯中断模式+1秒超时 WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1); sei(); }
配置完成后,看门狗只会触发中断,不会触发系统复位,只要在中断中及时复位计数器,即可维持正常运行。
内容的提问来源于stack exchange,提问作者itasahobby

