
STM32 内部看门狗与外部硬件看门狗案例验证实录
一、为什么要聊看门狗
设备交付到现场后,最怕的不是功能不对,而是“不回了”:
- 客户打电话来说设备没反应,只能让用户断电重启;
- 设备装在铁塔、井盖、偏远的泵站里,根本没法派人去按复位键;
- OTA 之后偶发死机,实验室里却怎么也复现不出来。
看门狗(Watchdog)就是解决这类问题的最后一道防线:
当程序失去响应时,强制复位芯片。
但很多工程师只停留在“开了个 IWDG”的层面,误以为这样就高枕无忧了。
二、先分清三个“看门狗”
| 时钟源 | 独立 LSI(约 32/40kHz) | PCLK1 | 芯片自带 RC 振荡器 |
| 喂狗方式 | 向 KR 写 0xAAAA | 在时间窗口内写 CR | 在超时前给 WDI 引脚一个电平翻转 |
| 喂太快 | 允许 | 复位(窗口期喂狗) | 通常允许(边沿检测) |
| 喂太慢 | 复位 | 复位 | 复位 |
| 程序跑飞后能否被改配置 | 能(同一颗芯片,寄存器可被误写) | 能 | 不能(物理隔离) |
| 调试器 Halt 时 | 可选冻结 | 默认暂停(可配) | 照常计时,照样咬人 |
| MCU 时钟/电源异常 | 时钟坏→失效 | 时钟坏→失效 | 电压监控 + 复位输出仍有效 |
| 成本 | 0 | 0 | 一颗芯片 + 一个电阻 |
| 主程序死循环、跑飞、卡死 | IWDG 或外部狗 |
| 主程序时序异常(任务周期不稳定) | WWDG |
| 跑飞后恰好跳到喂狗代码 | WWDG(窗口机制防这个) |
| 需要复位前做紧急处理 | WWDG(EWI 中断) |
| MCU 整片死机、电源异常 | 外部狗芯片 |
| 主时钟停振 | 外部狗芯片(WWDG 随主时钟死) |
| 高可靠工业 / 汽车 | 外部狗芯片(WWDG 不够独立) |
| 位置 | STM32 片内 | 独立芯片(MAX6369/SP706/CAT823) |
| 时钟源 | APB1 主时钟(PCLK1,最高 45MHz) | 外部独立 RC 振荡器 |
| 计数器 | 7 位递减(T [6:0]) | 外部 RC + 固定计数器 |
| 超时范围 | 极短:~0.1ms ~ 58ms | 通常 100ms ~ 几 秒(靠电容调) |
| 喂狗时机 | 必须在窗口内喂,太早 / 太晚都复位 | 任何时候喂都行,超时才复位 |
| 复位条件 | 计数器 < 窗口下限 或 > 上限 | 计数器减到 0 |
| 早期中断 | 有 EWI 提前唤醒中断 | 无(纯硬件复位) |
| 低功耗 | 随主时钟停 | 独立供电继续跑 |
| 喂狗方式 | 写 CR 寄存器 | GPIO 翻转 WDI 引脚 |
| 复位输出 | 内部复位线 | 直接拉 NRST 引脚 |
| 独立性 | 依赖主时钟,主时钟挂它也挂 | 完全独立于 MCU |
| 监控粒度 | 时序精监控(喂早了也复位) | 超时粗监控(只看最后) |
| 典型用途 | 检测程序时序异常、防止跑飞后过早喂狗 | 防 MCU 整片死机 |
一句话总结:
- IWDG:防“程序卡死”,最常用,但精度差、配置可被跑飞的程序破坏;
- WWDG:防“喂狗节奏异常”,能检测“程序还活着但节奏乱了”的软故障;
- 外部硬件看门狗:物理隔离、不可被软件欺骗,是系统级兜底。
三、案例验证
案例 1:IWDG 基础验证 —— 主循环卡死能否复位
目的:验证 IWDG 最基本的功能闭环。
配置要点(HAL 库):
IWDG_HandleTypeDef hiwdg;
void MX_IWDG_Init(void)
{
hiwdg.Instance = IWDG;
/* LSI ≈ 32kHz:预分频 32 → 1kHz 计数,重装值 1000 → 约 1s 超时 */
hiwdg.Init.Prescaler = IWDG_PRESCALER_32;
hiwdg.Init.Reload = 1000;
HAL_IWDG_Init(&hiwdg);
}
int main(void)
{
/* 略去时钟/外设初始化 */
MX_IWDG_Init();
while (1)
{
do_work();
HAL_IWDG_Refresh(&hiwdg); /* 正常喂狗 */
HAL_Delay(100);
}
}
复现步骤:在 do_work() 里注入 while(1); 模拟死循环,烧录运行。
预期现象:
- 约 1 秒后芯片复位,串口重新打印启动日志 → IWDG 生效;
- 抓复位原因寄存器(RCC->CSR 的 IWDGRSTF 位)确认是 IWDG 复位,而非上电复位:
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST))
{
printf("[RESET] IWDG reset!\\r\\n");
__HAL_RCC_CLEAR_RESET_FLAGS();
}
结论:
IWDG 能防“最简单的主循环卡死”。
这是底线能力,也是所有后续验证的对照组。
注意事项:
LSI 是低速 RC 振荡器,误差可达 ±10%~±30%,超时按最坏情况留余量,标称 1s 可能实际是 0.7~1.3s。
案例 2:喂狗位置错误 —— 中断里喂狗导致的“假活”

