32单片机——窗口看门狗
1、WWDG的简介
WWDG:Window watchdog,即窗口看门狗
窗口看门狗本质上是能产生系统复位信号和提前唤醒中断的递减计数器
WWDG产生复位信号的条件:
(1)当递减计数器值从0x40减到0x3F时复位(即T6位跳变到0)
(2)计数器的值大于W[6:0]值时喂狗会复位
(3)提前唤醒中断(EWI):当递减计数器等于0x40时可产生
喂狗:在窗口期(W[6:0]值~0x40)内重装载计数器的值,防止复位
作用:用于监测单片机程序运行时效是否精准,主要检测软件异常
应用:需要精准检测程序运行时间的场合
2、WWDG的工作原理
3、WWDG的框图
4、WWDG超出时间计算
:WWDG超出时间
:WWDG的时钟源频率,36MHZ
4096:WWDG固定的预分频系数
:wwDG_CFR寄存器设置的预分频系数值
T[5:0]:WWDG计数器低6位
5、WWDG的配置步骤
(1)WWDG工作参数初始化
HAL_WWDG_Init();
HAL_StatusTypeDef HAL_WWDG_Init(WWDG_HandleTypeDef *hwwdg);
{
WWDG_TypeDef *Instance; /* 寄存器基地址 */WWDG_InitTypeDef Init; /* WWDG必需参数 */
#if (USE_HAL_WWDG_REGISTER_CALLBACKS == 1)
void (* EwiCallback)(struct __WWDG_HandleTypeDef *hwwdg);void (* MspInitCallback)(struct __WWDG_HandleTypeDef *hwwdg);
#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */
} WWDG_HandleTypeDef;
typedef struct
{
uint32_t Prescaler; /* 预分频系数 */uint32_t Window; /* 0x3f<窗口值≤W[6:0] */
uint32_t Counter; /* 计数器值:0x40~0x7F */
uint32_t EWIMode ; /* 提前唤醒中断使能 */
} WWDG_InitTypeDef;
Prescaler的参数如下所示:
#define WWDG_PRESCALER_1 0x00000000u
/* WWDG计数器时钟=(PCLK1/4096)/1 */
#define WWDG_PRESCALER_2 WWDG_CFR_WDGTB_0/*!< WWDG计数器时钟=(PCLK1/4096)/2 */
#define WWDG_PRESCALER_4 WWDG_CFR_WDGTB_1/*!< WWDG计数器时钟=(PCLK1/4096)/4 */
#define WWDG_PRESCALER_8 (WWDG_CFR_WDGTB_1 | WWDG_CFR_WDGTB_0) /* WWDG计数器时钟=(PCLK1/4096)/8 */
EWIMode的参数如下所示:
#define WWDG_EWI_DISABLE 0x00000000u /* EWI禁用 */
#define WWDG_EWI_ENABLE WWDG_CFR_EWI /* EWI启用 */
(2)WWDG的Msp初始化
HAL_WWDG_MspInit();
__weak void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg);
(3)设置优先级,使能中断
HAL_NVIC_SetPriority();
在32单片机——外部中断_32中断-CSDN博客中解释过
(4)编写中断服务函数
WWDG_IRQHandler(); //设置在startup_stm32f103xe.s中
HAL_WWDG_IRQHandler(); //中断公共处理函数
void HAL_WWDG_IRQHandler(WWDG_HandleTypeDef *hwwdg)
{
/* Check if Early Wakeup Interrupt is enable */
if (__HAL_WWDG_GET_IT_SOURCE(hwwdg, WWDG_IT_EWI) != RESET)
{
/* Check if WWDG Early Wakeup Interrupt occurred */
if (__HAL_WWDG_GET_FLAG(hwwdg, WWDG_FLAG_EWIF) != RESET)
{
/* Clear the WWDG Early Wakeup flag */
__HAL_WWDG_CLEAR_FLAG(hwwdg, WWDG_FLAG_EWIF);#if (USE_HAL_WWDG_REGISTER_CALLBACKS == 1)
/* Early Wakeup registered callback */
hwwdg->EwiCallback(hwwdg);
#else
/* Early Wakeup callback */
HAL_WWDG_EarlyWakeupCallback(hwwdg);
#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */
}
}
}
(5)重定义提前唤醒回调函数
HAL_WWDG_EarlyWakeupCallback();
(6)在窗口期内喂狗
HAL_WWDG_Refresh();
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;
}
6、例子
先点亮LED0延时300ms后,初始化窗口看门狗,进入死循环,关闭LED0。然后等待窗口看门狗中断的到来,在中断里面,喂狗,并执行LED1的翻转操作。我们将通过LED0来指示STM32F1是否被复位了,如果被复位了就会点亮300ms。LED1用来指示中断喂狗,每次中断喂狗翻转一次。
wwdg.h
#ifndef _WWDG_H
#define _WWDG_H
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"//wwdg初始化
/*
参数1:定时器初始值
参数2:窗口值
参数3:预分频系数
*/
void wwdg_init(uint8_t tr,uint8_t wr,uint8_t prer);#endif
wwdg.c
#include "./BSP/WWDG/wwdg.h"
WWDG_HandleTypeDef wwdg_handle; //wwdg句柄
void wwdg_init(uint8_t tr,uint8_t wr,uint8_t prer){
//初始化wwdg的工作参数
wwdg_handle.Instance=WWDG; //寄存器基地址
wwdg_handle.Init.Counter=tr; //计数器值
wwdg_handle.Init.EWIMode=WWDG_EWI_ENABLE; //提前唤醒中断使能
wwdg_handle.Init.Prescaler=prer; //预分频系数
wwdg_handle.Init.Window=wr; //窗口值
HAL_WWDG_Init(&wwdg_handle);
}
//窗口看门狗的Msp回调:一般用来设置GPIO、NVIC、CLOCK
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg){
__HAL_RCC_WWDG_CLK_ENABLE(); //使能时钟
HAL_NVIC_SetPriority(WWDG_IRQn,0,0); //设置优先级
HAL_NVIC_EnableIRQ(WWDG_IRQn); //使能提前唤醒中断
}
//窗口看门狗提前唤醒中断的服务函数
void WWDG_IRQHandler(void){
HAL_WWDG_IRQHandler(&wwdg_handle); //中断公共处理函数
}
//提前唤醒中断的回调函数
//在此函数内写中断逻辑代码
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg){
if(hwwdg->Instance==WWDG){
//喂狗
HAL_WWDG_Refresh(hwwdg);
//HAL_WWDG_Refresh(&wwdg_handle); //效果同上
}
}
main.c
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/KEY/key.h"
#include "./BSP/WWDG/wwdg.h"
int main(void){
HAL_Init();
sys_stm32_clock_init(RCC_PLL_MUL9); //设置系统时钟
delay_init(72);
led_init();
beep_init();
key_init();
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
delay_ms(100);
wwdg_init(0x7f,0x5f,WWDG_PRESCALER_8); //初始化wwdg
while (1){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
}
}