STM32入门——看门狗相关(23)

STM32入门——看门狗相关(23)

一、WDG简介

  • WDG(Watchdog)看门狗
  • 看门狗可以监控程序的运行状态(监督员),当程序因为设计漏洞、硬件故障、电磁干扰等原因,出现卡死或跑飞现象时,看门狗能及时复位程序,避免程序陷入长时间的罢工状态,保证系统的可靠性和安全性
  • 看门狗本质上是一个定时器,核心原理是:系统需要在规定时间内定期“喂狗”(重置计数器),如果超过这个时间未能喂狗,看门狗就会强制触发系统复位,让程序从初始状态重新开始运行,从而脱离死机状态。
  • STM32内置两个看门狗,互为补充,适用于不同场景

独立看门狗(IWDG):独立工作,对时间精度要求较低

窗口看门狗(WWDG):要求看门狗在精确计时窗口起作用

二、IWDG独立看门狗

2.1.IWDG框图

img

  • 独立运行:由独立的低速内部时钟(LSI ~40kHz)驱动。这意味着即使主时钟(HCLK)发生故障(例如外部晶振失效),IWDG依然能正常工作,可靠性极高。
  • 自由运行:一旦启用,无法被软件禁用(除非复位)。这防止了软件意外或恶意禁用看门狗。
  • 简单粗暴:只有一个时间要求:必须在计数器减到0之前喂狗(重载计数器)。早喂晚喂都可以,只要别超时。
  • 时钟源:专用的LSI RC振荡器(通常约40kHz,但精度较低,需留有余量)。
  • 预分频器:8位(最大进行256分频),IWDG_PR与分频寄存器分配系数
  • 12位递减计数器:最大值2^12-1=4095,从初始值(重载值)开始向下计数,减到0产生复位信号
  • 重装载数值:正常运行时为避免复位,提前在重装寄存器IWDG_RLR写一个值,之后,在运行过程中,在键寄存器IWDG_KR写入一个特定数据,控制电路,进行喂狗,此时重装值会复制到当前计数器中,计数值回到重装值,重新自减运行。
  • 喂狗操作:手动重置计数器(递减计数器,减到0复位);程序正常运行时,为了避免复位,在计数器减到0之前,及时把计数值加大。若程序卡死,未及时加大计数值,则计数值减到0后就自动复位。
  • 状态寄存器IWDG_SR:两个更新同步位,标志电路运行状态

2.2.IWDG键寄存器

•键寄存器本质上是控制寄存器,用于控制硬件电路的工作

•在可能存在干扰的情况下,一般通过在整个键寄存器写入特定值来代替控制寄存器写入一位的功能,以降低硬件电路受到干扰的概率

写入键寄存器的值 作用
0xCCCC 启用IWDG独立看门狗
0xAAAA IWDG_RLR中的值重新加载到计数器(喂狗)
0x5555 解除IWDG_PR和IWDG_RLR的写保护(允许访问预分频器和重载寄存器)
0x5555之外的其他值 启用IWDG_PR和IWDG_RLR的写保护(防止误操作)

2.3.IWDG超时时间

超时时间:TIWDG = TLSI × PR预分频系数 × (RL + 1)

其中:TLSI = 1 / FLSI

预分频系数(4, 8, 16, 32, 64, 128, 256)

img

2.4.适用场景

  • 防止硬件故障(如外部晶振停振)。
  • 应对未知的软件错误(程序跑飞、死循环)。
  • 对时间要求不严格,只需保证系统最终能复位即可的场合。

三、WWDG窗口看门狗

3.1.WWDG框图

img

  • 依赖于主时钟(PCLK1):由APB1时钟分频后驱动,默认36MHz。如果主时钟失效,WWDG也会失效。因此它主要用于监测软件逻辑。
  • 看门狗预分频器WDGTB:调节后面计数器的时钟频率
  • 窗口概念:喂狗必须在一个精确的时间窗口内完成。
    • 不能太早:计数器值高于窗口寄存器的值(T6:0>W6:0)时喂狗,会立即复位。
    • 不能太晚:计数器减到0x3F(0 111111)之前仍未喂狗,会复位。
    • 必须在窗口期内:计数器值低于窗口值且大于0x3F时喂狗,才是正确的。
  • 窗口值(WWDG_CFR看门狗配置寄存器 W[6:0]):定义了喂狗的上限边界。计数器值必须低于这个值且高于0x3F时喂狗才安全。
  • WDGA:窗口看门狗的激活位,=1启用窗口看门狗
  • 可产生早期中断:在即将复位(计数器减到0x40)时,可以产生一个早期唤醒中断(EWI),允许在复位前执行一些紧急日志保存或安全处理操作。
  • 时钟源:APB1时钟(PCLK1)经过固定分频(默认1, 2, 4, 8)。
  • 6位递减计数器CNT:位于控制寄存器CR内,bits [6:0] 有效。T6为复位标志位,当计数器从0x40(1 000000)减到0x3F(0 111111)时,会产生复位。bit6 (T6) 是下降沿检测位。

