文章目录
基础知识
简介
- WWDG: Window watchdog,窗口看门狗
- 本质: 能产生系统复位信号和提前唤醒中断的计数器
- 特性:
- 递减的计数器
- 当递减计数器值从 0x40减到0x3F时复位(即T6位跳变到0)
- 计数器的值大于W[6:0]值时喂狗会复位
- 提前唤醒中断 (EWI):当递减计数器等于 0x40 时可产生
喂狗:在窗口期内重装载计数器的值,防止复位
作用
- 用于监测单片机程序运行时效是否精准,主要检测软件异常
- 应用:需要精准检测程序运行时间的场合(时钟来自于系统时钟)
工作原理
递减计数器的值递减过程中,当 T[6:0]>W[6:0]是不允许刷新 T[6:0]的值,即不允许喂狗,否则会产生复位。只有在 W[6:0]<T[6:0]< 0x3F 这个时间可以喂狗,这就是喂狗的窗口时间。当T[6:0]=0x3F,即 T6 位为 0 这一刻,也会产生复位。上限值 W[6:0]是由用户自己设置,但是一定要确保大于 0x40,否则就不存在上图的窗口了,下限值 0x40 是固定的,不可修改。
框图
- WWDG 有一个来自 RCC 的 PCLK1 输入时钟,经过一个 4096 的分频器(4096 分频在设计时已经设定死了,图中并没有给出来,但我们可以通过查看寄存器WWDG_CFR 的 WDGTB 位的描述知道),再经过一个分频系数可选(1、2、4、8)的可编程预分频器提供时钟给一个 7 位递减计数器。
- 结合寄存器分析窗口看门狗的上限值和下限值。W[6:0] 是 WWDG_CFR 寄存器的低 7 位,用于与递减计数器 T[6:0]比较的窗口值,也就是我们说的上限值,由用户设置。0x40 就是下限值,递减计数器达到这个值就会产生复位。T6 位就是WWDG_CR 寄存器的位 6,即递减计数器 T[6:0]的最高位。
寄存器
控制寄存器 WWDG_CR
- 该寄存器只有低八位有效,其中 T[6:0]用来存储看门狗的计数器的值,随时更新的,每隔(4096×2^ WDGTB[2:0])PCLK 个周期减 1。当该计数器的值从 0x40 变为 0x3F 的时候,将产生看门狗复位。
- WDGA 位则是看门狗的激活位,该位由软件置 1,启动看门狗,并且一定要注意的是该位一旦设置,就只能在硬件复位后才能清零了。
配置寄存器 WWDG_CFR
- 该寄存器中的 EWI 位是提前唤醒中断,如果该位置 1,当递减计数器等于 0x40 时产生提前唤醒中断,我们就可以及时喂狗以避免 WWDG 复位。因此,我们一般都会用该位来设置中断,当窗口看门狗的计数器值减到 0x40 的时候,如果该位设置,并开启了中断,则会产生中断,我们可以在中断里面向 WWDG_CR 重新写入计数器的值,来达到喂狗的目的。注意这里在进入中断后,必须在不大于 1 个窗口看门狗计数周期的时间(在 pclk1 频率为 42M 且 WDGTB 为0 的条件下,该时间为 97.52us)内重新写 WWDG_CR,否则,看门狗将产生复位!
状态寄存器 WWDG_SR
该寄存器用来记录当前是否有提前唤醒的标志。该寄存器仅有位 0 有效,其他都是保留位。当计数器值达到 0x40 时,此位由硬件置 1。它必须通过软件写 0 来清除。对此位写 1 无效。即使中断未被使能,在计数器的值达到 0x40 的时候,此位也会被置 1。
超时时间计算
- 计算公式
配置步骤
- 工作参数初始化
- Msp 初始化
- 设置优先级、使能中断
- 编写中断服务函数
- 重定义提前唤醒回调函数
- 在窗口期内喂狗
函数介绍
HAL_StatusTypeDef HAL_WWDG_Init(WWDG_HandleTypeDef *hwwdg)
{
/* Check the WWDG handle allocation */
if (hwwdg == NULL)
{
return HAL_ERROR;
}
typedef struct
#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */
{
WWDG_TypeDef *Instance; /*!< Register base address */
WWDG_InitTypeDef Init; /*!< WWDG required parameters */
#if (USE_HAL_WWDG_REGISTER_CALLBACKS == 1)
void (* EwiCallback)(struct __WWDG_HandleTypeDef *hwwdg); /*!< WWDG Early WakeUp Interrupt callback */
void (* MspInitCallback)(struct __WWDG_HandleTypeDef *hwwdg); /*!< WWDG Msp Init callback */
#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */
} WWDG_HandleTypeDef;
/* 初始化结构体 */
typedef struct
{
uint32_t Prescaler; /*!< Specifies the prescaler value of the WWDG.
This parameter can be a value of @ref WWDG_Prescaler */
uint32_t Window; /*!< Specifies the WWDG window value to be compared to the downcounter.
This parameter must be a number Min_Data = 0x40 and Max_Data = 0x7F */
uint32_t Counter; /*!< Specifies the WWDG free-running downcounter value.
This parameter must be a number between Min_Data = 0x40 and Max_Data = 0x7F */
uint32_t EWIMode ; /*!< Specifies if WWDG Early Wakeup Interrupt is enable or not.
This parameter can be a value of @ref WWDG_EWI_Mode */
} WWDG_InitTypeDef;
HAL_StatusTypeDef HAL_WWDG_Refresh(WWDG_HandleTypeDef *hwwdg)
{
/* Write to WWDG CR the WWDG Counter value to refresh with */
WRITE_REG(hwwdg->Instance->CR, (hwwdg->Init.Counter));
/* Return function status */
return HAL_OK;
}
实验
wwdg.c
- 初始化WWDG
WWDG_HandleTypeDef g_wwdg_handler; /* 窗口看门狗句柄 */
/**
* @brief 初始化窗口看门狗
* @param tr: T[6:0],计数器值
* @param tw: W[6:0],窗口值
* @note fprer:分频系数(WDGTB),范围:WWDG_PRESCALER_1~WWDG_PRESCALER_8,表示2^WDGTB分频
* Fwwdg=PCLK1/(4096*2^fprer). 一般PCLK1=42Mhz
* @retval 无
*/
void wwdg_init(uint8_t tr, uint8_t wr, uint32_t fprer)
{
g_wwdg_handler.Instance = WWDG;
g_wwdg_handler.Init.Prescaler = fprer; /* 设置分频系数 */
g_wwdg_handler.Init.Window = wr; /* 设置窗口值 */
g_wwdg_handler.Init.Counter = tr; /* 设置计数器值 */
g_wwdg_handler.Init.EWIMode = WWDG_EWI_ENABLE; /* 使能窗口看门狗提前唤醒中断 */
HAL_WWDG_Init(&g_wwdg_handler); /* 初始化WWDG */
}
- MSP回调函数
/**
* @brief WWDG底层驱动,时钟配置,中断配置
此函数会被HAL_WWDG_Init()调用
* @param hwwdg:窗口看门狗句柄
* @retval 无
*/
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{
__HAL_RCC_WWDG_CLK_ENABLE(); /* 使能窗口看门狗时钟 */
HAL_NVIC_SetPriority(WWDG_IRQn, 2, 3); /* 抢占优先级2,子优先级为3 */
HAL_NVIC_EnableIRQ(WWDG_IRQn); /* 使能窗口看门狗中断 */
}
- 中断服务函数
/**
* @brief 窗口看门狗中断服务程序
* @param 无
* @retval 无
*/
void WWDG_IRQHandler(void)
{
HAL_WWDG_IRQHandler(&g_wwdg_handler); /* 调用WWDG共用中断处理函数 */
}
- 提前唤醒回调函数
/**
* @brief 中断服务函数处理过程
此函数会被HAL_WWDG_IRQHandler()调用
* @param 无
* @retval 无
*/
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
HAL_WWDG_Refresh(&g_wwdg_handler); /* 更新窗口看门狗值 */
LED1_TOGGLE(); /* 绿灯闪烁 */
}
wwdg.h
#ifndef __WDG_H
#define __WDG_H
#include "./SYSTEM/sys/sys.h"
void wwdg_init(uint8_t tr, uint8_t wr, uint32_t fprer); /*窗口看门狗初始化*/
extern WWDG_HandleTypeDef g_wwdg_handle;
#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/WDG/wdg.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
LED0(0); /* 点亮LED0(红灯) */
if(__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET)
{
printf("窗口看门狗复位\r\n");
__HAL_RCC_CLEAR_RESET_FLAGS();
}
else
{
printf("外部复位\r\n\r\n");
}
printf("请在窗口期内喂狗!\r\n\r\n");
delay_ms(500);
wwdg_init(0X7F, 0X5F, WWDG_PRESCALER_8);/* 计数器值为7f,窗口寄存器为5f,分频数为8 */
while(1)
{
delay_ms(60); /* 窗口期为24.98~49.97ms,更改时间为20/40/60验证实验结果 */
HAL_WWDG_Refresh(&g_wwdg_handler);
LED0_TOGGLE(); /* LED0(红灯)闪烁 */
}
}