目的:验证一个经典反模式——把喂狗放进定时器中断。主循环已经死了,但中断还在跑,看门狗永远不咬人。
复现步骤:
/* 错误示范 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
HAL_IWDG_Refresh(&hiwdg); /* 主循环死了这里照样喂 */
}
int main(void)
{
while (1)
{
do_work();
while (1); /* 注入:主循环死锁,但中断仍存活 */
}
}
预期现象:
- 设备不再复位,串口无输出,但示波器测定时器中断引脚仍在周期性翻转;
- 现象上就是“设备死了但看门狗没救它”——这正是很多现场事故的真实原因。
正确做法:
喂狗动作只放在主循环末尾或最低优先级的任务里;
多任务系统用“任务健康度汇聚”模式——每个关键任务各自置位自己的标志位,主循环检查所有标志位齐全后才喂狗:
volatile uint32_t task_alive_mask = 0;
#define TASK_COMM_ALIVE (1u << 0)
#define TASK_SAMPLE_ALIVE (1u << 1)
void comm_task(void) { task_alive_mask |= TASK_COMM_ALIVE; }
void sample_task(void) { task_alive_mask |= TASK_SAMPLE_ALIVE; }
/* 主循环:全部任务都报活才喂狗 */
if (task_alive_mask == (TASK_COMM_ALIVE | TASK_SAMPLE_ALIVE))
{
HAL_IWDG_Refresh(&hiwdg);
task_alive_mask = 0;
}
/* 任何一个任务卡死 → mask 不齐 → 停止喂狗 → IWDG 复位 */
结论:看门狗防的是“程序失去响应”,喂狗位置本身就是架构问题。喂狗代码越靠近“真正在干活的代码”,看门狗越可信。
案例 3:WWDG 窗口验证 —— 喂得太快也要复位

