STM32学习笔记12-WDG看门狗

目录

WDG简介

独立看门狗

IWDG框图

IWDG键寄存器

IWDG超时时间

窗口看门狗

WWDG框图

WWDG工作特性

WWDG超时时间

IWDG与WWDG对比

WDG看门狗应用

独立看门狗

窗口看门狗


WDG简介

看门狗简单来说就是程序运行的保障措施,我们得在程序中定期喂狗,如果程序出问题卡死了没有在规定时间喂狗那么看门狗硬件电路就会自动帮我们复位一下,防止程序长时间卡死。

  • WDG(Watchdog)看门狗
  • 看门狗可以监控程序的运行状态,当程序因为设计漏洞、硬件故障、电磁干扰等原因,出现卡死或跑飞现象时,看门狗能及时复位程序,避免程序陷入长时间的罢工状态,保证系统的可靠性和安全性
  • 看门狗本质上是一个定时器,当指定时间范围内,程序没有执行喂狗(重置计数器)操作时,看门狗硬件电路就自动产生复位信号
  • STM32内置两个看门狗

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

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

独立看门狗

IWDG框图

框图类比定时器的时基单元,时基单元由预分频器、计数器、自动重装寄存器组成,定时器计数器可以自增自减,看门狗始终自减运行,自减到0定时器产生更新事件和中断,而看门狗是直接产生复位,另外重装值定时器是在更新事件重装,而看门狗需要我们在自减到0之前手动重装,因为减到0就复位了,正常运行情况下肯定是不能让它减到0的,那这个手动重装计数器的操作就是喂狗。

IWDG键寄存器

接下来介绍一下键寄存器。

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

IWDG超时时间

  • 超时时间:TIWDG = TLSI × PR预分频系数 × (RL + 1)
  • 其中:TLSI = 1 / FLSI=1/40K=0.025ms

窗口看门狗

窗口看门狗在功能上与独立看门狗比较像,大体上看比独立看门狗多了个最早喂狗时间限制。

WWDG框图

工作流程:时钟来源是PCLK1也就是APB1的时钟,默认是36MHz,36MHz时钟进来之后先经过一个预分频器进行分频,接着分频之后的时钟驱动计数器进行计数,和独立看门狗一样也是递减计数器,从图上看写了T6-T0,总共是7个位,下面却写的6位递减计数器,这是因为这个计数器只有T5-T0这6位是有效的计数值,最高位T6这里用来作溢出标志位,T6位等于1表示计数器没溢出,T6位等于0时表示计数器溢出。

举个例子:比如这个计数器初始值给111 1111,那么来一个计数脉冲值减一,直到减为100 0000这个数是关机键节点,此时包括T6位在内的数是100 0000,转为16进制是0x40,也就是说此时如果把T6位也当做计数器的一部分那计数器的值实际才减一半,但是如果把T6位剥离出去当做溢出标志位,那此时状态就是标志位为1,计数器为00 0000已经减到0了,再减一次值就是011 1111,这时最高位T6由1变0,即代表计数器溢出,这时最高位通过线路产生复位信号。

WDGA是窗口看门狗的激活位,也就是使能,WDGA写入1启用窗口看门狗。

喂狗时间最早界限由上面的WWDG_CFR实现,首先需要计算一个最早界限的计数值,写入到W6-W0中,这些值写入后固定不变,一旦我们执行写入CR操作时,与门开关就会打开,写入CR其实就是写入计数器也就是喂狗,在喂狗时这个比较器开始工作,一旦它比较我们当前的计数器T6:0>窗口值W6:0,比较结果就等于1,这个1通过或门也可以去申请复位。总结就是喂狗的时候把当前计数值和我预设的窗口值进行比较,如果发现狗余粮充足(计数器值还比较大),你喂狗还这么频繁那肯定有问题,我就给你复位一下不让喂狗太早了。

WWDG工作特性

  • 递减计数器T[6:0]的值小于0x40时,WWDG产生复位
  • 递减计数器T[6:0]在窗口W[6:0]外被重新装载时,WWDG产生复位
  • 递减计数器T[6:0]等于0x40时可以产生早期唤醒中断(EWI),用于重装载计数器以避免WWDG复位
  • 定期写入WWDG_CR寄存器(喂狗)以避免WWDG复位

