窗口看门狗 WWDG 其实和独立看门狗类似,它是一个 7 位递减计数器不断的往下递减计数,
当减到一个固定值 0X40 时还不喂狗的话,产生一个 MCU 复位,这个值叫窗口的下限,是固定的值,不能改变。
这个和独立看门狗是类似的,不同的是窗口看门狗的计数器的值在减到某一个数之前喂狗的话也会产生复位,这个值叫窗口的上限,上限值由用户独立设置。窗口看门狗计数器的值必须在上窗口和下窗口之间才可以刷新(喂狗),这也是窗口看门狗中“窗口”两个字的含义。
窗口看门狗时序图如下图所示:
从图中可以看到,T[6:0]是窗口控制寄存器(WWDG_CR)的低 7 位,W[6:0]
是窗口配置寄存器(WWDG_CFR)低 7 位。T[6:0]就是窗口看门狗的计数器值,而
W[6:0]是窗口看门狗的上窗口,下窗口是固定值 0X40。当窗口看门狗的计数器
在上窗口值之外或者低于下窗口值被刷新都会产生复位。
上窗口值(W[6:0])是由用户自己设定的,根据实际要求来设计窗口值,但
是一定要确保窗口值大于 0X40,否则窗口就不存在了。
窗口看门狗 WWDG 通常被用来监测,由外部干扰或不可预见的逻辑条件造成
的应用程序背离正常的运行序列而产生的软件故障。
窗口看门狗(WWDG)的时钟来自 PCLK1,即挂接在 APB1 总线上,由 RCC 时钟控制器开启。
APB1 时钟最大为 36M。
PCLK1 时钟并不是直接提供给窗口看门狗计数器时钟,而是通过一个 WDG 预分频器分频后输入给计数器时钟。
我们可以操作配置寄存器 WWDG_CFR 的位 8:7WDGTB[1:0]来设置分频因子,分频因子可以为 0、1、2、3。
分频后的计数器时钟为:CK_CNT= PCLK1/4096/(2^WDGTB),除以 4096 是中文参考手册内公式规定,没有为什么。PCLK1 等于 APB1 时钟,WDGTB 为分频因子(0-3),2^WDGTB 大小就是 1、2、4、8,与库函数中的分频参数对应。
每经过一个计数器时钟,计数器就减 1。
窗口看门狗的计数器是一个 7 位的递减计数器,计数最大值为 0X7F,其值存放在控制寄存器 WWDG_CR 中的 6:0 位,即 T[6:0]。
当递减到 T6 位变成 0 时,即从 0X40 变为 0X3F 时候,会产生看门狗复位。
这个值 0X40 是窗口看门狗能够递减到的最小值,所以计数器的值只能在 0X40~0X7F 之间,实际上用来计数的是 T[5:0]。
当递减计数器递减到 0X40 的时候,还不会马上产生复位,如果使能了提前唤醒中断,窗口配置寄存器(WWDG_CFR)位 9 EWI 置 1,则产生提前唤醒中断,也就是在快产生复位的前一段时间提醒我们,需要进行喂狗了,否则将复位。
我们通常都是在提前唤醒中断内向 WWDG_CR 重新写入计数器的值, 来达到喂狗的目的。需要注意的是:在进入中断后,必须在不大于 1 个窗口看 门狗计数周期的时间(在 PCLK1 频率为 36M WDGTB 为 0 的条件下,该时间 为 113us)内重新写 WWDG_CR,否则,看门狗将产生复位!
如果不使用提前唤醒中断来喂狗,我们要会计算窗口看门狗的超时时间,
计 算公式如下: Twwdg=(4096×2^WDGTB×(T[5:0]+1)) /PCLK1;
其中: Twwdg 为窗口看门狗的超时时间,单位为 ms。 PCLK1 为 APB1 的时钟频率,最大 36MHz。 WDGTB 为窗口看门狗的预分频系数。 T[5:0]为窗口看门狗的计数器低 6 位。
根据上面的公式,假设 PCLK1=54Mhz,
那么可以得到最小-最大超时时间表, 如下
我们知道窗口看门狗必须在窗口范围内进行喂狗才不会产生复位,窗口中的下窗口是一个固定值 0X40,上窗口值可以改变,具体的由配置寄存器 WWDG_CFR的位 W[6:0]设置。
其值必须大于 0X40,如果小于或者等于 0X40 就是失去了窗口的意义,而且也不能大于计数器的最大值 0X7F。
窗口值具体要设置成多大,这个得根据我们需要监控的程序的运行时间来决定。
假如我们要监控的程序段 A运行的时间为 Ta,当执行完这段程序之后就要进行喂狗,如果在窗口时间内没有喂狗的话,那程序就肯定是出问题了。
一般计数器的值 TR 设置成最大 0X7F,窗口值为 WW,计数器减一个数的时间为 T,那么时间:(TR-WW)*T 应该稍微大于 Ta 即可,这样就能做到刚执行完程序段 A 之后喂狗,起到监控的作用,这样也就可以算出 WW 的值是多少。
当计数器值超过配置寄存器内的上窗口设置值或者低于下窗口值,并且WDGA 位置 1,即开启窗口看门狗,将产生一个系统复位信号,促使系统复位。
《STM32F10x 中文参考手册》-18 窗口看门狗(WWDG)章节,里面有详细的讲解。
如果看不懂的可以暂时放下,因为我们使用的是库函数开发。
具体步骤如下:(WWDG 相关库函数在 stm32f10x_wwdg.c 和 stm32f10x_wwdg.h 文件中)
WWDG 不同于 IWDG,IWDG 有自己独立的 LSI 时钟,所以不存在使能问题,而
WWDG 使用的是 APB1 时钟,需要先使能时钟。
在库函数中实现函数如下:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
设置 WWDG 窗口值函数为:
void WWDG_SetWindowValue(uint8_t WindowValue);
窗口值最大值为 0X7F,最小不能低于 0X40,否则就失去了窗口的意义。
设置 WWDG 分频数函数为:
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
分频系数可以为
WWDG_Prescaler_1、WWDG_Prescaler_2、WWDG_Prescaler_4、WWDG_Prescaler_8。
通常对窗口看门狗进行喂狗是在提前唤醒中断内操作,所以需要打开 WWDG
的中断功能,并且配置对应的中断通道及分组。
中断分组及通道选择是在 NVIC_Init 初始化内完成,这个在前面章节中都介
绍过,这里我们看下使能 WWDG 中断,库函数如下:
WWDG_EnableIT();
库函数中提供了一个同时设置计数器初始值和使能 WWDG 的函数,
如下:
void WWDG_Enable(uint8_t Counter);
注意计数器最大值不能大于 OX7F。
库函数还提供了一个独立设置计数器值的函数,如下:
void WWDG_SetCounter(uint8_t Counter);
最后我们还需要编写一个 WWDG 中断服务函数,通过中断函数进行喂狗。
WWDG中断服务函数名在 STM32F1 启动文件内就有,WWDG 中断函数名如下:
WWDG_IRQHandler
在中断内要进行喂狗,可以直接调用 WWDG_SetCounter()函数,给它传递一
个窗口值即可,特别注意,在中断内喂狗一定要快,否则当看门狗计数器值减到
0X3F 时将产生复位。然后清除 WWDG 中断状态标志位 EWIF,
函数如下:
WWDG_ClearFlag(); //清除窗口看门狗状态标志
通过以上几步配置后,我们就可以正常使用窗口看门狗了,我们需要在中断
内快速喂狗,否则系统即会重新启动。
本章要实现的功能是:
使用窗口看门狗的中断来喂狗,通过 D1、D2 指示灯提示程序运行状态。
由于 WWDG 是 STM32F1 内部资源,因此本硬件电路非常简单,只有 D1、D2 指
示灯连接,这里就不多说。
D2 指示灯用来提示系统工作状态,D1 指示灯用来作为喂狗提示,每进入中断喂狗 D1 指示灯状态翻转一次。
系统开启时 D1 指示灯点亮 500ms 时间,然后熄灭。
D1 指示灯不断闪烁表示正在喂狗。
如果喂狗超时将重启系统,D1 指示灯点亮
500ms,然后熄灭,继续喂狗。
程序框架如下:
void WWDG_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 第1步:使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
// 第2步:设置 WWDG 窗口值、分频数
WWDG_SetWindowValue(0x5f); //WWDG 窗口值
WWDG_SetPrescaler(WWDG_Prescaler_8); //分频数
// 第3步:开启 WWDG 中断并分组
// 3-1设置中断优先级,使能定时器中断通道
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);
// 3-2开启 WWDG 使能
WWDG_Enable(0x7f); //使能 完毕 进入中断使能
// 第4步:设置计数器初始值并使能 WWDG
WWDG_ClearFlag(); // 清楚状态标记
WWDG_EnableIT();
}
// 第5步:编写 WWDG 中断服务函数
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(0x7f); // 重新写入初值 喂狗
WWDG_ClearFlag(); //清除窗口看门狗状态标志
LED1=!LED1;
}
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "key.h"
#include "wwdg.h"
int main()
{
u8 i=0;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组分2组
LED_Init();
USART1_Init(115200);
LED1 =0;
delay_ms(500);
WWDG_Init();
while(1)
{
i++;
if(i%10==0)
LED0=!LED0; // D0 核心板上的灯
delay_ms(10);
}
}
void WWDG_DeInit(void);复位
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);//WWDG 窗口值
void WWDG_SetWindowValue(uint8_t WindowValue);//分频数
void WWDG_EnableIT(void); 使能中断
void WWDG_SetCounter(uint8_t Counter); 设置计数器值
void WWDG_Enable(uint8_t Counter); 使能串口看门狗,并设置初始值
FlagStatus WWDG_GetFlagStatus(void);获取状态标记
void WWDG_ClearFlag(void); 清楚状态标记