场景
程序跑飞了(进入了 HardFault 或死循环),没人按复位键,设备就永远卡死了。看门狗的作用:程序必须定期"喂狗",如果超时不喂,硬件自动复位。
IWDG(独立看门狗)
使用独立的 40kHz 内部 RC 振荡器(LSI),即使主时钟挂了,IWDG 依然工作。
CubeMX 配置
IWDG → 勾选 Activated:
-
IWDG prescaler:分频系数
-
IWDG Counter Reload:重装载值
超时时间 = (4 × 2^prescaler) / 40kHz × reload
| Prescaler | 分频系数 | 最大超时(reload=0xFFF) |
|---|---|---|
| 4 | 64 | ~6.55s |
| 6 | 256 | ~26.2s |
实际使用:最大超时约 26.2s(Prescaler=6, reload=0xFFF),通常设到几秒:
Prescaler = 4(÷64),Counter = 3125
→ 64 / 40000 × (3125+1) = 5.0 秒(Prescaler=4 对应分频系数 64,无需额外乘 4)
代码
// 启动 IWDG(CubeMX 默认已生成)
MX_IWDG_Init();
// 主循环中喂狗
while (1)
{
HAL_IWDG_Refresh(&hiwdg); // 喂狗,重置计数器
// ... 执行其他任务 ...
HAL_Delay(1000);
}
如果程序在某处卡住超过 20 秒没喂狗,系统自动复位。
WWDG(窗口看门狗)
比 IWDG 复杂,但能更早检测到异常。挂在 APB1 上(有时钟才能工作)。
窗口 的含义:必须在"窗口期"内喂狗------太早或太晚都会触发复位。
复位
┌────────────────────┐
│ 不能喂狗的区域 │ ← 太早了也会复位
│(计数器 > 窗口值) │
├────────────────────┤
│ 可以喂狗的区域 │ ← 在这里喂才安全
│(窗口值 > 计数值 > 0x40)│
├────────────────────┤
│ 产生复位 │ ← 没及时喂,计数器降到 0x3F
└────────────────────┘
CubeMX 配置
-
左侧 WWDG
-
Activate
-
Counter:初始值(127~64)
-
Window value:窗口值
-
Prescaler:分频(/1、/2、/4、/8)
超时公式:T_WWDG = T_PCLK1 × 4096 × 2^WDGTB × (T[5:0] + 1)
推导说明:T_PCLK1 为 APB1 时钟周期,4096 为固定预分频系数,WDGTB 为分频因子(0~3 对应 2^0~2^3),T5:0 为递减计数器低 6 位(范围 0~63)
代码
// 喂狗(设到 TR 寄存器)
HAL_WWDG_Refresh(&hwwdg);
怎么选
| 看门狗 | 特点 | 适合场景 |
|---|---|---|
| IWDG | 独立时钟、简单、超时范围大 | 大多数项目------防止死机 |
| WWDG | 依赖系统时钟、窗口机制 | 安全关键系统(工业/医疗) |
| 两个同时用 | IWDG 防死机,WWDG 防时序错乱 | 高可靠性要求 |
调试时的坑
调试器暂停时,IWDG 也在跑!
你停在断点那不动,IWDG 超时复位了。CubeMX 默认生成代码中,IWDG 在调试模式下是停止的,但如果自己配置过的注意:
// 调试时停止 IWDG(在 HAL_Init 后加)
__HAL_DBGMCU_FREEZE_IWDG();
// 调试时停止 WWDG
__HAL_DBGMCU_FREEZE_WWDG();
推荐做法
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
// 所有初始化完成后才打开看门狗
MX_IWDG_Init();
while (1)
{
HAL_IWDG_Refresh(&hiwdg); // 主循环喂狗
// 如果有长的阻塞操作,中间也要喂狗
Long_Operation();
HAL_IWDG_Refresh(&hiwdg); // 再次喂狗
}
}
初始化没完成前不要开看门狗,否则因为初始化时间太长而反复复位。