0x40这时T6还是1,还没有溢出,再减一个数变为0x3F才是溢出,所以这个EWI就是在溢出前一刻发生,也可以叫死前中断,马上就要溢出复位了在提醒你一下。 

WWDG超时时间

超时时间(喂狗最晚时间):

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

窗口时间(喂狗最早时间):

        TWIN = TPCLK1 × 4096 × WDGTB预分频系数 × (T[5:0] - W[5:0])

其中:TPCLK1 = 1 / FPCLK1

IWDG与WWDG对比

WDG看门狗应用

独立看门狗

配置流程:

第一步开启时钟,只有这个LSI时钟开启了独立看门狗才能运行,手册里描述如果我们开启了看门狗那么LSI就会跟随强制打开,等LSI稳定就会自动为独立看门狗提供时钟了,所以开启LSI时钟就不用再写代码了执行了。

第二步解除写保护。写入预分频器和重装寄存器,在写入之前不要忘了写保护,首先写入0x5555解除写保护,然后再写入预分频和重装值。

第三步写入预分频和重装值。具体写多少根据公式计算。

最后这些配置工作做完我们就可以执行0xCCCC来启动看门狗了,然后再主循环我们可以不断执行0xAAAA来进行喂狗。

接下来看几个库函数:

void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);//写使能控制
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);//写预分频器
void IWDG_SetReload(uint16_t Reload);//写重装值
void IWDG_ReloadCounter(void);//重新装载寄存器,就是喂狗
void IWDG_Enable(void);//启动独立看门狗
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG);//获取标志位状态

在看一个rcc里的函数,我们想知道程序复位是看门狗导致的复位还是上电或复位键导致的复位。利用查看标志位的函数,也别忘了清除标志位:

FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);//查看标志位
void RCC_ClearFlag(void);//清除标志位

 准备工作:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"

uint8_t Key_Num;

int main(void)
{
	OLED_Init();
	
	OLED_ShowString(1, 1, "IWDG TEST");
	
	if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)//如果独立看门狗复位标志是SET,说明本次复位是独立看门狗导致的
	{
		OLED_ShowString(2, 1, "IWDGRST");
		Delay_ms(500);
		OLED_ShowString(2, 1, "       ");
		Delay_ms(100);
		
		RCC_ClearFlag();//清除标志位
	}
	else //普通复位
	{
		OLED_ShowString(3, 1, "RST");
		Delay_ms(500);
		OLED_ShowString(3, 1, "   ");
		Delay_ms(100);
	}

	while(1)
	{
		
	}
}

接下来写独立看门狗代码:

第一步开启时钟。前文说过不用手动写代码开启。 

第二步解除写保护。

IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//IWDG_WriteAccess_Enable定义了就是0x5555

 第三步写入预分频和重装值。计算参数之前我们需要确定一下我们想要设定的超时时间,比如旋转设置超时时间为1000ms,也就是1s,那就是要求喂狗时间间隔不能超过1000ms,所以公式就是:

  • 1000ms = 0.025ms * PR预分频系数 *(RL+1)

PR和RL都是待定的数值,可以有多种组合:

前两个预分频系数不能选因为它们最长时间都达不到1s,剩下这些分频都可以选,因为它们的计数最短时间和最长时间都包含了1000ms,这些可选项中优先选择预分频系数小的,最大化利用计数器的值,减小时间误差。所以我们选择16分频,即PR=16,公式就是:

  • 1000ms = 0.025ms * 16 *(RL+1)

算出来RL就是2499。

IWDG_SetPrescaler(IWDG_Prescaler_16);//配置预分频器,16分频
IWDG_SetReload(2499);//配置重装值,0-4096之间,我们计算得到2499

 下一步我们就可以启动看门狗了。当然在启动之前我们可以先喂一次狗,这样启动之后的第一个喂狗周期就是1000ms,这样严谨一些。

IWDG_ReloadCounter();//启动之前先喂一次狗
IWDG_Enable();//启动看门狗

 独立看门狗的初始化就完成了,接下来我们测试喂狗的逻辑。

完整代码:

main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"

uint8_t Key_Num;

