文章目录
看门狗
一、什么是看门狗
看门狗**(WatchDog)**,全称WatchDog Timer,即看门狗计时器。
更形象的说明就是看家的恶犬,和在睡觉的主人,睡觉的主人定时起床给给恶犬喂食,恶犬在吃饱后就心满意足的睡觉,主人也可以去睡觉,如果主人没有按时投喂肉包子给恶犬,恶犬就咬死主人,主人只能复活,然而复活后也要按时给恶犬喂肉包子。不然又要被咬死。。。。
二、看门狗的原理是什么
看门狗其实就是一个可以在一定时间内被复位的计数器。
当看门狗启动后,计数器开始自动计数,经过一定时间,如果计数器溢出,就会对CPU产生一个复位信号使系统重启(俗称“被狗咬”)。
如果计数器还未溢出时清零计数器,不让复位信号产生,则程序正常运行(俗称“喂狗”)
看门狗的作用:就是防止程序发生死循环或者说程序跑飞。
// 扩展知识
// 程序跑飞:PC指针没有按照预定的程序变更,可能PC指针指向一个不确定的或者根本就没有存放用户代码的程序存贮空间,造成PC指针根本无法恢复到用户代码空间之内,程序跑飞,当发生中断时,中断服务程序代码仍然可以被执行。
// PC寄存器:存储下一条指令的地址,也就是即将要执行的指令的地址。然后会由执行引擎来执行下一条指令
三、看门狗类型
举几个例子简单来说说 软件和硬件看门狗
3.1 使用 timer 模拟的看门狗
在主程序中开始时给count 赋值 1000 执行逻辑 , 在timer中修改,如果count 在主程序一次循环结束后还是1000 ,判断触发了看门狗。
volatile int count = 1000;
while(1)
{
count = 1000;
fun1
...
...
funx
if(count != 1000){
// 程序正常运行
}else{
// 程序异常,
// 复位
softReset();
}
}
void timer_IRQ(void)
{
// 清中断
// count--;
}
3.2 使用系统sdk提供的看门狗
gd32V stm32等mcu
// 例如
void IWDG_Init(u8 prer,u16 rlr)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //对IWDG->PR IWDG->RLR的写
IWDG_SetPrescaler(prer); //设置IWDG分频系数
IWDG_SetReload(rlr); //设置IWDG装载值
IWDG_ReloadCounter(); //reload //重载看门狗计数值
IWDG_Enable(); //使能看门狗
}
int main(void)
{
delay_init(); //初始化延时函数
LED_Init(); //初始化LED端口
KEY_Init(); //初始化按键
delay_ms(100); //延时100ms
IWDG_Init(64, 500); //与分频数为64,重载值为500,溢出时间为1s
while(1)
{
if(KEY_Scan(0)==WKUP_PRES)//如果WK_UP按下,则喂狗
{
IWDG_ReloadCounter(); //喂独立看门狗
}
delay_ms(10);
}
}
//检查系统是否已经从独立看门狗复位恢复:只需要几十ms
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
{
printf("IWDG RESET OK!\r\n");
PFout(8) = ~PFout(8);
}
3.3 使用外部输入电路
一个简单的电路
3.4 分析软硬件看门狗
这里将mcu的内部看门狗和timer模拟的统称为软件看门狗
两者相同点如下:
-
都是通过定时器实现;定时时间到没有喂狗就进行复位。
-
都是需要在规定的时间内喂狗。
-
两者都是靠软件进行喂狗。
两者的主要差异如下:
-
附加功能:硬件看门狗芯片一般还附带了延迟复位和电源检测的功能,软件则没有。
-
屏蔽方法:软件看门狗很容易屏蔽关闭,只需修改其中的寄存器,而硬件看门狗一旦启动不断电就停不下来。
-
初始化:硬件看门狗芯片上电就启用,软件看门狗则需要配置相关的寄存器(或者调用相关的看门狗子程序)。
两者的总结:
软件和硬件看门狗芯片的最主要差异在于开启看门狗的时机,软件看门狗必须初始化(配置)看门狗才能使用,硬件看门狗是上电自动启用的,无法通过配置寄存器等方法关闭,设备上电就可以依赖上拉电阻提供的高脉冲开启看门狗,只要软件不及时喂狗(mcu不及时给高低电平),就复位。
举例:
如果软件看门狗是厂商提供的SDK中的一个API接口,开发者又没有能力在 uboot、内核阶段开启看门狗和喂狗,只能在用户层开启看门狗,这时开启看门狗只能在用户层面开启,开启时机就很晚了,程序就有可能还没有跑到看门狗初始化就跑飞了,此时你看门狗并不能起到任何效果。如果你能完全掌控项目,能够在主芯片上电第一时间,就初始化和开启软件看门狗,理论上,软件看门狗也可以做的和硬件看门狗一样可靠。
四、独立看门狗
4.1 独立看门狗的特性
独立看门狗,顾名思义,就是很独立的一个看门狗,其独立的点在于,由其专用低速时钟 (LSI) 驱动,因此,即便在主时钟发生故障时仍然保持工作状态。
①独立看门狗时钟
独立看门狗的时钟由独立的 RC振荡器 LSI提供,即使主时钟发生故障它仍然有效,非常独立。LSI的频率一般在 30~60KHZ之间,根据温度和工作场合会有一定的漂移,我们一般取 40KHZ,所以独立看门狗的定时时间不精确,只适用于对时间精度要求比较低的场合。
②计数器时钟
递减计数器的时钟由 LSI经过一个 8位的预分频器得到,我们可以操作预分频器寄存器 IWDG_PR 来设置分频因子,分频因子可以是:[4,8,16,32,64,128,256,256],计数器时钟 CK_CNT= 40/ 4*2^PRV,一个计数器时钟计数器就减一。(要详细时钟请根据手册计算)
③计数器
独立看门狗的计数器是一个 12 位的递减计数器,最大值为 0XFFF,当计数器减到 0时,会产生一个复位信号:IWDG_RESET,让程序重新启动运行,如果在计数器减到 0 之前刷新了计数器的值的话,就不会产生复位信号,重新刷新计数器值的这个动作我们俗称喂狗。
④重装载寄存器
重装载寄存器是一个 12 位的寄存器,里面装着要刷新到计数器的值,这个值的大小决定着独立看门狗的溢出时间。超时时间 Tout = (4*2^prv) / 40 * rlv (s) ,prv是预分频器寄存器的值,rlv是重装载
看门狗功能由 VDD 电压域供电,**在停止模式和待机模式下仍能工作。**也就是可以工作在停止模式和待机模式
4.2 独立看门狗的使用场景
IWDG 最适合应用于那些需要看门狗作为一个在主程序之外,能够完全独立工作,并且对时间精度要求较低的场合。
4.3 具体分析代码
过于简单不做展示
五、窗口看门狗
5.1 先分析几段代码
int main(void)
{
printf("start \n")
rcu_periph_clock_enable(RCU_WWDGT);
wwdgt_config(127, 80, WWDGT_CFG_PSC_DIV8); // (100-80) * 500us == 10ms
wwdgt_enable();
while(1){
delay_1ms(5);
feed_dog();
}
}
int main(void)
{
printf("start \n")
rcu_periph_clock_enable(RCU_WWDGT);
wwdgt_config(127, 80, WWDGT_CFG_PSC_DIV8); // (100-80) * 500us == 10ms
wwdgt_enable();
while(1){
delay_1ms(50);
feed_dog();
}
}
int main(void)
{
printf("start \n")
rcu_periph_clock_enable(RCU_WWDGT);
wwdgt_config(127, 80, WWDGT_CFG_PSC_DIV8); // (100-80) * 500us == 10ms
wwdgt_enable();
while(1){
feed_dog();
}
}
int main(void)
{
printf("start \n")
rcu_periph_clock_enable(RCU_WWDGT);
/*
* set WWDGT clock = (PCLK1 (54MHz)/4096)/8 = 1648Hz (~606.8 us)
* set counter value to 127
* set window value to 80
* refresh window is:
* ~606.8 * (127-80)= 28.5ms < refresh window < ~606.8 * (127-63) =38.8ms.
*/
wwdgt_config(127, 80, WWDGT_CFG_PSC_DIV8);
wwdgt_enable();
while(1){
delay_1ms(20);
//delay_1ms(29);
//delay_1ms(40);
feed_dog();
}
}
5.1 窗口看门狗简介
窗口看门狗,之所以称为窗口,是因为其喂狗时间是一个有上下限的范围内,你可以通过设定相关寄存器,设定其上限时间和下限时间:喂狗的时间不能过早也不能过晚。
窗口看门狗的上窗口就是配置寄存器WWDG->CFR里设定的W[6:0];下窗口是固定的0x40;当窗口看门狗的计数器在上窗口值之外,或是低于下窗口值都会产生复位。
上窗口的值可以只有设定,7位二进制数最大只可以设定为127(0x7F),最小又必须大于下窗口的0x40,所以其取值范围为64127(即:0x400x7F);配置寄存器WWDG->CFR中为计数器设定时钟分频系数,确定这个计数器可以定时的时间范围,从而确定窗口的时间范围。
窗口看门狗的时钟来自于PCLK1,在时钟配置中,根据手册可以知道其定时时间计算方法:
5.2 窗口看门狗的使用场景
WWDG 最适合那些要求看门狗在精确计时窗口起作用的应用程序
5.3 具体分析
#include "wdg.h"
#include "stm32f10x_wwdg.h"
static u8 WWDG_CNT=0x7f; /*保存WWDG计数器的设置值,默认为最大127. */
//========================================================================================
/**
* 初始化窗口看门狗
* tr :T[6:0],计数器值
* wr :W[6:0],窗口值
* fprer:分频系数(WDGTB),仅最低2位有效
* Fwwdg=PCLK1/(4096*2^fprer).
// 计数器值为7f,窗口寄存器为5f,分频数为8
WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);
*/
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); /*WWDG时钟使能*/
WWDG_SetPrescaler(fprer); /*设置IWDG预分频值*/
WWDG_SetWindowValue(wr); /*设置窗口值*/
WWDG_CNT=tr&WWDG_CNT; /* 初始化WWDG_CNT. */
WWDG_Enable(WWDG_CNT); /*使能看门狗 , 设置 counter . */
WWDG_ClearFlag(); /*清除提前唤醒中断标志位*/
WWDG_NVIC_Init();/* 初始化窗口看门狗 NVIC */
WWDG_EnableIT(); /* 开启窗口看门狗中断 */
}
/**
* 窗口看门狗中断服务程序
*/
void WWDG_NVIC_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; /*WWDG中断*/
/* 抢占2,子优先级3 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断使能,
NVIC_Init(&NVIC_InitStructure);/* NVIC初始化*/
}
/**
* 重设置WWDG计数器的值,喂狗
*/
void WWDG_Set_Counter(u8 cnt)
{
WWDG_Enable(cnt);
}
/**
* 看门狗中断服务程序
*/
void WWDG_IRQHandler(void)
{
WWDG_Set_Counter(127); //xxxx
}
void mian(void)
{
WWDG_Init();
//初始化
while(1)
{
WWDG_Set_Counter(127);
}
}
// WWDG的中断不是用于日常喂狗的,如果用于日常喂狗动作,那WWDG的相对于IWDG,功能也就没什么特别了。
// WWDG的中断是给程序员最后一次喂狗的机会,一般进入这个中断时,表示你在其他地方安排的喂狗操不能凑效了,而发生这种现象时,肯定是出问题了,
// 在这种情况下,这个中断时为了让你的程序在发生真正的看门狗复位前,有一个紧急处理的机会,如保存重要数据,或者系统刹车,说白了,就是让CPU写“遗嘱”;
六、独立看门狗和窗口看门狗的比较
ps : 不能在中断中喂狗!!!
独立看门狗建议使用的场景
-
程序跑飞
-
死循环
-
睡眠模式
-
监测部分硬件错误 如外部晶振出现问题(这种硬件错误,独立看门狗是可以监测的)
窗口看门狗建议使用场景
- 程序跑飞
- 死循环
- 对软件时序有要求(超过预期时间或低于预期时间)
- 能记录死亡日志的,因为窗口看门狗带中断
要注意看门狗和外接复位IC同时存在的情况,外部电路可能会阻止看门狗复位。
为什么要窗口看门狗?
对于一般的看门狗,程序可以在它产生复位前的任意时刻刷新看门狗,但这有一个隐患,有可能程序跑乱了又跑回到正常的地方,或跑乱的程序正好执行了刷新看门狗操作,这样的情况下一般的看门狗就检测不出来了;
如果使用窗口看门狗,程序员可以根据程序正常执行的时间设置刷新看门狗的一个时间窗口,保证不会提前刷新看门狗也不会滞后刷新看门狗,这样可以检测出程序没有按照正常的路径运行非正常地跳过了某些程序段的情况。
七、补充知识点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lcC1nEHd-1688113066263)(…\看门狗分享\睡眠.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IRgPfD5a-1688113066264)(…\看门狗分享\vdd.png)]
VCC 电路的供电正电压 VDDD 芯片的工作数字正电压
GND 电路的供电负电压 VSSD 芯片的工作数字正电压
VDD 芯片的工作正电压 VREF+ ADC基准参考正电压
VSS 芯片的工作负电压 VREF- ADC基准参考负电压
VDDA 芯片的工作模拟正电压 VBAT 电池或其他电源供电
新看门狗操作,这样的情况下一般的看门狗就检测不出来了;
如果使用窗口看门狗,程序员可以根据程序正常执行的时间设置刷新看门狗的一个时间窗口,保证不会提前刷新看门狗也不会滞后刷新看门狗,这样可以检测出程序没有按照正常的路径运行非正常地跳过了某些程序段的情况。
七、补充知识点
VCC 电路的供电正电压 VDDD 芯片的工作数字正电压
GND 电路的供电负电压 VSSD 芯片的工作数字正电压
VDD 芯片的工作正电压 VREF+ ADC基准参考正电压
VSS 芯片的工作负电压 VREF- ADC基准参考负电压
VDDA 芯片的工作模拟正电压 VBAT 电池或其他电源供电
VSSA 芯片的工作模拟负电压 VEE 负电压供电