
🔥铅笔小新z:个人主页
🎬博客专栏:stm32学习
💫滴水不绝,可穿石;步履不休,能至渊。

STM32 IWDG 独立看门狗:从倒计时原理到启动阶段保护
适用平台:STM32F103ZET6,STM32CubeF1 HAL,裸机工程。寄存器与选项字节说明针对 STM32F1,迁移到其他系列时应重新核对手册。本文参考 DS5792 Rev 13、PM0075 Rev 2 与 AN2606 Rev 70;CubeMX 和 HAL 的本地版本以工程实际使用版本为准。
涉及技术:LSI、IWDG 分频与重装载、HAL 初始化与刷新、复位标志、选项字节、USART1 和系统存储器启动。
最终效果:正常任务完成后刷新看门狗,任务阻塞时自动复位;通过启动日志确认复位来源;对比软件启动和硬件自动启动能覆盖的时间范围。
适合读者:理解 C 语言、指针、基本寄存器操作和串口通信,能够创建、编译并下载一个 STM32 HAL 工程的初学者。
全文沿着一条主线展开:程序必须持续取得有效进展,才能获得下一次刷新看门狗的机会;一旦进展停止,独立运行的硬件倒计时就触发复位。 围绕它讲清六件事:异常与复位、计数与超时、寄存器与写保护、HAL 与喂狗位置、启动方式与选项字节、验证与排错。Flash 擦写算法和完整 Bootloader 协议只作必要说明。
复现说明:本文代码和输出用于说明可执行的实验方案,未在实体开发板上完成编译、下载和测量。带时间的结果均为计算值或预期现象,实际以本机工程和板上测试为准。
一、原理:程序为什么需要一个独立的倒计时
1. 程序还在执行,不代表任务还在推进
数组越界可能破坏返回地址,让 CPU 跳到错误位置;等待条件永远不成立,也可能让程序一直停留在合法的循环里。前一种常被称为"跑飞",后一种常被称为"卡死"。判断它们是否需要恢复,关键在于正常任务是否还能向前推进。
例如,在一个无法被 SysTick 抢占的中断里调用 HAL_Delay(),其等待的毫秒计数可能无法更新,延时也就无法结束。修复方法是调整程序结构和中断设计;看门狗提供的是故障发生后的恢复机会。
可以把 IWDG 想成一只需要定期重新拨满的沙漏。程序完成一轮检查,就重新拨满;如果程序停住,沙漏仍然独立流动,时间用完便触发复位。
回到真实硬件:IWDG 使用 LSI 时钟驱动一个 12 位递减计数器,刷新操作把重装载值装入计数器,超时后向芯片复位逻辑提出复位请求。
#mermaid-svg-KrcErJm4szMtBfzj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KrcErJm4szMtBfzj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KrcErJm4szMtBfzj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KrcErJm4szMtBfzj .error-icon{fill:#552222;}#mermaid-svg-KrcErJm4szMtBfzj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KrcErJm4szMtBfzj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KrcErJm4szMtBfzj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KrcErJm4szMtBfzj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KrcErJm4szMtBfzj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KrcErJm4szMtBfzj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KrcErJm4szMtBfzj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KrcErJm4szMtBfzj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KrcErJm4szMtBfzj .marker.cross{stroke:#333333;}#mermaid-svg-KrcErJm4szMtBfzj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KrcErJm4szMtBfzj p{margin:0;}#mermaid-svg-KrcErJm4szMtBfzj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KrcErJm4szMtBfzj .cluster-label text{fill:#333;}#mermaid-svg-KrcErJm4szMtBfzj .cluster-label span{color:#333;}#mermaid-svg-KrcErJm4szMtBfzj .cluster-label span p{background-color:transparent;}#mermaid-svg-KrcErJm4szMtBfzj .label text,#mermaid-svg-KrcErJm4szMtBfzj span{fill:#333;color:#333;}#mermaid-svg-KrcErJm4szMtBfzj .node rect,#mermaid-svg-KrcErJm4szMtBfzj .node circle,#mermaid-svg-KrcErJm4szMtBfzj .node ellipse,#mermaid-svg-KrcErJm4szMtBfzj .node polygon,#mermaid-svg-KrcErJm4szMtBfzj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KrcErJm4szMtBfzj .rough-node .label text,#mermaid-svg-KrcErJm4szMtBfzj .node .label text,#mermaid-svg-KrcErJm4szMtBfzj .image-shape .label,#mermaid-svg-KrcErJm4szMtBfzj .icon-shape .label{text-anchor:middle;}#mermaid-svg-KrcErJm4szMtBfzj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KrcErJm4szMtBfzj .rough-node .label,#mermaid-svg-KrcErJm4szMtBfzj .node .label,#mermaid-svg-KrcErJm4szMtBfzj .image-shape .label,#mermaid-svg-KrcErJm4szMtBfzj .icon-shape .label{text-align:center;}#mermaid-svg-KrcErJm4szMtBfzj .node.clickable{cursor:pointer;}#mermaid-svg-KrcErJm4szMtBfzj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KrcErJm4szMtBfzj .arrowheadPath{fill:#333333;}#mermaid-svg-KrcErJm4szMtBfzj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KrcErJm4szMtBfzj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KrcErJm4szMtBfzj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KrcErJm4szMtBfzj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KrcErJm4szMtBfzj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KrcErJm4szMtBfzj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KrcErJm4szMtBfzj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KrcErJm4szMtBfzj .cluster text{fill:#333;}#mermaid-svg-KrcErJm4szMtBfzj .cluster span{color:#333;}#mermaid-svg-KrcErJm4szMtBfzj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KrcErJm4szMtBfzj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KrcErJm4szMtBfzj rect.text{fill:none;stroke-width:0;}#mermaid-svg-KrcErJm4szMtBfzj .icon-shape,#mermaid-svg-KrcErJm4szMtBfzj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KrcErJm4szMtBfzj .icon-shape p,#mermaid-svg-KrcErJm4szMtBfzj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KrcErJm4szMtBfzj .icon-shape .label rect,#mermaid-svg-KrcErJm4szMtBfzj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KrcErJm4szMtBfzj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KrcErJm4szMtBfzj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KrcErJm4szMtBfzj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
开始一轮业务处理
执行任务并检查结果
所有关键任务均有进展?
刷新 IWDG
停止刷新
LSI 继续驱动计数
超时触发系统复位
重新启动并记录复位原因
刷新条件应代表业务进展,才能把异常转化成超时。
IWDG 不会理解业务逻辑,也无法判断一次计算是否正确。只要异常路径仍然不断刷新,它就不会超时。复位也不会自动修复错误固件、损坏的硬件或持续存在的外部故障;系统可能反复重启。
知识小结:看门狗通过"有没有及时刷新"间接监督程序。 原因是硬件只能观察刷新行为,不能理解业务结果;关键参数是允许的最长无刷新时间。常见误区是认为 CPU 还在运行就代表系统健康,或者认为有看门狗就可以忽略错误处理。
2. "独立"指的是时钟,不是额外一颗芯片
STM32F103ZET6 片内有 IWDG 和 WWDG。IWDG 是独立看门狗,WWDG 是窗口看门狗。本文使用的是芯片内部的硬件外设。