目的:验证 WWDG 的“窗口”特性,说明它和 IWDG 的互补性。
配置要点:
WWDG_HandleTypeDef hwwdg;
/* PCLK1 = 36MHz,预分频 8 → 计数周期约 4.44us(F103 参考) */
hwwdg.Init.Prescaler = WWDG_PRESCALER_8;
hwwdg.Init.Window = 0x50; /* 窗口值 */
hwwdg.Init.Counter = 0x7F; /* 从 0x7F 递减,到 0x40 触发复位 */
hwwdg.Init.EWIMode = ENABLE; /* 提前唤醒中断(可选) */
HAL_WWDG_Init(&hwwdg);
复现步骤(三个子实验):
| 3a | 远小于窗口时间(如 100us 就喂) | 复位——喂得太早 |
| 3b | 递减到窗口区间内喂(计数 < 0x50 且 > 0x40) | 正常 |
| 3c | 一直不喂,递减到 0x3F | 复位——喂得太晚 |
现象解读:
WWDG 强制要求程序“按节奏”喂狗。
如果某个函数执行时间突然变长(例如 Flash 操作被中断、任务调度抖动),喂狗点会漂移出窗口,从而被发现。
这能抓住 IWDG 抓不住的一类故障:
程序还活着,但执行时序已经异常。
实践建议:
WWDG 常配合提前唤醒中断(EWI)使用——在中断里保存现场信息(关键变量、任务栈水位)到 Backup SRAM 或 Flash,再复位,方便事后定位。
案例 4:IWDG 的失效场景 —— 为什么内部看门狗不够
目的:用实验证明 IWDG 在几种真实工况下会失效,这就是外部硬件看门狗存在的理由。
子实验 4a:调试器 Halt 导致看门狗“冻结”
- 复现:开启 IWDG,在 Keil/IAR 中点暂停。
- 现象:CPU Halt 后 IWDG 停止计数(DBGMCU 配置了冻结位时)。产品出厂后虽然不带调试器,但任何能挂上 SWD 的异常环境都可能让看门狗形同虚设;更常见的类比是低功耗 Stop 模式下 IWDG 行为与预期不符的设计缺陷。
子实验 4b:程序跑飞后改写 IWDG 寄存器
- 复现:通过故意越界的函数指针跳转到一段“野代码”,或直接在调试器里把 IWDG->PR / IWDG->RLR 改到最长超时。
- 现象:IWDG 超时被无限延长,复位不再发生。
- 本质:IWDG 与被监控的程序在同一颗芯片上,配置寄存器对软件是可写的——跑飞的程序理论上可以“关掉自己的看守”。
子实验 4c:主时钟异常
- 复现:运行中干扰晶振(工程上可用拔晶振/降激励模拟)。
- 现象:若时钟树进入异常状态,复位可能延迟甚至失效;复位后若时钟仍异常,需要 CSS(时钟安全系统)与看门狗配合设计。
结论:
内部看门狗的三个软肋——可被软件欺骗、可被调试/低功耗状态影响、依赖片上时钟。
要突破这三点,只能靠芯片外的硬件。
案例 5:外部硬件看门狗验证 —— TPS3823 兜底

目的:验证外部看门狗芯片在“MCU 内部看门狗全部失效”的场景下仍能复位系统。
硬件连接:
STM32 GPIO(如 PA8) ──→ TPS3823 WDI(喂狗输入,需周期性电平翻转)
TPS3823 /RESET(开漏) ──→ STM32 NRST(建议接 10k 上拉)
TPS3823 同时监控 VDD 3.3V(电压低于阈值自动复位)
喂狗代码(翻转电平而非写寄存器):
#define EXT_WDG_TOGGLE() HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8)
/* 主循环中,周期 < TPS3823 超时(典型 1.6s),且 > 最短脉宽要求 */
while (1)
{
do_work();
EXT_WDG_TOGGLE();
HAL_Delay(200);
}
复现步骤与预期现象:
| 主循环 while(1)(不喂任何狗) | 复位 ✓ | 约 1.6s 后复位 ✓ |
| 跑飞后改写 IWDG 配置 | 不复位 ✗ | 照常复位 ✓ |
| IWDG 喂狗放中断里(假活) | 不复位 ✗ | 照常复位 ✓ |
| 直接断供喂狗 GPIO 初始化 | 看配置 | 复位 ✓ |
| 拔掉喂狗线(模拟 GPIO 损坏) | — | 复位 ✓ |
| VDD 跌落到 2.9V | — | 电压监控强制复位 ✓ |
关键测量点(示波器双通道):
通道 1 接 PA8 确认喂狗波形停止的时间点;
通道 2 接 NRST,测量“最后一次喂狗”到“复位拉低”的实际间隔,与数据手册标称值对比(TPS3823 典型 1.6s,实际约 1.4~2.2s 分布)。
结论:
外部看门狗与 MCU 是物理隔离的——它不依赖任何 MCU 内部资源,只认 WDI 引脚有没有按时翻转。
软件再怎么跑飞,都骗不了它。
附带收益是电源监控(Brown-out 检测),解决了“电压异常导致 MCU 逻辑混乱”这一看门狗管不到的问题。
案例 6:双重看门狗架构 —— 实际产品的推荐做法

