一、窗口看门狗简介
独立看门狗(宠物狗);窗口看门狗(警犬)。
工作原理:
二、功能框图
(1)窗口看门狗时钟
窗口看门狗时钟来自PCLK1,PCLK1最大是42MHz,由RCC时钟控制器开启。
(2)计数器时钟
1、CNT_CLK = CK_CLK / ( 2^WDGTB )
2、CK_CLK = PCLK1 / 4096
(3)计数器
窗口看门狗的计数器是一个7位的递减计数器,当7个位全为1时是0x7F,这个是最大值。当从0x40变为0x3F时,会产生看门狗复位。这个值0x40是看门狗能够递减到的最小值,所以计数器的值只能是:0x40 ~ 0x7F之间。
(4)窗口值
窗口看门狗必须在计数器的值在一个范围内才可以喂狗,其中下窗口的值是固定的0x40,上窗口的值可以改变(必须大于0x40,小于0x7F)。窗口值具体要设置成过大,需要根据监控的程序的运行时间来决定。如果要监控的程序段A运行的时间为Ta,当执行完这段程序之后就要进行喂狗,如果在窗口时间内没有喂狗的话,那程序就肯定是出问题了。一般计数器的值TR设置成最大0x7F,窗口值为WR,计数器减一个数的时间为T,那么时间:(TR - WR)* T应该稍微大于Ta即可,这样就能做到当执行完程序段A之后喂狗,起到监控的作用,这样也就可以算出WR的值是多少。
三、计算窗口看门狗的超时时间
四、常用固件库函数
(1)设置预分频器的值
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
参数:
WWDG_Prescaler_1
WWDG_Prescaler_2
WWDG_Prescaler_4
WWDG_Prescaler_8
(2)设置上窗口的值
void WWDG_SetWindowValue(uint8_t WindowValue);
参数:
小于0x80
(3)设置计数器的值,使能WWDG
void WWDG_Enable(uint8_t Counter);
参数:
0x40 ~ 0x7F
(4)清除提前唤醒中断标志位
void WWDG_ClearFlag(void);
(5)打开WWDG中断
void WWDG_EnableIT(void);
(6)刷新递减计数器的值,即喂狗
void WWDG_SetCounter(uint8_t Counter);
参数:
0x40 ~ 0x7F
(7)获取状态标志位
FlagStatus WWDG_GetFlagStatus(void);
五、程序
#ifndef __BSP_WWDG_H__
#define __BSP_WWDG_H__
#include "stm32f4xx_conf.h"
extern void WWDG_Feed(void);
extern void WWDG_Config(uint8_t tr, uint8_t wr, uint32_t prv);
#endif
#include "./wwdg/bsp_wwdg.h"
#include "./led/bsp_led.h"
//用于记录看门狗 递减计数器的值,方便喂狗函数直接使用
static uint8_t wwdg_cnt;
/* WWDG 配置函数
* tr :递减计时器的值, 取值范围为:0x7f~0x40,超出范围会直接复位
* wr :窗口值,取值范围为:0x7f~0x40
* prv:预分频器值,取值可以是
* @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1(42MHz)/4096)/1 约10253Hz 97us
* @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1(42MHz)/4096)/2 约5126Hz 195us
* @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1(42MHz)/4096)/4 约2563Hz 390us
* @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1(42MHz)/4096)/8 约1281Hz 780us
*
* 例:tr = 127(0x7f,tr的最大值) wr = 80(0x50, 0x40为最小wr最小值) prv = WWDG_Prescaler_8
* ~780 * (127-80) = 36.6ms < 刷新窗口 < ~780 * 64(127-0x40+1) = 49.9ms
* 也就是说调用WWDG_Config进行这样的配置,若在之后的36.6ms前喂狗,系统会复位,在49.9ms后没有喂狗,系统也会复位。
* 需要在刷新窗口的时间内喂狗,系统才不会复位。
*/
void WWDG_Config(uint8_t tr,uint8_t wr,uint32_t prv)
{
NVIC_InitTypeDef nvicInitValue;
wwdg_cnt = tr; //保存CNT配置,用在喂狗
/*1、打开WWDG时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
/*2、设置预分频器的值*/
WWDG_SetPrescaler(prv);
/*3、设置上窗口值*/
WWDG_SetWindowValue(wr);
/*4、设置计数器的值,使能WWDG*/
WWDG_Enable(tr);
/*5、清除提前唤醒中断标志位*/
WWDG_ClearFlag();
/*6、配置NVIC*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
nvicInitValue.NVIC_IRQChannel = WWDG_IRQn;
nvicInitValue.NVIC_IRQChannelCmd = ENABLE;
nvicInitValue.NVIC_IRQChannelPreemptionPriority = 1;
nvicInitValue.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&nvicInitValue);
/*7、打开WWDG中断*/
WWDG_EnableIT();
}
// 喂狗
void WWDG_Feed(void)
{
// 喂狗,刷新递减计数器的值,设置成最大WDG_CNT=0X7F
WWDG_SetCounter( wwdg_cnt );
}
// WWDG 中断复服务程序,如果发生了此中断,表示程序已经出现了故障,
// 这是一个死前中断。在此中断服务程序中应该干最重要的事,
// 比如保存重要的数据等,这个时间具体有多长,要
// 由 WDGTB 的值决定:
// WDGTB:0 97us (PCLK1(42MHz)/4096)/1 约10253Hz 97us
// WDGTB:1 195us (PCLK1(42MHz)/4096)/2 约5126Hz 195us
// WDGTB:2 390us (PCLK1(42MHz)/4096)/4 约2563Hz 390us
// WDGTB:3 780us (PCLK1(42MHz)/4096)/8 约1281Hz 780us
void WWDG_IRQHandler(void)
{
// 清除中断标志位
WWDG_ClearFlag();
//LED2 亮,点亮 LED 只是示意性的操作,
//真正使用的时候,这里应该是做最重要的事情
LED_ON(2);
}
#include "./usart/bsp_usart.h"
#include "./led/bsp_led.h"
#include "./wwdg/bsp_wwdg.h"
#include "stdio.h"
static void Delay(__IO uint32_t nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}
int main(void)
{
uint8_t wwdg_tr, wwdg_wr;
/* LED 端口初始化 */
LED_Config();
// LED3亮
LED_ON(3);
Delay(0XFFFFFF);
/* WWDG 配置函数
* tr :递减计时器的值, 取值范围为:0x7f~0x40,超出范围会直接复位
* wr :窗口值,取值范围为:0x7f~0x40
* prv:预分频器值,取值可以是
* @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1(42MHz)/4096)/1
* 约10253Hz 97us
* @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1(42MHz)/4096)/2
* 约5126Hz 195us
* @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1(42MHz)/4096)/4
* 约2563Hz 390us
* @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1(42MHz)/4096)/8
* 约1281Hz 780us
*
* 例:tr = 127(0x7f,tr的最大值)
* wr = 80(0x50, 0x40为最小wr最小值)
* prv = WWDG_Prescaler_8 ,这时计数器计一次的时间为780us
则有:780 * (127-80) = 36.6ms < 刷新窗口 < ~780 * 64(127-0x40+1) = 49.9ms
* 也就是说调用WWDG_Config进行这样的配置,若在启动wwdg之后的36.6ms前喂狗,系统会复位,
* 在49.9ms后没有喂狗,系统也会复位。
* 需要在刷新窗口的时间内喂狗,系统才不会复位。
*/
// 初始化WWDG:配置计数器初始值,配置上窗口值,启动WWDG,使能提前唤醒中断
WWDG_Config(127,80,WWDG_Prescaler_8);
// 窗口值我们在初始化的时候设置成0X50,这个值不会改变
wwdg_wr = WWDG->CFR & 0X7F;
while(1)
{
// LED3灭
LED_OFF(3);
//-----------------------------------------------------
// 这部分应该写需要被WWDG监控的程序,这段程序运行的时间
// 决定了窗口值应该设置成多大。
//-----------------------------------------------------
// 计时器值,初始化成最大0X7F,当开启WWDG时候,这个值会不断减小
// 当计数器的值大于窗口值时喂狗的话,会复位,当计数器减少到0X40
// 还没有喂狗的话就非常非常危险了,计数器再减一次到了0X3F时就复位
// 所以要当计数器的值在窗口值和0X40之间的时候喂狗,其中0X40是固定的。
wwdg_tr = WWDG->CR & 0X7F;
if( wwdg_tr < wwdg_wr )
{
// 喂狗,重新设置计数器的值为最大0X7F
WWDG_Feed();
}
}
}