IWDG 的计数时钟来自 LSI 内部低速 RC 振荡器,不依赖 CPU 主时钟。主时钟异常或程序无法继续执行时,只要 IWDG 所需供电与硬件条件仍然成立,倒计时仍可继续。WWDG 使用 APB1 派生时钟,并要求在规定窗口内刷新,过早、过晚都可能复位。

"独立"不代表精确。 LSI 常按 40 kHz 估算,但它不是固定不变的 40 kHz。DS5792 表 26 在列出的测试条件下给出 30~60 kHz 的范围,40 kHz 为典型值。依据:STM32F103xC/D/E 数据手册,表 26
知识小结:IWDG 的独立性来自 LSI 计数时钟。 它提高了对主时钟和执行流程故障的覆盖能力,但时间精度受 LSI 影响;关键是核对目标芯片的频率范围。常见误区是用系统主频代入超时公式,或把 CubeMX 显示的 40 kHz 当成实测值。
二、参数与硬件:超时时间究竟怎么算
3. 先分清分频值、重装载值与实际计数器
#mermaid-svg-4k0GTMiYUD5PUKpk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4k0GTMiYUD5PUKpk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4k0GTMiYUD5PUKpk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4k0GTMiYUD5PUKpk .error-icon{fill:#552222;}#mermaid-svg-4k0GTMiYUD5PUKpk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4k0GTMiYUD5PUKpk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4k0GTMiYUD5PUKpk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4k0GTMiYUD5PUKpk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4k0GTMiYUD5PUKpk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4k0GTMiYUD5PUKpk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4k0GTMiYUD5PUKpk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4k0GTMiYUD5PUKpk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4k0GTMiYUD5PUKpk .marker.cross{stroke:#333333;}#mermaid-svg-4k0GTMiYUD5PUKpk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4k0GTMiYUD5PUKpk p{margin:0;}#mermaid-svg-4k0GTMiYUD5PUKpk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4k0GTMiYUD5PUKpk .cluster-label text{fill:#333;}#mermaid-svg-4k0GTMiYUD5PUKpk .cluster-label span{color:#333;}#mermaid-svg-4k0GTMiYUD5PUKpk .cluster-label span p{background-color:transparent;}#mermaid-svg-4k0GTMiYUD5PUKpk .label text,#mermaid-svg-4k0GTMiYUD5PUKpk span{fill:#333;color:#333;}#mermaid-svg-4k0GTMiYUD5PUKpk .node rect,#mermaid-svg-4k0GTMiYUD5PUKpk .node circle,#mermaid-svg-4k0GTMiYUD5PUKpk .node ellipse,#mermaid-svg-4k0GTMiYUD5PUKpk .node polygon,#mermaid-svg-4k0GTMiYUD5PUKpk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4k0GTMiYUD5PUKpk .rough-node .label text,#mermaid-svg-4k0GTMiYUD5PUKpk .node .label text,#mermaid-svg-4k0GTMiYUD5PUKpk .image-shape .label,#mermaid-svg-4k0GTMiYUD5PUKpk .icon-shape .label{text-anchor:middle;}#mermaid-svg-4k0GTMiYUD5PUKpk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4k0GTMiYUD5PUKpk .rough-node .label,#mermaid-svg-4k0GTMiYUD5PUKpk .node .label,#mermaid-svg-4k0GTMiYUD5PUKpk .image-shape .label,#mermaid-svg-4k0GTMiYUD5PUKpk .icon-shape .label{text-align:center;}#mermaid-svg-4k0GTMiYUD5PUKpk .node.clickable{cursor:pointer;}#mermaid-svg-4k0GTMiYUD5PUKpk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4k0GTMiYUD5PUKpk .arrowheadPath{fill:#333333;}#mermaid-svg-4k0GTMiYUD5PUKpk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4k0GTMiYUD5PUKpk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4k0GTMiYUD5PUKpk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4k0GTMiYUD5PUKpk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4k0GTMiYUD5PUKpk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4k0GTMiYUD5PUKpk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4k0GTMiYUD5PUKpk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4k0GTMiYUD5PUKpk .cluster text{fill:#333;}#mermaid-svg-4k0GTMiYUD5PUKpk .cluster span{color:#333;}#mermaid-svg-4k0GTMiYUD5PUKpk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4k0GTMiYUD5PUKpk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4k0GTMiYUD5PUKpk rect.text{fill:none;stroke-width:0;}#mermaid-svg-4k0GTMiYUD5PUKpk .icon-shape,#mermaid-svg-4k0GTMiYUD5PUKpk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4k0GTMiYUD5PUKpk .icon-shape p,#mermaid-svg-4k0GTMiYUD5PUKpk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4k0GTMiYUD5PUKpk .icon-shape .label rect,#mermaid-svg-4k0GTMiYUD5PUKpk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4k0GTMiYUD5PUKpk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4k0GTMiYUD5PUKpk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4k0GTMiYUD5PUKpk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} LSI
预分频器
12 位递减计数器
计数结束产生复位请求
IWDG_PR 设置分频
IWDG_RLR 保存重装载值
向 IWDG_KR 写入刷新键
触发重装载
IWDG_SR 显示参数更新状态
RLR 保存下一次重装载所用的值,内部计数器持续变化,两者不是同一个对象。
IWDG 的计数器不能像通用定时器的 CNT 那样直接读写。你配置 PR 和 RLR,再通过 KR 发出启动、解锁或刷新命令。
| 参数或寄存器 | 本实验使用值 | 含义或限制 |
|---|---|---|
| LSI 频率 | 标称 40 kHz | 计算边界时采用数据手册范围 |
预分频系数 P |
32 | 可选 4、8、16、32、64、128、256 |
IWDG_PR.PR[2:0] |
011,数值 3 |
编码 3 对应 32 分频,不能直接写十进制 32 |
IWDG_RLR.RL[11:0] |
1249 | 范围 0~4095 |
| 应用日志串口 | USART1,PA9/PA10 | 默认映射,需确认开发板实际连线 |
| 应用串口格式 | 115200,8N1 | 8 数据位、无校验、1 停止位 |
使用 HAL 时,IWDG_PRESCALER_32 是库提供的编码宏;它的作用是选择 32 分频,不要求你手写 PR 位编码。




