看门狗定时器
看门狗定时器(WTD)WTD概述一般很多嵌入式设备工作在无人环境下,不能实时监测系统工作的状态,因此需要用看门狗定时器来自动检测重启设备。看门狗定时器是系统内部的定时器。看门狗定时器用于检测系统设备在受到噪声和系统误差等故障干扰造成设备死机,可以通过看门狗定时器产生一个复位信号 或 请求中断服务,自动恢复让系统重新运行。WDT与PWM定时器的不同之处在于WDT产生复位信号。WTD实现过程1)PCL
(1)看门狗原理
在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗。
看门狗其实就是一个可以在一定时间内被复位的计数器。当看门狗启动后,计数器开始自动计数,经过一定时间,如果没有被复位,计数器溢出就会对CPU产生一个复位信号使系统重启(俗称“被狗咬”)。系统正常运行时,需要在看门狗允许的时间间隔内对看门狗计数器清零(俗称“喂狗”),不让复位信号产生。如果系统不出问题,程序保证按时“喂狗”,一旦程序跑飞,没有“喂狗”,系统“被咬”复位。
(2)看门狗分类
1)CPU内部自带的看门狗
-
此类看门狗一般是将一个芯片中的定时器来作为看门狗,通过程序的初始化,写入初值,设定溢出时间,并启动定时器。程序按时对定时器赋初值(或复位),以免被咬。这种看门狗是可以被禁用的(只要停止这个定时器即可)。大部分CPU都内置看门狗,硬件原理可参考各芯片数据手册。
-
优点:可以通过程序改变溢出时间;可以随时禁用
缺点:需要初始化;如果程序在初始化、启动完成前跑飞或在禁用后跑飞,看门狗就无法复位系统,这样看门狗的作用就没有了,系统恢复能力降低。
2)独立的看门狗芯片(IWDG)
- 这种看门狗主要有一个用于喂狗的引脚(一般与CPU的GPIO相连)和一个复位引脚(与系统的RESET引脚相连),如果没有在一定时间内改变喂狗脚的电平,复位引脚就会改变状态复位CPU。此类看门狗一上电就开始工作,无法禁用。
- 优点:无须配置,上电即用。无法禁用,系统必须按时喂狗,系统恢复能力高。
缺点:无法灵活配置溢出时间,无法禁用,灵活性降低。
3)WWDG窗口看门狗
4)软件看门狗
- 软件看门狗,其实也是一种监控软件。
一些重要的程序,必须让它一直跑着;而且还要时时关心它的状态——不能让它出现死锁现象。(当然,如果一个主程序会出现死锁,肯定是设计或者编程上的失误。首要做的事是Debug。)但如果时间紧迫可以用软件看门狗,暂时应急。
这种监控软件运行不出现界面窗口,具有一定的隐蔽性;它定时判断目标进程是否运行在当前系统中,如果没有则启动目标进程;判断目标进程是否“无响应”,如果是则终止目标进程;如果目标进程“无响应”的次数超过一定的数量,则重启整个系统。它的目的也是复位,但是它主要市复位进程,实在不行才复位CPU。
看门狗定时器(WTD)
WTD概述
一般很多嵌入式设备工作在无人环境下,不能实时监测系统工作的状态,因此需要用看门狗定时器来自动检测重启设备。看门狗定时器是系统内部的定时器。
- 看门狗定时器
用于检测系统设备在受到噪声和系统误差等故障干扰造成设备死机,可以通过看门狗定时器产生一个复位信号 或 请求中断服务,自动恢复让系统重新运行。 - WDT与PWM定时器的不同之处在于WDT产生复位信号。
WTD实现过程

