看门狗定时器(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 0x70
#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_EnableIT_EWKUP(WWDG);
/* WWDG interrupt Init */
NVIC_SetPriority(WWDG_IRQn, 0);
NVIC_EnableIRQ(WWDG_IRQn);
}
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--;
}
}
转载:https://blog.csdn.net/m0_46170433/article/details/116452921