云计算百科
云计算领域专业知识百科平台

【STM32】 内部看门狗与外部硬件看门狗案例验证实录


  STM32 内部看门狗与外部硬件看门狗案例验证实录   

一、为什么要聊看门狗

设备交付到现场后,最怕的不是功能不对,而是“不回了”:

  • 客户打电话来说设备没反应,只能让用户断电重启;
  • 设备装在铁塔、井盖、偏远的泵站里,根本没法派人去按复位键;
  • OTA 之后偶发死机,实验室里却怎么也复现不出来。

看门狗(Watchdog)就是解决这类问题的最后一道防线:

        当程序失去响应时,强制复位芯片。

        但很多工程师只停留在“开了个 IWDG”的层面,误以为这样就高枕无忧了。

二、先分清三个“看门狗”

特性

IWDG

(独立看门狗)

WWDG

(窗口看门狗)

外部硬件看门狗

(如 TPS3823)

时钟源 独立 LSI(约 32/40kHz) PCLK1 芯片自带 RC 振荡器
喂狗方式 向 KR 写 0xAAAA 在时间窗口内写 CR 在超时前给 WDI 引脚一个电平翻转
喂太快 允许 复位(窗口期喂狗) 通常允许(边沿检测)
喂太慢 复位 复位 复位
程序跑飞后能否被改配置 能(同一颗芯片,寄存器可被误写) 能 不能(物理隔离)
调试器 Halt 时 可选冻结 默认暂停(可配) 照常计时,照样咬人
MCU 时钟/电源异常 时钟坏→失效 时钟坏→失效 电压监控 + 复位输出仍有效
成本 0 0 一颗芯片 + 一个电阻
你想监控什么选谁
主程序死循环、跑飞、卡死 IWDG 或外部狗
主程序时序异常(任务周期不稳定) WWDG
跑飞后恰好跳到喂狗代码 WWDG(窗口机制防这个)
需要复位前做紧急处理 WWDG(EWI 中断)
MCU 整片死机、电源异常 外部狗芯片
主时钟停振 外部狗芯片(WWDG 随主时钟死)
高可靠工业 / 汽车 外部狗芯片(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);
}

复现步骤与预期现象:

注入的故障IWDG 结果TPS3823 结果
主循环 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(最终兜底)

分层逻辑:

  • WWDG(约几十 ms 窗口)​:监控主循环节奏,发现“活着但乱了”的软故障;
  • IWDG(几百 ms)​:WWDG 失效时的第二道内部防线;
  • 外部看门狗(约 1.6s)​:不信任 MCU 内部任何东西的最后防线。
  • 验证方法:

            对每一层分别注入故障(复用案例 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;
    }


    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【STM32】 内部看门狗与外部硬件看门狗案例验证实录
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!