int main(void)
{
	OLED_Init();
	Key_Init();
	
	OLED_ShowString(1, 1, "IWDG TEST");
	
	if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)//如果独立看门狗复位标志是SET,说明本次复位是独立看门狗导致的
	{
		OLED_ShowString(2, 1, "IWDGRST");//独立看门狗复位的提示信息
		Delay_ms(500);
		OLED_ShowString(2, 1, "       ");
		Delay_ms(100);
		
		RCC_ClearFlag();//清除标志位
	}
	else //普通复位
	{
		OLED_ShowString(3, 1, "RST");//普通复位的提示信息
		Delay_ms(500);
		OLED_ShowString(3, 1, "   ");
		Delay_ms(100);
	}
	
	/* 解除写保护 */
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//IWDG_WriteAccess_Enable定义了就是0x5555
	
	/* 写入预分频和重装值 */
	IWDG_SetPrescaler(IWDG_Prescaler_16);//配置预分频器,16分频
	IWDG_SetReload(2499);//配置重装值,0-4096之间,我们计算得到2499

	IWDG_ReloadCounter();//启动之前先喂一次狗
	IWDG_Enable();//启动看门狗
	while(1)
	{
		Key_GetNum();//按住按键不放,主循环就会阻塞,不能及时喂狗,独立看门狗就会复位
		
		IWDG_ReloadCounter();//喂狗
		
		OLED_ShowString(4, 1, "FEED");//喂狗的提示信息
		Delay_ms(200);
		OLED_ShowString(4, 1, "    ");
		Delay_ms(600);
	}
}

窗口看门狗

在独立看门狗的测试框架上写:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"

uint8_t Key_Num;

int main(void)
{
	OLED_Init();
	Key_Init();
	
	OLED_ShowString(1, 1, "WWDG TEST");
	
	if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)//如果窗口看门狗复位标志是SET,说明本次复位是窗口看门狗导致的
	{
		OLED_ShowString(2, 1, "WWDGRST");//窗口看门狗复位的提示信息
		Delay_ms(500);
		OLED_ShowString(2, 1, "       ");
		Delay_ms(100);
		
		RCC_ClearFlag();//清除标志位
	}
	else //普通复位
	{
		OLED_ShowString(3, 1, "RST");//普通复位的提示信息
		Delay_ms(500);
		OLED_ShowString(3, 1, "   ");
		Delay_ms(100);
	}
	
	
	
	while(1)
	{
		Key_GetNum();//按住按键不放,主循环就会阻塞,不能及时喂狗,独立看门狗就会复位
		
		
		OLED_ShowString(4, 1, "FEED");//喂狗的提示信息
		Delay_ms(20);
		OLED_ShowString(4, 1, "    ");
		Delay_ms(20);
	}
}

初始化流程:

第一步开启窗口看门狗APB1的时钟,需要我们自己执行。

第二步配置各个寄存器,比如预分频和窗口值。窗口看门狗没有写保护,所以第二步可以直接写这些寄存器。

第三步写入控制寄存器CR,控制寄存器包含看门狗使能位,计数器溢出标志位以及计数器有效位,这些东西需要一起设置。

之后在运行过程中,我们不断向计数器写入想要重装的值,这样就能进行喂狗了。

接下来看一下库函数:

void WWDG_DeInit(void);//恢复缺省配置
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);//写入预分频器
void WWDG_SetWindowValue(uint8_t WindowValue);//写入窗口值
void WWDG_EnableIT(void);//使能中断
void WWDG_SetCounter(uint8_t Counter);//写入计数器,喂狗就用这个函数
void WWDG_Enable(uint8_t Counter);//使能窗口看门狗,启动看门狗用这个函数,使能的时候顺便喂狗
FlagStatus WWDG_GetFlagStatus(void);//获取标志位
void WWDG_ClearFlag(void);//清除标志位

 第一步开启窗口看门狗APB1的时钟。

