看门狗定时器(WDT,Watch Dog Timer)是单片机的一个组成部分,它实际上是一个计数器,一般来说都是向下计数的,给看门狗一个数字,程序开始运行后看门狗开始倒计数。如果程序运行正常,过一段时间CPU应发出指令让看门狗复位(在计数器减到0之前),重新开始倒计数。如果看门狗减到0就认为程序没有正常工作,强制整个系统复位。本节将对看门狗定时器的工作原理和作用进行详细讲解。
看门狗的主要功能是在发生系统软件故障时,将系统复位。也可以用于将系统从休眠或空闲模式唤醒。一般在嵌入式开发中,整个程序都是在次循环往复不停的工作,正常运行期间会定时让看门狗复位,一旦程序发生故障,停在某一处,看门狗不能及时复位,计数器减到0,就会强制整个系统复位,一般情况下复位操作能解决大部分问题。
(资料图片仅供参考)
用户开发的产品很有可能运用到一些非常极端苛刻的环境中,容易发生故障,但是通过复位就能解决,如果人工复位,成本相当高,这时候看门狗的使用就非常有效了。
STM32结合安全度、计时精确度和易用性提供了IWDG和WWDG两种看门狗,监测软件跑飞,或未按预想地运行的情况触发系统复位或产生中断(中断仅针对窗口看门狗)
独立看门狗IWDG:专用时钟LSI(内部低速时钟),低功耗模式仍可运行,对定时的控制比较松,由于专门为其提供独立的时钟,因此称为独立看门狗。
窗口看门狗WWDG:总线时钟APB1,对定时控制比较严,要求主应用程序在规定时间窗口内喂狗。
独立看门狗IWDG工作原理框图分析
独立看门狗本质上就是一个计数定时器,既然是定时器,就和普通定时器的工作原理差不多,仍然包含计数器,重装载寄存器等关键部件。计数器递减的速度取决于时钟的频率,独立看门狗使用专用内部低速时钟,大小为32KHz。当计数器减到0后会触发一个复位异常,进行系统的重启。为了不让计数器减到0,在程序中每过一段时间都要进行喂狗操作,喂狗操作就是刷新递减计数器CNT的值,就是把重装载值再次刷进计数器中。状态寄存器是用来指示预分频器和重载寄存器更新的状态。
1.独立看门狗的时钟:STM32F4 的独立看门狗由内部专门的 32Khz 低速时钟(LSI)驱动,即使主时钟发生故障,它也仍然有效。这里需要注意独立看门狗的时钟是一个内部RC时钟(RC振荡器典型的缺陷就是比较容易受环境的影响,且精确地不高),所以并不是准确的32Khz,而是在 15~47Khz 之间的一个可变化的时钟,只是我们在估算的时候(之所以要估算时间是因为用户要确保计数器减到0 之前喂狗),以 32Khz 的频率来计算,看门狗对时间的要求不是很精确,所以,时钟有些偏差,都是可以接受的。
2.预分频器:分频之后的时钟才是真正驱动递减计数器的时钟。寄存器中只有低3位有效,不同的位组合表示不同的分频系数。
3.重载寄存器:低12位有效,最大值位2的12次方。只有在关键字寄存器中写入AAAAh时,重载寄存器厚葬的值才会自动装载到看门狗计数器中。
4.关键字寄存器:用来控制独立看门狗,写入不同的值执行不同的命令。正常情况下PR和PLR是不可写的,具有写保护,需要通过写入关键字寄存器值来解除写保护。另外一旦通过关键字寄存器启动看门狗,看门狗计数,将不会再停止,除非硬件复位,默认情况下看门狗关闭,所以正常情况下不轻易使用看门狗,否则系统会产生出乎意料的复位,造成损失。
5.状态寄存器:低2位有效,更新时一定要确定相应位为0才可以更新。
IWDG的超时时间
以预分频器分频系数为32为例,计数器的时钟频率为1KHz,每个脉宽的时间间隔为1ms,当计数器的值为最小值1时,则最短超时时间为1ms,当当计数器的值为最大值0xFFF时,最长超时时间为2的12次方乘以1ms为4096ms。因此用户可根据下表设置正确的超时时间防止看门狗复位。
IWDG独立看门狗实例
实验要求:开启STM32的独立看门狗,按键按下即喂狗,如果超时未喂狗,则看门狗复位系统,用指示灯指示系统复位。
步骤:
1.配置RCC
2.使能并配置IWDG
3.配置按键管脚为输入模式。
4.编写代码
//mian.c #include "main.h" #include "stm32f4xx_hal.h" #include "iwdg.h" #include "usart.h" #include "gpio.h" int main(){ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_IWDG_Init(); printf("this is iwdg testn"); HAL_GPIO_WritePin(GPIOF, GPIO_PIN_7, GPIO_PIN_SET);//默认指示灯熄灭 HAL_Delay(500); HAL_GPIO_WritePin(GPIOF, GPIO_PIN_7, GPIO_PIN_RESET); //指示灯亮,如果系统复位,指示灯就会熄灭0.5s后再次亮起 while(){ //******************************** //******************************** //******************************** //正常情况下喂狗操作之前有很多用户程序 //现在使用按键手动喂狗以便观察现象 if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { HAL_IWDG_Refresh(&hiwdg); //喂狗操作,将重载计数器的值再次装填到计数器中 ,防止复位 } } }
窗口看门狗WWDG剖析
窗口看门狗(WWDG)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。
递减计数器的值在 T6 位(WWDG->CR 的第六位)变成 0 前被刷新,看门狗电路在达到预置的时间周期时,会产生一个 MCU复位。WWDG->CR满载时为1111111,即0x7F,满载随着时钟减1,当减到1000000,即0x40时,如果再减1变为0x3F之前计数器没有被刷新(喂狗),则会产生 MCU 复位。由0x40变为0x3F第六为由1变为0,也就是第6为变为0之前计数器必须被刷新。0x40就是下窗口
在递减计数器达到窗口配置寄存器(WWDG->CFR)数值之前,如果 7 位的递减计数器数值(在控制寄存器中)被刷新, 那么也将产生一个 MCU 复位。也就是计数器在减到窗口配置寄存器(WWDG->CFR)中设定的数值之前被刷新(喂狗),就会就会产生 MCU 复位。这个数值就是上窗口
二者分别设定了一个喂狗的时间下限和一个喂狗的时间上限,中间形成了一个喂狗的窗口,因而称为窗口看门狗。这样对喂狗的时间有了更加精确的定位,要求也更严格。
WWDG的特性
可配置的时间窗,用来检测应用程序非预期中的运行流程(过早或过晚),上窗口的值可配置范围为0x40-0x7F(64-127)
看门狗复位条件:①使能了看门狗的情况下,自减计数器值小于0x40。② 使能了看门狗的情况下,喂狗时间大于上窗口的值
计数器值=0x40时可以使能一个早期唤醒中断(EWI),因为再减下去即将产生复位,如果有些数据还不保存就来不及了,可以在中断处理中做一些紧急保护的事情或者刷新看门狗计数器。
窗口看门狗WWDG工作原理框图分析
①窗口看门狗时钟:窗口看门狗时钟来自 PCLK1,也就是来自APB1,是主系统时钟,PCLK1 最大是 42M,由 RCC 时钟控制器开启。一旦主系统时钟崩溃,窗口看门狗就无法工作。
②计数器时钟:计数器时钟由 CK计时器时钟经过预分频器分频得到,WDG分频器仍然有一些分频系数供用户选择,分频系数由配置寄存器 CFR 的位 8:7 WDGTB[1:0]配置。
③计数器:窗口看门狗的计数器是一个递减计数器,共有 7 位,其值存在控制寄存器CR 的位 6:0,即 T[6:0],当 7 个位全部为 1时是 0X7F,这个是最大值,当递减到 T6 位变成 0时,即从0X40 变为 0X40时候,会产生看门狗复位。这个值 0X40 是看门狗能够递减到的最小值,当递减计数器递减到 0X40 的时候,还不会马上产生复位,如果使能了提前唤醒中断:CFR 位 9 EWI 置 1,则产生提前唤醒中断,如果真进入了这个中断的话,就说明程序肯定是出问题了,那么在提前唤醒中断的处理程序中我们就需要做最重要的工作,比如保存重要数据,或者报警等,这个中断我们也叫它死前中断。但是这个中断的处理时间只有0X40减到0X3F的一个CLK的时长。
④窗口值:下窗口的值是固定的0X40,上窗口的值可以改变,具体的由配置寄存器CFR 的位 6:0 W[6:0]设置。其值必须大于 0X40(十进制为64),如果小于或者等于 0X40就是失去了窗口的价值,而且也不能大于计数器的值,所以必须得小于 0X7F(十进制为127)。如何设置窗口值需要根据我们需要监控的程序的运行时间来决定。如果我们要监控的程序段 A 运行的时间为 Ta,当执行完这段程序之后就要进行喂狗,如果在窗口时间内没有喂狗的话,那程序就肯定是出问题了。一般计数器的值TR设置成最大 0X7F,窗口值为 WR,计数器减一个数的时间为 T,那么时间:(TR-WR)*T 应该稍微小于 Ta即可。
窗口看门狗WWDG实例
实验要求:开启STM32的窗口看门狗,并使能唤醒中断。主程序正常运行时喂狗,用按键中断模拟程序故障死机,此时将触发唤醒中断并产生复位,用LED灯指示唤醒中断的触发。
达到0X40会触发早期唤醒中断,但是再次减1就会产生系统复位,因此中断处理的事件就是1个CLK的持续时间,不同的分频系数,CLK的持续时间不同,总体来说早期唤醒中断的时间事件相当短,这个过程中只能做一些非常紧急的操作。
步骤:
1.配置RCC
2.配置LED灯管脚,设为输出模式
3.配置按键中断管脚,设为外部中断模式
4.使能并配置WWDG
5.中断配置
6.编写代码
//main.c #include "main.h" #include "stm32f4xx_hal.h" #include "usart.h" #include "wwdg.h" #include "gpio.h" int main(){ uint8_t wr, tr; HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); MX_WWDG_Init(); printf("this is wwdg testn"); HAL_GPIO_WritePin(GPIOF, GPIO_PIN_7, GPIO_PIN_SET);//初始时指示灯熄灭 wr = WWDG- >CFR & 0x7f ;//获取上窗口的值 while(){ tr = WWDG- >CR & 0x7f;//获取当前计数器的值 if(tr < wr){ //计数器的值小于上窗口的值,进行喂狗 //没有复位说明大于下窗口的值,不需要判断 HAL_WWDG_Refresh(&hwwdg); //刷新计数器的值,喂狗 } }}
//gpio.c //重写按键中断(外部中断)的中断处理回调函数 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_0) { printf("程序故障n"); while(1); //死循环,模拟故障死机 } }
//wwdg.c //重写提前唤醒中断的中断处理回调函数 void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg) { HAL_GPIO_WritePin(GPIOF, GPIO_PIN_7, GPIO_PIN_RESET); //LED亮 printf("EarlyWakeup INTn"); //提前唤醒中断处理的时间非常有限,串口信息很有可能打印不完整 }