一、WWDG介绍
全称:Windows watchdog,即窗口看门狗;
本质:能产生系统复位信号和提前唤醒中断的计数器;
特性:
1、递减计数器;
2、产生复位信号的条件有两个:当递减计数器从0x40减至0x3F时会产生复位信号;当计数器的值大于W[6:0]值时,喂狗会产生复位信号。
3、提前唤醒中断(EWI):当递减计数器等于0x40时,会产生一个提前唤醒中断。
喂狗:在窗口期内重装载计数器的值,防止复位信号的产生;
作用:用于监测单片机程序运行时效是否准确,主要检测软件异常。
应用:在需要精准检测程序运行时间的场合;
二、WWDG工作原理
产生复位的条件:1、当递减计数器的值小于0x40,(若看门狗被启动)则产生复位。2、 当递减计数器在窗口外被重新装载,(若看门狗被启动)则产生复位。
产生中断的条件:启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI)。
三、WWDG工作原理图
四、WWDG相关寄存器
控制寄存器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 频率为 36M 且 WDGTB 为 0 的条件下,该时间为 113us)内重新写 WWDG_CR,否则,看门狗将产生复位!
状态寄存器(WWDG_SR)
用于判断是否发生了WWDG提前唤醒中断。
该寄存器用来记录当前是否有提前唤醒的标志。该寄存器仅有位 0 有效,其他都是保留位。 当计数器值达到 0x40 时,此位由硬件置 1。它必须通过软件写 0 来清除。对此位写 1 无效。即 使中断未被使能,在计数器的值达到 0x40 的时候,此位也会被置 1。
五、WWDG超时时间计算
六、WWDG配置步骤
(1)WWDG工作参数初始化: HAL_WWDG_Init()
(2)WWDG Msp初始化: HAL_WWDG_MspInit()配置NVIC、CLOCK等
(3)设置优先级,使能中断: HAL_NVIC_SetPriority()、 HAL_NVIC_EnableIRQ()
(4)编写中断服务函数: WWDG_IRQHandler()àHAL_WWDG_IRQHandler
(5)重定义提前唤醒回调函数: HAL_WWDG_EarlyWakeupCallback()
(6)在窗口期内喂狗:HAL_WWDG_Refresh()
相关HAL库函数介绍
1.HAL_WWDG_Init 函数
WWDG 的初始化函数,其声明如下:
HAL_StatusTypeDef HAL_WWDG_Init(WWDG_HandleTypeDef *hwwdg);
形参 1 是 WWDG 句柄,WWDG_HandleTypeDef 结构体类型,其定义如下:
typedef struct
{
WWDG_TypeDef *Instance; /* WWDG 寄存器基地址 */
WWDG_InitTypeDef Init; /* WWDG 初始化参数 */
}WWDG_HandleTypeDef;
1)Instance:指向 WWDG 寄存器基地址。 2)Init:WWDG 初始化结构体,用于配置计数器的相关参数。 WWDG_InitTypeDef 这个结构体类型定义如下
typedef struct
{
uint32_t Prescaler; /* 预分频系数 */
uint32_t Window; /* 窗口值 */
uint32_t Counter; /* 计数器值 */
uint32_t EWIMode; /* 提前唤醒中断使能 */
} WWDG_InitTypeDef;
1) Prescaler:预分频系数,WWDG_PRESCALER_1\ WWDG_PRESCALER_2\ WWDG_PRESCALER_4\ WWDG_PRESCALER_8 四个值,分别表示 1~8 分频。
2)Window:窗口值,即上限值。
3)Counter:计数器值,用于保存要设置计数器的值。
4)EWIMode:提前唤醒中断使能。
2. HAL_WWDG_Refresh 函数
HAL_WWDG_Refresh 函数是窗口看门狗的喂狗函数。其声明如下:
HAL_StatusTypeDef HAL_WWDG_Refresh(WWDG_HandleTypeDef *hwwdg);
函数描述: 该函数实际就是往 CR 寄存器重写 Counte 这个预先保存的计数器值。
函数形参: 形参 1 是 WWDG_HandleTypeDef 结构体指针类型的 WWDG 句柄。
七、相关代码
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/wwdg/wwdg.h"
#include "./BSP/led/led.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
led_init(); /* LED初始化 */
led0(0); /* LED点亮 */
delay_ms(300); /* 延时 300ms*/
wwdg_init(0X7F,0X5F,WWDG_PRESCALER_8 ); /* 计数值为 7f,窗口值为 5f,分频数为 8 */
while(1)
{
led0(1);
}
}
wwdg.c
#include "./BSP/wwdg/wwdg.h"
#include "./BSP/led/led.h"
WWDG_HandleTypeDef wwdg_handler;
//窗口看门狗初始化函数
void wwdg_init(uint8_t tr,uint8_t wr,uint32_t fprer )
{
wwdg_handler.Instance= WWDG; //设置寄存器基地址;
wwdg_handler.Init.Counter= tr; //设置计数器的值;
wwdg_handler.Init.Prescaler= fprer; //设置预分频系数;
wwdg_handler.Init.Window= wr; //设置窗口值;
wwdg_handler.Init.EWIMode=WWDG_EWI_ENABLE; //设置使能窗口看门狗唤醒中断;
HAL_WWDG_Init(&wwdg_handler);
}
//WWDG.Msp回调函数
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{
__HAL_RCC_WWDG_CLK_ENABLE(); //使能窗口看门狗时钟;
HAL_NVIC_SetPriority(WWDG_IRQn, 2, 3); //设置抢占优先级,子优先级;
HAL_NVIC_EnableIRQ(WWDG_IRQn);
}
//WWDG中断服务函数
void WWDG_IRQHandler(void)
{
HAL_WWDG_IRQHandler(&wwdg_handler);//HAL库函数中断服务函数,会进行回调函数
}
//WWDG提前唤醒函数
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
HAL_WWDG_Refresh(&wwdg_handler);
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);//led1的状态发生转变。
}
led.c
#include "./BSP/led/led.h"
void led_init(void)
{
GPIO_InitTypeDef led_Init0;
__HAL_RCC_GPIOB_CLK_ENABLE();
led_Init0.Mode= GPIO_MODE_OUTPUT_PP;
led_Init0.Pin=GPIO_PIN_5;
led_Init0.Pull=GPIO_PULLUP;
led_Init0.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &led_Init0);
GPIO_InitTypeDef led_Init1;
__HAL_RCC_GPIOE_CLK_ENABLE();
led_Init1.Mode= GPIO_MODE_OUTPUT_PP;
led_Init1.Pin=GPIO_PIN_5;
led_Init1.Pull=GPIO_PULLUP;
led_Init1.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOE, &led_Init1);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5, GPIO_PIN_SET);
}
void led0(uint8_t i)
{
if(i==1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5, GPIO_PIN_SET);
}
else
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5, GPIO_PIN_RESET);
}