一、WWDG简介
WWDG有什么作用?
二、WWDG工作原理
三、WWDG框图
四、WWDG寄存器
控制寄存器 (WWDG_CR)
配置寄存器 (WWDG_CFR)
状态寄存器 (WWDG_SR)
五、WWDG超时时间计算
WWDG最短最长超时时间(F1为例)
六、WWDG配置步骤
七、编程实战:验证窗口看门狗功能
八、IWDG和WWDG的主要区别
九、总结
WWDG(窗口看门狗)是一种用于监控系统的硬件设备。下面是关于WWDG的一些重要信息:
WWDG主要用于监控系统的运行状态,当系统出现异常或长时间未喂狗时,WWDG会触发复位以确保系统的可靠性。提前唤醒中断功能允许系统在WWDG计数器即将归零时提前唤醒,可以在某些情况下降低功耗。
WWDG与IWDG(独立看门狗)类似,但WWDG在计数器归零前需要在特定的窗口期内喂狗,否则会触发复位。这种窗口机制增加了对系统运行状态的更严格监控。
WWDG(窗口看门狗)主要用于监测单片机程序的运行时效性,特别是在需要对程序执行时间进行精准监测的场合。其主要作用和应用包括:
监测程序运行时效: WWDG用于监测程序的运行是否在预定的时间窗口内完成。通过设定计数器的窗口范围,WWDG能够检测程序是否在规定的时间内执行完成,从而确保程序的时效性。
检测软件异常: WWDG可以检测到由于软件异常导致的程序运行超时或错误。如果程序在窗口期内没有正常完成,WWDG将触发复位,从而引起系统的重启,有助于应对软件中的异常情况。
精准检测程序运行时间: 在需要对程序的执行时间进行精确监测的应用场合,WWDG可以提供更为严格的监控机制。通过配置适当的窗口范围和计数器值,可以实现对程序执行时间的高精度监测。
提前唤醒中断: WWDG还提供了提前唤醒中断(EWI),当递减计数器等于0x40时,可产生提前唤醒中断,允许系统在窗口期结束前提前唤醒,以执行一些预定义的操作,例如降低功耗。
总体而言,WWDG在需要对程序运行时效性进行监测的场合,尤其是对程序执行时间有严格要求的应用中,发挥着重要的作用。
计数器初始值: WWDG在启动时,计数器的初始值被设定为窗口上限值。
计数值递减: 计数器的数值递减,不断减小。
窗口上限值: 窗口上限值是一个阈值,当计数器的值大于窗口上限值时,WWDG可以产生中断。
可产生中断: 在窗口期内,当计数器的值大于窗口上限值时,WWDG可以产生中断。这时,系统可以执行一些预定义的中断服务程序。
窗口下限值,产生复位: 当计数器的值小于等于窗口下限值时,WWDG将触发复位。窗口下限值是一个临界值,如果计数器的值在窗口期内没有被重载,则会导致复位。
窗口期,可以喂狗: 在窗口期内,如果计数器的值大于窗口下限值(0x3F),可以通过喂狗的方式重载计数器的值,防止触发复位。
非窗口期,喂狗则会复位: 如果计数器的值小于等于窗口下限值,表示已经超过了窗口期,此时喂狗将无效,WWDG将触发复位。
注意:窗口期的起始和结束条件取决于窗口上限值和窗口下限值的设置,以及对计数器的喂狗操作。确保窗口下限值(0x3F)小于窗口上限值,否则将无法形成有效的窗口期。
时钟源: PCLK(Peripheral Clock)是来自RCC(Reset and Clock Control)时钟控制器的时钟源。不同型号的STM32芯片,其PCLK的频率可能不同,例如,F103系列是36MHz,F407系列是42MHz,F429系列是45MHz,F767系列是54MHz,H743系列是100MHz,等等。
分频: 时钟源通过一个固定的分频系数(4096)进入WDG预分频器(WDGTB)。
递减计数器(CNT): WDGTB输出的时钟进入看门狗控制寄存器(WWDG_CR),其中T0到T5是一个6位的递减计数器,T6用于判断是否处于窗口期。
看门狗配置寄存器(WWDG_CFR): 该寄存器用于配置WWDG的一些参数,包括窗口上限值W[6:0]和比较器(COM)。
看门狗控制寄存器(WWDG_CR): 包含递减计数器的当前值T0-T5,判断位T6,激活位WDGA等。
比较器: 当T0到T5的值大于窗口上限值W[6:0]时,比较器输出为1。
喂狗操作: 通过写WWDG_CR寄存器,喂狗操作即将递减计数器的当前值重新加载到W[6:0],这样可以避免复位。在窗口期内有效,可以通过该操作刷新看门狗计数。
判断是否复位: 复位的判断条件包括T6为0和WDGA激活位为1,同时还要判断是否在窗口期内进行了喂狗操作。如果不在窗口期内进行喂狗或者T6为0,则WWDG将触发复位。
总的来说,WWDG的工作框图涉及到时钟源的分频、递减计数器、窗口配置、比较器、喂狗操作,以及复位的判断条件。这些都是为了保证WWDG的正常工作和对程序运行的监测。
控制寄存器(WWDG_CR)是WWDG的一个重要寄存器,具体说明如下:
T[6:0] - 递减计数器: 这是一个7位的字段,用来存储WWDG的计数器的值。它随时更新,每隔(4096 × 2^WDGTB[2:0])PCLK 个周期减 1。当计数器的值从0x40变为0x3F时,将产生看门狗复位。
WDGA - 看门狗激活位: 这是看门狗的激活位,由软件设置为1来启动看门狗。需要注意的是,一旦设置了该位,只有在硬件复位后才能被清零。这是为了防止在程序中无法清除WDGA位,确保看门狗只能在硬件复位后重新激活。
综合起来,WWDG_CR寄存器的T[6:0]字段存储递减计数器的值,WDGA位用于启动或关闭看门狗,并有特殊的清零条件。配置和操作该寄存器是设置WWDG工作模式的重要步骤。
WWDG_CFR寄存器是WWDG的配置寄存器,具体说明如下:
EWI(位9)- 提前唤醒中断使能: 如果该位被设置为1,当递减计数器等于0x40时,会产生提前唤醒中断。这意味着,如果启用了该位并配置了相应的中断,当计数器达到窗口下限时会触发中断。
WDGTB[1:0](位8:7)- 预分频器 定时器时基: 这是一个2位的字段,用于配置WWDG的预分频系数。根据配置的不同值,可以选择不同的时基来递减计数器。
W[6:0](位[6:0])- 7位窗口值: 这是一个7位的字段,用于配置窗口看门狗的窗口上限值。在窗口期内,如果递减计数器的值大于W[6:0],则不会产生复位。在窗口期外,任何时候喂狗都会导致复位。
总的来说,WWDG_CFR寄存器的配置对WWDG的工作模式和中断产生条件起着关键作用。
该寄存器中的 EWI 位是提前唤醒中断,如果该位置 1,当递减计数器等于 0x40 时产生提前唤醒中断,我们就可以及时喂狗以避免 WWDG 复位。 因此,我们一般都会用该位来设置中断,当窗口看门狗的计数器值减到 0X40 的时候,如果该位设置,并开启了中断,则会产生中断,我们可以在中断里面向WWDG_CR 重新写入计数器的值,来达到喂狗的目的。注意这里在进入中断后,必须在不大于 1 个窗口看门狗计数周期的时间(在 pclk1 频率为 36M 且 WDGTB为 0 的条件下,该时间为 113us)内重新写 WWDG_CR,否则,看门狗将产生复位!
WWDG_SR寄存器是窗口看门狗的状态寄存器,用于提供有关WWDG的状态信息。在WWDG工作期间,可以读取该寄存器来获取当前的状态。以下是该寄存器的主要位说明:
EWIF(位0) - 提前唤醒中断标志: 当EWI(提前唤醒中断使能)位被设置为1,且递减计数器等于0x40时,会产生提前唤醒中断。该标志位在中断发生后由硬件自动清零,可以用于检测中断是否已经发生。
通过读取WWDG_SR寄存器的EWIF位,可以了解WWDG是否因为递减计数器达到0x40而产生了提前唤醒中断。
该寄存器用来记录当前是否有提前唤醒的标志。该寄存器仅有位 0 有效,其他都是保留位。
当计数器值达到 0x40 时,此位由硬件置 1。它必须通过软件写 0 来清除。对此位写 1 无效。即使中断未被使能,在计数器的值达到 0x40 的时候,此位也会被置 1。
T out = 4096 × 2 W D G T B × ( T [ 5 : 0 ] + 1 ) F wwdg T_{\text{out}} = \frac{4096 \times 2^{WDGTB} \times (T[5:0] + 1)}{F_{\text{wwdg}}} Tout=Fwwdg4096×2WDGTB×(T[5:0]+1)
其中:
这个公式反映了WWDG超时时间与预分频系数、计数器的当前值以及时钟源频率之间的关系。
推导WWDG的超时时间计算公式可以分为以下步骤:
计算WWDG工作的时钟频率:
F 工作频率 = F wwdg 4096 × 2 W D G T B F_{\text{工作频率}} = \frac{F_{\text{wwdg}}}{4096 \times 2^{WDGTB}} F工作频率=4096×2WDGTBFwwdg
其中, F wwdg F_{\text{wwdg}} Fwwdg 是WWDG的时钟源频率,4096是WWDG的固定预分频系数, 2 W D G T B 2^{WDGTB} 2WDGTB 是WWDG_CFR寄存器设置的预分频系数值。
计算一个计数周期所需的时间:
Time per count = 4096 × 2 W D G T B F wwdg \text{Time per count} = \frac{4096 \times 2^{WDGTB}}{F_{\text{wwdg}}} Time per count=Fwwdg4096×2WDGTB
WWDG的超时时间:
T out = Time per count × ( T [ 5 : 0 ] + 1 ) T_{\text{out}} = \text{Time per count} \times (T[5:0] + 1) Tout=Time per count×(T[5:0]+1)
即 T out = 4096 × 2 W D G T B × ( T [ 5 : 0 ] + 1 ) F wwdg T_{\text{out}} = \frac{4096 \times 2^{WDGTB} \times (T[5:0] + 1)}{F_{\text{wwdg}}} Tout=Fwwdg4096×2WDGTB×(T[5:0]+1)
其中, T [ 5 : 0 ] T[5:0] T[5:0] 是WWDG计数器的低6位,表示计数器的当前值。
这样就得到了WWDG超时时间的计算公式。这个公式反映了WWDG的超时时间与时钟源频率、预分频系数以及计数器的当前值之间的关系。
在STM32F1系列中,WWDG的时钟源频率 F wwdg F_{\text{wwdg}} Fwwdg 通常为PCLK1的频率,而PCLK1为36MHz。假设WDGTB设置为八分频( 2 3 2^3 23),我们可以使用推导公式来计算WWDG的最短和最长超时时间:
计算WWDG工作的时钟频率:
F wwdg = 36 MHz 4096 × 2 3 F_{\text{wwdg}} = \frac{36 \, \text{MHz}}{4096 \times 2^3} Fwwdg=4096×2336MHz
计算一个计数周期所需的时间:
Time per count = 4096 × 2 3 36 MHz \text{Time per count} = \frac{4096 \times 2^3}{36 \, \text{MHz}} Time per count=36MHz4096×23
WWDG的最短超时时间:
T out_min = Time per count × ( 0 + 1 ) ≈ 0.9102 m s T_{\text{out\_min}} = \text{Time per count} \times (0 + 1) ≈ 0.9102ms Tout_min=Time per count×(0+1)≈0.9102ms
WWDG的最长超时时间:
T out_max = Time per count × ( 63 + 1 ) ≈ 58.254 m s T_{\text{out\_max}} = \text{Time per count} \times (63 + 1) ≈ 58.254ms Tout_max=Time per count×(63+1)≈58.254ms
你可以使用上述公式计算出具体的数值,其中 T out_min T_{\text{out\_min}} Tout_min 表示WWDG的最短超时时间, T out_max T_{\text{out\_max}} Tout_max 表示WWDG的最长超时时间。
WWDG工作参数初始化: 在这一步中,需要填写WWDG_InitTypeDef 结构体的成员,主要包括预分频系数(Prescaler)和窗口寄存器的值(WindowValue)。这些值将影响WWDG的工作时钟和窗口期。
WWDG_InitTypeDef wwdg_init;
wwdg_init.Prescaler = WWDG_PRESCALER_8; // 根据需求选择合适的预分频系数
wwdg_init.WindowValue = 127; // 窗口寄存器的值,取决于应用需求
HAL_WWDG_Init(&wwdg_init);
WWDG Msp初始化: 在这一步中,可以配置NVIC(Nested Vectored Interrupt Controller)和一些与时钟有关的设置。
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{
// 配置NVIC,设置WWDG的中断优先级
HAL_NVIC_SetPriority(WWDG_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(WWDG_IRQn);
// 可以进行其他与时钟相关的配置
// ...
}
设置优先级,使能中断: 这一步是为了配置WWDG的中断。可以使用HAL_NVIC_SetPriority和HAL_NVIC_EnableIRQ函数。
编写中断服务函数: 如果WWDG计数器达到窗口值,会触发中断。编写相应的中断服务函数处理中断事件。
void WWDG_IRQHandler(void)
{
HAL_WWDG_IRQHandler(&hwwdg); // 调用HAL库提供的处理函数
}
重定义提前唤醒回调函数: 如果需要在WWDG提前唤醒中执行特定操作,可以重定义提前唤醒的回调函数。
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
// 在这里执行提前唤醒时的操作
// ...
}
在窗口期内喂狗: 在正常的程序运行中,需要定期刷新WWDG计数器。可以使用HAL_WWDG_Refresh函数进行喂狗。
HAL_WWDG_Refresh(&hwwdg);
这些步骤应该根据具体的应用需求进行调整。
WWDG_HandleTypeDef 结构体:
WWDG_TypeDef *Instance
: WWDG 寄存器基地址WWDG_InitTypeDef Init
: WWDG 初始化参数WWDG_InitTypeDef 结构体:
uint32_t Prescaler
: 预分频系数uint32_t Window
: 窗口值uint32_t Counter
: 计数器值uint32_t EWIMode
: 提前唤醒中断使能函数 HAL_WWDG_Init:
函数 HAL_WWDG_Refresh:
这些结构体和函数提供了对窗口看门狗的初始化和喂狗操作。你可以使用这些函数来配置和操作 WWDG,确保系统的稳定性。
预设:计数器值为0x7F,窗口值为0x5F,预分频系数为8,计算非窗口期时间和最大的超时时间的确如下:
WWDG的超时时间计算公式:
Tout = 4096 × 2 WDGTB × ( T [ 5 : 0 ] + 1 ) / F wwdg \text{Tout} = 4096 \times 2^{\text{WDGTB}} \times (T[5:0]+1) / F_{\text{wwdg}} Tout=4096×2WDGTB×(T[5:0]+1)/Fwwdg
非窗口期时间:
其中,
代入计算:
T 非窗口期 = 4096 × 8 × ( 127 − 95 ) / 36000 ≈ 29.13 ms \text{T}_{\text{非窗口期}} = 4096 \times 8 \times (127-95) / 36000 \approx 29.13 \, \text{ms} T非窗口期=4096×8×(127−95)/36000≈29.13ms
最大的超时时间:
Tmax = 4096 × 8 × ( 127 − 63 ) / 36000 ≈ 58.25 ms \text{Tmax} = 4096 \times 8 \times (127-63) / 36000 \approx 58.25 \, \text{ms} Tmax=4096×8×(127−63)/36000≈58.25ms
这表示在窗口看门狗的配置下,系统每隔约 29.13ms 就要喂狗一次,最大的超时时间为约 58.25ms。这些数值可以用来确保系统在正常运行的情况下不会因为看门狗复位。
wdg.c
#include "./BSP/WDG/wdg.h"
#include "./BSP/LED/led.h"
#include "./SYSTEM/delay/delay.h"
WWDG_HandleTypeDef g_wwdg_handle;
/* 窗口看门狗初始化函数 */
void wwdg_init(uint8_t tr, uint8_t wr, uint32_t fprer)
{
// 设置 WWDG 句柄实例和初始化参数
g_wwdg_handle.Instance = WWDG;
g_wwdg_handle.Init.Counter = tr; // 计数器值
g_wwdg_handle.Init.Window = wr; // 窗口值
g_wwdg_handle.Init.Prescaler = fprer; // 预分频系数
g_wwdg_handle.Init.EWIMode = WWDG_EWI_ENABLE; // 启用提前唤醒中断模式
HAL_WWDG_Init(&g_wwdg_handle); // 初始化 WWDG
}
/* WWDG MSP回调函数 */
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{
// 使能 WWDG 时钟
__HAL_RCC_WWDG_CLK_ENABLE();
// 配置 WWDG 中断优先级和抢占优先级
HAL_NVIC_SetPriority(WWDG_IRQn, 2, 3);
// 使能 WWDG 中断
HAL_NVIC_EnableIRQ(WWDG_IRQn);
}
/* WWDG中断服务函数 */
void WWDG_IRQHandler(void)
{
// 处理 WWDG 中断
HAL_WWDG_IRQHandler(&g_wwdg_handle);
}
/* WWDG提前唤醒回调函数 */
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
// 在提前唤醒回调中刷新 WWDG 计数器,同时切换 LED1 状态
HAL_WWDG_Refresh(&g_wwdg_handle);
LED1_TOGGLE();
}
wdg.h
#ifndef __WDG_H
#define __WDG_H
#include "./SYSTEM/sys/sys.h"
extern WWDG_HandleTypeDef g_wwdg_handle;
void wwdg_init(uint8_t tr, uint8_t wr, uint32_t fprer);
#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/WDG/wdg.h"
int main(void)
{
// 初始化HAL库
HAL_Init();
// 设置时钟为72MHz
sys_stm32_clock_init(RCC_PLL_MUL9);
// 初始化延时函数
delay_init(72);
// 串口初始化,波特率设置为115200
usart_init(115200);
// 初始化LED
led_init();
// 检测是否是窗口看门狗复位
if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET)
{
printf("窗口看门狗复位\r\n");
// 清除复位标志
__HAL_RCC_CLEAR_RESET_FLAGS();
}
else
{
printf("外部复位\r\n");
}
delay_ms(500);
printf("请在窗口期内喂狗\r\n\r\n");
// 初始化窗口看门狗,计数器值为0x7f,窗口值为0x5f,预分频系数为8
wwdg_init(0x7f, 0x5f, WWDG_PRESCALER_8);
while (1)
{
delay_ms(90);
// 刷新窗口看门狗计数器,并切换LED状态
HAL_WWDG_Refresh(&g_wwdg_handle);
LED0_TOGGLE();
}
}
时钟源:
复位条件:
中断:
递减计数器位数:
应用场合: