资讯动态

STM32看门狗详解:IWDG与WWDG配置实战与避坑指南

发布时间:2026/9/1 18:46:05 来源:尧图企业网站定制
简介STM32看门狗教程项目代码是一份面向嵌入式初学者的实操代码包配套讲解基于HAL库和STM32CubeMX配置独立看门狗IWDG与窗口看门狗WWDG。代码包共4个文件包含watchdog_test.py主脚本、index.html说明页、.inscode工程配置及.gitignore辅助文件整体仅7KB结构精简便于直接导入练习。已有54人学习下载。通过该代码包开发者可以对照描述中的工程创建、时钟配置、喂狗函数编写及看门狗中断处理流程结合LED闪烁例程快速验证两种看门狗的工作机制理解IWDG由LSI低速时钟驱动、WWDG由APB1时钟驱动且支持精确喂狗窗口的差异从而在实际项目中利用看门狗防止程序跑飞、提升系统稳定性。 做嵌入式这些年我最常跟新人说的一句话就是你写的代码永远比你自己想象中更容易“死机”。尤其是单片机这种东西跑在现场、跑在设备上旁边一有电机启动、继电器吸合、大功率负载开关电源一抖、干扰一窜程序指针就可能飞到外太空。更麻烦的是芯片本身没坏它还在“似死非死”地运行着灯还亮着、中断还跳着可主逻辑早就卡死了。这种问题靠人盯着根本盯不过来所以芯片设计者们早就留了一手——看门狗。这篇文章就围绕STM32的看门狗展开把我配置IWDG和WWDG的完整思路、代码、计算公式、调试经验一起放出来。如果你是刚入门STM32、或者正在做产品需要过可靠性测试这篇内容应该能帮你少走不少弯路。1. 看门狗到底在防什么程序跑飞与系统的“假活”状态1.1 一个真实的事故屏幕卡死但陀螺仪还在转我以前调过一台带云台的设备现场反馈说机器偶尔“死机”——但神奇的是云台上的陀螺仪数据还在更新指示灯也在按规律闪烁。查了很久才发现问题出在主循环里一段等待标志位的代码上某个外设没有按时返回状态主循环就一直在while里空转而定时器中断照常运行所以看起来“系统还活着”。这是嵌入式系统最典型的失效模式叫假活CPU没死、中断没停、外设还在工作但用户想让它干的事已经完全不执行了。这种状态程序计数器没有跑到非法地址HardFault也没有触发你写再多异常处理代码都抓不到它。看门狗解决的就是这个问题它在芯片内部独立跑一个计数器程序必须定期“喂狗”重装计数器一旦喂狗超时看门狗直接强制复位整个芯片。它不关心你的代码逻辑哪里错了它只知道到你该来喂狗的时候你没来那就说明系统已经不正常了干脆重启。1.2 IWDG与WWDG的核心区别一个只看下限一个连上限都管STM32内置两个看门狗外设一个叫独立看门狗IWDG一个叫窗口看门狗WWDG。虽然都叫看门狗但它们的脾性完全不同。IWDG的机制最简单粗暴计数器从你设置的重装载值往下递减减到0就复位。你在它减到0之前把计数器重新装回初值它就不复位。它只关心“最晚什么时候来喂”不管你是提前喂、按时喂、还是喂得很早只要在超时点之前喂就行。WWDG就不一样。它要求喂狗必须发生在一个窗口期内太早喂不行太晚喂也不行必须在计数器递减到一个窗口值之后、溢出之前这段时间喂狗。也就是说WWDG连你的喂狗节奏都一起监控了如果程序因为某种异常导致喂狗周期发生了变化即使没有超时只要在窗口外喂了照样复位。这个区别很关键后面第3章我会详细展开。1.3 一个必须说清楚的认知看门狗救不了逻辑Bug写代码前先泼一盆冷水**看门狗不是用来兜底程序逻辑Bug的。**如果是因为你写错了某个业务逻辑导致系统进入了错误状态而这个状态本身能正常喂狗、正常运行那看门狗一点忙都帮不上。它防的是“程序跑飞、死循环、外设长时间无响应、系统资源被异常占用导致主流程无法推进”这类故障是给系统兜底用的最后一道防线不是让你放飞自我的理由。2. 独立看门狗IWDG从寄存器到超时计算的完整配置2.1 IWDG的硬件组成与LSI时钟特性IWDG的结构其实非常简单核心就是几个寄存器配合一个递减计数器IWDG_KR键值寄存器负责解锁、喂狗、启动IWDG_PR预分频寄存器IWDG_RLR重装载寄存器IWDG_SR状态寄存器用于判断预分频值和重装载值是否正在更新其中最值得注意的一点是IWDG的时钟源用的是LSI低速内部RC振荡器而不是主时钟。STM32F1系列的LSI典型频率是40kHz。为什么选它因为LSI是独立运行的即使主时钟HSE、HIS或者PLL挂了LSI照样在跑。看门狗的意义就在于“在系统最混乱的时候依然能工作”如果它也用主时钟主时钟一挂它也罢工那这看门狗就形同虚设了。提示LSI本身精度不高手册标称范围一般是30kHz到60kHz实际大多数芯片在40kHz附近。所以IWDG的超时时间别卡得太死给足余量。2.2 超时时间计算公式与实例IWDG的超时时间由预分频系数和重装载值共同决定公式是T (IWDG_RLR值 1) × 预分频系数 / LSI频率等一下这里有个关键细节实际重装载值是IWDG_RLR寄存器里的值加1。比如你写IWDG_SetReload(625)实际计数器初值是626递减到0需要626个计数周期。如果把LSI按40kHz算预分频64那计数频率就是40000 / 64 625Hz一个tick的时间是1.6ms重装载625时实际超时时间大约是626 × 1.6ms ≈ 1.002秒。取整说就是1秒。反过来如果要算某个超时时间对应的重装载值IWDG_RLR 超时秒数 × LSI频率 / 预分频系数 - 1比如目标2秒LSI按40kHz预分频642 × 40000 / 64 - 1 1249写IWDG_SetReload(1249)就是约2秒。2.3 标准库代码实现与喂狗我用的是标准外设库代码很直观void IWDG_Init(void) { // 1. 向键值寄存器写0x5555解除写保护 IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); // 2. 设置预分频系数为64 IWDG_SetPrescaler(IWDG_Prescaler_64); // 3. 设置重装载值625配合LSI约40kHz超时约1秒 IWDG_SetReload(625); // 4. 重装载计数器相当于先喂一次狗 IWDG_ReloadCounter(); // 5. 启动独立看门狗 IWDG_Enable(); }喂狗更简单一行就行void FeedDog(void) { IWDG_ReloadCounter(); }如果你用的HAL库对应的初始化流程也差不多只是把寄存器的操作封装成了HAL_IWDG_Init和HAL_IWDG_Refresh。原理是一样的寄存器级理解清楚之后切什么库都不慌。3. 窗口看门狗WWDG把复位时机也约束在一个区间3.1 窗口期的机制太早喂和太晚喂都算故障WWDG的计数器是7位的最大值0x7F也就是127。它有一个配置寄存器WWDG_CFR里的W[6:0]窗口值还有一个控制寄存器WWDG_CR里的T[6:0]当前计数值。它的规则是计数器从0x7F开始递减当计数值降到小于等于窗口值W之前不允许喂狗这时候如果你写了WWDG_CR芯片立即复位当计数值降到窗口值以下、但还没降到0x40时可以安全喂狗喂狗后计数器重新装载0x7F一旦计数值降到0x3F即小于0x40还没有喂狗芯片立即复位为什么要有“太早喂也算错”的规则因为很多故障场景下程序并不是完全卡死而是陷入了一个高频率的错误循环。如果只用IWDG程序只要保证“每1秒来喂一次狗”哪怕它是卡在一个错误的循环里反复执行也能蒙混过关。WWDG的窗口期强制要求喂狗必须发生在特定时间区间这就把“程序没按正常节奏运行”也纳入了监控范围。3.2 WWDG的时钟链路与时间计算WWDG的时钟来源不是LSI而是APB1总线时钟PCLK1经过一个固定的4096分频再经过WDGTB预分频寄存器分频最后才给到递减计数器。这也是WWDG和IWDG在独立性上的本质差异如果系统主时钟挂了WWDG自己也会停摆。以STM32F103为例库函数默认将PCLK1配置为36MHz假设WDGTB 8计数频率就是Fcnt 36MHz / 4096 / 8 ≈ 1098Hz每个tick约0.91ms。如果我设置窗口值WWDG_SetWindowValue(0x50)也就是80计数值从127递减到80需要127 - 80 47个tick约47 × 0.91 ≈ 42.8ms。也就是说42.8ms之前喂狗直接复位从127递减到64是63个tick约57.3ms也就是说最晚必须在57.3ms前喂狗所以安全喂狗区间就是42.8ms到57.3ms之间。实际项目中通常取中间值或者稍偏早的位置喂比如50ms左右既避开窗口下限又留足安全余量。注意WWDG的W[6:0]窗口值越大允许喂狗的时间段越宽因为计数器要从127递减到W才开始允许喂狗设置得太小窗口会非常窄定时稍有抖动就误复位。3.3 中断喂狗的实现方式WWDG有个很实用的特性它支持早期唤醒中断EWI。当计数器递减到0x40时可以触发一次中断在中断里做业务检查、喂狗。这样可以把“临界时刻”拉回软件可控的范围。标准库代码示例void WWDG_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WDGTB 8分频窗口值0x50计数值初值0x7F WWDG_SetPrescaler(WWDG_Prescaler_8); WWDG_SetWindowValue(0x50); WWDG_Enable(0x7F); // 使能早期唤醒中断 WWDG_ClearFlag(); WWDG_EnableIT(); } void WWDG_IRQHandler(void) { // 到了0x40临界点先清标志位再执行喂狗前的逻辑检查 if (WWDG_GetFlagStatus() ! RESET) { WWDG_ClearFlag(); // 在这里做喂狗前的最终确认 // 比如检查关键任务标志是否都置位 // 都正常再重装载计数器 WWDG_SetCounter(0x7F); } }要不要在中断里喂狗我的建议是可以用但前提是中断里除了喂狗还要检查主流程是否真的健康否则就变成了中断自己给自己续命和我在第4章要讲的问题一样。3.4 IWDG和WWDG的关键参数对比对比项IWDGWWDG时钟源LSI独立RC振荡器PCLK1总线时钟时钟独立性高主时钟挂了还能工作低主时钟挂了它会停摆超时范围可配置从几十毫秒到几十秒较短F1系列通常几十毫秒级别喂狗时机只要求最晚时间要求必须在窗口期内能否检测节奏异常不能能调试冻结需要配置DBGMCU_IWDG_STOP需要配置DBGMCU_WWDG_STOP项目里怎么选我的一般原则系统主时钟可靠性要求高、需要长时间无人值守时用IWDG需要监测任务执行周期、业务节奏可控的场合用WWDG。两者同时用不是不行但喂狗时序容易互相干扰除非你有明确的层次设计否则建议先只用一个。4. 喂狗的正确姿势放主循环还是放任务里4.1 最典型的错误在定时器中断里喂狗这是新手最容易踩的坑。有人觉得“我开一个1ms的定时器中断中断里每隔一段时间喂一次狗这样最稳。”看起来好像很稳但实际上这等于把看门狗废掉了。为什么因为看门狗要保护的恰恰是主循环逻辑。如果你的主循环已经卡死了而定时器中断还在正常触发中断里的喂狗照常执行看门狗永远不会复位系统就一直卡死在错误状态。这就像公司里的安全员每天自己签到但真正的生产环节早就停摆了一样监控了又等于没监控。4.2 按任务流程喂狗的进阶方案我推荐的做法是把喂狗位置放在主循环里而且要放在关键业务流程完成之后。简单版本是这样while (1) { run_task1(); // 采集传感器数据 run_task2(); // 执行控制算法 run_task3(); // 更新显示 // 所有核心任务都跑完一圈才算一次健康周期 IWDG_ReloadCounter(); }进阶版本更适合多任务场景给每个核心任务设置一个完成标志只有所有标志都置位才统一喂狗喂完狗清标志。这样任何一个任务卡死都会导致喂狗失败最终触发复位。uint8_t task1_done 0; uint8_t task2_done 0; uint8_t task3_done 0; while (1) { run_task1(); task1_done 1; run_task2(); task2_done 1; run_task3(); task3_done 1; if (task1_done task2_done task3_done) { IWDG_ReloadCounter(); task1_done task2_done task3_done 0; } }这个方案能防住“某个任务卡死但其他任务正常”的故障代价是喂狗周期变长看门狗超时时间也要相应放宽。如果你的主循环里有阻塞式的延时或者外设等待一定要把最坏情况下的执行时间算进超时时间里别让正常的慢操作触发误复位。4.3 调试时的最大坑一进断点就复位根本没法查IWDG和WWDG默认情况下在调试器暂停时是不会停的。也就是说你在Keil里打断点、单步调试CPU停了但看门狗计数器还在跑几秒后芯片直接复位调试会话直接断掉。解决方法是配置调试单元的冻结位。STM32F1系列在DBGMCU_CR寄存器里提供了两个位DBG_IWDG_STOP和DBG_WWDG_STOP把它们置1调试器暂停时看门狗计数也跟着暂停。标准库写法RCC_APB2PeriphClockCmd(RCC_APB2Periph_DBGMCU, ENABLE); // 调试时冻结IWDG DBGMCU_Config(DBGMCU_IWDG_STOP, ENABLE); // 调试时冻结WWDG DBGMCU_Config(DBGMCU_WWDG_STOP, ENABLE);加这两行之后你就可以放心大胆地打断点了。但有个隐藏问题要注意如果你忘了这个配置直接烧录了带看门狗的程序进调试器一进断点就复位很多人会误以为是代码跑飞了往往折腾半天才发现是看门狗在捣乱。5. 实际项目里踩过的坑不复位、误复位与低功耗冲突5.1 坑一LSI漂移导致IWDG超时严重偏差前面说LSI是RC振荡器精度不高。实际项目中我遇到过目标超时1秒结果实测只有600ms的情况原因就是那颗芯片的LSI偏到了60kHz以上。这种偏差在温度变化大的现场还会继续漂。解决方案有两个思路一是程序里必须算足余量比如我要保证主循环最慢500ms喂一次狗那么IWDG超时时间至少设到1.5秒到2秒不能卡着500ms设二是如果系统对时间精度要求很高、又必须用IWDG可以考虑在系统初始化时用定时器实测一下LSI频率然后动态调整重装载值。不过绝大多数项目用第一种方案就够了。5.2 坑二WWDG复位标志不清程序一直重复复位很多人在STOP或者异常复位后没有检查RCC_CSR寄存器里的复位标志。WWDG复位会置位WWDGRSTF位如果这个标志不清除程序一启动就处于一种“我上次是被看门狗复位的”状态如果你在代码里对这个标志做了判断就可能陷入重复复位的循环。我见过一个很隐蔽的坑程序里用WWDG做软复位板上电没反应后来发现是RCC_CSR里WWDGRSTF一直没清每次启动都走进了一个“发现上次看门狗复位主动停机等待”的分支。排查方法很简单启动时打一下复位标志if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) ! RESET) { // 确实是WWDG导致的复位 // 做对应处理然后清除标志 RCC_ClearFlag(); }RCC_ClearFlag()必须调用否则下次上电标志还挂着。5.3 坑三低功耗模式下IWDG仍然运行唤醒即复位如果你的产品需要进入STOP低功耗模式IWDG的问题就来了。F1系列的IWDG一旦启动即使进入了STOP模式LSI仍然在运行看门狗计数器还在递减。如果你的STOP模式持续时间超过了IWDG超时时间唤醒后CPU会立刻被复位。有些系列的单片机支持把IWDG配置为STOP模式下冻结但F1不支持。处理办法有几个一是进入STOP前不喂狗允许唤醒后复位然后通过复位标志判断是不是从低功耗唤醒回来的做相应恢复二是如果低功耗时间可能超过看门狗周期就用WWDG替代IWDG因为WWDG的时钟来自PCLK1STOP模式下PCLK1停了WWDG也就停了。这里需要特别提醒IWDG启动后没有任何办法软件关闭只有系统复位后才能通过“不复位就不启动”的方式变相关闭。所以你必须在启动IWDG之前想清楚这个系统后续会不会有长休眠的场景。5.4 坑四库函数默认配置与时钟树不一致用标准库或者HAL库时WWDG的初始化依赖PCLK1的实际频率而PCLK1是你在SystemInit或者时钟配置里设的。如果代码里用的是库默认的RCC_PCLK1Config配置实际频率和你计算时用的频率不一致那窗口时间就会完全算错。比如你算的时候以为是36MHz实际PCLK1已经配成72MHz了那计数频率翻倍安全窗口直接缩短一半喂狗稍微慢一点就会触发复位。所以配置WWDG之前先确认时钟树把RCC_GetClocksFreq()打出来看一眼再算时间避免纸上谈兵。我在写WWDG相关代码时习惯在调试初期把窗口算成比较宽的值比如0x5F、0x60等整体跑稳定了再逐步收紧到目标窗口值。这样就算时钟树配置有偏差也不至于一上电就疯狂复位给排查留出空间。最后分享一个我个人的实操习惯量产产品的代码里我通常会拿一个空闲GPIO翻转来显示喂狗状态逻辑上叫“心跳灯”。现场如果出现异常复位通过示波器看这个引脚的翻转频率基本就能判断是主循环卡死还是定时器异常比纯靠猜快得多。看门狗这种东西平时默默无闻关键时刻全靠它拉一把配置的时候多花点心思后面能省下大把排查时间。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价