窗口看门狗概述
之所以称为窗口就是因为其喂狗时间是一个有上下限的范围内,可以通过设定相关寄存器,设定其上限时间(下限固定)。喂狗的时间不能过早也不能过晚。
为什么要窗口看门狗?
对于一般的看门狗,程序可以在它产生复位前的任意时刻刷新看门狗,但这有一个隐患,有可能程序跑乱了又跑回到正常的地方,或跑乱的程序正好执行了刷新看门狗操作,这样的情况下一般的看门狗就检测不出来了。
如果使用串口看门狗,程序员可以根据程序正常执行的时间设置刷新看门狗的一个时间窗口,保证不会提前刷新看门狗也不会滞后刷新看门狗,这样可以检测出程序没有按照正常的路径运行非正常地跳过了某些程序段的情况。
工作示意图
窗口看门狗框图
工作过程总结
STM32F的窗口看门狗中有一个7位的递减计数器T[6:0],它会在出现下述2种情况之一时产生看门狗复位:
- 当喂狗的时候如果计数器的值大于某一设定数值W[6:0]时,此设定数值在WWDG_CFR寄存器定义。
- 当计数器的数值从0x40减到0x3F时[T6位跳变为0]。
如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可以用于喂狗以避免WWDG复位。
窗口看门狗超时时间
36M时钟下窗口看门狗的最小最大超时表:
其他注意事项
- 上窗口值W[6:0]必须大于下窗口值0x40,否则就没有窗口了。
- 窗口看门狗时钟来源PCLK1(APB1总线时钟)分频后。
相关寄存器
控制寄存器WWDG_CR
相关库函数
void WWDG_Enable(uint8_t Counter); //启动并设置初始值
void WWDG_SetCounter(uint8_t Counter); //喂狗
配置寄存器WWDG_CFR
相关库函数
void WWDG_EnableIT(void); //使能提前唤醒中断
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler); //设置预分频系数
void WWDG SetWindowValue(uint8_t WindowValue); //设置窗口值
状态寄存器WWDG_SR
相关库函数
FlagStatus WWDG_GetFlagStatus(void); //查看标志位
void WWDG_ClearFlag(void); //清除标志位
窗口看门狗配置过程
- 使能看门狗时钟:
RCC_APB1PeriphClockCmd();
- 设置分频系数:
WWDG_SetPrescaler();
- 设置上窗口值:
WWDG_SetWindowValue();
- 开启提前唤醒中断并分组(可选):
WWDG_EnableIT();
NVIC_Init(); - 使能看门狗:
WWDG_Enable();
- 喂狗:
WWDG_SetCounter();
- 编写中断服务函数:
WWDG_IRQHandler();
窗口看门狗实验
led.c和led.h文件内容与跑马灯中的一样,直接移植过来即可,在这里不再展示。
by库函数
wwdg.h
#ifndef __WWDG_H
#define __WWDG_H
#include "sys.h"
void WWDG_Init(u8 tr,u8 wr,u32 fprer); //初始化WWDG
void WWDG_Set_Counter(u8 cnt); //设置WWDG的计数器
void WWDG_NVIC_Init(void); //WWDG NVIC中断初始化
#endif
wwdg.c
#include "wwdg.h"
#include "sys.h"
#include "led.h"
//保存WWDG计数器的设置值,默认最大
u8 WWDG_CNT=0X7F;
//初始化窗口看门狗
//tr: T[6:0],计数值
//wr: W[6:0],窗口值
//fprer: 预分频系数
//Fwwdg=PCLK1/(4096*2^fprer)
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);//WWDG时钟初始化
WWDG_CNT=tr&WWDG_CNT;//初始化WWDG_CNT
WWDG_SetPrescaler(fprer);//设置预分频系数
WWDG_SetWindowValue(wr);//设置上窗口值
WWDG_Enable(WWDG_CNT);//使能看门狗,并设置计数值
WWDG_ClearFlag();//清除提前唤醒中断标志位
WWDG_NVIC_Init();//初始化窗口看门狗NVIC
WWDG_EnableIT();//开启窗口看门狗中断
}
//冲设置WWDG计数器的值,即喂狗
void WWDG_Set_Counter(u8 cnt)
{
WWDG_SetCounter(cnt);//喂狗
}
//窗口看门狗中断服务程序
void WWDG_NVIC_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=WWDG_IRQn;//WWDG中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;//响应优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能中断
NVIC_Init(&NVIC_InitStructure);//NVIC初始化
}
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(WWDG_CNT);//喂狗
WWDG_ClearFlag();//清除提前唤醒中断标志位
LED1=!LED1;
}
main.c
#include "wwdg.h"
#include "led.h"
#include "delay.h"
#include "sys.h"
int main(void)
{
delay_init();//延时函数初始化
LED_Init();//LED初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC中断分组为2
LED0=0;//LED0灯亮
delay_ms(500);
WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);//窗口看门狗初始化
while(1)
{
LED0=1;
}
}
by寄存器
wwdg.h
#ifndef __WWDG_H
#define __WWDG_H
#include "sys.h"
void WWDG_Init(u8 tr,u8 wr,u32 fprer);//初始化WWDG
void WWDG_Set_Counter(u8 cnt);//设置WWDG的计数器,喂狗
void WWDG_NVIC_Init(void);
#endif
wwdg.c
#include "wwdg.h"
#include "sys.h"
#include "led.h"
u8 WWDG_CNT=0X7F;
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{
RCC->APB1ENR|=1<<11;//使能WWDG时钟
WWDG->CFR|=fprer<<7;//设置预分频系数
WWDG_CNT=tr&WWDG_CNT;//初始化WWDG_CNT
WWDG->CR|=WWDG_CNT<<0;//设置计数器值
WWDG->CFR&=0xff80;//上窗口值清零
WWDG->CFR|=wr;//初始化上窗口值
WWDG->CR|=1<<7;//使能窗口看门狗
MY_NVIC_Init(2,2,WWDG_IRQn,2);//设置WWDG NVIC中断
WWDG->SR=0x00;//清除提前中断标志
WWDG->CFR|=1<<9;//使能窗口看门狗中断
}
void WWDG_Set_Counter(u8 cnt)
{
WWDG->CR|=cnt;//设置上窗口值
}
void WWDG_IRQHandler()
{
WWDG->CR|=WWDG_CNT<<0;//设置上窗口值,喂狗
WWDG->SR=0x00;//清除提前中断标志
LED1=!LED1;
}
main.c
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "wwdg.h"
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
LED_Init();
LED0=0;
delay_ms(500);
WWDG_Init(0x7f,0x5f,0x03);//计数值为0x7f,上窗口值0x5f,预分频数8
while(1)
{
LED0=1;
}
}