目录
[1 WWDG (窗口看门狗) 概述](#1 WWDG (窗口看门狗) 概述)
[1.1 窗口边界定义](#1.1 窗口边界定义)
[1.2 WWDG 运行过程](#1.2 WWDG 运行过程)
[1.3 窗口机制的工程意义](#1.3 窗口机制的工程意义)
[1.4 WWDG 与 IWDG 的区别](#1.4 WWDG 与 IWDG 的区别)
[2 WWDG 功能框图剖析](#2 WWDG 功能框图剖析)
[2.1 时钟源](#2.1 时钟源)
[2.2 预分频器WDGTB](#2.2 预分频器WDGTB)
[2.2.1 两级分频](#2.2.1 两级分频)
[2.2.2 计数器时钟频率和计数周期计算](#2.2.2 计数器时钟频率和计数周期计算)
[2.3 看门狗控制寄存器T6:0](#2.3 看门狗控制寄存器T[6:0])
[2.4 看门狗配置寄存器W6:0](#2.4 看门狗配置寄存器W[6:0])
[2.5 比较器](#2.5 比较器)
[2.6 复位模块](#2.6 复位模块)
[2.6.1 复位路径一:计数器溢出(喂狗过晚)](#2.6.1 复位路径一:计数器溢出(喂狗过晚))
[2.6.2 复位路径二:比较器触发(喂狗过早)](#2.6.2 复位路径二:比较器触发(喂狗过早))
[2.6.3 早期唤醒中断(EWI)](#2.6.3 早期唤醒中断(EWI))
[3. WWDG配置步骤](#3. WWDG配置步骤)
[3.1 开启WWDG时钟](#3.1 开启WWDG时钟)
[3.2 确定配置参数](#3.2 确定配置参数)
[3.3 设置预分频系数和窗口值](#3.3 设置预分频系数和窗口值)
[3.4 启动 WWDG 并首次喂狗](#3.4 启动 WWDG 并首次喂狗)
[3.5 周期性喂狗](#3.5 周期性喂狗)
[4. 本章节实验](#4. 本章节实验)
[4.1 窗口看门狗实验](#4.1 窗口看门狗实验)
[4.1.1 实验目标](#4.1.1 实验目标)
[4.1.2 硬件设计](#4.1.2 硬件设计)
[4.1.3 软件设计](#4.1.3 软件设计)
[4.1.4 实验现象](#4.1.4 实验现象)
1 WWDG (窗口看门狗) 概述
窗口看门狗(Window Watchdog,WWDG)是 STM32 微控制器内部集成的第二个看门狗外设。与普通看门狗仅检测"是否超时未喂狗"不同,WWDG 在此基础上进一步引入了"时间窗口"机制,对程序刷新计数器(又叫刷新/喂狗操作)的时机进行双向约束。
在 WWDG 中,程序不仅必须在规定时间内完成喂狗,还必须避免过早喂狗。如果喂狗时机超出允许范围,无论过早还是过晚,WWDG 都会认为系统运行节拍异常,并立即触发系统复位。
因此,WWDG 并不仅仅用于检测程序是否死锁或卡死,更重要的是用于监控程序是否按照预期的时间节奏运行。例如:
- 主循环执行周期异常波动;
- 程序运行速度异常加快;
- 某些逻辑被提前跳过;
- 程序跑飞后误执行到喂狗代码;
这些情况虽然不一定会导致系统立即停滞,但都可能破坏系统原有的时序稳定性,而窗口看门狗正是用于检测此类"执行节拍异常"的硬件机制。
1.1 窗口边界定义
WWDG 的核心是一个由 APB1 总线时钟驱动的递减计数器 CNT( T6:0 )。系统运行期间,计数器会持续向下计数,程序必须在指定的计数区间内完成喂狗操作,否则系统将被硬件自动复位。
其中,WWDG 的合法刷新区间由下图所示的两个边界共同决定:

(1)窗口上限(Window Value,W6:0)
窗口上限由用户配置,存放在 WWDG_CFR 寄存器的 W6:0 位中。
该值定义了允许喂狗的最早时刻。当当前计数值 T6:0 仍然大于窗口值 W6:0 时,说明程序喂狗过早。此时如果向 WWDG_CR 寄存器写入新的计数值,硬件会立即判定为窗口违规,并直接触发系统复位。
因此,窗口上限的本质作用是限制程序不能过早喂狗。
(2)窗口下限(固定值 0x40)
WWDG 的下限由硬件固定为 0x40,用户无法修改。
当计数器递减经过 0x40 后,计数器最高位 T6 会由 1 变为 0。硬件逻辑会将该状态变化判定为计数器超时溢出,从而立即产生复位信号。
因此,0x40 实际上定义了允许喂狗的最晚时刻。如果程序未能在计数器递减到 0x40 之前完成喂狗,系统同样会发生复位。
综合上述两个边界,WWDG 仅允许程序在区间W6:0 ≥ T6:0 ≥ 0x40内执行喂狗操作。超出该范围都会触发系统复位,这也是窗口看门狗名称的来源。
1.2 WWDG 运行过程
结合递减计数器的变化过程,WWDG 的一次完整工作周期可以划分为如下图所示的三个阶段。

(1)刷新锁定阶段(T6:0 > W6:0)
系统启动后,或者完成一次合法喂狗后,计数器会从设定值开始递减。
在计数器尚未减小到窗口值 W6:0 之前,系统处于"不允许刷新阶段"。此时如果程序提前刷新 WWDG_CR 寄存器,硬件会认为程序运行节拍异常,从而立即触发系统复位。
该阶段的主要作用是防止程序"喂狗过早"。
(2)合法刷新窗口(W6:0 ≥ T6:0 ≥ 0x40)
当计数器递减进入窗口范围后,系统正式进入合法刷新阶段。
此时程序可以安全地向 WWDG_CR 写入新的计数值,从而重新开始下一轮计数周期。
窗口持续时间由:W6:0 - 0x40 决定。二者差值越小,合法刷新时间越短,WWDG 对程序运行节拍的约束也越严格。
(3)溢出复位阶段(T6:0 < 0x40)
如果程序由于死锁、跑飞、高优先级中断长期占用 CPU 等原因,未能在合法窗口内完成刷新,计数器将继续递减。
当计数值低于 0x40 时,T6 位被清零,WWDG 会立即产生复位信号,强制系统重新启动。
因此,该阶段本质上用于检测程序是否已经失去正常运行能力。
1.3 窗口机制的工程意义
普通看门狗只能检测程序是否彻底停止运行,而无法判断程序是否仍然按照正确的执行节拍工作。例如在某些异常情况下:
- 主循环周期突然异常变短;
- 某个任务提前退出;
- 程序跳过部分关键逻辑;
- 程序跑飞后误执行到喂狗代码;
虽然系统表面上仍然能够持续喂狗,但其实际运行状态已经发生异常。
WWDG 通过限制刷新时机,使系统不仅能够检测"程序是否运行",还能够进一步检测"程序是否按照预期节奏运行"。因此,相比普通看门狗,WWDG 更适用于:
- 实时控制系统;
- 周期性任务系统;
- RTOS 调度系统;
- 工业控制设备;
- 对执行时序一致性要求较高的应用场景。
从本质上看,WWDG 更像是一种"程序时序监控机制",而不仅仅是简单的防死机电路。
1.4 WWDG 与 IWDG 的区别
STM32 内部同时集成了:
- IWDG(Independent Watchdog,独立看门狗)
- WWDG(Window Watchdog,窗口看门狗)
二者虽然都属于看门狗外设,但设计目标并不相同。
IWDG 更偏向系统级可靠性保护,用于防止系统彻底死机;而 WWDG 更关注程序执行过程中的时序稳定性,用于检测运行节拍异常。
核心架构差异对比表:
| 对比维度 | IWDG (独立看门狗) | WWDG (窗口看门狗) |
|---|---|---|
| 监控维度 | 单一边界:仅监控超时(重装载过晚) | 双重边界:同时监控超时与提前(重装载过晚或过早) |
| 时钟源架构 | 内部低速时钟 (LSI, 约 40 kHz),独立于系统主时钟 | APB1 总线时钟 (PCLK1, 最高 36 MHz),依赖系统主时钟 |
| 异常复位条件 | 12位递减计数器溢出至 0 | T6:0 递减跨过 0x40,或在 T6:0 > W6:0 时执行重装载 |
| 计数器位宽 | 12 位有效计数 | 6 位有效计数T5~T0 + 1 位溢出标志 (T6) |
| 典型超时范围 | 宽泛 (0.1 ms ~ 26.2 s),适合宏观状态监控 | 精密 (113 μs ~ 58.25 ms),适合微观时序监控 |
| 中断支持 | 无硬件中断支持,直接触发复位 | 具备早期唤醒中断 (EWI),允许复位前执行紧急保存或关断 |
| 寄存器防护 | 具有硬件键值寄存器 (IWDG_KR) 写保护 | 无特殊写保护机制,直接操作控制寄存器 |
工程选型原则:
-
选用 IWDG: 适用于主时钟可能面临停振风险的环境,或仅需对系统进行宏观维度的"防死锁"保护。其超长周期的容限使其成为可靠性设计的最后一道防线。
-
选用 WWDG: 适用于主时钟稳定的环境,且系统内部存在精确的时间关键型任务(Time-Critical Tasks)。利用其 EWI 早期唤醒中断,还可在复位前 1 个计数周期内完成核心状态的非易失性存储或物理部件的安全关断。
-
协同工作机制: 在高等级安全标准的硬件设计中,通常采用"双看门狗协同"策略:IWDG 作为底层独立硬件防护,应对主时钟失效及深度死机;WWDG 作为应用层时序防护,监控复杂软件逻辑的执行节拍。
2 WWDG 功能框图剖析
窗口看门狗内部的硬件结构如下图所示。通过对功能框图系统分析,可以更加深入的理解窗口看门狗的工作机制。

将WWDG结构框图分成6个子模块,下面按照信号流向依次进行介绍。
2.1 时钟源
窗口看门狗的时钟来自APB1总线时钟(PCLK1),该时钟由RCC时钟控制器管理。在STM32F103系列中,APB1总线的最大时钟频率为36MHz,如下图所示:

与独立看门狗不同,窗口看门狗的时钟并非独立时钟源,而是与系统主时钟耦合。这意味着:
- 窗口看门狗的时钟需要通过RCC时钟控制器手动开启,默认状态下处于关闭状态;
- 当系统主时钟发生故障时,窗口看门狗也将停止工作;
- 时钟频率相对较高(36MHz vs 40kHz),使得窗口看门狗更适合短周期、高精度的时序监控;
PCLK1时钟信号作为窗口看门狗的基础时钟源,后续将经过两级分频处理,最终驱动递减计数器运行。
2.2 预分频器WDGTB
2.2.1 两级分频
PCLK1时钟并不直接驱动递减计数器,而是首先经过两级分频处理:
第一级:固定4096分频
所有来自PCLK1的时钟信号首先经过一个固定的4096分频器。该分频器在功能框图中并未直接绘出,但在STM32参考手册的超时时间计算公式中明确体现。这一级分频是硬件固定的,用户无法修改。
第二级:可编程预分频器WDGTB
经过4096分频后的时钟信号进入可编程预分频器,用户可通过配置寄存器WWDG_CFR的位8:7 ------ 时基WDGTB1:0来设置分频系数。

对应的分频系数如下表所示:
| WDGTB1:0 | 分频系数Prescaler |
|---|---|
| 00 | 1 |
| 01 | 2 |
| 10 | 4 |
| 11 | 8 |
2.2.2 计数器时钟 频率和计数周期计算
(1)计数器时钟频率计算
经过两级分频后,最终驱动递减计数器的时钟频率(CK_CNT)计算公式为:
:APB1总线时钟频率,最大36MHz
:对应的分频系数为1, 2, 4, 8
- WDGTB:预分频系数配置值,取值范围00,0 1, 10, 11
(2)计数周期计算
递减计数器(CNT)每递减一次所需的时间(即计数周期)为:
以PCLK1 = 36MHz为例,由于 WWDG 的有效计数范围T5:0只有 63 个计数周期,根据超时时间计算公式:
不同WDGTB配置下的计数器时钟频率和计数周期如下:
| WDGTB | 分频系数Prescaler | CK_CNT频率 | 单次计数周期T_CNT | 最小超时时间 | 最大超时时间 |
|---|---|---|---|---|---|
| 0 | 1 | 8789 Hz | 约0.114 ms | 113us | 7.28ms |
| 1 | 2 | 4395 Hz | 约0.227 ms | 227us | 14.56ms |
| 2 | 4 | 2197 Hz | 约0.455 ms | 455us | 29.12ms |
| 3 | 8 | 1099 Hz | 约0.910 ms | 910us | 58.25ms |
通过调整WDGTB的值,可以在一定范围内灵活控制窗口看门狗的超时时间范围。
2.3 看门狗控制寄存器T6:0
窗口看门狗的递减计数器由控制寄存器WWDG_CR的位6:0表示,记为T6:0,共7位。但这7位的功能并不完全相同,其中最高位T6具有特殊含义。

(1)计数器结构
- T5:0:低6位,构成实际的递减计数器,取值范围为0x00 ~ 0x3F(十进制063)
- T6:最高位,作为溢出标志位,用于指示计数器是否溢出
(2)递减计数过程
整个7位寄存器T6:0在被写入初值后,随计数器时钟CK_CNT的每个脉冲递减1。其工作机制如下:
- 正常递减阶段:当T6 = 1时,表示计数器尚未溢出,递减操作正常进行。例如,从0x7F(二进制:0111 1111)开始递减:0x7F → 0x7E → 0x7D → ... → 0x41 → 0x40
- 溢出触发阶段:当T6:0从 0x40 再减1时,T6位会从1翻转为0。此时整个7位寄存器的值从0x40(二进制0100 0000)变为0x3F(二进制0011 1111)。
- 复位产生:硬件电路持续监测T6位的状态。一旦检测到T6 = 0,且未使能提前唤醒中断,则立即判定为计数器溢出,产生系统复位信号。
(3)关键数值0x40
0x40 是窗口看门狗计数器能够递减到的最小安全值,也是窗口的固定下限。这个值的特殊性在于:
- 当计数器值 ≥ 0x40时,T6 = 1,系统处于安全状态
- 当计数器值 < 0x40时,T6 = 0,系统立即复位
因此,在向WWDG_CR写入计数器初值时,必须确保T6位为1,即写入值必须在0x40~0x7F范围内。否则,看门狗一经使能可能立即触发复位。
(4)有效计数范围
虽然T6:0共有7位,但实际用于计数的只有低6位T5:0,所以其有效计数范围为:
- 最小值:0x40(T6=1, T5:0=0x00)
- 最大值:0x7F(T6=1, T5:0=0x3F)
所以,计数器的有效计数次数只有 64次(即从0x3F到0x00)。
理解T6作为溢出标志位的机制,是正确配置和使用窗口看门狗的关键。
2.4 看门狗配置寄存器W6:0
窗口看门狗的"窗口"特性由配置寄存器WWDG_CFR的位6:0定义,记为W6:0,用于设置窗口的上限值。

(1)窗口上限的作用
窗口上限值W6:0定义了允许喂狗的最早时刻。当程序执行喂狗操作时,硬件会将当前计数器值T6:0与窗口值W6:0进行比较:
- 如果T6:0 > W6:0,说明计数器值仍然较高,距离超时还有较长时间,此时喂狗被判定为"过早",触发复位
- 如果T6:0 ≤ W6:0,说明计数器已进入允许喂狗的窗口区间,喂狗操作被接受
(2)窗口值的配置范围
窗口值W6:0的设置必须满足以下约束条件:
- 下限约束:W6:0 > 0x40,必须大于固定的窗口下限值。如果W6:0 ≤ 0x40,则失去了窗口的意义,退化为类似独立看门狗的单边界监控。
- 上限约束:W6:0 < 计数器初值T6:0,必须小于计数器的初始装载值。通常计数器初值设为最大值0x7F,因此W6:0的实际取值范围为0x41~0x7E。
(3)合法喂狗窗口
综合窗口上限W6:0和固定下限0x40,合法的喂狗窗口可以表示为:
只有当计数器值落在这个区间内时,喂狗操作才不会触发复位。
(4)窗口值的计算方法
在实际应用中,窗口值的设置需要根据被监控程序段的执行时间来确定。假设:
- 被监控程序段的执行时间为
- 计数器初值设为最大值0x7F
- 单次计数周期为
- 窗口值设为W6:0
则从计数器初值递减到窗口值所经过的时间(即窗口时间)为:
为了确保程序段执行完毕后立即进入合法喂狗窗口,应使窗口时间略小于或等于程序执行时间:
由此可以反推出窗口值:
通过合理设置窗口值,可以实现对程序执行节奏的精确监控。
2.5 比较器
窗口看门狗的比较器是实现"窗口机制"的核心硬件单元,用于判断喂狗操作是否发生在合法的时间窗口范围内。

从功能框图可以看出,比较器的一路输入来自当前递减计数器值 T6:0,另一路输入来自窗口值寄存器 W6:0。比较器始终对两者进行持续比较,并实时输出当前的窗口状态结果。
其判断逻辑如下:
- 当 T6:0 > W6:0 时,说明计数器尚未进入窗口区间,即喂狗过早,此时比较器输出为高电平;
- 当 T6:0 ≤ W6:0 时,说明计数器已进入允许刷新窗口区间,喂狗操作合法,此时比较器输出为低电平。
通过该比较机制,窗口看门狗具备了对"喂狗过早"情况的检测能力,从而实现窗口上限约束。该机制在后续系统复位逻辑中进一步参与控制判定。
2.6 复位模块
从 WWDG 的内部功能框图可以看出,窗口看门狗内部存在两条独立的复位触发路径,分别用于检测 "喂狗过晚" 和 "喂狗过早" 两种异常情况。这两条路径分别对应时间窗口的下限监控与上限监控,最终在硬件内部汇合后生成系统复位信号。如下图所示:

2.6.1 复位路径一:计数器溢出(喂狗过晚)
窗口看门狗的第一条复位路径用于检测程序是否在规定时间内完成喂狗,这是看门狗最基本的超时保护机制。
窗口看门狗启动后,计时器时钟驱动 7 位递减计数器 T6:0 持续递减。其中,T6 不仅是计数器最高位,同时也是硬件内部的溢出状态标志位。
当计数器递减到 0x40 时,T6:0 = 100 0000,此时系统仍处于安全状态。如果程序仍未及时喂狗,计数器继续减 1,T6:0 = 011 1111。由于低 6 位发生借位,最高位 T6 会由 1 翻转为 0。硬件将 T6 = 0 解释为计数器已经溢出,即程序在超时时间内未能完成喂狗。
随后,T6位的状态信号进入取反器进行逻辑取反:
- 当T6 = 1时(计数器未溢出,系统安全),取反后输出低电平;
- 当T6 = 0时(计数器溢出,喂狗过晚),取反后输出高电平。
因此,当计数器发生溢出后,取反器会输出为高电平的异常信号。该信号进入或门 B,根据或门的逻辑特性,只要有一个输入为高电平,或门 B的输出就为高电平,无论此时与门 A 的输出是高电平还是低电平。因此,只要计数器溢出,或门 B 就输出高电平。
或门 B 的输出随后进入与门 C,与门 C 的另一路输入为 WDGA 激活位。只有在 WDGA = 1(启用窗口看门狗)的情况下,与门C才输出高电平,产生最终的系统复位信号。这种设计确保了只有在看门狗被明确激活后,复位机制才真正生效。
因此,这一触发路径的触发信号流向为:

这一路径本质上用于检测程序是否"长时间未执行喂狗",确保了看门狗在计数器递减到最低安全值0x40以下时能够可靠地触发复位。
**注意:**WDGA位一旦被置1,就无法通过软件清零,这是硬件设计的保护机制,防止程序跑飞后意外关闭看门狗。只有系统复位后,WDGA位才会恢复为0。
2.6.2 复位路径二:比较器触发(喂狗过早)
窗口看门狗的第二条复位触发路径用于检测程序是否"过早喂狗",这是窗口看门狗区别于独立看门狗的核心特征。其核心硬件模块是比较器。

从上面的功能框图可以看出,比较器作为一个硬件电路模块,始终对当前递减计数器值T6:0与窗口值W6:0进行实时监测,不断输出当前的比较结果:
- 当T6:0 > W6:0时,输出高电平;
- 当T6:0 ≤ W6:0时,输出低电平。
在未执行喂狗操作时,与门 A 的输入信号 "写入WWDG_CR" 始终保持低电平。根据与门的逻辑特性,只要有一个输入为低电平,与门A的输出就始终为低电平。因此,此时比较器的输出无论是高电平还是低电平,都不会影响到后续复位逻辑电路的状态,系统保持正常运行。
当程序向控制寄存器WWDG_CR写入新的计数器值时(即,执行喂狗操作),硬件会对当前的喂狗时机进行合法性检测。此时与门 A 的"写入WWDG_CR"输入端被拉高,与门A被激活,开始对比较器的当前输出结果进行采样:
-
T6:0 ≤ W6:0:说明计数器已经递减到合法喂狗的刷新窗口范围内,喂狗操作被允许。此时比较器输出低电平, 而与门A的另一个输入信号 "写入WWDG_CR" 为高电平,所以与门A 输出低电平,不会触发后续的复位逻辑电路。因此,此次喂狗操作不会触发复位机制 。
-
T6:0 > W6:0:说明当前的计数器值仍然高于窗口上限,距离超时还有较长时间,此时喂狗被判定为喂狗过早。此时比较器输出高电平,与门A 输出高电平,所以或门B输出高电平,然后或门B的输出经过与门C与WDGA激活位进行逻辑与运算,此时看门狗已激活,WDGA = 1,所以与门C输出高电平,产生最终的系统复位信号。
这一触发路径的触发信号流向为:

这一路径本质上用于检测程序是否"在非法时间窗口内执行喂狗",从而避免程序跑飞后因频繁误喂狗而导致系统无法被复位。
2.6.3 早期唤醒中断(EWI)
除了直接复位外,窗口看门狗还提供了一个早期唤醒中断功能(Early Wakeup Interrupt,EWI)。
如果启动了看门狗,并使能了EWI中断(配置寄存器WWDG_CFR的位9 EWI置1),那么当递减计数器递减到0x40时(即T6=1, T5:0=0x00),硬件会触发EWI中断,相应的中断服务程序(ISR)可以被用于重装载计数器,以避免WWDG产生计数器溢出复位。

早期唤醒中断的触发时刻处于计数器即将溢出之前(再减1就会导致T6翻转为0,触发计数器溢出复位),因此被称为**"早期唤醒"** 或**"提前唤醒中断"**。其主要用途包括:
- 紧急数据保存:在复位发生前的最后时刻,将关键运行数据写入非易失存储器
- 安全关断:执行关闭危险负载、断开功率输出等安全操作
- 最后喂狗机会:在中断服务程序中执行喂狗操作,阻止复位发生(需在1个计数周期内完成)
需要注意的是,从触发EWI中断到计数器溢出复位,只有1个计数周期的时间。在PCLK1为36MHz、WDGTB为0的条件下,这个时间约为113微秒。因此,中断服务程序必须非常简短高效。
3. WWDG配置步骤
窗口看门狗的配置过程可以概括为:
- 开启 WWDG 时钟
- 计算时间参数
- 配置预分频与窗口值
- 写入初始计数值并启动 WWDG
- 周期性喂狗
以下结合STM32标准库和实验代码,详细说明各步骤的具体实现。
3.1 开启WWDG时钟
窗口看门狗的时钟来自APB1总线时钟PCLK1,默认频率为36MHz。与独立看门狗不同,窗口看门狗的时钟在系统复位后默认处于关闭状态,需要手动开启:
cpp
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
3.2 确定配置参数
在配置硬件寄存器之前,需要根据应用需求,先确定两个关键的时间参数:超时时间 (系统允许的最晚喂狗时刻)和窗口时间
(计数器从重装载初值递减至窗口上限值所需的时间)。本实验设定目标参数如下:
= 50 ms
= 30 ms

如图所示,主循环喂狗周期要在30ms到50ms范围内,接下来根据目标超时时间与窗口时间,进一步反推窗口看门狗的各项配置参数,包括预分频系数、计数器初值以及窗口值。
(1)预分频系数 WDGTB
首先根据目标超时时间筛选WDGTB可用的预分频系数。根据第2.2章介绍的超时时间计算公式:
将T5:0的最大值63代入,分别计算各预分频系数下,递减计数器CNT能达到的最大超时时间:
- 预分频系数1:最大超时时间 ≈ 7.28ms < 50ms,不满足要求
- 预分频系数2:最大超时时间 ≈ 14.56ms < 50ms,不满足要求
- 预分频系数4:最大超时时间 ≈ 29.12ms < 50ms,不满足要求
- 预分频系数8:最大超时时间 ≈ 58.25ms > 50ms,满足要求
因此选择预分频系数8,对应的标准库宏定义为 WWDG_Prescaler_8。
(2)计数器重装载值T5:0
确定预分频系数后,然后根据目标超时时间( = 50 ms)反推计数器重装载值。公式如下:
其中,为递减计数器CNT递减一次所需的时间:
再代入原式:
取整数54。此时实际超时时间为 ,与目标值基本一致。T5:0 = 54在有效范围0~63内,参数有效。
(3)窗口值 W5:0
窗口值即窗口上限值,根据窗口时间反推。窗口时间计算公式:
已知窗口时间为30ms,T5:0 = 54,代入求解:
取整为33,则:
此时实际窗口时间为 ,与目标值一致。W5:0 = 21在有效范围 0 ~ 63内,参数有效。
**注意:**为防止写入控制寄存器(CR)或配置寄存器(CFR)后立即触发硬件复位,必须确保写入的数据中最高位(即位 6)为 1。因此,实际的十六进制写入值需与固定的下限值 0x40 组合:
- 计数器初值:0x40 | 54 = 0x76
- 窗口配置值:0x40 | 21 = 0x55
3.3 设置预分频系数和窗口值
完成参数计算后,即可开始配置 WWDG。首先设置预分频系数:
cpp
WWDG_SetPrescaler(WWDG_Prescaler_8);
该函数用于配置 WWDG_CFR 寄存器中的 WDGTB1:0 位。随后设置窗口值:
cpp
WWDG_SetWindowValue(0x40 | 21);
该函数会将窗口值写入 WWDG_CFR 寄存器中的 W6:0 位。
与独立看门狗不同,WWDG 的寄存器没有键值写保护机制,因此这些配置可以直接写入。
窗口值配置完成后,内部比较器便会持续对当前计数值 T6:0 与窗口值 W6:0 进行实时比较,为后续的窗口检测提供判断依据。
3.4 启动 WWDG 并首次喂狗
完成参数配置后,需要写入初始计数值并启动窗口看门狗:
cpp
WWDG_Enable(0x40 | 54); // 使能看门狗并设置计数器初值为0x76
该函数内部同时完成两项操作:
- 设置 WDGA 使能位;
- 写入计数器初值。
函数执行后,窗口看门狗正式启动,递减计数器开始按照设定时钟持续递减。
这里需要注意,WWDG 的递减计数器属于自由运行结构。只要外设时钟已经开启,内部计数逻辑实际上已经处于运行状态。因此,在启动 WWDG 时必须同时写入一个合法的初始值,否则可能因当前计数状态不可预知而导致系统立即复位。
此外,WDGA 位一旦被置 1,软件便无法再将其清零,只能通过系统复位恢复默认状态。这是 WWDG 的硬件保护机制,用于防止程序跑飞后意外关闭看门狗。
3.5 周期性喂狗
WWDG 启动后,主程序必须严格遵守时间约束,在合法时间窗口内周期性执行喂狗操作:
cpp
WWDG_SetCounter(0x40 | 54); // 重装载计数器值为0x76
该函数向控制寄存器WWDG_CR写入新的计数器值。由于WDGA位在使能后已固定为1,函数内部会将传入参数与0x7F进行按位与运算后再写入,确保只有T6:0位被更新,WDGA位保持不变。
4. 本章节实验
4.1 窗口看门狗实验
4.1.1 实验目标
本实验旨在通过编程和运行验证窗口看门狗的以下功能:
- 正常窗口内喂狗不复位:在设定的30~50ms时间窗口内执行喂狗,程序保持正常运行,不发生复位。
- 喂狗过晚触发复位:通过阻塞式按键函数Key_GetNum模拟主循环卡死,导致喂狗超时(超过50ms),窗口看门狗产生复位。
- 喂狗过早触发复位:通过Delay_ms缩短喂狗间隔,使其小于30ms的窗口上限,验证过早喂狗的触发复位。
- 复位来源区分:在程序启动时判断复位原因,窗口看门狗复位显示"WWDGRST",正常复位显示"RST"。
- 时间边界验证:通过Delay_ms调整喂狗间隔,使其略大于或小于窗口上限(如31ms和29ms)和超时上限(如51ms和49ms),确认合法窗口的上下边界。
4.1.2 硬件设计

4.1.3 软件设计
软件在已完成 OLED 与按键驱动移植的工程基础上编写,程序整体可分为以下几个部分。
(1)初始化
程序启动后,首先完成 OLED 与按键模块初始化,并在 OLED 第一行显示 "WWDG TEST",作为实验运行标识。
(2)复位源判断与显示
系统每次复位后,RCC 的控制/状态寄存器 RCC_CSR 都会保存本次复位的来源信息,如下图所示:

当窗口看门狗触发系统复位时,硬件会自动将 WWDGRSTF 标志位置 1。因此,程序重新启动后,可以通过检测该标志位判断上一次复位是否由 WWDG 引起:
cpp
if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)
- 若检测到该标志位为 SET,说明系统刚刚发生过窗口看门狗复位,此时 OLED 第二行闪烁显示 "WWDGRST";
- 若为RESET,则认为是正常上电、下载程序或按键复位等其他情况,在第三行显示 "RST"。
显示完成后,需要调用
cpp
RCC_ClearFlag();
清除 RCC_CSR 中保存的复位标志位。否则该标志会一直保持为 1,导致后续即使不是 WWDG 复位,程序仍会误判为窗口看门狗复位。
(3)开启时钟
窗口看门狗使用 APB1 总线时钟 PCLK1 作为时钟源,因此在配置 WWDG 前,必须先开启其外设时钟:
cpp
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
这也是 WWDG 与 IWDG 的一个重要区别:
- IWDG 使用独立的 LSI 时钟,启动后自动运行;
- WWDG 依赖 APB1 时钟,必须由软件手动开启。
(4)WWDG初始化
根据第 3 章计算得到的参数,对窗口看门狗进行初始化:
cpp
WWDG_SetPrescaler(WWDG_Prescaler_8); // 设置计数器时钟的预分频系数为8
WWDG_SetWindowValue(0x40 | 21); // 设置允许喂狗的最早时刻(窗口上限30ms)
WWDG_Enable(0x40 | 54); // 使能WWDG并首次喂狗(超时时间约50ms)
执行 WWDG_Enable() 后,WWDG 开始工作,递减计数器开始按照设定的时钟周期持续向下计数。
(5)主循环
主循环代码如下:
cpp
while (1)
{
Key_GetNum();
OLED_ShowString(4, 1, "FEED");
Delay_ms(20);
OLED_ShowString(4, 1, " ");
Delay_ms(20);
WWDG_SetCounter(0x40 | 54);
}
整个循环的核心作用,是周期性执行喂狗操作。
程序每次喂狗前,都会先经历两次 20 ms 延时,因此喂狗间隔约为:
cpp
20 ms + 20 ms + 少量代码执行时间 ≈ 40 ms
该时间位于:
cpp
30 ms < 喂狗时间 < 50 ms
因此属于合法窗口范围。
根据不同运行状态,系统会表现出不同现象。
① 正常运行状态
正常情况下:
- OLED 第四行会周期性闪烁 "FEED";
- 程序每隔约 40 ms 喂狗一次;
- 喂狗时间始终位于合法窗口内;
- WWDG 不会触发复位;
- 系统持续稳定运行。
这说明窗口看门狗已正常工作。
② 按键模拟主循环卡死(喂狗过晚)
Key_GetNum() 是一个阻塞式按键扫描函数。当按住 PB1 不放时,程序会卡在:
cpp
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
此时:
- while主循环停止向下执行;
- 后续延时与喂狗代码均无法运行;
- WWDG 计数器持续递减;
- 大约 50 ms 后计数器溢出;
- 窗口看门狗触发系统复位。
复位后:
- RCC 自动置位 WWDGRSTF 标志;
- 程序重新启动;
- OLED 第二行显示 "WWDGRST"。
该现象说明窗口看门狗能够检测程序"长时间未喂狗"的异常情况。
③ 修改 Delay_ms() 模拟喂狗过早
若将两个 Delay_ms(20) 修改为 Delay_ms(14),则新的喂狗间隔约为 28 ms,此时喂狗时间早于 30 ms 窗口边界。程序虽然仍在持续执行喂狗代码,但由于喂狗发生得过早,WWDG 会立即判定为非法刷新,并直接触发系统复位。
此时会观察到:
- OLED 来不及稳定显示 "FEED";
- 系统不断重复重启;
- OLED 第二行持续反复显示 "WWDGRST"。
该现象说明WWDG 不仅检测"是否喂狗",还检测"喂狗时机是否正确"。
(6)窗口边界测试方法
窗口看门狗的合法喂狗区间为:30 ms ~ 50 ms,因此,可以通过修改两个 Delay_ms() 的延时时间(也可以在while中只保留一个Delay_ms去测试),逐步逼近窗口边界,从而验证窗口上下限是否配置正确。
其核心原理是:程序每次喂狗前都会执行两次延时,因此:
cpp
喂狗间隔 ≈ Delay1 + Delay2 + 代码执行时间
通过调整延时长度,就可以人为控制喂狗发生的时间位置。
① 验证窗口上限(最早喂狗时间)
- 情况1:设置 Delay_ms(15),因此喂狗间隔约为:15 ms + 15 ms + 少量执行时间 ≈ 31 ms,此时喂狗时间已经略晚于窗口上限,喂狗进入合法窗口,系统保持正常运行。
- 情况2:设置 Delay_ms(14),喂狗间隔约为 28 ms,此时喂狗发生在窗口之外,WWDG 判定为过早喂狗,系统持续复位。
因此可以确定,窗口上限大致位于 30 ms 附近。
② 验证超时下限(最晚喂狗时间)
-
情况1:设置 Delay_ms(24),因此喂狗间隔约为:24 ms + 24 ms + 执行时间 ≈ 49 ms,此时喂狗仍早于超时点,系统保持正常运行。
-
情况2:设置 Delay_ms(25),喂狗间隔约为 51 ms,此时喂狗时间已经超过超时边界,计数器在喂狗前已经溢出,系统触发复位。
因此可以验证,WWDG 的超时时间确实约为 50 ms。
以下是实验的关键代码文件:
- main.c文件:
cpp
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Key_Init(); //按键初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "WWDG TEST");
/*判断复位信号来源*/
if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET) //如果是窗口看门狗复位
{
OLED_ShowString(2, 1, "WWDGRST"); //OLED闪烁WWDGRST字符串
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(100);
RCC_ClearFlag(); //清除标志位
}
else //否则,即为其他复位
{
OLED_ShowString(3, 1, "RST"); //OLED闪烁RST字符串
Delay_ms(500);
OLED_ShowString(3, 1, " ");
Delay_ms(100);
}
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //开启WWDG的时钟
/*WWDG初始化*/
WWDG_SetPrescaler(WWDG_Prescaler_8); //设置预分频为8
WWDG_SetWindowValue(0x40 | 21); //设置窗口值,窗口时间为30ms
WWDG_Enable(0x40 | 54); //使能并第一次喂狗,超时时间为50ms
while (1)
{
Key_GetNum(); //调用阻塞式的按键扫描函数,模拟主循环卡死
OLED_ShowString(4, 1, "FEED"); //OLED闪烁FEED字符串
Delay_ms(20); //喂狗间隔为20+20=40ms
OLED_ShowString(4, 1, " ");
Delay_ms(20);
WWDG_SetCounter(0x40 | 54); //重装计数器,喂狗
}
}
- Key.c文件
cpp
#include "stm32f10x.h" // Device header
#include "Delay.h"
/**
* 函 数:按键初始化
* 参 数:无
* 返 回 值:无
*/
void Key_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB1和PB11引脚初始化为上拉输入
}
/**
* 函 数:按键获取键码
* 参 数:无
* 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下
* 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手
*/
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0; //定义变量,默认键码值为0
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //读PB1输入寄存器的状态,如果为0,则代表按键1按下
{
Delay_ms(20); //延时消抖
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); //等待按键松手
Delay_ms(20); //延时消抖
KeyNum = 1; //置键码为1
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) //读PB11输入寄存器的状态,如果为0,则代表按键2按下
{
Delay_ms(20); //延时消抖
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); //等待按键松手
Delay_ms(20); //延时消抖
KeyNum = 2; //置键码为2
}
return KeyNum; //返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}
4.1.4 实验现象
将程序编译下载至开发板后,可以通过以下操作观察几种典型现象:
(1)正常运行
系统上电后:
- OLED第一行显示 "WWDG TEST";
- 第三行显示"RST"约500ms后消失;
- 随后第四行以约40ms的周期快速闪烁"FEED",表明程序正常运行并持续在合法窗口内喂狗;
- 系统持续稳定运行,不发生复位。
说明程序始终在合法窗口内完成喂狗。
(2)按键模拟卡死(喂狗过晚)
按住 PB1 不放后:
- 程序阻塞在Key_GetNum()内部,第四行"FEED"停止闪烁;
- 等待约50ms后,系统自动复位,OLED第二行显示"WWDGRST"。这表明窗口看门狗因喂狗超时产生了复位,且复位源被正确识别为窗口看门狗复位
- 松开按键后,程序恢复至正常喂狗状态
说明 WWDG 成功检测到"长时间未喂狗"的异常状态。
(3)修改延时导致喂狗过早
将两个 Delay_ms(20) 都修改为 Delay_ms(14) ,或只保留一个Delay_ms(28) ,然后重新编译下载:
- 系统刚启动便不断复位;
- 即便不按按键,OLED 第二行持续显示 "WWDGRST",表明窗口看门狗因过早喂狗触发了复位。
- "FEED" 不会显示。
说明喂狗发生在合法窗口之前,WWDG 检测到了"过早喂狗"。
(4)窗口边界验证现象
通过逐步修改延时,可观察到窗口边界附近的行为变化:
| 延时配置 | 实验现象 |
|---|---|
| ≈31 ms | 正常运行 |
| ≈28 ms | 持续复位 |
| ≈49 ms | 正常运行 |
| ≈51 ms | 持续复位 |
这些现象表明窗口边界与第 3 章的理论计算结果基本一致。
在实际工程中,喂狗时间通常不会设置在窗口边界附近,而是会保留一定时间余量,以避免时钟误差、代码执行波动等因素导致误复位。