Brilliantwxx头像
【STM32】 内部看门狗与外部硬件看门狗案例验证实录封面图

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


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

一、为什么要聊看门狗

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

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

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

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

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

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

特性

IWDG

(独立看门狗)

WWDG

(窗口看门狗)

外部硬件看门狗

(如 TPS3823)

时钟源独立 LSI(约 32/40kHz)PCLK1芯片自带 RC 振荡器
喂狗方式向 KR 写 0xAAAA在时间窗口内写 CR在超时前给 WDI 引脚一个电平翻转
喂太快允许复位(窗口期喂狗)通常允许(边沿检测)
喂太慢复位复位复位
程序跑飞后能否被改配置能(同一颗芯片,寄存器可被误写)能不能(物理隔离)
调试器 Halt 时可选冻结默认暂停(可配)照常计时,照样咬人
MCU 时钟/电源异常时钟坏→失效时钟坏→失效电压监控 + 复位输出仍有效
成本00一颗芯片 + 一个电阻
你想监控什么选谁
主程序死循环、跑飞、卡死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(最终兜底)

分层逻辑:

  1. WWDG(约几十 ms 窗口)​:监控主循环节奏,发现“活着但乱了”的软故障;
  2. IWDG(几百 ms)​:WWDG 失效时的第二道内部防线;
  3. 外部看门狗(约 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;
}

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2602_95649725/article/details/168971813

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--