第2章:看门狗工作原理:内部定时器机制、喂狗操作、超时复位流程

大家好,我是你们的老朋友。上一章我们聊了看门狗为什么重要,这一章咱们深入内部,看看它到底是怎么工作的。

说白了,看门狗就是一个会倒计时的定时器。你必须在它归零之前告诉它:“我还活着,别复位!”——这就是喂狗。如果你没来得及喂,它就认为系统卡死了,然后强制复位。

听起来简单吧?但里面的门道可不少。我当年第一次用看门狗,就因为它“咬”了我一口,搞得我排查了整整两天。

2.1 内部定时器机制

看门狗的核心,就是一个递减计数器。它从你设定的初始值开始,每个时钟周期减1。减到0的时候,就触发复位。

你想想看,这个机制其实很朴素。但为什么有的看门狗准,有的不准?问题出在时钟源上。

时钟源类型 典型精度 我的评价
内部RC振荡器 ±10% ~ ±30% 便宜但飘得厉害,温度一变它就变
外部晶振 ±50ppm 稳,但多一个引脚和成本
PLL分频时钟 取决于源时钟 小心PLL失锁,我踩过这个坑

我个人习惯,只要成本允许,一定用外部晶振做看门狗时钟。为什么?因为看门狗本身就是用来兜底的,如果它的时钟都不靠谱,那还兜什么底?

⚠️ 重要提醒: 内部RC振荡器在高温下频率可能下降30%以上。这意味着你设的1秒超时,实际可能变成1.3秒。系统死机了,要等更久才复位——这在某些场景下是致命的。

2.2 喂狗操作

喂狗,就是往看门狗的一个特殊寄存器里写入一个特定序列。比如STM32的独立看门狗,你需要先写0xAAAA,再写0x5555,最后写0xCCCC。

为什么要搞这么复杂?直接写一个位不行吗?

嗯,这里要注意:这是为了防止程序跑飞时“意外喂狗”。如果只是写一个位,飞掉的程序万一碰巧写到了那个位,看门狗就被误喂了,系统死机了也不知道。

// 典型的喂狗代码(以STM32 IWDG为例)
void IWDG_Feed(void)
{
    IWDG->KR = 0xAAAA;  // 使能访问重装载寄存器
    IWDG->KR = 0x5555;  // 写入重装载值
    IWDG->KR = 0xCCCC;  // 启动重装载
}

我曾经在一个项目中,把喂狗代码放在了中断服务函数里。结果主循环卡死了,中断还能正常触发,看门狗一直被喂着,系统死活不复位。排查了三天才发现——喂狗位置选错了。

💡 我的喂狗原则:
  • 喂狗代码只放在主循环的关键路径上
  • 不要在中断里喂狗(除非你很清楚自己在做什么)
  • 喂狗前检查关键任务是否完成

2.3 超时复位流程

当计数器减到0时,看门狗会做什么?

不同芯片略有差异,但大体流程是这样的:

  1. 计数器归零:递减到0的那一刻,触发复位事件
  2. 复位信号产生:内部产生一个持续若干时钟周期的复位脉冲
  3. 系统复位:CPU、外设全部回到初始状态
  4. 复位标志置位:有个寄存器会记录“这次复位是看门狗引起的”

第4点特别重要。为什么?因为你要区分是上电复位还是看门狗复位。如果是看门狗复位,说明系统之前出过问题,你需要记录下来,甚至进入安全模式。

// 检查复位原因(以某MCU为例)
if (RCC->CSR & RCC_CSR_WDGRSTF)
{
    // 看门狗复位!
    Log_Error("系统因看门狗超时而复位");
    Enter_SafeMode();
    
    // 清除标志位
    RCC->CSR |= RCC_CSR_RMVF;
}
else
{
    // 正常上电复位
    Init_Normal();
}
🔑 核心要点: 看门狗复位后,不要直接恢复运行。先检查复位原因,记录日志,再决定是正常启动还是进入安全模式。这是我用血泪换来的经验。

2.4 知识体系总览

下面这张图,是我自己总结的看门狗工作原理全景图。你看一遍,应该就能把整个流程串起来了。

看门狗工作原理全景图 时钟源 RC / 晶振 / PLL 递减计数器 每个时钟周期减1 比较器 计数器 == 0 ? 触发系统复位 置位复位标志 喂狗操作 重装载计数器 未喂狗 核心逻辑:喂狗 → 计数器重装载 → 继续运行 不喂狗 → 计数器归零 → 系统复位

2.5 避坑指南

最后,分享几个我亲身踩过的坑:

  • 坑1:喂狗代码放在中断里 —— 前面说过了,主循环卡死但中断还能跑,看门狗失效
  • 坑2:超时时间设得太短 —— 系统正常运行时偶尔也会超过,导致误复位。我一般留50%的余量
  • 坑3:没检查复位标志 —— 上电和看门狗复位用同一套初始化,问题被掩盖了
  • 坑4:在调试器暂停时喂狗 —— 调试时断点停下,看门狗还在跑,等你单步执行完,它已经复位了
🛠️ 调试小技巧: 调试阶段可以把看门狗超时设得很长(比如10秒),或者干脆用宏控制是否使能。等功能稳定了,再把时间调回来。我习惯这样写:
#ifdef DEBUG
    #define WDT_TIMEOUT_MS 10000  // 调试时10秒
#else
    #define WDT_TIMEOUT_MS 2000   // 正式版2秒
#endif

好了,这一章的内容就到这里。看门狗的工作原理,说白了就是一个会咬人的定时器。你按时喂它,它就乖乖的;你不喂它,它就咬你一口——让系统重启。

下一章我们聊聊怎么选型,不同应用场景该用什么样的看门狗。嗯,到时候见。


公众号:蓝海资料掘金营,微信deep3321