WDG简介
看门狗的作用(防死机机制):单片机在复杂环境中(如受电磁干扰)可能会“跑飞”或“死机”。看门狗是一个硬件倒计时器,程序在正常运行时,必须定时去“喂狗”(重置计数器)。如果程序死机了,来不及喂狗,倒计时归零后,看门狗就会强制让单片机复位重启。
STM32的两种看门狗的区别:
独立看门狗(IWDG):使用的是内部的低速时钟(LSI),即使主时钟坏了它依然能工作。它只要求在一个时间内喂狗即可(不能喂太晚),安全性高,适合用来防止程序彻底死锁。
窗口看门狗(WWDG):使用的是主时钟(PCLK1)。它有“喂狗窗口”的限制,既不能喂得太晚,也不能喂得太早。如果程序跑飞了疯狂乱喂狗,提前喂狗也会触发复位。它适合对时间精度要求极高、需要监控程序精确执行周期的场景(比如要求必须在某段时间内完成任务)。
IWDG框图
STM32独立看门狗(IWDG)的内部硬件框图。
1.8V供电区(寄存器配置部分)
IWDG_PR(预分频寄存器):用来设置8位预分频器的分频系数(范围0-7,对应4~256分频)。由于LSI是40kHz,通过分频可以控制计数器的递减速度。
IWDG_RLR(重装载寄存器):存放12位的重装载数值(最大4095)。这是计数器的初始值。
IWDG_KR(键寄存器):非常关键的寄存器!写入特定值触发特定动作:
写入
0xCCCC:启动看门狗。写入
0xAAAA:执行“喂狗”(看图中的箭头,它把RLR的值重新加载给递减计数器)。写入
0x5555:解除PR和RLR的写保护。
IWDG_SR(状态寄存器):用来指示预分频器和重装载值的更新是否完成(因为时钟不同步,写入需要时间同步)。
VDD供电区(核心逻辑电路)
LSI(40kHz内部低速时钟):这是独立看门狗的时钟源。因为它独立于主时钟(即使主时钟坏了它也能工作,这就是它叫“独立”看门狗的原因)。
8位预分频器:接收LSI时钟,根据PR寄存器的配置进行降频。
12位递减计数器:核心定时器。在时钟驱动下不断递减。如果减到0,就会向右产生IWDG复位信号,强制单片机重启。
工作流程总结
启动:配置好PR和RLR后,往KR写入0xCCCC,计数器开始从RLR的值往下减。
正常喂狗:程序正常运行时,定时往KR写入0xAAAA,计数器会重新加载RLR的值(归位),不会触发复位。
发生故障:如果程序跑飞,没能按时喂狗,12位递减计数器一路减到0,触发系统复位。
IWDG键寄存器
1. 键寄存器的本质与设计初衷
本质:它本质上是一个控制寄存器,用于控制看门狗硬件电路的工作。
设计原因(防干扰):普通的控制寄存器通常是“写1位”来控制某个功能。但在存在电磁干扰等恶劣环境下,1个位很容易被意外翻转。因此,STM32采用了“写入整个寄存器特定值(Magic Number)”的方式来代替单个位的操作,极大地降低了硬件受到干扰而误动作的概率。
2. 键寄存器写入值对照表
写入IWDG_KR的值 | 作用 |
|---|---|
0xCCCC | 启用独立看门狗 |
0xAAAA | 将 IWDG_RLR 中的值重新加载到计数器(喂狗) |
0x5555 | 解除 IWDG_PR 和 IWDG_RLR 的写保护 |
0x5555之外的其他值 | 启用 IWDG_PR 和 IWDG_RLR 的写保护 |
实际操作流(结合前两张图)
在实际写代码时,这几张图的知识是串联起来的,标准配置流程如下:
解除保护:先往
IWDG_KR写入0x5555,允许我们修改预分频(PR)和重装载值(RLR)。配置参数:配置好
IWDG_PR(决定时钟分频)和IWDG_RLR(决定超时时间)。启用看门狗:往
IWDG_KR写入0xCCCC,看门狗开始跑。定时喂狗:在程序正常运行的主循环或定时器中断里,定期往
IWDG_KR写入0xAAAA(硬件会自动把 RLR 的值重载进 12位递减计数器,防止它归零复位)。
重点注意事项
为什么操作完IWDG_PR和IWDG_RLR后,只要写入其他任意值就会再次锁定?
这是为了防止跑飞的程序意外修改了超时时间。一旦参数配置完毕并启动看门狗,这两个寄存器就会被锁死,保证看门狗的安全防线坚不可摧。如果需要重新配置,只能先写入0x5555再次解锁。
IWDG超时时间
1. 核心公式
超时时间:TIWDG=TLSI×PR预分频系数×(RL+1)
其中:TLSI=1/FLSI(即LSI时钟的周期)
2. 看门狗超时时间参考表
假设输入时钟 FLSI=40k,根据不同的预分频系数(PR)和重装载值(RL)计算得出的时间范围:
| 预分频系数 | PR[2:0]位 | 最短时间(ms) (RL = 0x000) | 最长时间(ms) (RL = 0xFFF) |
|---|---|---|---|
| /4 | 0 | 0.1 | 409.6 |
| /8 | 1 | 0.2 | 819.2 |
| /16 | 2 | 0.4 | 1638.4 |
| /32 | 3 | 0.8 | 3276.8 |
| /64 | 4 | 1.6 | 6553.6 |
| /128 | 5 | 3.2 | 13107.2 |
| /256 | 6或7 | 6.4 | 26214.4 |
深度解析与计算验证
1. 公式参数拆解:
TLSI:上一张图提到LSI是40kHz,所以 TLSI=1/40000=0.000025 秒 =0.025 毫秒 (ms)。
PR预分频系数:由
IWDG_PR寄存器配置,对应表格中的 /4 到 /256。(RL+1):
IWDG_RLR中存放的是12位重装载值 RL(范围0~4095)。因为计数器减到0会触发复位,所以实际计数的时钟节拍是 RL+1 次。
2. 表格数据验证(以第一行为例):
最短时间(RL=0x000,即RL=0):0.025ms×4×(0+1)=0.1ms
最长时间(RL=0xFFF,即RL=4095):0.025ms×4×(4095+1)=0.025×4×4096=409.6ms0.025ms×4×(4095+1)=0.025×4×4096=409.6ms(与表格相符)
绝对最大超时时间(分频/256):0.025ms×256×4096=26214.4ms0.025ms×256×4096=26214.4ms(约26.2秒)
WWDG框图
STM32窗口看门狗(WWDG)的内部硬件框图。
时钟源:PCLK1
不同于IWDG使用独立的40kHz LSI时钟,WWDG的时钟来源于PCLK1(来自RCC时钟控制器,即APB1总线时钟)。这意味着WWDG的计数频率与系统主频挂钩。
看门狗预分频器(WDGTB)
用于对PCLK1进行分频,从而调整计数器的递减速度。
看门狗控制寄存器(WWDG_CR)
如图下方所示,它包含6位递减计数器(CNT),即
T6, T5, T4, T3, T2, T1, T0。最高位T6是判断超时的关键位。
看门狗配置寄存器(WWDG_CFR)
包含7位窗口值
W6:0。它用来设定喂狗的“时间窗口”上限。
比较器与复位逻辑(核心所在)
图中标注了比较逻辑:
当 T6:0 > W6:0 比较结果 = 1。同时有一个“写入 WWDG_CR”(即喂狗操作)的信号。
复位逻辑(如何触发复位?)
结合图中的逻辑门(一个与门 AND,一个或门 OR),WWDG产生复位信号有两个途径:
途径一:喂狗太早(窗口外喂狗)
当程序执行“写入 WWDG_CR”(喂狗)时,比较器会判断当前计数器的值
T6:0是否大于窗口值W6:0。如果
T6:0 > W6:0(即计数器的值还很大,处于窗口上限之外),比较器输出1。此时与门(AND)两个输入均为1,输出1给或门(OR),触发复位。
途径二:喂狗太晚(超时)
图左侧有一条从
T6位直接引出,连接到或门(OR)的线。W参看图下方6位递减计数器,初始通常设为
0x7F(即全1,T6=1)。当计数器递减到0x3F时(T6位从1变成0,T5~T0全为1),T6会向或门输出一个1,触发复位。
WWDG工作特性
递减计数器 T[6:0] 的值小于 0x40 时,产生复位(喂狗太晚)
原理:0x40(十进制64)是二进制的
0100 0000,最高位 T6=1。当计数值递减到0x3F(二进制0011 1111)时,T6位由1变成0。T6位一旦变0,硬件直接触发复位。
递减计数器在窗口 W[6:0] 外被重新装载时,产生复位(喂狗太早)
原理:呼应图158中的比较器。如果当前计数值
T[6:0] > W[6:0],此时去喂狗(重装载),比较器输出1,触发复位。这意味着程序跑得太快、或者干扰导致程序乱喂狗,都会被抓出来。
递减计数器等于 0x40 时,可以产生早期唤醒中断(EWI)
原理:这是一个“缓冲机制”。当计数器减到
0x40时,离死机复位只有一步之遥。如果开启了EWI中断,单片机可以跳进中断服务函数,在这里紧急喂一次狗(重装载),从而避免复位。这常用于在复位前保存关键数据。
定期写入 WWDG_CR 寄存器(喂狗)以避免复位
这是正常操作,必须在合法的“刷新窗口”内执行。
图159:窗口看门狗时序图精讲
这张图完美地将上述文字转化成了可视化的时间轴:
纵轴(计数器值):从顶部的初始值(通常是
0x7F)开始,随着时间(横轴)向右递减。禁止刷新区(W[6:0] 之上):斜线处于高位时,属于“禁区”。此时如果执行喂狗,会立刻触发复位。
刷新窗口(W[6:0] 到 0x3F 之间):斜线穿过 W 值后,进入“安全区”。此时喂狗,计数器重置回顶部,一切正常。
喂狗太晚(降至 0x3F):看图表最下方,当计数器触及
3Fh时:T6位波形图由高变低(T6位翻转)。复位波形图立刻产生一个脉冲(高电平),单片机复位。
通俗比喻理解(跳伞理论)
如果您觉得寄存器逻辑绕口,可以把WWDG想象成跳伞:
初始值(0x7F):刚从飞机跳下,高度很高。
窗口值 W[6:0]:安全开伞的最高高度。
0x3F 红线:安全开伞的最低高度(地面)。
喂狗:拉开降落伞。
WWDG的逻辑就是:
如果在最高高度之上就开伞(喂狗太早),降落伞会被强气流撕碎(复位)。
如果一直不掉高度,跌破最低高度(喂狗太晚),直接摔到地上(T6位变0,复位)。
只有在这两个高度之间(刷新窗口)拉开降落伞,才能安全着陆。
EWI中断:相当于你的高度计在极度接近最低高度时发出的“滴滴”警报,提醒你赶紧拉伞。
WWDG超时时间
1. 超时时间公式
TPCLK1:APB1总线时钟的周期,即 1/FPCLK11/FPCLK1。
4096:这是WWDG内部固定的分频值(212212)。PCLK1进入看门狗后,会先固定除以4096,然后再进入WDGTB分频器。
WDGTB预分频系数:下方表格补充了公式,即 2WDGTB2WDGTB。WDGTB取值0,1,2,3,对应分频系数 1, 2, 4, 8。
(T[5:0]+1):计数器从初值递减到 0x3F 的节拍数。T[5:0]最大为0x3F(即63),所以最大计数值是64。
一句话总结:超时时间 = PCLK1时钟周期 × 4096 × 时基分频 × 计数器节拍数。
2. 窗口时间公式
这里的 T[5:0]通常指的是计数器的初始值(即通常配置为0x3F,也就是63)。
W[5:0] 是配置寄存器中设定的窗口值。
物理意义:这个公式算出来的其实是“从初值递减到窗口值所需的时间”。也就是禁止喂狗的“安全等待期”。
注意避坑:真正的“允许喂狗的黄金窗口期”是“超时时间减去窗口时间”,即从窗口值降到 0x3F 的这段时间。
数据验算(为什么表格是这些数字?)
我们用下方表格里的PCLK1 = 36MHz来验证一下(36MHz周期 ≈ 27.78ns):
基础节拍时间= 27.78ns×4096≈113.78μs(即WDGTB=0时的时钟周期)。
WDGTB = 0 (分频1):
最小超时值(T[5:0]=0):113.78μs×(0+1)=113.78μs(表格为 113μs,吻合)
最大超时值(T[5:0]=63):113.78μs×(63+1)=7281.9μs≈7.28ms(表格为 7.28ms,吻合)
WDGTB = 3 (分频8):
基础节拍变为 113.78μs×8=910.24μs113.78μs×8=910.24μs
最大超时值:910.24μs×64=58255μs≈58.25ms
IWDG和WWDG对比
IWDG示例
(Key函数一定要有while;按下按键->模拟停止喂狗)
#include "stm32f10x.h" #include "Delay.h" #include "OlED.h" #include "Key.h" int main(void) { //初始化 OLED_Init(); //按键初始化 KeyInit(); OLED_ShowString(1, 1, "IWDG TEST"); if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET) { OLED_ShowString(2, 1, "IWDGRST"); Delay_ms(500); OLED_ShowString(2, 1, " "); Delay_ms(100); RCC_ClearFlag(); } else { OLED_ShowString(3, 1, "RST"); Delay_ms(500); OLED_ShowString(3, 1, " "); Delay_ms(100); } /* ================= IWDG 独立看门狗初始化 ================= * IWDG 时钟来源:内部低速 LSI ≈ 40kHz(与主频无关,故"独立") * 溢出时间公式:T = (4 × 2^prescaler × (reload + 1)) / 40kHz * 本配置:prescaler = 16,reload = 2499 * T = (4 × 16 × 2500) / 40000 = 4 s /... */ // 1. 写使能:IWDG 的 PR/RLR 寄存器受写保护,必须先解锁才能修改 IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); // 2. 设置预分频:LSI(40kHz) 先固定 4 分频,再乘本预分频系数 // 这里选 16,即进入计数器的时钟 = 40kHz / 4 / 16 = 625Hz IWDG_SetPrescaler(IWDG_Prescaler_16); // 3. 设置重装载值:计数器从 reload 递减到 0 即产生复位 // 重装值 2499 → 实际计数 2500 次 // 溢出时间 = 2500 / 625Hz = 4 s(?见下方说明) IWDG_SetReload(2499); // 4. 喂狗:把计数器重新装回 reload 值,从头开始倒数 // 必须在 IWDG_Enable() 之前先喂一次,保证初始计数值正确 IWDG_ReloadCounter(); // 5. 使能 IWDG:启动看门狗 // 一旦使能,除复位外无法关闭;此后必须周期喂狗,否则溢出复位 IWDG_Enable(); while(1) { if (presskey() == 0) { //喂狗 IWDG_ReloadCounter(); OLED_ShowString(4, 1, "FEED"); Delay_ms(200); OLED_ShowString(4, 1, " "); Delay_ms(600); } } return 0; }