目的:验证“内部 WWDG(快周期、精细监控)+ 外部硬件看门狗(慢周期、系统兜底)”的分层架构。
架构设计:
任务层:各任务报活 → 汇聚检查
↓ 全部报活
WWDG 喂狗(窗口内,检测时序抖动)
↓ 主循环正常
IWDG 喂狗(防 WWDG 配置被破坏后的兜底)
↓ 主循环正常
GPIO 翻转 → 外部 TPS3823(最终兜底)
分层逻辑:
验证方法:
对每一层分别注入故障(复用案例 2/4 的手段),确认“下层失效时上层能接住”。
例如同时骗过 WWDG 和 IWDG(案例 4b 手段),外部看门狗仍然在约 1.6s 内完成复位。
复位后的自检建议:
从 RCC->CSR / Backup 寄存器读取复位原因并上报云端(若带通信能力);
统计看门狗复位频率——频繁触发本身就是产品缺陷的信号,不要让复位掩盖 bug。
四、对比总结与实践 Checklist
选型决策:
小家电/玩具类,死机无所谓? → IWDG 就够了
一般工业/消费产品? → IWDG + 正确的喂狗架构(案例 2)
长周期无人值守(表计、井盖终端)? → IWDG + 外部硬件看门狗(案例 5)
高可靠/安全相关(医疗、工控)? → WWDG + IWDG + 外部看门狗 三层(案例 6)
上线前 Checklist:
- 喂狗代码不在任何中断里(案例 2)
- 多任务系统使用报活汇聚,而不是单一位置喂狗
- IWDG 超时按 LSI 最坏误差留余量(±30%)
- 复位原因寄存器被读取并上报/记录
- 验证过低功耗模式进/出后看门狗行为符合预期
- 调试器挂载工况下的看门狗行为已确认并锁定发布配置
- 外部看门狗:喂狗信号周期实测在窗口内,NRST 连接与上拉正确
- 量产固件关闭调试口(RDP)或至少确认看门狗不可通过 SWD 冻结
- 看门狗复位计数有遥测,异常增多会触发告警
五、参考资料
- ST:RM0008(STM32F10x 参考手册)Independent watchdog / Window watchdog 章节
- ST:AN4228《Getting started with STM32 IWDG》/ AN3522
- TI:TPS3823 数据手册(Supervisory circuits with watchdog timer)
- Maxim/ADI:MAX6369 系列数据手册(可调超时的外部看门狗)
面试题
一、原理与概念
Q1. 看门狗的核心作用是什么?它能防止什么?不能防止什么?
A:
作用是程序失控后自动复位 MCU,实现无人值守自恢复。
能防:跑飞、死循环、HardFault 卡死、栈溢出导致 PC 跑飞。
不能防:电源失效、时钟源本身损坏、SRAM 数据全部翻转、外部硬件故障。
Q2. 为什么说看门狗是"最后一道防线"?
A:
所有软件恢复机制(assert、异常处理、HardFault handler)都依赖 CPU 能正确取指执行。
一旦跑飞,这些代码根本不会被执行。
看门狗独立于主程序,CPU 失控后它还在走,所以是最后一道。
Q3. IWDG 的"独立"体现在哪几个方面?
A:
① 时钟独立(LSI 不依赖 HSE/HSI);
② 计数器独立(硬件自主递减,不依赖 CPU 指令);
③ 复位独立(减到 0 强制复位,软件无法屏蔽)。
Q4. LSI 和 HSI 的区别?为什么看门狗用 LSI 不用 HSI?
A:
LSI 是低速内部 RC(32kHz),HSI 是高速内部 RC(16MHz)。
看门狗用 LSI 因为:
① 低速、功耗低;
② 即使主系统时钟配置出错,LSI 仍独立运行;
③ 不需要精确时钟,粗粒度超时足够。
Q5. IWDG 计数器是多少位的?最大值多少?
A:
12 位递减计数器,范围 0~4095(2^12-1)。
Q6. IWDG 的超时时间由哪几个参数决定?
A:三个:
① LSI 频率;
② 预分频系数 PR(4/8/16/…/256);
③ 重装载值 RLR(0~4095)。
公式 T = 4×2^PR×RLR / f_LSI。
Q7. IWDG 最小超时和最大超时分别是多少?
A:
最小:PR=0、RLR=1 → 4×1×1/32000 ≈ 125µs;
最大:PR=6、RLR=4095 → 4×64×4095/32000 ≈ 32.8 秒。
二、寄存器细节
Q8. IWDG_KR 寄存器为什么是"只写"的?写不同值分别做什么?
A:
KR 是命令寄存器,读无意义。
0xCCCC 启动狗;
0xAAAA 喂狗(重载计数器);
0x5555 解锁 PR/RLR 写保护。
Q9. 写 PR 或 RLR 之前为什么必须先写 0x5555?
A:
PR 和 RLR 默认写保护,防止程序跑飞后随机修改看门狗参数。
写 0x5555 解锁一次,写完自动重锁。
这是两步验证机制。
Q10. IWDG_SR 的 PVU 和 RVU 位是什么意思?为什么要等它们清零?
A:
PVU=预分频更新中,RVU=重装载更新中。
写 PR/RLR 后,硬件需要几个 LSI 周期把新值同步到异步域,这期间对应位=1。
不等清零就写会导致配置丢失或错乱。
Q11. IWDG_DR 寄存器是干什么的?
A:
只读,返回当前递减计数器的值。
可以实时观测还剩多少计数,判断喂狗是否及时。
Q12. RCC_CSR 里有哪些复位标志?IWDGRSTF 什么时候置位?
A:
IWDGRSTF(独立狗)、WWDGRSTF(窗口狗)、SFTRSTF(软件复位)、
PORRSTF(上电)、PINRSTF(NRST 引脚)、BORRSTF(低压检测)、
LPWRRSTF(低功耗复位)。
IWDG 计数器减到 0 产生复位时置位。
Q13. 怎么清除复位标志位?
A:写 RCC_CSR 的 RMVF 位(Remove Flag),一次清除所有复位标志。
三、写保护与安全性
Q14. 写保护的设计目的是什么?
A:
防止程序跑飞后 PC 随机写寄存器,意外把 RLR 改大或 PR 改大,导致看门狗超时被拉长甚至失效。
两步解锁(0x5555 + 写配置)让误操作概率极低。
Q15. 为什么 KR 用 0xAAAA、0x5555 这种魔法数字,而不是置某个 bit?
A:
如果设计成"写 KR.bit0=1 就是喂狗",跑飞后随机写寄存器很容易恰好命中。
用 16 位特定值,跑飞后随机写对的概率是 1/65536,大幅降低误喂狗概率。
Q16. IWDG 启动后软件能关闭吗?为什么?
A:
不能。写 KR=0xCCCC 后只能靠系统复位停止。
这是故意设计——防止跑飞后软件"自救"把狗关了,让看门狗失去意义。
Q17. option byte 里配置的"硬件看门狗"是什么意思?
A:
option byte 可以配置 IWDG 为硬件启动模式——上电后看门狗自动启动,不需要软件写 0xCCCC。
这样即使 main 函数跑飞,狗也已经在跑了,可靠性更高。
四、工程实战
Q18. 喂狗应该放在哪里?为什么不能放中断里?
A:
放在主循环末尾,所有关键任务正常跑完才喂。
放中断里的话,主程序死循环但中断还活着,狗永远被喂,等于白装。
Q19. 喂狗间隔和超时时间怎么配比?
A:
超时时间设为最长任务执行时间的 23 倍;喂狗间隔设为超时时间的 1/31/2。
例如超时 2 秒,喂狗 500ms 一次,留足余量。
Q20. LSI 有 ±10% 误差,配置时要注意什么?
A:
LSI 偏快 → 计数器减得快 → 实际超时比标称短。
按"最快 LSI"(f×1.1)反推 RLR,不要按标称值留余量。
要求精确时用 TIM5 输入捕获实测 LSI 频率。
Q21. 调试时为什么会被 IWDG 复位?怎么解决?
A:
CPU halt 时 IWDG 还在计数,没人喂狗就复位。
解决:配置 DBGMCU_APB1FZ 的 DBG_IWDG_STOP 位,调试时冻结 IWDG。
Q22. 低功耗模式(停止/待机)下 IWDG 还跑吗?
A:
跑。LSI 在停止/待机模式下继续振荡。
进入低功耗前要算好超时,否则唤醒时已经被复位。
Q23. 程序启动后怎么知道上次是被看门狗复位的?
A:
读 RCC_CSR 的 IWDGRSTF 位。
为 1 说明本次复位由 IWDG 引起,可以记录日志、进安全模式,然后用 RMVF 清除。
Q24. 喂狗太频繁有问题吗?
A:
IWDG 没有窗口限制,任何时候喂都行,频繁喂不影响功能。
但注意不要因为频繁喂狗而掩盖了"任务偶尔超时"的问题——喂狗间隔要和任务周期匹配。
五、对比与综合
Q25. IWDG 和 WWDG 的核心区别?
A:
① 时钟:IWDG 用 LSI 独立,WWDG 用 APB1 主时钟;
② 喂狗时机:IWDG 任意时刻,WWDG 必须在窗口内(太早也复位);
③ 中断:WWDG 有 EWI 早期唤醒中断,IWDG 没有;
④ 用途:IWDG 防跑飞粗监控,WWDG 监控时序精监控。
Q26. IWDG 和外部硬件看门狗芯片的区别?
A:
内部 IWDG 和 MCU 同生死,MCU 整片挂了它也挂;
外部狗芯片独立供电独立时钟,MCU 完全死机还能拉 NRST。
高可靠场合两个一起上。
Q27. 为什么 WWDG 要"太早喂狗也复位"?
A:
防止跑飞后 PC 随机跳到喂狗代码,意外把狗喂了。
正常主循环喂狗时机稳定落在窗口内;
跑飞后随机喂狗大概率落在窗口外(太早),直接复位。
Q28. WWDG 的 EWI 中断有什么用?
A:
计数器减到窗口上限时触发,在真正复位前给 CPU 抢救窗口——保存现场、记录日志、紧急喂狗、优雅停机。
IWDG 到 0 直接复位,没有这个机会。
六、手撕代码
Q29. 手撕:寄存器版初始化 IWDG,超时 2 秒(LSI=32kHz)。
A:
IWDG->KR = 0x5555;
while(IWDG->SR & IWDG_SR_PVU);
IWDG->PR = 5; // 128 分频 → CK_CNT=250Hz → 4ms
while(IWDG->SR & IWDG_SR_PVU);
IWDG->RLR = 500; // 500×4ms = 2s
while(IWDG->SR & IWDG_SR_RVU);
IWDG->KR = 0xAAAA;
IWDG->KR = 0xCCCC;
Q30. 手撕:喂狗函数 + 复位原因检测。
A:
void IWDG_Feed(void) {
IWDG->KR = 0xAAAA;
}
uint8_t Check_WD_Reset(void) {
if(RCC->CSR & RCC_CSR_IWDGRSTF) {
RCC->CSR |= RCC_CSR_RMVF;
return 1; // 上次被狗复位
}
return 0;
}

网硕互联帮助中心






评论前必须登录!
注册