1、综述
(1)与独立看门狗的区别?
窗口看门狗(WWDG )其喂狗时间是一个有上下限的范围(窗口),计数器的下限是固定的(0x40),可以通过设定相关寄存器,设定其上限时间(上限计数值)。喂狗的时间不能过早,也不能过晚。你喂得过早或者过晚,都会复位的。
(2)有了独立看门狗来检测程序运行是否正常,为何还需要窗口看门狗?
对于一般的看门狗,程序可以在它产生复位前的任意时刻刷新看门狗,但是,这有一个隐患:有可能程序跑乱了,但又跑回到正常的地方,或者,跑乱了的程序正好执行了刷新看门狗的操作。这样的情况下,一般的看门狗就检测不出来了。
如果使用窗口看门狗,程序员可以根据程序正常执行的时间设置刷新看门狗的一个时间窗口,保证不会提前刷新看门狗,也不会滞后刷新看门狗,这样,可以检测出程序没有按照正常的路径运行而非正常地跳过了某些程序段的情况。
2、相关寄存器
(1)控制寄存器 WWDG_CR
32位寄存器,低8位有效。这8位中,低7位T[6:0]用来存储看门狗的计数器值,随时更新。当该计数器的值从0x40变为0x3F的时候,将产生复位;除了这低7位,还有第8位(位7,叫做WDGA位),是看门狗的激活位。该位由软件置1,用来启动看门狗,并且,一旦启动以后,该位会一直保持为1,在硬件复位以后才能清零。
(2)配置寄存器 WWDG_CFR
32位寄存器,我们用到10位。
<1>低7位W[6:0],是7位窗口值。这7位就是窗口看门狗的上窗口。
<2>位9(第10位),叫做EWI,是提前唤醒中断位。如果我们把这位设置为1,那么,只要递减计数器的计数值下降到0x40,就会产生一个中断。之所以叫做唤醒中断位,就是因为,当计数器的值减到0x40,如果不产生中断,下一个值就是0x3F,此时就会复位;而这里产生一个中断,那么我们就可以在中断服务函数里面及时地喂狗,来唤醒它,避免复位。
注意:这里在进入中断后,必须在不大于一个窗口看门狗计数周期的时间内重新写WWDG_CR寄存器去喂狗,否则,将会复位。
<3>位8和位7(叫做WDGTB[1:0]),叫做定时器时基(Time base),这两位用来修改计数时钟的分频系数。
(3)状态寄存器 WWDG_SR
该寄存器用来记录当前是否有提前唤醒的标志。该寄存器只有最低位(位0)一位有效。当计数值达到0x40时,该位由硬件置1.它必须通过软件写0来清除,软件对它写1是无效的。注意:它与是否使能中断没有关系,即使中断没有被使能,在计数器的值达到0x40的时候,该位也会被硬件置1。
3、配置窗口看门狗的步骤
(1)使能WWDG时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
WWDG与IWDG不同,独立看门狗有自己独立的32kHz时钟,用的是LSI,而窗口看门狗用的是PCLK1的时钟,挂载在总线APB1上,所以,使用之前,需要先使能。
(2)设置分频系数与上窗口值
设置看门狗的分频系数(CFR寄存器,位WDGTB[1:0])的函数是:
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);//设置分频系数
设置看门狗的上窗口值(CFR寄存器,位W[6:0])的函数是:
void WWDG_SetWindowValue(uint8_t WindowValue);//设置上窗口值
(3)开启提前唤醒中断,并分组
开启WWDG中断的函数为:
WWDG_EnableIT();//开启窗口看门狗中断
开启WWDG中断后,进行中断优先级配置,调用函数NVIC_Init()即可。
(4)使能看门狗
void WWDG_Enable(uint8_t Counter);//使能看门狗
(5)编写中断服务函数
编写中断服务函数WWDG_IRQHandler(),在里面调用函数WWDG_SetCounter(),去喂狗。但要注意,喂狗要迅速,否则当WWDG计数到0x3F的时候,就会引起复位了。还有一个值得注意的地方就是,在中断服务函数里面,要将状态寄存器的EWIF位清空。
4、实例代码
#include "wwdg.h"
#include "led.h"
u8 WWDG_CNT = 0x7F;
/*
WWDG初始化函数,三个入口参数:
tr:计数器值,T[6:0]
wr:上窗口值,W[6:0]
fprer:分频系数,WDGTB
*/
void WWDG_Init(u8 tr, u8 wr, u32 fprer)
{
NVIC_InitTypeDef NVIC_InitStructure;
//使能WWDG时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
//设置分频系数
WWDG_SetPrescaler(fprer);
//设置上窗口值
WWDG_SetWindowValue(wr);
//开启提前唤醒中断,并分组
WWDG_EnableIT();
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);
//使能看门狗
WWDG_Enable(WWDG_CNT);
//设置计数值,喂狗
WWDG_SetCounter(WWDG_CNT);
}
/*编写中断服务函数*/
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(WWDG_CNT);//重设窗口看门狗计数值,喂狗
WWDG_ClearFlag();//清除提前唤醒中断标志位
LED1 = !LED1;
}