1)PCLK经过一级分频器和二级选择器后产生看门狗时钟后,通过计数逻辑模块WTCNT,开始计数。
2)WTDAT是预先存放的计数初值,WTCNT是减量计数模块。当WTCNT从WTDAT赋的值减到0的时候,如果还没有更新重载WTCNT,则认为没有喂狗,系统死机产生reset复位信号。
注意:更新赋新值的寄存器是WTCNT,不是WTDAT。
3)发出reset 信号,强制系统重启
独立看门狗
注意:独立看门狗没有上限,只有下限,且IWDG没有中断,也没有ISR中断服务函数
https://www.cnblogs.com/pertor/p/9483445.html
https://blog.csdn.net/qq_35362464/article/details/53814711
窗口看门狗

窗口看门狗产生复位 2 中情况:
(1)CNT寄存器的值在WWDG_CFG之前,重新加载 CNT寄存器:上限,上限值必须大于0x40
(2)CNT值递减到0x40->0x3F时,导致T6位由1->0,产生MCU复位
注意:在CNT递减到0x40时,如果开启了EWI(early wakeup interrupt),则会产生提前唤醒中断,可在此中断中喂狗;中断标志flag需要手动软件清除WWDG_FLAG_EWIF
如果系统复位后,WWDG处于 close 状态,一旦激活WWDG则除非系统复位,否则无法关闭WWDG

