14 WDG看门狗
14.1.1 WDG简介
•WDG(Watchdog)看门狗
目的:看门狗可以监控程序的运行状态,当程序因为设计漏洞、硬件故障、电磁干扰等原因,出现卡死或跑飞现象时,看门狗能及时复位程序,避免程序陷入长时间的罢工状态,保证系统的可靠性和安全性。
它只能帮你避免一下没有预料的漏洞,但不能解决漏洞,所以还是要在写代码时就想好可能会出现的问题。
运行逻辑:看门狗本质上是一个定时器,当指定时间范围内,程序没有执行喂狗(重置计数器)操作时,看门狗硬件电路就自动产生复位信号。
硬件配置:STM32内置两个看门狗
独立看门狗(IWDG):独立工作,对时间精度要求较低
窗口看门狗(WWDG):要求看门狗在精确计时窗口起作用
14.2.1 IWDG 独立看门狗
时钟:LSI内部低速40kHz时钟
预分频:8位最大256分频
递减计数器:12位最大4095计数
重装载寄存器:12位最大4095重装值
状态寄存器:只有两个更新同步位
键寄存器:写入特殊值控制电路重装计数器(喂狗)
当自减到0,下一个时钟脉冲重装计数值,产生IWDG复位。
看门狗处于VDD供电区,停机和待机仍能正常工作。
14.2.2 IWDG 键寄存器
-
键寄存器本质上是控制寄存器,用于控制硬件电路的工作
-
在可能存在干扰的情况下,一般通过在整个键寄存器写入特定值来代替控制寄存器写入一位的功能,以降低硬件电路受到干扰的概率
14.2.3 IWDG 超时时间
•超时时间:TIWDG = TLSI × PR预分频系数 × (RL + 1)
•其中:TLSI = 1 / FLSI
最长时间就是重装载值给最大
14.3.1 WWDG 窗口看门狗
PCLK1:APB1的时钟36MHz。
递减计数器:只有T0~T5是有效值,T6是溢出标志位。所以写的是6位自减计数器。
当然也有两种不同的溢出方式计算,如果你把T6当作溢出标志位则T0~T5减到0下一个脉冲溢出,如果你把T6当作计数位那么整个计数器减到0x40(01000000)之后下一个脉冲溢出即0x3F。(下面会出现这两种描述方法,需要理解一下)
但没有重装载寄存器,想要装载就需要自己手动填值。
WDGA位:使能看门狗,1使能。
WWDG_CFR:喂狗的最早时间界限。
14.3.2 WWDG工作特性
•递减计数器T[6:0]的值小于0x40时,WWDG产生复位
•递减计数器T[6:0]在窗口W[6:0]外被重新装载时,WWDG产生复位
•递减计数器T[6:0]等于0x40时可以产生早期唤醒中断(EWI),用于重装载计数器以避免WWDG复位
•定期写入WWDG_CR寄存器(喂狗)以避免WWDG复位
14.3.3 WWDG超时时间
PCLK1在进入WWDG时会固定分一个4096分频。
分频系数只能时0~3。
14.3.4 IWDG和WWDG的对比
tips:硬件看门狗:在选项字节里设置,硬件上电自动开启看门狗,防止在开启看门狗之前就程序卡死。
14.4.1 IWDG
由于代码较少不封装,仅展示main.c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
int main(void)
{
OLED_Init();
Key_Init();
//获取标志位判断是复位键复位还是看门狗复位
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST)==SET)
{
OLED_ShowString(1,1,"IWDGRST");
Delay_ms(500);
OLED_ShowString(1,1," ");
Delay_ms(100);
RCC_ClearFlag();
}
else
{
OLED_ShowString(2,1,"RST");
Delay_ms(500);
OLED_ShowString(2,1," ");
Delay_ms(100);
}
//解除写保护
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
//设置预分频值
IWDG_SetPrescaler(IWDG_Prescaler_16);
//设置重装值
IWDG_SetReload(2499);
//喂狗,使第一次喂狗周期是1s
IWDG_ReloadCounter();
//开启看门狗,会自动打开LSI时钟,不需要我们开启
IWDG_Enable();
while (1)
{
//获取键码值导致超时喂狗
Key_GetNum();
//喂狗
IWDG_ReloadCounter();
OLED_ShowString(3,1,"FEED");
Delay_ms(600);
OLED_ShowString(3,1," ");
Delay_ms(330);
}
}
喂狗和使能看门狗后,会自动给键寄存器写0x5555之外的值,开启写保护。所以我们不用手动给写保护。
14.5.1 WWDG
由于代码较少不封装,仅展示main.c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
int main(void)
{
OLED_Init();
Key_Init();
//获取标志位判断是复位键复位还是看门狗复位
if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST)==SET)
{
OLED_ShowString(1,1,"WWDGRST");
Delay_ms(500);
OLED_ShowString(1,1," ");
Delay_ms(100);
RCC_ClearFlag();
}
else
{
OLED_ShowString(2,1,"RST");
Delay_ms(500);
OLED_ShowString(2,1," ");
Delay_ms(100);
}
//由于WWDG是APB1的时钟所以需要手动开启
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
//设置预分频
WWDG_SetPrescaler(WWDG_Prescaler_8);
//设置窗口时间
WWDG_SetWindowValue(0x40 | 21);
//使能WWDG并第一次喂狗
WWDG_Enable(0x40 | 54);
while (1)
{
Key_GetNum();
OLED_ShowString(3,1,"FEED");
Delay_ms(20);
OLED_ShowString(3,1," ");
Delay_ms(20);
//喂狗
WWDG_SetCounter(0x40 | 54);
}
}
由于使能时会喂狗,所以第二次喂狗要放在延时函数之后否则就会导致快速喂狗。
因为看门狗不开启时,自减计数器也在运行,所以使能函数中就包含了第一次喂狗的函数。