3.1 公式中的 +1 不要漏掉
设 LSI 实际频率为 f_LSI,预分频系数为 P,RLR 值为 RL:
text
计数频率:f_cnt = f_LSI / P
计数周期:T_tick = P / f_LSI
按计数周期估算超时:
text
超时时间:T_timeout ≈ P × (RL + 1) / f_LSI
RL 是计数起点的编码,周期数要计入零值对应的计数周期。参考手册的超时表中,RL=0 时仍有一个计数周期,RL=4095 时对应 4096 个周期。不要把"数到零复位"的口头描述直接变成 RL 个周期。
当 P=32、f_LSI=40000 Hz 时,每个计数周期为 0.8 ms。要得到标称 1 秒:
text
RL = (1 × 40000 / 32) - 1 = 1249
若使用 RL=1250,则标称超时为 1000.8 ms。两个值都能用于约 1 秒的演示,但应正确解释对应时间。RL=0 虽是合法值,通常也不适合正常任务刷新。
3.2 为什么 800 ms 刷新仍可能复位
对 P=32、RL=1249,代入 30~60 kHz 的边界:
text
最短超时:T_min = 32 × 1250 / 60000 s ≈ 666.7 ms
最长超时:T_max = 32 × 1250 / 30000 s ≈ 1333.3 ms
所以,"标称 1 秒"不等于每块板子都有 1 秒。每 800 ms 刷新并不能覆盖这个频率范围;每 1100 ms 刷新也不能保证必然超时。
本文用 200 ms 正常任务延时 和 2000 ms 故障延时拉开差距。实际设计应满足:
text
两次刷新之间的最坏耗时 + 时间裕量 < 最短超时时间 T_min
最坏耗时包括任务、日志发送、中断占用和调度延迟。故障检测的最慢时间则要用 T_max 评估。想兼顾更精确的时间,可测量 LSI 并重新核算;参数选择还应覆盖产品工作的温度、电压条件。
知识小结:超时由分频系数、RL+1 和 LSI 实际频率共同决定。 LSI 越快,超时越短;本实验的标称 1 秒对应约 666.7~1333.3 ms 的计算范围。常见误区是混淆 PR 编码与分频系数,或者只按典型频率留刷新余量。
4. KR 与 SR:既要允许修改,也要等修改生效
PR、RLR 有写保护。KR 接收特定键值,分别执行不同动作:
写入 IWDG_KR 的值 |
作用 | 容易混淆的地方 |
|---|---|---|
0x5555 |
开放 PR、RLR 写访问 | 解锁不等于刷新 |
0xAAAA |
将 RLR 装入计数器 | 同时恢复参数写保护 |
0xCCCC |
软件启动 IWDG | 启动后不能在本次运行中普通关闭 |
KR 是命令入口,不能靠读回某个键值来判断"还是否解锁"。写保护降低意外修改配置的风险,但不是能证明软件正确的保护机制。
SR 的 PVU 表示预分频值更新中,RVU 表示重装载值更新中。位由硬件维护,软件读取状态;写访问权限与参数同步完成是两件事。手动修改参数时应遵守手册的忙状态要求,写入后确认更新完成,再按新配置刷新。
跨时钟域同步需要时间,不能仅凭 CPU 已经执行完赋值语句,就认定 LSI 侧参数已生效。
知识小结:KR 控制操作权限和命令,SR 反映参数同步状态。 关键是分清 0x5555、0xAAAA、0xCCCC,以及 PVU/RVU。常见误区是把解锁当成刷新,或认为修改 RLR 就已经把正在递减的计数器重新装载。
三、配置:先建立能观察结果的最小环境
1. 硬件连接
准备 STM32F103ZET6 开发板、下载器、USB 转串口工具及串口终端。若开发板已有连接 USART1 的 USB 串口芯片,可直接使用对应端口。
使用外部 3.3 V TTL 串口模块时:模块 RX 接 PA9,模块 TX 接 PA10,GND 共地。确认板上跳线未让两个发送器同时驱动同一线路。BOOT0 正常运行时置低;进入系统存储器时才改变启动配置。
2. CubeMX 配置
- 选择
STM32F103ZET6,开启SYS → Debug → Serial Wire。 - 本文完整示例使用 HSI 8 MHz、AHB/APB 均不分频、不启用 PLL,减少对板载晶振的依赖。IWDG 始终使用 LSI。
- 开启 USART1 异步模式,默认 PA9/PA10,设为
115200、8 Bits、Parity None、1 Stop Bit。 - 激活 IWDG,选择 32 分频、Reload 为 1249。
- 保留默认 SysTick 毫秒时基;代码生成选择将初始化函数放在
main.c,不为每个外设单独生成一对.c/.h文件。 - 生成适合本机 IDE 的工程,确认
HAL_IWDG_MODULE_ENABLED已开启且 IWDG 驱动参与编译。
若沿用 HSE 8 MHz、PLL ×9、SYSCLK 72 MHz 的现有工程,可以保留已验证的时钟配置。主时钟变化影响串口等外设的配置,但不把 IWDG 变成 72 MHz 计数。
这里特意统一应用日志为 8N1 。若已有工程使用 UART_WORDLENGTH_9B + UART_PARITY_ODD,有效数据为 8 位加奇校验,电脑端应选择 8O1。两套设置均可通信,但必须两端一致;Bootloader 使用的格式另见后文。
3. 调整初始化顺序
本实验先初始化串口、打印启动标志,再进入 IWDG 初始化,以便观察"启动之前卡住"的对照实验。它刻意保留了软件启动模式的监控空白。
实际产品可以把软件启动 IWDG 的位置提前,随后让耗时初始化按阶段检查并刷新。保护从看门狗实际启动的时刻开始,并非一定要等整个初始化函数成功返回。
四、代码:从一次刷新到完整实验
1. 最小示例
下面片段放在 CubeMX 已生成的工程中:已完成 HAL_Init()、时钟初始化,存在 hiwdg,SysTick 正常工作。它用于先理解主线,完整错误处理见后面的 main.c。
c
/* 设置的是分频选择宏,而不是把十进制 32 直接写进 PR。 */
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_32;
hiwdg.Init.Reload = 1249U; /* 按 40 kHz 计算,1250 个周期约为 1 秒。 */
if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
{
Error_Handler(); /* 初始化失败时进入故障路径,不能假装保护已就绪。 */
}
while (1)
{
HAL_Delay(200U); /* 模拟一轮业务耗时;真实项目应检查任务结果。 */
HAL_IWDG_Refresh(&hiwdg); /* 在任务完成之后刷新,让任务卡住能够被发现。 */
}
把 200U 改为 2000U,任务将超过前面计算的最长超时,预计在下一次刷新之前复位。不要用无限增加刷新次数的办法掩盖任务异常。
2. 读懂两个 HAL 接口
c
/* 初始化并启动 IWDG;参数指向包含外设地址和配置的句柄。 */
HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg);
/* 刷新已配置的 IWDG;它不检查业务是否健康。 */
HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);
句柄中,Instance 指向 IWDG 寄存器组;Init.Prescaler 和 Init.Reload 保存配置。它没有保存一个可供读取的"当前剩余时间"。
初始化的核心流程为:检查句柄和参数,写启动键,开放配置写访问,设置 PR/RLR,等待更新标志清零,最后刷新并返回。空句柄可返回 HAL_ERROR;等待参数更新超时可返回 HAL_TIMEOUT;合法配置成功返回 HAL_OK。部分参数检查通过 assert_param 实现,是否生效取决于工程断言配置。
IWDG HAL 初始化 应用 IWDG HAL 初始化 应用 #mermaid-svg-WRNqYczbngAKYiW3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WRNqYczbngAKYiW3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WRNqYczbngAKYiW3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WRNqYczbngAKYiW3 .error-icon{fill:#552222;}#mermaid-svg-WRNqYczbngAKYiW3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WRNqYczbngAKYiW3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WRNqYczbngAKYiW3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WRNqYczbngAKYiW3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WRNqYczbngAKYiW3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WRNqYczbngAKYiW3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WRNqYczbngAKYiW3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WRNqYczbngAKYiW3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WRNqYczbngAKYiW3 .marker.cross{stroke:#333333;}#mermaid-svg-WRNqYczbngAKYiW3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WRNqYczbngAKYiW3 p{margin:0;}#mermaid-svg-WRNqYczbngAKYiW3 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-WRNqYczbngAKYiW3 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-WRNqYczbngAKYiW3 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-WRNqYczbngAKYiW3 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-WRNqYczbngAKYiW3 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-WRNqYczbngAKYiW3 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-WRNqYczbngAKYiW3 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-WRNqYczbngAKYiW3 .sequenceNumber{fill:white;}#mermaid-svg-WRNqYczbngAKYiW3 #sequencenumber{fill:#333;}#mermaid-svg-WRNqYczbngAKYiW3 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-WRNqYczbngAKYiW3 .messageText{fill:#333;stroke:none;}#mermaid-svg-WRNqYczbngAKYiW3 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-WRNqYczbngAKYiW3 .labelText,#mermaid-svg-WRNqYczbngAKYiW3 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-WRNqYczbngAKYiW3 .loopText,#mermaid-svg-WRNqYczbngAKYiW3 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-WRNqYczbngAKYiW3 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-WRNqYczbngAKYiW3 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-WRNqYczbngAKYiW3 .noteText,#mermaid-svg-WRNqYczbngAKYiW3 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-WRNqYczbngAKYiW3 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-WRNqYczbngAKYiW3 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-WRNqYczbngAKYiW3 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-WRNqYczbngAKYiW3 .actorPopupMenu{position:absolute;}#mermaid-svg-WRNqYczbngAKYiW3 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-WRNqYczbngAKYiW3 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-WRNqYczbngAKYiW3 .actor-man circle,#mermaid-svg-WRNqYczbngAKYiW3 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-WRNqYczbngAKYiW3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop 等待同步或超时 传入句柄和配置 写启动键 0xCCCC 写解锁键 0x5555 写 PR 与 RLR 读取 SR PVU 与 RVU 写刷新键 0xAAAA HAL_OK
启动动作早于函数返回,初始化失败后也不能直接假定看门狗未运行。
刷新函数的关键动作就是向 KR 写入刷新键。所核对的 HAL 实现随后返回 HAL_OK,并不会测量剩余时间,也不验证任务结果;必须传入有效句柄。依据:ST 官方 HAL IWDG 驱动源码
3. 刷新应该放在哪里
在简单裸机程序里,可以在一轮关键任务完成后刷新。如果通信任务或采样任务没有按要求完成,应进入错误处理并停止刷新,而不是为了避免复位继续刷新。
不要无条件放进 SysTick 或周期定时器中断:主循环已经卡住时,中断可能继续运行,刷新也继续发生,看门狗便无法检测这类故障。多个任务的系统可由监控逻辑检查每个任务在本轮是否都取得进展,再决定是否刷新;标志必须按监控周期更新,不能只在启动时置一次。
知识小结:HAL 负责操作 IWDG,应用负责证明任务健康。 初始化会启动计数,刷新只执行重装载;关键条件是所有受监控任务在时限内有有效进展。常见误区是在中断里无条件刷新,或把 HAL_OK 理解成整个系统运行正常。
4. 完整实验用 main.c
以下是基于前述 CubeMX 工程的完整应用入口,包含时钟、GPIO、串口、IWDG 配置、日志、复位来源和故障注入。它依赖生成工程的启动文件、链接脚本、CMSIS、HAL、stm32f1xx_hal_msp.c 和 stm32f1xx_it.c,不是脱离工程即可单独编译的 C 文件。
用它替换生成工程的 main.c;保留生成的中断文件,其中 SysTick_Handler() 应调用 HAL_IncTick()。如果原工程单独生成了 usart.c、iwdg.c,请先按上文重新生成统一布局,避免句柄和初始化函数重复定义。后续重新生成代码前应备份本文件。
DEMO_MODE 取 0~3:0 正常运行;1 每轮耗时 2 秒;2 三轮正常后进入死循环;3 在 IWDG 初始化之前等待 10 秒。
c
#include "main.h" /* 使用生成工程提供的 HAL 配置和芯片定义。 */
#include <stdint.h>
#include <string.h>
#define DEMO_MODE 0U /* 每次只选择一种实验,便于比较日志。 */
#if DEMO_MODE > 3U
#error DEMO_MODE_must_be_0_to_3 /* 拒绝无效实验编号,避免误判现象。 */
#endif
UART_HandleTypeDef huart1; /* 与生成的 MSP 初始化共用 USART1 句柄。 */
IWDG_HandleTypeDef hiwdg; /* 句柄必须在整个程序运行期间保持有效。 */
void SystemClock_Config(void);
static void App_UART_Init(void);
static void App_IWDG_Init(void);
static void Log_Text(const char *text);
int main(void)
{
/* 尽早快照复位标志,避免后续清标志使启动原因丢失。 */
const uint32_t reset_csr = RCC->CSR;
uint32_t round = 0U;
HAL_Init(); /* 初始化 HAL 和默认毫秒时基。 */
SystemClock_Config(); /* 使用内部 HSI,避免依赖板载晶振参数。 */
App_UART_Init(); /* 先建立日志通路,便于观察初始化前故障。 */
if ((reset_csr & RCC_CSR_IWDGRSTF) != 0U)
{
Log_Text("[BOOT] IWDG reset flag set\r\n");
}
else
{
Log_Text("[BOOT] IWDG reset flag clear\r\n");
}
/* 复位标志可以同时出现;这里只报告 IWDG 位,不作唯一原因推断。 */
__HAL_RCC_CLEAR_RESET_FLAGS();
#if DEMO_MODE == 3U
Log_Text("[INIT] before 10 s wait\r\n");
HAL_Delay(10000U); /* 故意放在软件启动 IWDG 之前作对照。 */
Log_Text("[INIT] after 10 s wait\r\n");
#endif
App_IWDG_Init(); /* 软件模式在这里启动,硬件模式在这里重配。 */
Log_Text("[IWDG] configured\r\n");
while (1)
{
#if DEMO_MODE == 2U
if (round >= 3U)
{
Log_Text("[FAULT] main loop stuck\r\n");
while (1)
{
__NOP(); /* 保持 CPU 执行,但不再让业务推进或刷新。 */
}
}
#endif
#if DEMO_MODE == 1U
HAL_Delay(2000U); /* 大于计算出的最长超时,预期无法走到刷新。 */
#else
HAL_Delay(200U); /* 模拟任务耗时,不作为真实健康检测算法。 */
#endif
/* 本实验延时完成代表一轮结束;实际项目应先检查业务结果。 */
if (HAL_IWDG_Refresh(&hiwdg) != HAL_OK)
{
Error_Handler();
}
round++;
Log_Text("[FEED] task round complete\r\n");
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef osc = {0};
RCC_ClkInitTypeDef clk = {0};
osc.OscillatorType = RCC_OSCILLATORTYPE_HSI;
osc.HSIState = RCC_HSI_ON;
osc.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
osc.PLL.PLLState = RCC_PLL_NONE; /* 正常复位后 PLL 未开启,无需使用倍频。 */
if (HAL_RCC_OscConfig(&osc) != HAL_OK)
{
Error_Handler();
}
clk.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
clk.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
clk.AHBCLKDivider = RCC_SYSCLK_DIV1;
clk.APB1CLKDivider = RCC_HCLK_DIV1;
clk.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
static void App_UART_Init(void)
{
GPIO_InitTypeDef gpio = {0};
/* 显式配置默认引脚,使日志不依赖自定义板级串口重定向函数。 */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_USART1_CLK_ENABLE();
gpio.Pin = GPIO_PIN_9;
gpio.Mode = GPIO_MODE_AF_PP;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio);
gpio.Pin = GPIO_PIN_10;
gpio.Mode = GPIO_MODE_INPUT;
gpio.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &gpio);
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200U;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
static void App_IWDG_Init(void)
{
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_32;
hiwdg.Init.Reload = 1249U; /* 按典型 LSI 频率配置标称 1 秒。 */
if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
{
Error_Handler(); /* 初始化可能已启动 IWDG,失败后停止刷新。 */
}
}
static void Log_Text(const char *text)
{
/* 本实验只传入短字符串;有限超时避免日志永久阻塞业务。 */
const size_t length = strlen(text);
if (HAL_UART_Transmit(&huart1, (uint8_t *)text,
(uint16_t)length, 20U) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
__disable_irq(); /* 保留故障现场;已启动的 IWDG 继续硬件计数。 */
while (1)
{
__NOP(); /* 故障路径不刷新,等待复位;未启动则会停在此处。 */
}
}
这里直接使用 HAL_UART_Transmit(),无需额外实现 printf 重定向。20 ms 是日志调用等待上限,仍依赖正常 HAL 时基;如果 SysTick 停止,IWDG 的硬件计时仍可提供已启动后的恢复路径。
5. 编译和下载
在生成工程的 IDE 中执行构建,确认无重复符号、无缺失 HAL 驱动;通过原有 SWD 下载器下载程序。STM32CubeIDE 的编译产物通常位于工程 Debug 或 Release 目录,可通过下载配置运行;其他 IDE 使用其对应构建和下载入口。
保持 BOOT0=0,断开调试会话后按复位键,使用串口观察独立运行。先验证模式 0,再依次尝试其他模式。若启用 IDE 的半主机功能,关闭依赖调试器的日志路径,以免断开调试器时出现额外故障。
五、验证:把正常、超时和启动空白分别观察清楚
1. 正常刷新与运行阶段故障
先保持 WDG_SW=1,使用软件启动模式。模式 0 的预期日志:
text
[BOOT] IWDG reset flag clear
[IWDG] configured
[FEED] task round complete
[FEED] task round complete
...
若上一轮确实由 IWDG 复位,第一行也可能为 set。关键是之后持续出现任务完成日志,而启动日志没有周期性重现。
模式 1 的预期结果:打印已配置,进入 2 秒等待,在到达第一次刷新前复位。后续启动可看到:
text
[BOOT] IWDG reset flag set
[IWDG] configured
[BOOT] IWDG reset flag set
[IWDG] configured
...
模式 2 预计先出现三次任务完成,再打印故障信息,随后重新启动。这个实验说明:CPU 在循环里持续执行 NOP,并不妨碍 IWDG 超时。
确认复位应同时看启动日志和 RCC_CSR.IWDGRSTF。复位标志会累积且可能有多位为真,因此先快照,再清除,不能仅凭"串口重新打印"就断言复位来源。
2. 软件启动的监控空白
模式 3 在 App_IWDG_Init() 前等待 10 秒。软件启动模式下,预期先看到 before,约 10 秒后看到 after,再完成 IWDG 配置。将有限等待换成无限等待,它便会一直停在那里,因为此时 IWDG 尚未启动。
准确结论是:软件启动无法保护实际启动之前的代码。 若先启动 IWDG,再初始化其他模块,后续初始化阶段也可以被监督。不能笼统地认为所有初始化过程都无法受到软件启动 IWDG 的保护。
3. 硬件自动启动:同一个 IWDG,从更早的时刻开始计数
"软件看门狗""硬件看门狗"在这里指 同一个片内 IWDG 的两种启动方式:
- 软件启动:选项位
WDG_SW=1,程序写启动键后计数。 - 硬件启动:选项位
WDG_SW=0,系统复位后由硬件自动启动。
这与操作系统中纯软件定时监控的含义不同,也不是从一个看门狗切换到另一只看门狗。应用后续调用 HAL,是重新配置并继续刷新同一个外设。
#mermaid-svg-4QRo4pwu7ZPtWp1y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4QRo4pwu7ZPtWp1y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4QRo4pwu7ZPtWp1y .error-icon{fill:#552222;}#mermaid-svg-4QRo4pwu7ZPtWp1y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4QRo4pwu7ZPtWp1y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4QRo4pwu7ZPtWp1y .marker.cross{stroke:#333333;}#mermaid-svg-4QRo4pwu7ZPtWp1y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4QRo4pwu7ZPtWp1y p{margin:0;}#mermaid-svg-4QRo4pwu7ZPtWp1y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4QRo4pwu7ZPtWp1y .cluster-label text{fill:#333;}#mermaid-svg-4QRo4pwu7ZPtWp1y .cluster-label span{color:#333;}#mermaid-svg-4QRo4pwu7ZPtWp1y .cluster-label span p{background-color:transparent;}#mermaid-svg-4QRo4pwu7ZPtWp1y .label text,#mermaid-svg-4QRo4pwu7ZPtWp1y span{fill:#333;color:#333;}#mermaid-svg-4QRo4pwu7ZPtWp1y .node rect,#mermaid-svg-4QRo4pwu7ZPtWp1y .node circle,#mermaid-svg-4QRo4pwu7ZPtWp1y .node ellipse,#mermaid-svg-4QRo4pwu7ZPtWp1y .node polygon,#mermaid-svg-4QRo4pwu7ZPtWp1y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4QRo4pwu7ZPtWp1y .rough-node .label text,#mermaid-svg-4QRo4pwu7ZPtWp1y .node .label text,#mermaid-svg-4QRo4pwu7ZPtWp1y .image-shape .label,#mermaid-svg-4QRo4pwu7ZPtWp1y .icon-shape .label{text-anchor:middle;}#mermaid-svg-4QRo4pwu7ZPtWp1y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4QRo4pwu7ZPtWp1y .rough-node .label,#mermaid-svg-4QRo4pwu7ZPtWp1y .node .label,#mermaid-svg-4QRo4pwu7ZPtWp1y .image-shape .label,#mermaid-svg-4QRo4pwu7ZPtWp1y .icon-shape .label{text-align:center;}#mermaid-svg-4QRo4pwu7ZPtWp1y .node.clickable{cursor:pointer;}#mermaid-svg-4QRo4pwu7ZPtWp1y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4QRo4pwu7ZPtWp1y .arrowheadPath{fill:#333333;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4QRo4pwu7ZPtWp1y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4QRo4pwu7ZPtWp1y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4QRo4pwu7ZPtWp1y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4QRo4pwu7ZPtWp1y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4QRo4pwu7ZPtWp1y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4QRo4pwu7ZPtWp1y .cluster text{fill:#333;}#mermaid-svg-4QRo4pwu7ZPtWp1y .cluster span{color:#333;}#mermaid-svg-4QRo4pwu7ZPtWp1y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4QRo4pwu7ZPtWp1y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4QRo4pwu7ZPtWp1y rect.text{fill:none;stroke-width:0;}#mermaid-svg-4QRo4pwu7ZPtWp1y .icon-shape,#mermaid-svg-4QRo4pwu7ZPtWp1y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4QRo4pwu7ZPtWp1y .icon-shape p,#mermaid-svg-4QRo4pwu7ZPtWp1y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4QRo4pwu7ZPtWp1y .icon-shape .label rect,#mermaid-svg-4QRo4pwu7ZPtWp1y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4QRo4pwu7ZPtWp1y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4QRo4pwu7ZPtWp1y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4QRo4pwu7ZPtWp1y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1
0
系统复位并装载选项字节
WDG_SW 值
等待应用写启动键
应用启动前存在监控空白
应用配置 IWDG
硬件自动启动 IWDG
启动阶段即受计时约束
按业务健康状态决定刷新
两种方式改变保护起点,后续仍由同一套寄存器控制。
硬件启动的默认参数是 PR=0,即 4 分频,RLR=0xFFF。按典型 40 kHz 计算:
text
启动默认超时 ≈ 4 × 4096 / 40000 s = 409.6 ms
按前述 LSI 边界计算,约为 273.1~546.1 ms 。这段时间从硬件开始计数时累计,已经包含进入 main() 之前的启动代码耗时。应用要及时配置或刷新,不能等串口、显示屏、外部存储全部慢慢初始化完再处理。
3.1 选项字节地址要按字节看
STM32F103ZET6 的选项字节区域是 0x1FFFF800~0x1FFFF80F。以 0x1FFFF800 为起点按 32 位字查看时,排列为:低 8 位 RDP,接着 nRDP,再是 USER,最高 8 位 nUSER。

所以 WDG_SW 是 USER 字节的 bit 0 ,对应字节地址 0x1FFFF802 ;在上述 32 位字中它才是 bit 16 。一个字节只有 bit 0~7,不能称作"第一个字节的 bit 16"。FLASH_OBR 是装载结果寄存器,又有自己的位布局。依据:PM0075,表 8、表 9
选项字节具有配套反码及专用编程流程,不应通过普通指针赋值修改。这里使用编程工具设置逻辑选项。
3.2 用 CubeProgrammer 开启硬件启动
先完成模式 0 的软件启动实验,再改选项字节。若有受 STM32CubeProgrammer 支持的 ST-LINK,可通过 SWD 配置;若现有下载器不受该工具支持,可使用 USART1 Bootloader 通路。
串口通路步骤:
- 关闭应用串口终端,释放端口;连接 PA9/PA10 和共地。
- 令 BOOT0=1、BOOT1=0,复位进入系统存储器。BOOT1 对应 PB2,应确认板上电平。
- 在 CubeProgrammer 选择 UART 和实际串口号。可先使用
57600、8E1、Flow Control Off;端口号随电脑变化。 - 连接后读取 Option Bytes,保存原有选项配置;在 User Configuration 中找到
WDG_SW,确认界面描述,再取消勾选使其为 0,应用并读回确认。 - 恢复 BOOT0=0,再复位进入用户程序。
应用日志使用 115200/8N1;ROM Bootloader 通信使用偶校验,这两套格式不要混用。 AN2606 的 STM32F10xxx 章节列出 USART1 的 PA9/PA10 和 8E1,并说明已硬件启用的 IWDG 会由 Bootloader 配置分频并周期刷新。依据:AN2606,第 21 章
改选项字节时只调整目标选项,保持读保护、写保护等其他选项不变。尤其不要为连接问题随意切换读保护状态,解除读保护可能涉及主 Flash 擦除。
3.3 为什么要理解 Bootloader
对本型号,主 Flash 用于存放应用,系统存储器保存 ST 固化的 Bootloader,选项字节保存持久设置。正常从主 Flash 启动时,CPU 直接从所选启动映射取向量,并不是必须先运行 ROM Bootloader 再跳应用。
#mermaid-svg-2MhtXMU5AnSJKWFd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2MhtXMU5AnSJKWFd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2MhtXMU5AnSJKWFd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2MhtXMU5AnSJKWFd .error-icon{fill:#552222;}#mermaid-svg-2MhtXMU5AnSJKWFd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2MhtXMU5AnSJKWFd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2MhtXMU5AnSJKWFd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2MhtXMU5AnSJKWFd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2MhtXMU5AnSJKWFd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2MhtXMU5AnSJKWFd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2MhtXMU5AnSJKWFd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2MhtXMU5AnSJKWFd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2MhtXMU5AnSJKWFd .marker.cross{stroke:#333333;}#mermaid-svg-2MhtXMU5AnSJKWFd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2MhtXMU5AnSJKWFd p{margin:0;}#mermaid-svg-2MhtXMU5AnSJKWFd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2MhtXMU5AnSJKWFd .cluster-label text{fill:#333;}#mermaid-svg-2MhtXMU5AnSJKWFd .cluster-label span{color:#333;}#mermaid-svg-2MhtXMU5AnSJKWFd .cluster-label span p{background-color:transparent;}#mermaid-svg-2MhtXMU5AnSJKWFd .label text,#mermaid-svg-2MhtXMU5AnSJKWFd span{fill:#333;color:#333;}#mermaid-svg-2MhtXMU5AnSJKWFd .node rect,#mermaid-svg-2MhtXMU5AnSJKWFd .node circle,#mermaid-svg-2MhtXMU5AnSJKWFd .node ellipse,#mermaid-svg-2MhtXMU5AnSJKWFd .node polygon,#mermaid-svg-2MhtXMU5AnSJKWFd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2MhtXMU5AnSJKWFd .rough-node .label text,#mermaid-svg-2MhtXMU5AnSJKWFd .node .label text,#mermaid-svg-2MhtXMU5AnSJKWFd .image-shape .label,#mermaid-svg-2MhtXMU5AnSJKWFd .icon-shape .label{text-anchor:middle;}#mermaid-svg-2MhtXMU5AnSJKWFd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2MhtXMU5AnSJKWFd .rough-node .label,#mermaid-svg-2MhtXMU5AnSJKWFd .node .label,#mermaid-svg-2MhtXMU5AnSJKWFd .image-shape .label,#mermaid-svg-2MhtXMU5AnSJKWFd .icon-shape .label{text-align:center;}#mermaid-svg-2MhtXMU5AnSJKWFd .node.clickable{cursor:pointer;}#mermaid-svg-2MhtXMU5AnSJKWFd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2MhtXMU5AnSJKWFd .arrowheadPath{fill:#333333;}#mermaid-svg-2MhtXMU5AnSJKWFd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2MhtXMU5AnSJKWFd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2MhtXMU5AnSJKWFd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2MhtXMU5AnSJKWFd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2MhtXMU5AnSJKWFd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2MhtXMU5AnSJKWFd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2MhtXMU5AnSJKWFd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2MhtXMU5AnSJKWFd .cluster text{fill:#333;}#mermaid-svg-2MhtXMU5AnSJKWFd .cluster span{color:#333;}#mermaid-svg-2MhtXMU5AnSJKWFd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2MhtXMU5AnSJKWFd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2MhtXMU5AnSJKWFd rect.text{fill:none;stroke-width:0;}#mermaid-svg-2MhtXMU5AnSJKWFd .icon-shape,#mermaid-svg-2MhtXMU5AnSJKWFd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2MhtXMU5AnSJKWFd .icon-shape p,#mermaid-svg-2MhtXMU5AnSJKWFd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2MhtXMU5AnSJKWFd .icon-shape .label rect,#mermaid-svg-2MhtXMU5AnSJKWFd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2MhtXMU5AnSJKWFd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2MhtXMU5AnSJKWFd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2MhtXMU5AnSJKWFd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} BOOT0 为 0
BOOT0 为 1 且 BOOT1 为 0
BOOT0 为 1 且 BOOT1 为 1
系统复位
硬件加载选项设置
启动引脚组合
主 Flash 应用
系统存储器中的 ROM Bootloader
SRAM 启动
USART1 接收编程命令
启动来源的选择与 IWDG 硬件自动启动是不同机制。
对 STM32F103ZET6,主 Flash 为 0x08000000~0x0807FFFF,系统存储器为 0x1FFFF000~0x1FFFF7FF;这些地址用于理解本型号的映射,不能直接套用其他芯片。
3.4 对比启动阶段现象
保持模式 3,再启用硬件启动。预计会反复出现启动信息和 before 10 s wait,无法到达 after。若串口初始化本身尚未完成就超时,也可能看不到日志,此时应观察复位信号或使用调试器定位。
随后改回模式 0:只要启动和初始化及时完成,就会按重新配置的参数正常刷新。实验结束后,通过同一工具将 WDG_SW 恢复为 1,应用并读回、复位确认;软件仍会在调用初始化时启动 IWDG。
知识小结:硬件启动把同一个 IWDG 的监控起点提前到系统启动阶段。 关键是 WDG_SW=0、默认 4 分频与 0xFFF 重装载,以及进入应用前已经消耗的时间。常见误区是把硬件启动当成换用另一外设,或认为删掉初始化代码就能取消选项字节中的自动启动设置。
六、排错:先确认是不是 IWDG,再检查时间和刷新路径
1. 正常任务也周期复位
现象:模式 0 仍反复出现启动日志。
可能原因:实际用了 800 ms 刷新、配置没有生效、日志或其他任务阻塞、选项字节仍启用硬件启动,或启动阶段已超时。
确认方法 :先看 IWDG 复位标志;检查实际编译的 Prescaler、Reload 和宏;观察启动日志停在哪里;在调试器中查看 PR、RLR、SR,以及工具读回的 WDG_SW。
修复:按最快 LSI 核算最短超时,为全部正常路径留余量;将长初始化拆成可检查的阶段。不要单纯把超时改得极大来掩盖问题。
2. 主循环卡住却一直不复位
可能原因:IWDG 尚未启动,或某个中断、其他任务仍在无条件刷新;也可能调试器暂停时冻结了 IWDG。
确认方法 :搜索全部 HAL_IWDG_Refresh、重装载宏和 KR 写入位置;确认故障发生在初始化前还是后;脱离调试会话复测。
修复:统一刷新决策,让受监控任务全部有进展才能刷新;调整启动位置。调试冻结适合单步观察,但验证独立运行时要排除它的影响。
3. 一打断点就复位,或者调试时永远不复位
CPU 暂停时,LSI 可能继续运行。IWDG 是否随调试暂停,受 DBGMCU 的冻结配置影响;可使用 HAL 的冻结/解除冻结宏,但该设置服务于调试,不能当作正式停用接口。依据:ST HAL IWDG 驱动的调试模式说明
分别记录脱离调试器和进入调试会话时的结果,再判断问题。
4. 串口乱码或没有日志
确认顺序:检查共地与 TX/RX 交叉连接、PA9/PA10 映射、电脑端口是否被占用、应用端和电脑端的波特率/校验是否一致、时钟配置是否匹配。
如果用的是本文完整代码,应用端为 115200/8N1;ROM 通路为 8E1。无日志还可能是程序在串口初始化前复位,不能仅凭无日志判断看门狗没工作。
5. 修改选项字节后,其他程序也不断重启
先读回 WDG_SW。若为 0,硬件启动设置会跨程序下载保留;新固件没有及时刷新就会复位。用支持的 SWD 连接方式或本型号系统存储器 Bootloader 恢复选项,恢复 BOOT0 后复测。
若 SWD 因连续复位难以连接,可在下载器和接线支持时尝试 Connect Under Reset,确保连接 NRST。切换串口 Bootloader 通路前,确认 BOOT0/BOOT1 组合和复位时机。
6. HAL 初始化等待不结束
检查 SR 更新标志和 HAL 时基。HAL 的等待超时依赖 HAL_GetTick();SysTick 未递增时,软件超时判断也可能无法推进。已启动的 IWDG 仍可能先触发复位,因此要记录初始化卡点,并检查中断屏蔽、时基实现和本地 HAL 版本。
#mermaid-svg-5Bgdko9klKYOFnHf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5Bgdko9klKYOFnHf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5Bgdko9klKYOFnHf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5Bgdko9klKYOFnHf .error-icon{fill:#552222;}#mermaid-svg-5Bgdko9klKYOFnHf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5Bgdko9klKYOFnHf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5Bgdko9klKYOFnHf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5Bgdko9klKYOFnHf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5Bgdko9klKYOFnHf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5Bgdko9klKYOFnHf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5Bgdko9klKYOFnHf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5Bgdko9klKYOFnHf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5Bgdko9klKYOFnHf .marker.cross{stroke:#333333;}#mermaid-svg-5Bgdko9klKYOFnHf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5Bgdko9klKYOFnHf p{margin:0;}#mermaid-svg-5Bgdko9klKYOFnHf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5Bgdko9klKYOFnHf .cluster-label text{fill:#333;}#mermaid-svg-5Bgdko9klKYOFnHf .cluster-label span{color:#333;}#mermaid-svg-5Bgdko9klKYOFnHf .cluster-label span p{background-color:transparent;}#mermaid-svg-5Bgdko9klKYOFnHf .label text,#mermaid-svg-5Bgdko9klKYOFnHf span{fill:#333;color:#333;}#mermaid-svg-5Bgdko9klKYOFnHf .node rect,#mermaid-svg-5Bgdko9klKYOFnHf .node circle,#mermaid-svg-5Bgdko9klKYOFnHf .node ellipse,#mermaid-svg-5Bgdko9klKYOFnHf .node polygon,#mermaid-svg-5Bgdko9klKYOFnHf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5Bgdko9klKYOFnHf .rough-node .label text,#mermaid-svg-5Bgdko9klKYOFnHf .node .label text,#mermaid-svg-5Bgdko9klKYOFnHf .image-shape .label,#mermaid-svg-5Bgdko9klKYOFnHf .icon-shape .label{text-anchor:middle;}#mermaid-svg-5Bgdko9klKYOFnHf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5Bgdko9klKYOFnHf .rough-node .label,#mermaid-svg-5Bgdko9klKYOFnHf .node .label,#mermaid-svg-5Bgdko9klKYOFnHf .image-shape .label,#mermaid-svg-5Bgdko9klKYOFnHf .icon-shape .label{text-align:center;}#mermaid-svg-5Bgdko9klKYOFnHf .node.clickable{cursor:pointer;}#mermaid-svg-5Bgdko9klKYOFnHf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5Bgdko9klKYOFnHf .arrowheadPath{fill:#333333;}#mermaid-svg-5Bgdko9klKYOFnHf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5Bgdko9klKYOFnHf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5Bgdko9klKYOFnHf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Bgdko9klKYOFnHf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5Bgdko9klKYOFnHf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Bgdko9klKYOFnHf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5Bgdko9klKYOFnHf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5Bgdko9klKYOFnHf .cluster text{fill:#333;}#mermaid-svg-5Bgdko9klKYOFnHf .cluster span{color:#333;}#mermaid-svg-5Bgdko9klKYOFnHf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5Bgdko9klKYOFnHf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5Bgdko9klKYOFnHf rect.text{fill:none;stroke-width:0;}#mermaid-svg-5Bgdko9klKYOFnHf .icon-shape,#mermaid-svg-5Bgdko9klKYOFnHf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Bgdko9klKYOFnHf .icon-shape p,#mermaid-svg-5Bgdko9klKYOFnHf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5Bgdko9klKYOFnHf .icon-shape .label rect,#mermaid-svg-5Bgdko9klKYOFnHf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Bgdko9klKYOFnHf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5Bgdko9klKYOFnHf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5Bgdko9klKYOFnHf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
出现重启或无响应
读取并保存复位标志
IWDG 标志是否置位?
检查供电 NRST 和其他复位原因
检查 WDG_SW 与启动阶段
核对 PR RLR 与 LSI 边界
统计两次刷新之间最坏耗时
检查全部刷新位置与任务进展
排除调试冻结和时基影响
脱离调试器重新验证
先确认复位来源,再沿启动、参数、执行路径定位。
七、总结
一页纸总结
IWDG 是一个由 LSI 驱动的片内硬件倒计时。程序取得有效进展后,通过向 KR 写刷新键,将 RLR 中的值装入计数器;如果刷新停止,计数结束就触发系统复位。
PR 控制每一步有多长,RLR 控制计数周期数,KR 发出操作命令,SR 显示参数更新状态。超时按 P × (RL+1) / f_LSI 核算,40 kHz 是典型频率;正常路径必须留在最短超时以内,故障恢复延迟则按最长超时评估。
HAL 初始化把启动、配置、等待同步和首次刷新封装起来;HAL 刷新只负责重装载,业务健康条件由应用设计。把刷新放在任务完成检查之后,才能让任务失去进展表现为超时。
软件启动保护从程序写启动键开始,硬件启动由持久化的 WDG_SW=0 提前建立保护。后续配置和刷新始终操作同一个 IWDG。硬件启动要求整个启动链及时处理默认倒计时;实验结束应恢复所需选项,避免影响后续程序。
验证时同时观察启动日志、IWDG 复位标志和正常/故障对照,不把示意日志当成实测结果。反复复位说明系统仍需定位故障,不能用看门狗代替修复。
关键参数表
| 参数 | 数值或关系 | 对结果的影响 |
|---|---|---|
| LSI | 典型 40 kHz;DS5792 所列条件下 30~60 kHz | 决定实际超时范围 |
分频 P |
本文 32,PR 编码为 3 | 决定计数周期 |
重装载 RL |
本文 1249,范围 0~4095 | 标称 1 秒对应 1250 个周期 |
| 超时公式 | P × (RL+1) / f_LSI |
避免少算一个周期 |
| 本文超时边界 | 约 666.7~1333.3 ms | 正常用 200 ms,故障用 2000 ms 拉开差距 |
| KR 键值 | 0x5555 / 0xAAAA / 0xCCCC |
解锁 / 刷新 / 启动 |
| SR 状态 | PVU / RVU |
配置更新完成前不能假定已生效 |
| WDG_SW | 1 软件启动,0 硬件启动 | 决定保护起点,设置持久保存 |
| 硬件启动默认超时 | 典型 409.6 ms,按上述边界约 273.1~546.1 ms | 约束启动链和早期初始化 |
| 应用 / ROM 串口 | 115200/8N1;ROM 使用 8E1 | 两种场景不能混用配置 |
| IWDG 复位标志 | RCC_CSR.IWDGRSTF |
应尽早保存,再统一清除 |
六个自测问题
- CPU 主时钟异常后,为什么 IWDG 仍可能超时?它还依赖哪些基本条件?
- 32 分频、Reload=1249,为什么标称是 1 秒,却不能保证每 800 ms 刷新一定安全?
- 写 RLR、写
0xAAAA和写0x5555分别改变了什么?为什么要观察 SR? - 主循环死循环但定时中断还在运行时,无条件在中断里刷新会造成什么结果?
- 如何让启动阶段受到看门狗监督?硬件启动后的默认时间从什么时候开始消耗?
- 下载新程序后仍不断复位,如何区分选项字节保留、刷新路径异常、串口配置问题和其他复位原因?
展开参考答案
- IWDG 的计数时钟来自独立 LSI,不使用 CPU 主时钟;它仍需要适当供电、LSI 和相关硬件正常,且未因调试配置被冻结。
- 时间计算含 1250 个周期。LSI 为 60 kHz 时约 666.7 ms 就超时,800 ms 已超过该值,还未计入其他执行开销。
- RLR 设置重装载数据;
0xAAAA触发重装载并恢复配置写保护;0x5555开放配置写访问。SR 用来确认参数跨时钟域更新是否完成。 - 中断会替已经失去进展的主循环继续刷新,IWDG 因而无法检测该故障。应由监控逻辑确认所有关键任务本轮有进展后再刷新。
- 可以提前软件启动以覆盖后续初始化,或用硬件自动启动覆盖更早阶段。硬件启动的计时早于进入 main,启动代码和初始化都消耗默认时间预算。
- 先保存复位标志,再读回 WDG_SW,检查启动停点与真实刷新位置,按 LSI 边界核算耗时;独立核对串口配置,并脱离调试器复测。没有 IWDG 标志时检查供电、复位引脚及其他复位来源。