注意:
(1)t pCLK1 是 经APB分频以后得到的,需要注意PCLK1是系统时钟经过多少分频得到的
(2)WDGTB取值为(0, 1 ,2, 3),分别对应PCLK1经过1/2/4/8分频得到WWDG的时钟源
(3)t[5:0]是最大值为0x3F,因此t[5:0]max = 0x3F = 63
(4)注意单位是毫秒ms
公式一般是:tWWDG = (1 / 32(或者/72)MHZ) * 4096 * (1/2/4/8)* (63 + 1)ms
代码
其中变量 ww_dog 可以根据用户自定义 控制喂狗时间
HAL库
/**---------------------------- 窗口看门狗(WWDG) ---------------------------*/
#define WINDOWS_VAL 70
#define COUNTER_VAL 127
#define WWDG_TIME 300
static uint32_t ww_dog = WWDG_TIME;
/**
* @brief 窗口看门狗的中断产生:上限和下限
* WWDG Counter refresh is allowed between the following limits :
* (++) min time (mS) = 1000 * (Counter - Window) * (4096 * Prescaler) / WWDG clock
* (++) max time (mS) = 1000 * (Counter - 0x40) * (4096 * Prescaler) / WWDG clock
* max = 58.25ms
* min = 51.88ms ?
*/
WWDG_HandleTypeDef wwdg;
int wwdg_init(void)
{
__HAL_RCC_WWDG_CLK_ENABLE();
wwdg.Instance = WWDG;
wwdg.Init.Prescaler = WWDG_PRESCALER_8;
wwdg.Init.Window = WINDOWS_VAL;
wwdg.Init.Counter = COUNTER_VAL;
wwdg.Init.EWIMode = WWDG_EWI_ENABLE;
if (HAL_WWDG_Init(&wwdg) != HAL_OK) {
printf("WWDG init fail\n");
return -1;
}
HAL_NVIC_SetPriority(WWDG_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(WWDG_IRQn);
return 0;
}
void WWDG_feedDog(void)
{
ww_dog = WWDG_TIME;
}
/**
* @brief ww_dog: 起控制窗口看门狗复位重启MCU的延迟时间,
* 在一定时间内不喂狗,MCU会在(ww_dog * max_time毫秒)后复位
*/
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
if (ww_dog > 0) {
HAL_WWDG_Refresh(&wwdg);
ww_dog--;
// _debug(SYS_INFO,"ww_dog=%d",ww_dog);
} else {
_debug(SYS_EMERG, "fail wwdg refresh: ww_dog(%d)", ww_dog);
}
}
void WWDG_IRQHandler(void)
{
HAL_WWDG_IRQHandler(&wwdg);
}
/**
* @brief (1)关闭WWDG中断
* (2)关闭WWDG的RCC时钟源
* (3)挂起wwdg_task任务
* @attention (1)低功耗 (2)升级 都需要提前关闭wwdg
*/
void close_wwdg(void)
{
HAL_NVIC_DisableIRQ(WWDG_IRQn);
__HAL_RCC_WWDG_CLK_DISABLE();
}
void open_wwdg(void)
{
HAL_NVIC_EnableIRQ(WWDG_IRQn);
__HAL_RCC_WWDG_CLK_ENABLE();
}
STD标准库
///-------------窗口看门狗---------//
#define WWDOG_TIME 100 //大概5.8s左右 (58.25ms * 100)
#define WWDOG_H 70
static uint32_t ww_dog = WWDOG_TIME;
void Wwdong_Init(void)
{
//return;
NVIC_InitTypeDef NVIC_InitStructure;
//时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
WWDG_SetPrescaler(WWDG_Prescaler_8);
/* Set Window value to 80; WWDG counter should be refreshed only when the counter
is below 80 (and greater than 64 (0x40)) otherwise a reset will be generated */
WWDG_SetWindowValue(WWDOG_H); //设置窗口比较值
/*
72MHZ / 2 pre = 36Mhz
4096 * 64 * 8 / 36 = 58.25 ms
- On Value line devices,
Enable WWDG and set counter value to 127, WWDG timeout = ~1366 us * 64 = 87.42 ms
In this case the refresh window is: ~1366us * (127-80) = 64.20 ms < refresh window < ~1366us * 64 = 87.42ms
- On other devices
Enable WWDG and set counter value to 127, WWDG timeout = ~910 us * 64 = 58.25 ms
In this case the refresh window is: ~910 us * (127-80) = 42.77 ms < refresh window < ~910 us * 64 = 58.25ms
*/
WWDG_Enable(0x7F);
WWDG_ClearFlag();
//注册中断
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
WWDG_EnableIT(); //使能窗口看门狗中断
}
void Wwdong_Uninit(void)
{
ww_dog = WWDOG_TIME;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, DISABLE);
//WWDG_ClearFlag();
// WWDG_SetCounter(0x7F);//重设计时器值
}
void WWDG_FeedDog(void)
{
ww_dog = WWDOG_TIME;
}
void WWDG_IRQHandler(void)
{
WWDG_ClearFlag(); //去除提前唤醒中断标志位 //用户代码
if(ww_dog > 0){
WWDG_SetCounter(0x7F);//重设计时器值
ww_dog--;
}
}
LL库
#define WWDG_CMP 70 // 0x46(0x40:提前唤醒中断)
#define WWDG_CNT 0x7F
#define WWDG_TIME 100 // 喂狗次数
static int wwdg_n = WWDG_TIME;
/**
* @brief 32Mhz
* tWWDG = (1 / 32Mhz) * 4096 * (2 ^ pre) * (t[5:0] + 1) ms
* tWWDG = 4096 * 64 * 8 = 65.536ms
* t = tWWDG * 100 = 6.55s
*/
void wwdg_init(void)
{
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_WWDG);
LL_WWDG_SetCounter(WWDG, WWDG_CNT);
LL_WWDG_SetPrescaler(WWDG, LL_WWDG_PRESCALER_8);
LL_WWDG_SetWindow(WWDG, WWDG_CMP);
LL_WWDG_Enable(WWDG);
LL_WWDG_ClearFlag_EWKUP(WWDG);// 注意:wwdg初始化时,必须得先清除提前中断唤醒标志,否则会出错
/* WWDG interrupt Init */
NVIC_SetPriority(WWDG_IRQn, 1);
NVIC_EnableIRQ(WWDG_IRQn);
LL_WWDG_EnableIT_EWKUP(WWDG);
}
void WWDG_FeedDog(void)
{
wwdg_n = WWDG_TIME;
}
void WWDG_IRQHandler(void)
{
LL_WWDG_ClearFlag_EWKUP(WWDG);
if (wwdg_n > 0) {
LL_WWDG_SetCounter(WWDG, WWDG_CNT);
wwdg_n--;
}
}
注意:
(1)wwdg初始化时,必须得先清除提前中断唤醒标志
(2)中断服务中,手动清除中断唤醒标志
否则会出错
更多推荐


所有评论(0)