一、窗口看门狗概述
1.1 窗口看门狗
之所以成为窗口就是因为其喂狗时间是一个有上下限的范围内(窗口),你可以通过设定相关寄存器,设定其上限时间(下限固定)。喂狗的时间不能过早也不能过晚。
而独立看门狗限制喂狗时间在0-x内,x由相关寄存器决定。喂狗的时间不能过晚。
图1 窗口看门狗工作示意图
图一里计数器必须要在到达刷新窗口上下限值的中间才可以喂狗,当计数器的值到达了刷新窗口的上限时T6位会跳变为零,而一旦过了刷新窗口的区间就会进行复位。
图2 窗口看门狗框图
首先窗口看门狗的时钟来源是PCLK1,它在经过预分频之前需要除以4096,之后计数器的递减由该时钟控制。WWDG_CR寄存器的T7位是启动位,用来对该寄存器进行使能,WWDG_CFR寄存器与该寄存器的值会经过一个比较器,当控制寄存器(WWDG_CR)的值T6:0>配置寄存器(WWDG_CFR)的值W6:0,也就是计数器值处在不允许刷新区间时进行喂狗的话就会产生看门狗复位。第二种情况下,当计数器的值小于0x3F,也就是T6位跳变为0的时候,由于这里进入或门存在一个非门,所以T6跳变为0后进入或门就是1。也就是当计数器超过了刷新窗口区间,看门狗也会进行复位。
1.2 窗口看门狗工作过程总结
STM32F的窗口看门狗中有一个7位的递减计数器T[6:0],它会在以下两种情况下产生看门狗复位。
①当计数器的值大于某一设定数值W[6:0]时喂狗,次设定数值在WWDG_CFR寄存器定义。
②当计数器的数值从0x40减到0x3F时[T6位跳变到0]
如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),他可以用于喂狗以避免WWDG复位。
1.3 窗口看门狗超时时间
窗口看门狗的超时公式如下:
其中:
Twwdg:WWDG超时时间(单位为ms)
Fpclk1:APB1的时钟频率(单位为Khz)
WDGTB:WWDG的预分频系数
T[5:0]:窗口看门狗的计数器低6位
1.4 为什么要窗口看门狗
对于一般的看门狗,程序可以在它产生复位前的任意时刻刷新看门狗,但这存在一个隐患,有可能程序跑乱了又跑回到正常的地方,或跑乱的程序正好执行了刷新看门狗操作,这样的情况下一般的看门狗就检测不出来了;
如果使用窗口看门狗,程序员可以根据程序正常执行的时间设置刷新看门狗的一个时间窗口,保证不会提前刷新看门狗也不会滞后刷新看门狗,这样可以检测出程序非正常地跳过了某些程序段的情况。
1.5 窗口看门狗其他注意事项
①上窗口值W[6:0]必须大于下窗口值0x40,否则就无窗口了。
②窗口看门狗时钟来源PCLK1(APB1总线时钟)分频后。
二、常用寄存器和库函数配置
2.1 控制寄存器WWDG_CR
CR寄存器里位7就是看门狗的使能位,这里置1启动置0禁止。位0~6就是用来存储看门狗计数器的值,每()个PCLK1周期减1,当计数器值从40h变为3Fh时(T6变成0),产生看门狗复位。
CR寄存器对应库函数
①void WWDG_Enable(uint8_t Counter);//启动并设置初始值
②void WWDG_SetCounter(uint8_t Counter);//喂狗
2.2 配置寄存器WWDG_CFR
CFR寄存器位0~6用来设置上窗口值,位7~8为时基用来设置位分频系数来确定看门狗的时钟频率,位9是提前唤醒中断,我们把该位设置为1,当计数器值达到40h就会产生中断。
CFR寄存器对应库函数
①void WWDG_EnableIT(void);//使能提前唤醒中断
②void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
③void WWDG_SetWindowValue(uint8_t WindowValue);
2.3 状态寄存器WWDG_SR
SR寄存器只有一个标志位,当我们提前唤醒中断,当计数器值达到40H的时候,硬件就会置1。
SR寄存器对应库函数
①FlagStatus WWDG_GetFlagStatus(void);
②void WWDG_ClearFlag(void);
三、窗口看门狗实验
3.1 窗口看门狗配置过程
①使能看门狗时钟:
RCC_APB1PeriphClockCmd();
②设置分频系数:
WWDG_SetPrescaler();
③设置上窗口值:
WWDG_SetWindowValue();
④开启提前唤醒中断并分组(可选):
WWDG_EnableIT();
⑤使能看门狗:
WWDG_Enable();
⑥喂狗:
WWDG_SetCounter();
⑦编写中断服务函数:
WWDG_IRQHandler();
3.2 代码实现
wwdg.c文件里相关程序与独立看门狗大致相同,在看门狗初始化函数里WWDG先要时钟使能,随后设置预分频系数、窗口值,使能看门狗,清除唤醒中断标志位,初始化NVIC最后开启中断。IWDG不同之处在于这里第三步是设置重装载值,随后进行重装载,最后直接使能。
IWDG主要就是一个初始化函数,后面的喂狗可以单独声明函数也可以直接在循环里喂狗。而WWDG使用到了中断,这里还设置了三个函数分别是重设置计数值、中断服务程序以及对应的初始化函数。
#include "wwdg.h"
#include "led.h"
u8 WWDG_CNT=0x7f;
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
WWDG_SetPrescaler(fprer);
WWDG_SetWindowValue(wr);
WWDG_Enable(tr);
WWDG_ClearFlag();
WWDG_NVIC_Init();
WWDG_EnableIT();
}
void WWDG_Set_Counter(u8 cnt)
{
WWDG_Enable(cnt);
}
void WWDG_NVIC_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void WWDG_IRQHandler(void)
{
// Update WWDG counter
WWDG_SetCounter(0x7F);
// Clear EWI flag */
WWDG_ClearFlag();
// Toggle GPIO_Led pin 7 */
LED1=!LED1;
}
最后是main.c文件的代码
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "wwdg.h"
int main(void)
{
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(9600);
LED_Init();
LED0=0;
delay_ms(300);
WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);
while(1)
{
LED0=1;
}
}