一,窗口看门狗简介
独立看门狗虽然方便,但时钟精度不够,如果故障在独立看门狗要复位时恢复正常,则喂狗正常执行,无法检测故障的发生。如果我们要精确检测某一时间端是否有故障产生,就需要用到窗口看门狗。
窗口看门狗,顾名思义,看住的是窗口内的情况,要在窗口期执行喂狗操作(刷新递减计数器),提前执行或延后执行都会引起复位。
这是窗口看门狗的工作原理图
T[6:0]就是 WWDG_CR 的低七位,W[6:0]即是 WWDG->CFR 的低七位。T[6:0] 就是窗口看门狗的计数器,而 W[6:0]则是窗口看门狗的上窗口,由我们自己设定,下窗口值是固定的(0X40)。达到0x3F即发生复位,在达到上窗口值前不允许刷新,在上下窗口间刷新。
窗口看门狗的超时公式如下: Twwdg=(4096×2^WDGTB×(T[5:0]+1)) /Fpclk1;
其中: Twwdg:WWDG 超时时间(单位为 ms)
Fpclk1:APB1 的时钟频率(单位为 Khz)
WDGTB:WWDG 的预分频系数
T[5:0]:窗口看门狗的计数器低 6 位
二,寄存器介绍
首先是控制寄存器(WWDG_CR),也是计数寄存器。
该寄存器低八位有效,其余为保留位,T[6:0]用来存储看门狗的计数器值,计数器值减到0x3F时复位。WDGA 位则是看门狗的激活位,该位由软件置 1,以启动看门狗,并且一定要注意的是该 位一旦设置,就只能在硬件复位后才能清零了。
之后是配置寄存器(WWDG_CFR),
该寄存器有10位有效,其他位保留,W[6:0]用来保留窗口值,8,9位用来保存设置的预分频系数。
10位中的 EWI 是提前唤醒中断,也就是在快要产生复位的前一段时间(T[6:0]=0X40)来 提醒我们,需要进行喂狗了,否则将复位!因此,我们一般用该位来设置中断,当窗口看门狗 的计数器值减到 0X40 的时候,如果该位设置,并开启了中断,则会产生中断,我们可以在中 断里面向 WWDG_CR 重新写入计数器的值,来达到喂狗的目的。注意这里在进入中断后,必 须在不大于 1 个窗口看门狗计数周期的时间(在 PCLK1 频率为 36M 且 WDGTB 为 0 的条件下, 该时间为 113us)内重新写 WWDG_CR,否则,看门狗将产生复位!
最后我们要介绍的是状态寄存器(WWDG_SR),该寄存器用来记录当前是否有提前唤醒 的标志。该寄存器仅有位 0 有效,其他都是保留位。当计数器值达到 40h 时,此位由硬件置 1。 它必须通过软件写 0 来清除。对此位写 1 无效。即使中断未被使能,在计数器的值达到 0X40的时候,此位也会被置 1。
三,配置步骤
1)使能 WWDG 时钟
与独立看门狗不同, WWDG 使用的是 PCLK1 的时钟,需要先使能时钟。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WWDG 时钟使能
2)设置窗口值和分频数
设置窗口值的函数是:
void WWDG_SetWindowValue(uint8_t WindowValue);
入口参数为窗口值
设置分频数的函数是:
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
入口参数是分频值
3)开启 WWDG 中断并分组
开启 WWDG 中断的函数为:
WWDG_EnableIT(); //开启窗口看门狗中断
之后的中断优先级设置使用NVIC_Init()函数。
4)设置计数器初始值并使能看门狗
这一步在库函数里面是通过一个函数实现的:
void WWDG_Enable(uint8_t Counter);
该函数既设置了计数器初始值,同时使能了窗口看门狗。
5)编写中断服务函数
在最后,还是要编写窗口看门狗的中断服务函数,通过该函数来喂狗。
四,硬件设计
本实验用到的硬件资源有:
1) 指示灯 DS0 和 DS1
2) 窗口看门狗
窗口看门狗为stm32内部资源,两个指示灯前面已经多次使用。最后现象与独立看门狗相同。
五,软件设计
窗口看门狗相较于独立看门狗只增加了固件库中的stm32f10x_wwdg.c及其头文件,以及wwdg.c和wwdg.h。
wwdg.c代码如下
#include "led.h"
#include "wwdg.h"
u8 WWDG_CNT=0x7f;
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
// WWDG 时钟使能
WWDG_SetPrescaler(fprer);设置 WWDG 预分频值
WWDG_SetWindowValue(wr);//设置窗口值
WWDG_Enable(tr); //使能看门狗 , 设置 counter .
WWDG_ClearFlag();
WWDG_NVIC_Init();//初始化窗口看门狗 NVIC
WWDG_EnableIT(); //开启窗口看门狗中断
}
//重设置 WWDG 计数器的值
void WWDG_Set_Counter(u8 cnt)
{
WWDG_Enable(cnt);
}
//窗口看门狗中断服务程序
void WWDG_NVIC_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占 2 子优先级 3 组 2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //抢占 2,子优先级 3,组 2
NVIC_Init(&NVIC_InitStructure);//NVIC 初始化
}
void WWDG_IRQHandler(void)
{
// Update WWDG counter
WWDG_SetCounter(0x7F); //当禁掉此句后,窗口看门狗将产生复位
// Clear EWI flag */
WWDG_ClearFlag(); //清除提前唤醒中断标志位
// Toggle GPIO_Led pin 7 */
LED1=!LED1;
}
本函数的思路是先初始化wwdg,其中第一个参数是设置计数器初始值,第二个参数设置上窗口值,第三个参数设置预分频值。注意到这里有个全局变量 WWDG_CNT,该变量用来保存 最初设置 WWDG_CR 计数器的值。
之后的WWDG_Set_Counter 函数就是把CNT中储存的数值放到计数器上。
最后在中断服务函数里面,先重设窗口看门狗的计数器值,然后清除提前唤醒中断标志。 最后对 LED1(DS1)取反,来监测中断服务函数的执行状况。
之后是主函数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;
}
}
本函数用DS0指示是否正在初始化,用DS1指示是否发生复位,每次复位DS1会变化亮灭。我们先让 LED0 亮 300ms,然后关闭以用于判断是否有复位发生了。在初始化 WWDG 之后, 我们回到死循环,关闭 LED0,并等待看门狗中断的触发/复位。
检查无误后下载验证,现象为DS0亮一下后熄灭,之后DS1不断闪烁。