/* 第一步开启窗口看门狗APB1的时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);

 第二步设置预分频和窗口值。参数计算根据项目需求确定想要的超时时间(最晚时间)和窗口时间(最早时间),比如我们想要超时时间50ms,窗口时间30ms,只能选最后一个分频系数。公式就是:

  • 50ms = 1/36MHz * 4096 * 8 * (T[5:0] + 1)

计算得到T[5:0] = 54。54是T[5:0],还有一个T6要设置为1,我们需要或上0x40,把控制寄存器次高位的T6置1。窗口值:

  • 30ms = 1/36MHz * 4096 * 8 * (54 - W[5:0])

计算得到W[5:0] = 21。同理W6这一位也要或上0x40置1。

/* 第二步设置预分频和窗口值 */
WWDG_SetPrescaler(WWDG_Prescaler_8);
WWDG_SetWindowValue(0x40 | 21);

 第三步使能窗口看门狗。 顺便喂一次狗。

/* 第三步使能窗口看门狗 */
WWDG_Enable(0x40 | 54);

为了防止两次喂狗间隔太近,喂狗放在延时之后。

完整代码:

main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"

uint8_t Key_Num;

int main(void)
{
	OLED_Init();
	Key_Init();
	
	OLED_ShowString(1, 1, "WWDG TEST");
	
	if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)//如果窗口看门狗复位标志是SET,说明本次复位是窗口看门狗导致的
	{
		OLED_ShowString(2, 1, "WWDGRST");//窗口看门狗复位的提示信息
		Delay_ms(500);
		OLED_ShowString(2, 1, "       ");
		Delay_ms(100);
		
		RCC_ClearFlag();//清除标志位
	}
	else //普通复位
	{
		OLED_ShowString(3, 1, "RST");//普通复位的提示信息
		Delay_ms(500);
		OLED_ShowString(3, 1, "   ");
		Delay_ms(100);
	}
	
	/* 第一步开启窗口看门狗APB1的时钟 */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
	
	/* 第二步设置预分频和窗口值 */
	WWDG_SetPrescaler(WWDG_Prescaler_8);
	WWDG_SetWindowValue(0x40 | 21);
	
	/* 第三步使能窗口看门狗,顺便喂一次狗 */
	WWDG_Enable(0x40 | 54);
	
	while(1)
	{
		Key_GetNum();//按住按键不放,主循环就会阻塞,不能及时喂狗,独立看门狗就会复位
		
		OLED_ShowString(4, 1, "FEED");//喂狗的提示信息
		Delay_ms(20);
		OLED_ShowString(4, 1, "    ");
		Delay_ms(20);
		
		WWDG_SetCounter(0x40 | 54);//喂狗
	}
}

 

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F103C8T6是一款基于ARM Cortex-M3内核的微控制器,它具有看门狗(Watchdog)功能。看门狗是一种硬件定时器,用于监控系统的运行状态,当系统出现异常情况时,看门狗会自动重启系统,保证系统的稳定性和可靠性。 在STM32F103C8T6中,看门狗的配置需要以下步骤: 1. 使能看门狗时钟:通过RCC寄存器使能看门狗时钟。 2. 配置看门狗预分频器和重载值:看门狗预分频器和重载值用于确定看门狗计数器的计数周期,通过IWDG_PR和IWDG_RLR寄存器进行配置。 3. 启动看门狗:配置好看门狗后,通过IWDG_KR寄存器启动看门狗。 4. 定时喂狗:在系统运行正常时,需要定时喂狗,以避免看门狗超时,导致系统重启。通过IWDG_KR寄存器定时喂狗。 以下是一个简单的示例代码,演示如何配置和使用看门狗: ``` #include "stm32f10x.h" void WDG_Init(void) { RCC_APB1PeriphResetCmd(RCC_APB1Periph_IWDG, ENABLE); // 使能看门狗时钟 IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); // 允许写入IWDG_PR和IWDG_RLR寄存器 IWDG_SetPrescaler(IWDG_Prescaler_256); // 配置预分频器,分频系数为256 IWDG_SetReload(0xFFF); // 配置重载值,最大值为0xFFF IWDG_ReloadCounter(); // 重载看门狗计数器 IWDG_Enable(); // 启动看门狗 } int main(void) { WDG_Init(); // 初始化看门狗 while(1) { // 定时喂狗 IWDG_ReloadCounter(); // 喂狗 } } ``` 在上面的例子中,预分频器的分频系数为256,重载值为0xFFF,这意味着看门狗计数器的计数周期为256*4096/40M=26.2144ms,如果系统在这个周期内没有重载看门狗计数器,那么看门狗就会超时,导致系统重启。通过定时喂狗,可以避免这种情况的发生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值