看门狗超时原因分析

做嵌入式开发这些年,我遇到过最让人头疼的问题之一,就是看门狗超时复位。系统跑着跑着突然重启,现场数据全丢了,客户那边还等着交付。说实话,这种问题排查起来特别费劲,因为它不是每次都复现,有时候跑几个小时才出一次。

看门狗超时的本质,就是系统没有在规定时间内去"喂狗"。那为什么喂狗会失败呢?我根据自己的项目经验,把常见原因归纳为四类:软件死循环、任务调度阻塞、中断服务程序过长、低功耗模式下的喂狗失败。下面我一个一个说。

软件死循环

这个最常见,也最容易被忽视。你写了一个while循环,条件判断出了点小问题,结果就永远跳不出来了。看门狗等着你去喂它,可你的程序还在循环里转悠呢。

典型场景:

  • 等待某个硬件标志位,但硬件一直没置位
  • 通信接收缓冲区的空/满判断逻辑写反了
  • 状态机里某个状态没有对应的处理分支

我记得有一次,一个同事调试CAN通信程序,接收中断里有个while循环等待发送完成标志。结果发送一直不成功,循环就卡死了。看门狗超时复位,他还以为是硬件问题,查了两天才发现是逻辑错误。

我的建议:所有while循环都要加超时保护。哪怕你觉得"这个循环肯定能出来",也要加个计数器。我个人的习惯是,超过一定次数就强制退出,并记录错误日志。

/* 错误写法:没有超时保护 */
while (CAN_TxReady() == FALSE) {
    /* 如果硬件异常,这里永远出不去 */
}

/* 正确写法:加超时保护 */
uint32_t timeout = 10000;
while (CAN_TxReady() == FALSE) {
    if (--timeout == 0) {
        /* 记录错误,强制退出 */
        Error_Handler();
        break;
    }
}

任务调度阻塞

用RTOS的时候,这个问题特别突出。你想想看,系统里跑着好几个任务,喂狗的任务优先级不够高,被高优先级任务一直抢占着,它就得不到CPU时间。或者更糟,某个任务里调用了阻塞函数,比如等待信号量、等待消息队列,结果一直等不到。

为什么会这样?说白了就是任务优先级设计不合理。我曾经在一个项目里,把喂狗任务优先级设成了最低,结果有个通信任务偶尔会长时间占用CPU,喂狗任务就被饿死了。系统时不时复位一次,查了三天才找到根因。

避坑指南:

  • 喂狗任务优先级要适中,别太低也别太高
  • 所有阻塞等待都要设超时,别死等
  • 高优先级任务里别做耗时操作
问题类型 典型表现 排查方法
优先级反转 低优先级任务持有资源,高优先级任务等待 检查任务优先级和资源锁
任务饿死 喂狗任务长期得不到调度 用RTOS的统计功能查看任务运行时间
死锁 两个任务互相等待对方释放资源 检查信号量和互斥锁的使用顺序

中断服务程序过长

嗯,这里要注意。中断服务程序(ISR)里如果代码太多,执行时间太长,会导致两个问题:一是其他中断被延迟响应,二是主循环里的喂狗代码迟迟得不到执行。

我见过有人把整个通信协议解析都放在中断里做,一个中断跑了几毫秒。看门狗超时周期通常也就几十毫秒,这么搞肯定出事。你想想看,中断优先级最高,它占着CPU不放,主循环里的喂狗函数根本跑不了。

中断设计原则:

  1. ISR里只做最紧急的事,比如拷贝数据、置标志位
  2. 耗时操作放到任务级去处理
  3. ISR执行时间要控制在看门狗周期的1/10以内
/* 错误做法:在中断里做大量处理 */
void UART_IRQHandler(void) {
    uint8_t data = UART->DR;
    /* 这里做协议解析、校验、响应... 耗时太长! */
    ProcessProtocol(data);
}

/* 正确做法:中断只收数据,处理交给任务 */
void UART_IRQHandler(void) {
    uint8_t data = UART->DR;
    /* 把数据放进队列,立刻退出中断 */
    Queue_Send(&rx_queue, data);
}

低功耗模式下的喂狗失败

这个坑我踩过好几次。系统进入低功耗模式后,CPU时钟停了或者降频了,看门狗还在跑。但喂狗代码依赖定时器中断来触发,而定时器在低功耗模式下可能不工作了。

说白了,就是低功耗模式和看门狗没配合好。有些MCU在低功耗模式下,看门狗会自动暂停,但有些不会。你得仔细看芯片手册,确认低功耗模式下看门狗的行为。

我曾经犯过的错:一个电池供电的项目,为了省电让系统进入深度睡眠。结果看门狗还在计数,醒来时已经超时复位了。后来我改成在进入睡眠前先关闭看门狗,醒来后再重新初始化。

低功耗模式 看门狗行为 处理建议
睡眠模式 通常继续运行 用低功耗定时器唤醒喂狗
深度睡眠 可能暂停或复位 进入前关闭看门狗,唤醒后重新开启
待机模式 通常停止 使用独立看门狗(IWDG)

这里有个小技巧:很多MCU都有独立看门狗(IWDG)和窗口看门狗(WWDG)。IWDG使用独立的RC振荡器,低功耗模式下也能工作。如果你需要在低功耗模式下保持看门狗监控,建议用IWDG。

个人经验:我习惯在系统里同时使用两种看门狗。IWDG负责监控低功耗模式,WWDG负责监控正常运行时的任务调度。这样不管系统在什么状态下,都有看门狗在盯着。

好了,这四种原因基本覆盖了90%以上的看门狗超时问题。实际排查时,我建议先从最简单的入手——先检查有没有死循环,再看中断服务程序是不是太长,然后分析任务调度,最后考虑低功耗模式的影响。按这个顺序来,大部分问题都能快速定位。