STM32 内部看门狗(IWDG)与窗口看门狗详解:原理、配置与实战
👁 6 阅读 · 2026-08-14 · STM32 看门狗
看门狗是嵌入式系统防死机的最后一道防线。STM32 内置独立看门狗(IWDG)和窗口看门狗(WWDG),两者原理、超时特性与使用场景截然不同。本文深入剖析二者的硬件机制、时钟源与喂狗窗口,给出基于 HAL 库的完整配置示例和代码,并总结常见误区和调试技巧,帮助开发者根据系统需求选对看门狗,写出稳定可靠的固件。
## 1. 为什么需要看门狗?
嵌入式系统在恶劣电磁环境或软件异常时可能陷入死循环、跑飞,导致系统失去响应。看门狗(Watchdog)是一个独立于 CPU 运行逻辑的定时器,若程序未在规定时间内“喂狗”(重装载计数器),则触发复位,使系统恢复可控。STM32 提供两种看门狗:**独立看门狗 IWDG** 和 **窗口看门狗 WWDG**,两者各有侧重。
## 2. IWDG 独立看门狗
### 2.1 原理与特点
IWDG 由**独立的 40kHz 低速内部时钟(LSI)**驱动,即使主时钟故障也能运行。它是一个 12 位递减计数器,当计数到 0 时产生复位。通过写入 `IWDG_KR` 键值 `0xAAAA` 可重装载计数器,实现喂狗。
关键特点:
- **完全独立**:不依赖系统主时钟,抗干扰能力强。
- **喂狗时间窗宽松**:只要计数器未减到 0,任何时刻喂狗均有效,没有最早喂狗限制。
- **超时范围宽**:通过预分频器和重装载寄存器可配置约 0.1ms 到 32.768s 的复位周期。
### 2.2 配置步骤(HAL 库)
1. 使能 LSI(若未由其他外设开启)。
2. 设置预分频器 `IWDG_PRESCALER_64`(分频系数 64)。
3. 设置重装载值 `Reload`(12 位,0~4095)。
4. 调用 `HAL_IWDG_Init()` 完成初始化。
5. 在主循环中周期性调用 `HAL_IWDG_Refresh()` 喂狗。
```c
// 独立看门狗初始化示例
IWDG_HandleTypeDef hiwdg;
void MX_IWDG_Init(void)
{
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64; // LSI=40kHz,分频后 625Hz
hiwdg.Init.Reload = 1250; // 超时约 1250/625 = 2s
if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
{
Error_Handler();
}
}
// 主循环中喂狗
void main_loop(void)
{
while (1)
{
// 执行任务...
if (system_healthy()) {
HAL_IWDG_Refresh(&hiwdg); // 必须在超时前调用
}
// 若系统卡死,则无法喂狗,触发复位
}
}
```
### 2.3 注意事项
- IWDG 一旦启动,**无法停止**,除非系统复位。初始化后不能再修改预分频和重装载值。
- 喂狗间隔必须小于超时时间,但无需最小间隔限制。
- 在进入低功耗模式(STOP/SLEEP)前,确认 IWDG 是否会引发复位,必要时在低功耗前喂狗或使用备份域控制。
## 3. WWDG 窗口看门狗
### 3.1 原理与特点
WWDG 由**主时钟(APB1 时钟 PCLK1)**驱动,是一个 7 位递减计数器。它有两个关键阈值:
- **窗口上限(WWDG_CFR 的 W[6:0])**:必须在计数器值小于该窗口值时才允许喂狗。
- **下限(固定为 0x3F,即 63)**:当计数器递减到 0x3F 时,若尚未喂狗,则产生复位。
因此,WWDG 要求喂狗发生在**一个时间窗口内**:不能太早(计数器值大于窗口上限),也不能太晚(计数器值小于 0x3F)。常用于检测程序执行时序是否异常,如任务执行过快或过慢。
### 3.2 配置步骤(HAL 库)
1. 使能 WWDG 时钟(`__HAL_RCC_WWDG_CLK_ENABLE()`)。
2. 设置预分频器 `WWDG_PRESCALER_DIV8`。
3. 设置窗口值 `Window`。
4. 设置计数器初值 `Counter`(7 位)。
5. 开启早期唤醒中断(EWI)可选。
```c
WWDG_HandleTypeDef hwwdg;
void MX_WWDG_Init(void)
{
hwwdg.Instance = WWDG;
hwwdg.Init.Prescaler = WWDG_PRESCALER_DIV8; // PCLK1=36MHz,分频后 4.5MHz
hwwdg.Init.Window = 0x50; // 窗口上限(喂狗时计数器须小于等于0x50)
hwwdg.Init.Counter = 0x7F; // 计数器初值,递减周期约 (0x7F-0x3F)/4.5MHz ≈ 14.2us
hwwdg.Init.EWIMode = WWDG_EWI_ENABLE; // 使能早期唤醒中断,可在复位前保存数据
if (HAL_WWDG_Init(&hwwdg) != HAL_OK)
{
Error_Handler();
}
}
// 在满足窗口条件时喂狗
void feed_wwdg(void)
{
// 关键:必须确保当前计数器值已经小于等于 Window,但大于 0x3F
if ((WWDG->CR & WWDG_CR_T) <= (0x50 << 0))
{
HAL_WWDG_Refresh(&hwwdg); // 重装载计数器
}
}
```
### 3.3 注意事项
- WWDG 的时钟来自 APB1,当主时钟故障时,WWDG 也会失效,因此其可靠性不如 IWDG。
- 窗口值必须大于等于 0x40(64),否则窗口不存在(下限固定 0x3F)。
- 一旦初始化,WWDG 也无法停止(除非复位)。
- 早期唤醒中断(EWI)可在计数器达到 0x3F 前触发,用于执行数据保存或标志位设置,但中断处理时间必须极短,否则复位仍然会发生。
## 4. IWDG 与 WWDG 选型对比
| 特性 | IWDG | WWDG |
|------|------|------|
| 时钟源 | 独立 LSI(~40kHz) | APB1 主时钟 |
| 计数范围 | 12 位递减 | 7 位递减 |
| 喂狗限制 | 无最早时间限制 | 必须在窗口内喂狗 |
| 超时精度 | 依赖 LSI 精度(可校准) | 依赖系统时钟,精度高 |
| 可靠性 | 主时钟故障仍工作 | 主时钟故障则失效 |
| 典型用途 | 防止程序跑飞 | 检测任务时序异常 |
## 5. 实战建议与常见误区
### 5.1 不要在主循环中无条件喂狗
许多初学者把 `HAL_IWDG_Refresh()` 直接放在 `while(1)` 开头,这样即使主循环内部某段代码死机,只要循环还能执行到喂狗,就不会复位。正确做法是:**喂狗应放在关键任务完成之后,且喂狗路径必须经过异常检测**。
### 5.2 注意喂狗间隔的裕量
IWDG 超时时间应比最大任务周期大 30%~50% 左右,避免因偶发中断阻塞导致误复位。例如任务周期 1s,设置超时 2s 较合理。
### 5.3 调试时如何避开看门狗
在调试阶段,可以先用宏开关禁用看门狗,或者使用调试器暂停时自动喂狗(部分 STM32 调试支持)。更通用的做法是在条件编译中设置:
```c
#define DEBUG_WATCHDOG 1
void watchdog_task(void)
{
#if DEBUG_WATCHDOG
// 调试模式下关闭
return;
#else
// 正常喂狗逻辑
#endif
}
```
### 5.4 在中断中喂狗需谨慎
如果喂狗放在中断服务函数中,而主程序陷入死循环但中断仍能触发,可能导致看门狗从未超时,掩盖了主程序的错误。因此喂狗逻辑应放在主循环的任务调度中,而不是中断里。
## 6. 总结
STM32 的 IWDG 和 WWDG 各有其设计哲学:IWDG 是“最后一道防线”,保证任何情况下系统不会死锁;WWDG 则是“时序裁判”,能捕获程序执行节奏的异常。实际项目中,有时会同时启用两种看门狗:IWDG 守护全局,WWDG 监控关键任务的执行窗口。理解它们的工作原理和硬件限制,才能合理配置,避免“被狗咬”或“狗失效”的尴尬。
(文中代码基于 STM32 HAL 库,实测于 STM32F103 系列,其他型号原理相同,配置略有差异。)