3.2.WWDG工作特性

•递减计数器T[6:0]的值小于0x40时,WWDG产生复位

•递减计数器T[6:0]在窗口W[6:0]外被重新装载时(过早喂狗),WWDG产生复位

•递减计数器T[6:0]等于0x40时可以产生早期唤醒中断(EWI),用于重装载计数器以避免WWDG复位

•定期写入WWDG_CR寄存器(喂狗)以避免WWDG复位

img

3.3.WWDG超时时间

超时时间(最晚时间):TWWDG = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] + 1)

窗口时间(最早时间):TWIN = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] - W[5:0])

其中:TPCLK1 = 1 / FPCLK1

4096为固定分频

img

3.4.适用场景

  • 监测特定任务的执行周期。例如,一个任务必须每10ms执行一次,不能太快也不能太慢。WWDG可以确保该任务严格按预期的时间规划执行。
  • 需要在上复位前进行紧急处理的复杂系统

四、IWDG&WWDG对比

IWDG独立看门狗 WWDG窗口看门狗
复位 计数器减到0后 计数器T[5:0]减到0后、过早重装计数器
中断 早期唤醒中断
时钟源 LSI(40KHz) PCLK1(36MHz)
预分频系数 4、8、32、64、128、256 1、2、4、8
计数器 12位 6位(有效计数)
超时时间 0.1ms~26214.4ms 113us~58.25ms
喂狗方式 写入键寄存器,重装固定值RLR 直接写入计数器,写多少重装多少
防误操作 键寄存器和写保护
用途 独立工作,对时间精度要求较低 要求看门狗在精确计时窗口起作用

五、使用建议

  1. IWDG用于保底:在大多数项目中,都应启用IWDG作为最后一道防线,防止系统彻底死锁。
  2. WWDG用于精密控制:在对任务执行时序有严格要求的关键应用中,使用WWDG来确保软件的健壮性。
  3. 喂狗位置:喂狗操作应放在主循环或关键任务完成的地方,确保系统正常运行时就一定能喂到狗。
  4. 中断中喂狗需谨慎:避免在高级别中断中喂狗,否则即使主程序已卡死,中断仍能正常喂狗,导致看门狗失效。
  5. 计算超时时间要留余量:尤其是LSI时钟精度较低,计算出的超时时间可能不准,设置 reload value 时要留出足够的余量。

六、独立看门狗代码编程

6.1.独立看门狗配置流程:

1.开启LSI时钟(如果独立看门狗已经由硬件选项或软件启动,LSI振荡器将被强制在打开状态,并且不能被关闭。在LSI振荡器稳定后,时钟供应给IWDG)
2.解除写保护0x5555,写入预分频器和重装寄存器(根据超时时间公式计算)
3.启动独立看门狗0xCCCC
4.在主循环,重复执行0xAAAA喂狗

6.2.独立看门狗库函数

1.写使能控制
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);
2.写预分频器
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);
3.写重装值
void IWDG_SetReload(uint16_t Reload);
4.重新装载寄存器(喂狗)
void IWDG_ReloadCounter(void);
5.启动独立看门狗
void IWDG_Enable(void);
6.获取标志位状态
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG);
7.标志位查询看门狗导致复位还是上电/复位键导致的复位
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);
void RCC_ClearFlag(void);标志位不会自动清零,需手动清零

6.3独立看门狗代码

#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);
	}
}

七、窗口看门狗代码编程

7.1窗口看门狗初始化:

①开启APB1时钟(PCLK1)
②配置预分频寄存器(窗口看门狗无写保护,直接写入寄存器)
③配置控制寄存器(看门狗使能位,计数器溢出标志位,计数器有效位)
④在运行过程中,不断写入重装值,就可喂狗

7.2窗口看门狗库函数

1.恢复缺省配置
void WWDG_DeInit(void);
2.写入预分频器
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
3.写入窗口值
void WWDG_SetWindowValue(uint8_t WindowValue);
4.使能中断
void WWDG_EnableIT(void);
5.写入计数器
void WWDG_SetCounter(uint8_t Counter);
6.使能看门狗
(递减计数器处于自由运行状态,即使开门狗被禁止递减,计数器仍继续递减。当看门狗被启用时,T6位必须被设置,以防止立即产生一个复位。 )
void WWDG_Enable(uint8_t Counter);
7.获取标志位
FlagStatus WWDG_GetFlagStatus(void);
8.清除标志位
void WWDG_ClearFlag(void);

7.3窗口看门狗代码

#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);					//重装计数器,喂狗
	}
}

posted @ 2026-04-06 19:12  Q&25  阅读(194)  评论(0)    收藏  举报