1. 看门狗基础概念:什么是看门狗定时器(WDT),为什么消防报警系统需要看门狗,看门狗的工作原理(喂狗与超时复位)

大家好,我是你们的嵌入式系统讲师。今天咱们来聊聊看门狗——这个在嵌入式系统里看似不起眼、但关键时刻能救你一命的家伙。

说实话,我最早接触看门狗是在做一款消防报警控制器的时候。那会儿刚入行,觉得这玩意儿不就是个定时器嘛,有啥好研究的?结果有一次现场调试,系统莫名其妙死机了,消防主机不响应任何按键,报警灯也不闪了。客户急得直跳脚,我满头大汗地翻代码……嗯,从那以后,我再也不敢小看这个“狗”了。

什么是看门狗定时器(WDT)?

看门狗定时器,英文叫 Watchdog Timer,简称 WDT。说白了,它就是一个硬件定时器,专门用来监视系统是否还在“活着”。

你想想看,嵌入式系统跑在野外、机房、楼道里,没人天天盯着它。万一程序跑飞了、卡在某个死循环里、或者堆栈溢出了,系统就彻底瘫痪了。这时候谁去把它拉回来?

看门狗就是干这个的。

它的工作方式很简单:

  • 系统正常运行时,每隔一段时间去“喂狗”——也就是重置这个定时器
  • 如果系统挂了,没人喂狗,定时器就会溢出
  • 溢出后,看门狗直接拉一个复位信号,让系统重启

我在项目中遇到过一种情况:某款消防报警主机的通信模块偶尔会卡住,但CPU本身还在跑。如果不用独立看门狗,你根本发现不了通信已经断了。后来加了外部看门狗,专门监视通信心跳,问题才彻底解决。

核心要点:看门狗不是用来防止故障的,而是用来在故障发生后,让系统自动恢复的。它是最后一道防线。

为什么消防报警系统需要看门狗?

消防报警系统,你想想看,它是什么场景?

  • 24小时不间断运行,没人值守
  • 一旦发生火灾,必须可靠报警
  • 如果系统死机,后果不堪设想

我参与过的一个项目,消防主机在高温环境下(比如靠近厨房的烟感探测器)偶尔会出现程序跑飞。没有看门狗的话,系统就彻底哑巴了。加了看门狗之后,系统能在几百毫秒内自动复位,恢复正常工作。

消防报警系统对可靠性的要求极高,具体来说:

需求 看门狗的作用
无人值守运行 自动检测死机并恢复,无需人工干预
高可靠性要求 防止程序跑飞导致报警失效
实时性要求 快速复位(毫秒级),减少系统不可用时间
电磁干扰环境 抵抗干扰导致的程序异常

我个人习惯,在设计消防报警系统时,至少会配置两级看门狗:

  1. 片内看门狗:MCU自带的,用于检测CPU本身的异常
  2. 外部看门狗:独立芯片,用于检测整个系统(包括电源、时钟等)的健康状态
我的经验:片内看门狗虽然方便,但如果MCU的时钟都停了,它也就失效了。所以关键系统一定要加外部看门狗。我曾经在一个项目中只用了片内看门狗,结果电源纹波导致MCU时钟抖动,看门狗也跟着乱跳……那叫一个惨。

看门狗的工作原理:喂狗与超时复位

看门狗的工作原理,其实就三个字:喂、等、咬

咱们拆开来看:

1. 喂狗(Kicking the Dog)

喂狗,就是程序主动去重置看门狗定时器的计数值。通常是在主循环里,或者在一个高优先级的任务里,定期执行一次喂狗操作。

代码示例(以STM32为例):

// 喂狗函数
void FeedTheDog(void)
{
    // 写入特定序列,重置看门狗计数器
    IWDG->KR = 0xAAAA;  // 喂狗,重置计数器
}

// 主循环中调用
int main(void)
{
    // 初始化看门狗,设置超时时间为1秒
    IWDG_Init(IWDG_PRESCALER_4, 1250);  // 约1秒超时
    
    while(1)
    {
        // 执行系统任务
        ProcessSensors();
        CheckAlarmConditions();
        UpdateDisplay();
        
        // 喂狗——告诉看门狗:我还活着
        FeedTheDog();
        
        // 注意:喂狗间隔必须小于看门狗超时时间
        // 我一般留50%的余量,比如超时1秒,我每500ms喂一次
    }
}
注意:喂狗的位置非常关键。如果你在中断里喂狗,主循环卡死了,看门狗也不会复位。我曾经见过一个同事把喂狗放在定时器中断里,结果主循环死锁了,看门狗还在被定时喂着……系统一直不重启,用户投诉说设备“假死”。所以,喂狗一定要放在主循环的关键路径上。

2. 超时(Timeout)

看门狗定时器有一个预设的超时时间。比如你设置1秒,那么从上次喂狗开始,如果1秒内没有再次喂狗,定时器就会溢出。

超时时间怎么选?我一般遵循这个原则:

  • 比系统最长的正常任务周期长一些(比如主循环最长执行200ms,我设500ms超时)
  • 但也不能太长,否则系统死机后要等很久才复位(消防系统建议不超过2秒)
  • 留有余量,但不要超过任务周期的3倍

举个例子:

系统类型 主循环周期 推荐超时时间
消防报警主机 50-200ms 500ms - 1s
烟感探测器 100-500ms 1s - 2s
消防广播系统 10-50ms 200ms - 500ms

3. 复位(Reset)

一旦看门狗超时,它会做什么?

它会直接拉低MCU的复位引脚,或者触发一个不可屏蔽的中断(NMI)。系统强制重启,从main函数重新开始执行。

这里有个细节:复位后,系统需要能区分是上电复位还是看门狗复位。这样你可以在初始化时判断:如果是看门狗复位,说明之前系统出过问题,可以记录日志或者进入安全模式。

代码示例:

void CheckResetSource(void)
{
    if (RCC->CSR & RCC_CSR_WDGRSTF)
    {
        // 看门狗复位!
        // 记录日志:系统曾因看门狗复位
        LogEvent("Watchdog reset occurred");
        
        // 清除标志位
        RCC->CSR |= RCC_CSR_RMVF;
        
        // 进入安全模式,比如只运行基本报警功能
        EnterSafeMode();
    }
    else
    {
        // 正常上电复位
        NormalInit();
    }
}
避坑指南:我曾经在一个项目里,看门狗复位后没有清除标志位,结果每次复位都重复记录日志,把Flash写坏了。所以,复位后第一件事就是清除标志位,然后再做其他操作。

总结一下

看门狗定时器,说白了就是一个“系统健康监视器”。它不复杂,但用好了能救命。

消防报警系统为什么特别需要它?因为这类系统不能容忍“死机”这种故障。你想想,火灾来了,报警主机却卡死了——这可不是闹着玩的。

我个人建议,在设计任何嵌入式系统时,尤其是涉及安全的产品,先把看门狗加上。哪怕你觉得自己代码写得再稳,也架不住硬件干扰、电源波动、极端温度这些外部因素。

嗯,下一章咱们聊聊看门狗的具体配置方法,包括怎么选超时时间、怎么在RTOS里喂狗、以及一些常见的坑。到时候我会拿我踩过的几个雷给大家当反面教材,保证印象深刻。