WDT ISR未执行导致程序在while循环处挂起的原因排查
问题背景
以下代码用于借助Timer1测量AVR单片机看门狗定时器(WDT)频率:将Timer1配置为221kHz计数频率,待WDT触发中断时停止计数,通过计数值乘以Timer1溢出周期计算WDT中断的时长。实际运行时WDT未产生任何中断,程序卡在轮询wdtisr标志的while循环处挂死。
参考实现代码
void watchdog_disable() { cli(); // Disable global interrupt MCUSR &= ~(1<<WDRF); // Clear the Watchdog System Reset Flag (WDRF) WDTCSR |= (1<<WDCE) | (1<<WDE); // In the same operation, write a logic one to WDCE & WDE WDTCSR = 0x00; // Within the next four clock cycles, write the WDTCSR.WDE and Watchdog prescaler bits group // (WDTCSR.WDP) as desired, but with the WDTCSR.WDCE cleared. This must be done in one operation. } // RV8A1, Date: 01-Jun-2022, Enable watchdog timer for system reset void watchdog_enable() { cli(); // Disable global interrupt WDTCSR |= (1<<WDCE) | (1<<WDE); // In the same operation, write a logic one to WDCE & WDE WDTCSR = 0x08; // Within the next four clock cycles, write the WDTCSR.WDE and Watchdog prescaler bits group // (WDTCSR.WDP) as desired, but with the WDTCSR.WDCE cleared. This must be done in one operation. // WDE = 1, WDIE = 0, WDP[2:0] = 000; System Reset Mode Enabled; sei(); } // RV8A1, Date: 01-Jun-2022, Enable watchdog timer in Interrupt mode void watchdog_interrupt_enable() { cli(); // Disable global interrupt WDTCSR |= (1<<WDCE) | (1<<WDE); // In the same operation, write a logic one to WDCE & WDE WDTCSR = 0x61; // Within the next four clock cycles, write the WDTCSR.WDE and Watchdog prescaler bits group // (WDTCSR.WDP) as desired, but with the WDTCSR.WDCE cleared. This must be done in one operation. // WDE = 0, WDIE = 1, WDP[3:0] = 1001; Interrupt Mode Enabled; sei(); } //******************************************************************************* ////////////////////////////main begin//////////////////////////////////////////////////////////// char numtochar(int charrxvd) { char ch = 'x'; switch (charrxvd) { case 0: ch = '0'; break; case 1: ch = '1'; break case 2: ch = '2'; break; case 3: ch = '3'; break; case 4: ch = '4'; break; case 5: ch = '5'; break; case 6: ch = '6'; break; case 7: ch = '7'; break; case 8: ch = '8'; break; case 9: ch = '9'; break; } return ch; } void sendnumber(unsigned int num) { cpsrvrflg = 0; Send2esp8266("AT+CIPSERVER=0\r\n"); for(i=0; i<10; i++) { _delay_ms(250); } ////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// Send2esp8266("AT+CIPMUX=0\r\n"); // Single for(i=0; i<6; i++) { _delay_ms(250); } //////////////////////////////////////////////////////////////////////////////////////////// //Send2esp8266("AT+CIPSTART=\"UDP\",\"192.168.42.14\",5001\r\n"); Send2esp8266("AT+CIPSTART=\"UDP\",\"192.168.42.1\", 5002\r\n"); for(i=0; i<10; i++) { _delay_ms(250); } ////////////////////////////////////////////////////////////////////////////////////////// Send2esp8266("AT+CIPMODE=1\r\n"); // for(i=0; i<5; i++) { _delay_ms(250); } ////////////////////////////////////////////////////////////////////////////////////////// Send2esp8266("AT+CIPSEND\r\n"); // 7 characters for(i=0; i<6; i++) { _delay_ms(250); } ////////////////////////////////////////////////////////////////////////////////////////// Send2esp8266("WDT FREQ = "); int x; while((num/10.0) != 0) { x=num%10; uart_transmit(numtochar(x)); num=num/10; } Send2esp8266("+++"); for(i=0; i<15; i++) { _delay_ms(250); } Send2esp8266("AT+CIPCLOSE\r\n"); for(i=0; i<10; i++) { _delay_ms(250); } Send2esp8266("AT+CIPMODE=0\r\n"); // Transparent mode off for(i=0; i<6; i++) { _delay_ms(250); } } // timer freq = 11059200 Hz; for 1 tick = 1/11059200 sec = 5.79 usec // total tick = 50; tcnt0 start 255-50 = 205 = 0xCD; timer frequency = 11059200/50 = 221184 Hz float wdttimeout = 0; int wdtisr = 0; void timer1_init() { TCNT1 = 0xFFCD; // 0x80 org // dec 128 TIMSK1 = 0x01; TCCR1A &= ~(1<<WGM10); // RV09_H, Date: 05-May-2022, set Normal mode operation TCCR1A &= ~(1<<WGM11); TCCR1B &= ~(1<<WGM13); TCCR1B &= ~(1<<WGM12); TCCR1B |= (1<<CS10); // No prescaler; freq = fosc = 11059200 Hz } void timer1_stop() { TCCR1B = 0x00; TIMSK1 = 0x00; } ISR(TIMER1_OVF_vect) { //uart_transmit(timeoutsec); TCNT1 = 0xFFCD; wdttimeout++; //tmrflag = 1; //uart_transmit(timeoutsec); //uart_transmit('\n'); } float trackwdt() { wdttimeout = 0; wdtisr = 0; int num = 0; timer1_init(); watchdog_interrupt_enable(); while(wdtisr == 0) ; watchdog_disable(); // RV8A1, 01-Jun-2022, watchdog disable num = (int)(wdttimeout*(50/11059200))*1000; // Time in ms return(wdttimeout); } ISR(WDT_vect) { timer1_stop(); wdtisr = 1; } void main() { trackwdt(); }
故障根因分析
- 看门狗配置寄存器写入值错误,违反硬件配置时序要求
代码注释明确要求,置位WDCE/WDE位后写入新配置时必须清零WDCE位,但watchdog_interrupt_enable中实际写入WDTCSR的值为0x61,换算为二进制是0b01100001,其中WDCE位(位5,对应掩码0x20)仍为置1状态,不符合硬件操作规范,导致整个WDT配置操作无效,看门狗中断始终未被使能。
此外注释要求WDP预分频位配置为1001(对应典型超时时间2s),但0x61的低四位为0001,预分频配置也完全错误。按照注释的功能需求,正确的配置值应为0x49:仅WDIE位(位6)置1,WDCE、WDE位清零,WDP3、WDP0位置1,对应二进制0b01001001。 - 配置WDT前未清除看门狗复位标志
watchdog_disable函数中包含清除MCUSR寄存器WDRF(看门狗系统复位标志)的操作,但watchdog_interrupt_enable函数中缺失该步骤。AVR硬件明确要求,修改WDT任何配置前必须先清零WDRF标志,否则WDT的配置修改操作会被硬件忽略,无法正常切换到中断模式。 - 存在显性编译错误(非卡死直接原因但会阻碍程序正常运行):
numtochar函数中case 1分支的break语句末尾缺少分号,会直接触发编译失败。
内容的提问来源于stack exchange,提问作者vishnu m c
相关产品推荐
相关产品推荐

