窗口看门狗
窗口看门狗概述
之所以称为窗口就是因为其喂狗时间是一个有上下限的范围内(窗口),你可以通过设定相关寄存器,设定其上限时间(下限固定)。喂狗的时间不能过早也不能过晚。
而独立看门狗限制喂狗时间在0-x内,x由相关寄存器决定。喂狗的时间不能过晚。
窗口看门狗工作示意图
计数器从初值计数到上窗口的值,不能喂狗,必须在上窗口值到0x3f值这个过程中喂狗。0x3f=0011 1111,所以0x3f上面一个数是0100 0000,也就是说t6位由1跳变为0.如果说t6位由1跳变为0,并且仍然没有喂狗,那么就复位。
窗口看门狗框图:
窗口看门狗工作过程总结
STM32F的窗口看门狗中有一个7位的递减计数器T[6:0],它会在出现下述2种情况之一时产生看门狗复位:
当喂狗的时候如果计数器的值大于某一设定数值W[6:0]时,此设定数值在WWDG_CFR寄存器定义。
当计数器的数值从0x40减到0x3F时【T6位跳变到0】。
如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可以用于喂狗以避免WWDG复位。中断时间不宜过长。
窗口看门狗超时时间
为什么要窗口看门狗?
对于一般的看门狗,程序可以在它产生复位前的任意时刻刷新看门狗,但这有一个隐患,有可能程序跑乱了又跑回到正常的地方,或跑乱的程序正好执行了刷新看门狗操作,这样的情况下一般的看门狗就检测不出来了;
如果使用窗口看门狗,程序员可以根据程序正常执行的时间设置刷新看门狗的一个时间窗口,保证不会提前刷新看门狗也不会滞后刷新看门狗,这样可以检测出程序没有按照正常的路径运行、非正常地跳过了某些程序段的情况。
窗口看门狗注意事项:
上窗口值W[6:0]必须大于下窗口值0x40。否则就无窗口了。
窗口看门狗时钟来源PCLK1(APB1总线时钟)分频后。
常用寄存器和库函数配置
控制寄存器WWDG_CR
配置寄存器WWDG_CFR
状态寄存器WWDG_SR
可以通过查这个标志位来判断是否到达0x40.
WWDG操作HAL库函数
HAL_WWDG_Start是启动看门狗,HAL_WWDG_Start_IT还开启了提前唤醒中断
HAL_StatusTypeDef HAL_WWDG_Init(WWDG_HandleTypeDef *hwwdg);
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg);
HAL_StatusTypeDef HAL_WWDG_Start(WWDG_HandleTypeDef *hwwdg);
HAL_StatusTypeDef HAL_WWDG_Start_IT(WWDG_HandleTypeDef *hwwdg);
HAL_StatusTypeDef HAL_WWDG_Refresh(WWDG_HandleTypeDef *hwwdg);
void HAL_WWDG_IRQHandler(WWDG_HandleTypeDef *hwwdg);
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg);
窗口看门狗配置过程
1.使能窗口看门狗时钟:
HAL_WWDG_MspInit中
2.初始化窗口看门狗:设置分频系数,窗口值,计数值等。
HAL_WWDG_Init();
该函数还可以使能窗口看门狗提前唤醒中断 。
3.设置提前唤醒中断优先级:
HAL_WWDG_MspInit
4.编写提前唤醒中断处理函数,喂狗:
HAL_WWDG_EarlyWakeupCallback();
HAL_WWDG_Refresh();
窗口看门狗实验
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "exti.h"void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{__HAL_RCC_WWDG_CLK_ENABLE();//使能时钟HAL_NVIC_EnableIRQ(WWDG_IRQn); //使WWDG_IRQn断通道HAL_NVIC_SetPriority(WWDG_IRQn,3,3); //抢占优先级3,子优先级3
}
WWDG_HandleTypeDef wwdg_handler;void WWDG_IRQHandler(void)
{HAL_WWDG_IRQHandler(&wwdg_handler);}
void HAL_WWDG_WakeupCallback(WWDG_HandleTypeDef* hwwdg)
{HAL_WWDG_Refresh(&wwdg_handler,0x7f);LED1=!LED1;
}
int main(void)
{HAL_Init(); //初始化HAL库 Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhzdelay_init(180); //初始化延时函数uart_init(115200); //初始化USARTLED_Init(); //初始化LED EXTI_Init();LED0 = 0;delay_ms(300);wwdg_handler.Instance=WWDG; wwdg_handler.Init.Counter=0x7f;//初始值wwdg_handler.Init.Prescaler=WWDG_PRESCALER_8;wwdg_handler.Init.Window=0x5f;//上窗口值HAL_WWDG_Init(&wwdg_handler);HAL_WWDG_Start_IT(&wwdg_handler);while(1){LED0=1;}
}
PCLK1=180/4=45MHZ
45MHZ/4096/8=1373hz
0x7f-0x40+1个时钟=64个时钟
64/1373=0.0466s=22hz
经过0.0466s后执行中断服务函数。
在之前肯定会进入while循环把LED0灯灭掉。
执行中断服务函数之后,LED1灯会进行反转,并对计数值刷新 重新从0x7f计数。
每隔0.0466s,LED1进行一次翻转。开启后LED0只进行一次闪烁。
如果把回调函数中的喂狗操作删掉,那么进行到0x3f后会进行复位,led0会一直进行闪烁。
进行到0x40时,LED1翻转,然后又进行到0x3f,程序又进行复位。最终led0和led1都会闪烁,而且他们的频率相近。