WDG看门狗
WDG(Watchdog)看门狗是一种硬件电路或软件机制,用于监控程序的运行状态,防止程序因设计漏洞、硬件故障或电磁干扰等原因出现卡死或跑飞现象,从而保证系统的可靠性和安全性
工作原理
看门狗本质上是一个定时器,当程序在指定时间范围内没有执行“喂狗”(重置计数器)操作时,看门狗会自动产生复位信号,重启系统
。喂狗操作是通过程序定期清零计数器来完成的,只要程序正常运行,计数器就不会溢出,系统也不会被复位
分类
-
独立看门狗(IWDG):使用独立的时钟源(如内部低速时钟LSI),即使主时钟出现问题,看门狗仍能正常工作。它对时间精度要求较低,只要在最晚喂狗期限前完成喂狗即可。
-
窗口看门狗(WWDG):要求在精确的时间窗口内完成喂狗操作。如果喂狗时间过早或过晚,看门狗都会触发复位。它适用于对时间精度要求较高的场合。
喂狗(Dog Feeding)是一个形象的说法,用于描述对看门狗定时器(Watchdog Timer)进行的一种特定操作。看门狗定时器是一种硬件或软件定时器,用于防止系统在遇到错误或死锁时停止响应。
喂狗的含义:
-
防止超时:看门狗定时器会在设定的超时时间内等待一个信号(即“喂狗”操作)。如果在超时时间内没有收到这个信号,看门狗定时器会触发一个系统复位或中断,以尝试恢复系统的正常运行。
-
保持系统活跃:在系统正常运行时,需要定期执行“喂狗”操作,即向看门狗定时器发送一个信号,重置其计时器。这样做可以防止看门狗定时器超时,从而避免不必要的系统复位。
-
监控系统状态:喂狗操作通常在系统的主循环或关键任务中执行,以确保这些任务正在正常运行。如果在某个点系统卡住或遇到错误,喂狗操作可能会被遗漏,导致看门狗定时器超时并触发系统复位。
-
1.8V供电区:这是看门狗电路的供电区域,确保看门狗在1.8V的电压下工作。
-
VDD供电区:看门狗功能处于VDD供电区,这意味着即使在停机和待机模式下,看门狗也能正常工作。
-
LSI(40 kHz):这是内部低速时钟(Low-Speed Internal clock),频率为40 kHz。它为看门狗提供独立的时钟源。
-
8位预分频器:预分频器用于调整时钟频率。它将LSI的频率分频,以适应看门狗计数器的需求。
-
预分频寄存器(IWDG_PR):这个寄存器用于设置预分频器的分频值。通过改变这个寄存器的值,可以调整看门狗计数器的时钟频率。
-
状态寄存器(IWDG_SR):这个寄存器用于存储看门狗的状态信息,如计数器的当前值和看门狗是否处于复位状态。
-
重装载寄存器(IWDG_RLR):这个寄存器用于设置看门狗计数器的重装值。当计数器达到这个值时,看门狗会产生复位信号。
-
12位重装数值:这是看门狗计数器的重装值,用于设置计数器的初始值。
-
12位递减计数器:这是看门狗的核心部分,它是一个12位的计数器,用于计时。当计数器从重装值递减到0时,看门狗会产生复位信号。
-
键寄存器(IWDG_KR):这个寄存器用于写入操作,通常需要特定的键值才能写入其他寄存器,以防止误操作。
-
IWDG复位:当计数器递减到0时,看门狗会产生复位信号,重启系统。
40000 (40 kHz):这是LSI(Low-Speed Internal clock)的频率,即内部低速时钟的频率。这个频率是40 kHz,意味着每秒钟有40,000个周期。这个值通常由微控制器的硬件规格决定。
假设我们选择预分频系数为16(即PR[2:0] = 2),并且RL[11:0] = 0x000(即RL = 0)和RL[11:0] = 0xFFF(即RL = 4095)。
-
LSI时钟周期 TLSI: TLSI=FLSI1=400001秒=0.025毫秒
-
最短时间计算(RL = 0): TIWDG=TLSI×PR预分频系数×(RL+1)=0.025毫秒×16×(0+1)=0.4毫秒
-
最长时间计算(RL = 4095): TIWDG=0.025毫秒×16×(4095+1)=1638.4毫秒
WWDG框图
-
PCLK1:这是来自RCC时钟控制器的时钟信号,用于驱动看门狗的预分频器。
-
看门狗预分频器(WWDGTB):这个模块用于分频PCLK1时钟信号,以产生适合看门狗计数器的时钟频率。
-
6位递减计数器(CNT):这是看门狗的核心计数器,它是一个6位的计数器,用于计时。计数器从预设的重装值递减到0,然后自动重装。T6是标志位
-
看门狗控制寄存器(WWDG_CR):这个寄存器用于控制看门狗的行为,包括设置窗口上限(W)和启动看门狗(WDGA)。
-
看门狗配置寄存器(WWDG_CFR):这个寄存器用于配置看门狗的窗口下限(W)和窗口使能(WINEN)。
-
比较器:这个模块用于比较计数器的当前值(T6:0)和窗口上限(W6:0)。如果计数器的值超出了窗口范围(即T6:0 > W6:0),比较器的输出为1,触发看门狗复位。
-
复位:当计数器的值超出窗口范围时,看门狗会产生复位信号,重启系统。
-
写入WWDG_CR:通过写入控制寄存器,可以启动看门狗并设置窗口上限。
标志位减到0产生溢出进行复位
喂狗太晚,6位计数器减到0了,复位,喂狗太早,计数器的值超过窗口值(上面的看门狗配置寄存器),复位
代码示例:
1.独立看门狗
第一步:开启LSI时钟(不需要写)
第二步:解除写保护
第三步:写入预分频器和重装寄存器
最后一步,独立看门狗使能
设置一下超时时间,选择分频系数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Key_Init(); //按键初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "IWDG TEST");
/*判断复位信号来源*/
if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET) //如果是独立看门狗复位
{
OLED_ShowString(2, 1, "IWDGRST"); //OLED闪烁IWDGRST字符串
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(100);
RCC_ClearFlag(); //清除标志位
}
else //否则,即为其他复位
{
OLED_ShowString(3, 1, "RST"); //OLED闪烁RST字符串
Delay_ms(500);
OLED_ShowString(3, 1, " ");
Delay_ms(100);
}
/*IWDG初始化*/
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //独立看门狗写使能
IWDG_SetPrescaler(IWDG_Prescaler_16); //设置预分频为16
IWDG_SetReload(2499); //设置重装值为2499,独立看门狗的超时时间为1000ms
IWDG_ReloadCounter(); //重装计数器,喂狗
IWDG_Enable(); //独立看门狗使能
while (1)
{
Key_GetNum(); //调用阻塞式的按键扫描函数,模拟主循环卡死
IWDG_ReloadCounter(); //重装计数器,喂狗
OLED_ShowString(4, 1, "FEED"); //OLED闪烁FEED字符串
Delay_ms(200); //喂狗间隔为200+600=800ms
OLED_ShowString(4, 1, " ");
Delay_ms(600);
}
}
2.窗口看门狗
第一步:开启窗口看门狗APB1时钟
第二步:配置各个寄存器(预分频,窗口值)
第三步:写入控制寄存器CR(看门狗使能位,计数器溢出标志位,计数器有效位)
之后不断向计数器写入想要的重装值,就可以进行喂狗了
设置超时时间和窗口时间
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Key_Init(); //按键初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "WWDG TEST");
/*判断复位信号来源*/
if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET) //如果是窗口看门狗复位
{
OLED_ShowString(2, 1, "WWDGRST"); //OLED闪烁WWDGRST字符串
Delay_ms(500);
OLED_ShowString(2, 1, " ");
Delay_ms(100);
RCC_ClearFlag(); //清除标志位
}
else //否则,即为其他复位
{
OLED_ShowString(3, 1, "RST"); //OLED闪烁RST字符串
Delay_ms(500);
OLED_ShowString(3, 1, " ");
Delay_ms(100);
}
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //开启WWDG的时钟
/*WWDG初始化*/
WWDG_SetPrescaler(WWDG_Prescaler_8); //设置预分频为8
WWDG_SetWindowValue(0x40 | 21); //设置窗口值,窗口时间为30ms
WWDG_Enable(0x40 | 54); //使能并第一次喂狗,超时时间为50ms
while (1)
{
Key_GetNum(); //调用阻塞式的按键扫描函数,模拟主循环卡死
OLED_ShowString(4, 1, "FEED"); //OLED闪烁FEED字符串
Delay_ms(20); //喂狗间隔为20+20=40ms
OLED_ShowString(4, 1, " ");
Delay_ms(20);
WWDG_SetCounter(0x40 | 54); //重装计数器,喂狗
}
}