4. 硬件看门狗设计:独立看门狗(IWDG)与窗口看门狗(WWDG)
看门狗这东西,说白了就是一个「监工」。你干活的时候,得时不时跟它打个招呼:「嘿,我还活着呢!」。如果你忘了打招呼,它就认为系统挂了,直接给你来个复位。
我刚开始做运动控制那会儿,觉得看门狗就是个累赘。直到有一次,电机在高速运转时,主控芯片因为一个中断卡死,电机直接飞车……嗯,从那以后,我再也不敢轻视看门狗了。
4.1 独立看门狗(IWDG)—— 最可靠的「保底」
独立看门狗,我习惯叫它「硬汉」。为什么?因为它完全独立于主系统时钟,用的是自己的低速内部振荡器(LSI)。就算主时钟挂了,它照样能工作。
核心特点:
- 使用独立的40kHz LSI时钟源
- 12位递减计数器,最大可设4096分频
- 一旦启动,无法关闭(除非复位)
- 超时范围:约125μs ~ 32.7s
我在项目中遇到过这样的情况:电机驱动器在强电磁干扰下,主晶振偶尔会失锁。这时候,只有IWDG能兜底。其他看门狗?早就跟着主时钟一起「躺平」了。
IWDG的寄存器操作
操作IWDG其实很简单,就几个关键寄存器:
// 解锁写保护
IWDG->KR = 0x5555; // 喂狗前必须先解锁
// 设置预分频器(4分频)
IWDG->PR = 0x02; // 分频系数 = 4 * 2^PR = 16
// 设置重装载值(1000)
IWDG->RLR = 1000; // 超时时间 = (RLR * 分频系数) / 40kHz
// 喂狗操作
IWDG->KR = 0xAAAA; // 重装载计数器
// 启动看门狗
IWDG->KR = 0xCCCC; // 启动后无法停止
注意:IWDG一旦启动,只有系统复位才能关闭。所以调试阶段,我建议先别开,等程序稳定了再启用。我曾经在调试时忘了关IWDG,结果每次单步执行到一半就被复位,折腾了一下午才发现问题。
4.2 窗口看门狗(WWDG)—— 更「智能」的监工
窗口看门狗,比IWDG多了一个「窗口」的概念。什么意思呢?你喂狗的时间不能太早,也不能太晚,必须在设定的时间窗口内完成。
为什么要有窗口?你想想看,如果程序跑飞了,但碰巧在某个循环里还在喂狗,IWDG就检测不到异常。WWDG就不一样了——你喂早了不行,喂晚了也不行,必须卡在正确的时间点。
WWDG的关键参数:
- 时钟源:PCLK1(APB1总线时钟)
- 7位递减计数器(T[6:0])
- 窗口值(W[6:0]):喂狗必须在计数器值 < 窗口值时进行
- 超时范围:约0.1ms ~ 50ms(取决于时钟配置)
WWDG的寄存器配置
// 配置窗口寄存器
// 假设PCLK1 = 36MHz,分频系数 = 8
// 计数器最大值 = 127,窗口值设为 80
WWDG->CFR = (1 << 7) // WDGTB = 1,分频系数 = 8
| (80 << 0); // 窗口值 W = 80
// 配置控制寄存器
// 计数器初始值设为 127,使能看门狗
WWDG->CR = (127 << 0) // T[6:0] = 127
| (1 << 7); // WDGA = 1,使能
// 喂狗操作(必须在计数器值 < 80 时进行)
WWDG->CR = (127 << 0); // 重装载计数器
我的经验:WWDG的窗口值设置很有讲究。设得太宽,失去窗口意义;设得太窄,正常程序都可能喂不上。我一般会先跑一遍程序,用逻辑分析仪抓出喂狗的时间点,然后留20%的余量设置窗口。
4.3 IWDG vs WWDG:怎么选?
| 对比项 | IWDG | WWDG |
|---|---|---|
| 时钟源 | 独立LSI(40kHz) | 系统时钟PCLK1 |
| 可靠性 | 极高(独立于主时钟) | 中等(依赖主时钟) |
| 超时范围 | 125μs ~ 32.7s | 0.1ms ~ 50ms |
| 窗口功能 | 无 | 有(防止提前喂狗) |
| 典型应用 | 安全关键系统、强干扰环境 | 实时性要求高的控制环路 |
我个人习惯:运动控制的主循环用WWDG,因为它能检测到程序跑飞后「碰巧」喂狗的情况。同时,再开一个IWDG作为最后一道防线,超时时间设得长一些(比如10秒),防止WWDG失效时系统彻底死掉。
4.4 实战中的避坑指南
我曾经踩过的坑:
- 中断里喂狗:千万别在中断服务函数里喂狗!如果主循环卡死了,但中断还在正常触发,看门狗就形同虚设。我见过有人把喂狗放在定时器中断里,结果主程序死锁了,电机还在转,直到冒烟了才发现。
- 喂狗位置不对:喂狗应该放在主循环的末尾,而不是开头。放在开头的话,如果喂完狗程序就卡住了,看门狗要等到下一个超时周期才能复位,这段时间系统可能已经出大问题了。
- 调试时忘记关闭:仿真调试时,IWDG不会因为断点而暂停。所以调试前,最好加个条件编译,把看门狗关掉。
// 推荐的做法:条件编译控制看门狗
#ifdef DEBUG
// 调试模式:关闭看门狗
#define WDG_ENABLE() do{}while(0)
#define WDG_FEED() do{}while(0)
#else
// 发布模式:正常启用
#define WDG_ENABLE() IWDG->KR = 0xCCCC
#define WDG_FEED() IWDG->KR = 0xAAAA
#endif
void main(void)
{
// 系统初始化
SystemInit();
// 使能看门狗
WDG_ENABLE();
while(1)
{
// 执行运动控制任务
MotorControlTask();
// 执行通信任务
CommTask();
// 喂狗(放在最后)
WDG_FEED();
}
}
4.5 知识体系总览
下面这张图,是我自己总结的看门狗选型与配置流程。每次做新项目,我都会先过一遍这个图,避免漏掉关键步骤。
这张图的核心逻辑其实就一句话:环境恶劣用IWDG,追求实时性用WWDG,不差钱两个都用。我在做伺服驱动器时,就是按照这个思路,IWDG设5秒超时保底,WWDG设10ms窗口监控主循环,用了三年没出过看门狗相关的故障。
最后说一句:看门狗不是万能的。它只能检测到程序「死掉」的情况,检测不到「程序活着但逻辑错误」的情况。所以,该做的异常处理、状态机保护,一个都不能少。看门狗只是最后一道防线,不是唯一的防线。