STM32——看门狗通俗解析

笔者在学习看门狗的视频后,对看门狗仍然是一知半解,后面在实际应用中发现它是一个很好用的检测或者调试工具。所以总结一下笔者作为初学小白对看门狗的理解。

主函数初始化阶段、循环阶段和复位

众所周知,程序的运行一般是这样的:

程序在进入循环阶段之前,会在初始化阶段将每个寄存器或者某些变量赋值。初始化阶段的代码执行一次后,就不再执行了。而循环阶段的代码会执行很多次,一直循环反复的执行下去。这时,如果进行了 复位,程序就会从头开始,执行一次初始化代码,再反复执行循环代码。

而如何进行 复位 呢?常用的方法就是**"RESET" 键** ,也就是复位键 。在程序故障、跑飞或者卡死的时候,让它重头开始跑一遍,避免程序陷入到长时间的罢工状态。 不过复位键是人为按下,在程序自动运行的应用环境中,显然不适用。于是,在程序中使用 看门狗 就可以代替 复位键来重启程序。

避免程序陷入到长时间的罢工状态。 这一句话非常关键,比如下面这个检测按键是否被按下 的程序当中,**while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);**会等待按键是否被释放(松手)。

如果这个按键一直被按下,或者这个按键出现了故障,导致这个while循环一直出不来,就会造成程序始终被卡在这个地方。

因此,另一种复位方式------看门狗,它可以通过定时来重新启动程序。 看门狗本质上是一个递减的定时器,如果程序在某个循环卡住了,当看门狗定时器时间跑完,看门狗就会复位程序,从而跳出循环,重头开始。

喂狗

看门狗只是一个检测程序故障的工具。 当我们在程序的初始化阶段设置了一个看门狗,当看门狗定时器时间跑完就会进行复位,重头开始。而我们的程序是一个循环过程,这就导致了我们需要用一个方法,让看门狗在程序正常时不作为,又要在程序故障时起作用。

这时就要进行喂狗操作 。喂狗一般是在循环阶段的最后进行,喂狗的本质是将定时器重装,从头递减,一旦喂了狗,看门狗就会重新定时,不会执行复位操作。而遇到程序卡死在某一个等待循环时,就不会执行到喂狗操作函数。这个时候,看门狗一到时间就会进行复位。另外,如果没有及时喂狗,在看门狗定时器倒计时结束前还没有喂狗,也会重启程序。也就是说循环程序执行一次 的时间需要在看门狗定时器的时间规定范围内。

独立看门狗和窗口看门狗

独立看门狗和窗口看门狗都是规定了程序循环时间,一旦时间到了,就会执行复位操作。独立看门狗是规定了一个最大时间点 ,时间从这个最大时间点递减为0,即倒计时为0后,执行复位。而窗口看门狗是规定了一个时间段 ,如果没有在这个时间段范围进行喂狗,也会执行复位。

|----------------|------------------------|
| 写入键寄存器的值 | 作用 |
| 0xCCCC | 启用独立看门狗 |
| 0xAAAA | 重新加载到计数器 (喂狗) |
| 0x5555 | 解除IWDG_PR和IWDG_RLR的写保护 |
| 除0x5555其他值 | 启用IWDG_PR和IWDG_RLR的写保护 |

键寄存器(KR)是一个控制寄存器,通过写入值来控制看门狗的操作。

独立看门狗(IWDG)初始化

首先,要设置看门狗需要有个钥匙,这个钥匙是保护寄存器被其他程序随意修改,降低干扰。

IWDG_WriteAccess_Enable 将写保护解除,后续可以设置预分频寄存器和重装寄存器

IWDG_Prescaler_16 设置预分频寄存器为16分频。

独立看门狗时钟是由一个 独立 的内部低速时钟LSI 提供。LSI = 40kHz,IWDG超时时间计算公式:T(IWDG) = 1 / LSI * PR预分频系数 * (RL重装值+1)

上面代码预分频器为16分频,40k/16=2500,即一秒可以计数2500,设置重装值为2499,最大定时时间为一秒。

IWDG_ReloadCounter(); 为喂狗操作,这里作用是重新计数,在每次循环都需要进行喂狗操作。

程序完整版:

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

窗口看门狗(WWDG)初始化

窗口看门狗时钟是由APB1时钟分频得到,PCLK=36MHz。

|-----------|------------|--------------|
| 预分频系数 | 最小超时值 | 最大超时值 |
| 1 | 113 μs | 7.28 ms |
| 2 | 227 μs | 14.56 ms |
| 4 | 455 μs | 29.12 ms |
| 8 | 910 μs | 58.25 ms |

WWDG超时时间计算公式:T(WWDG) = 1 / PCLK * 4096 * WDG预分频系数 * (T[5:0]+1)

WWDG窗口时间计算公式:T(WIN) = 1 / PCLK * 4096 * WDG预分频系数 * ( T[5:0] - W[5:0] )

图中,想让T(WWDG) = 50 ms , 50 ms = 1 / 36 Mhz * 4096 * 8 *(T[5:0]+1),求得T[5:0]=54

想让T(WIN) = 30 ms , 30 ms = 1 / 36 Mhz * 4096 * 8 *(T[5:0]- W[5:0]),求得T[5:0]=21

WWDG_SetCounter(0x40 | 54); 为喂狗操作,这里作用是重新计数,在每次循环都需要进行喂狗操作。

循环程序时间应该在30ms和50ms之间,低于或者超过都会 让看门狗进行复位。

程序完整版:

#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);					//重装计数器,喂狗
	}
}
相关推荐
枯无穷肉3 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名6773 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式科普3 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
嵌入式大圣3 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
云山工作室4 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费4 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
qq_397562316 小时前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机
liyinuo20176 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范
艺术家天选6 小时前
STM32点亮LED灯
stm32·单片机·嵌入式硬件
向阳逐梦6 小时前
基于STM32F4单片机实现ROS机器人主板
stm32·单片机·机器人