重要的内容写在前面:

  1. 该系列是以up主江协科技的STM32视频教程为基础写下去的,大部分内容都参考了老师的课件,对于一些个人认为比较重要但是老师仅口述的部分,笔者都有用文字的方式记录并标出了重点。
  2. 文中的图片基本都来源于老师的课件以及开发板和芯片的手册,粘贴过来是为了方便阅读。
  3. 如果有条件的可以先学习一些相关课程再去看STM32的教程,学起来会更加轻松(不太建议零基础开始直接STM32,听起来可能会有点困难,可以先学51单片机),相关课程有数字电路(强烈推荐先学数电,不然可能会有很多地方理解起来很困难)、模拟电路、计算机组成原理(像寄存器、存储器、中断等在这门课里有很详细的介绍)、计算机网络等。
  4. 如有错漏欢迎指出。

视频链接:[14-1] WDG看门狗_哔哩哔哩_bilibili

一、概述

1、WDG(Watchdog)看门狗简介

(1)看门狗可以监控程序的运行状态,当程序因为设计漏洞、硬件故障、电磁干扰等原因,出现卡死或跑飞现象时,看门狗能及时复位程序,避免程序陷入长时间的罢工状态,保证系统的可靠性和安全性。

(2)看门狗本质上是一个定时器,在指定时间范围内,程序没有执行喂狗(即重置计数器)操作时,看门狗硬件电路会自动产生复位信号。

(3)STM32内置两个看门狗:

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

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

2、独立看门狗(IWDG)

(1)IWDG框图:

①在时钟产生电路有4个震荡源,其中一个是内部的40kHz低速RC振荡器(LSI RC 40kHz),它负责给独立看门狗提供时钟信号,这个时钟不需要用户手动开启。

②LSI时钟信号进入预分频器进行分频,分频系数由预分频寄存器IWDG_PR进行配置。LSI时钟信号经过预分频处理后,驱动递减计数器进行自减计数(12位计数器的最大值为4095),当计数器的值自减为0时,会产生IWDG复位信号。

③如果不想让IWDG产生复位信号,那么在计数器的值自减为0之前,及时给计数器重装载一个值(也就是“喂狗”)即可。重装值存储在重装载寄存器IWDG_RLR中,程序可通过写访问键寄存器IWDG_KR,控制将重装载寄存器中的重装值写入递减计数器。

(2)IWDG键寄存器:

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

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

写入键寄存器的值

作用

0xCCCC

启用独立看门狗

0xAAAA

IWDG_RLR中的值重新加载到计数器(喂狗)

0x5555

解除IWDG_PR和IWDG_RLR的写保护

0x5555之外的其它值

启用IWDG_PR和IWDG_RLR的写保护

(3)IWDG超时时间:

3、窗口看门狗(WWDG)

(1)WWDG框图:

①窗口看门狗的时钟源是PCLK1(36MHz),时钟信号进入预分频器WDGTB前会先进行4096分频(下图未体现)。

②WWDG_CR将递减计数器和控制寄存器合二为一,需要同时满足T6位为0、WDGA位为1两个条件,WWDG才会产生复位信号。

        递减计数器:T0~T5是有效计数位,T6是溢出标志位,计数值自减过程中T6位为0代表溢出

        控制寄存器:WDGA位置为1,窗口看门狗启动

③窗口看门狗没有重装载寄存器,需要喂狗时,直接往递减计数器中写重装值即可。

④窗口看门狗的最早喂狗时间写在看门狗配置寄存器WWDG_CFR中。当对递减计数器进行写操作(写入WWDG_CR)时,比较器使能,这时比较器会比较递减计数器(喂狗前一刻)的值和配置寄存器的值,如果递减计数器的值较大,说明喂狗时间过早,比较器输出高电平,如果WDGA=1,就会产生复位信号。

(2)WWDG工作特性:

①递减计数器T[6:0]的值小于0x40(也就是T6=0)时,说明没有及时喂狗,WWDG产生复位信号。

②递减计数器T[6:0]在窗口W[6:0]外被重新装载时,说明喂狗时间过早,WWDG产生复位信号。

③递减计数器T[6:0]等于0x40时,可以产生早期唤醒中断(EWI,又称作“死前中断”),在中断函数中可以重装载计数器,以避免WWDG复位(在喂狗的最后期限使用中断喂狗)。

④程序需要定期写入WWDG_CR寄存器(喂狗),以避免WWDG复位。

(3)WWDG超时时间:

4、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、独立看门狗使用示例

(1)按照下图所示接好电路,并将OLED显示屏的项目文件夹复制一份作为模板使用。

(2)在stm32f10x_iwdg.h文件中有IWDG模块相关的函数。

[1] IWDG_WriteAccessCmd函数:使能或禁用对独立看门狗预分频寄存器和重装载寄存器的写访问权限。(键寄存器写入0x5555或其它值,解除或开启IWDG_PR和IWDG_RLR的写保护)。

函数原型:void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);

参数解释:

        uint16_t IWDG_WriteAccess:指定对IWDG_PR和IWDG_RLR寄存器的写访问状态,        IWDG_WriteAccess_Enable表示使能写访问,IWDG_WriteAccess_Disable表示禁用写访问

返回值:无返回值

[2] IWDG_SetPrescaler函数:设置独立看门狗的时钟预分频系数(写访问IWDG_PR)。

函数原型:void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);

参数解释:

        uint8_t IWDG_Prescaler:指定IWDG的预分频值

返回值:无返回值

[3] IWDG_SetReload函数:设置独立看门狗的重装值(写访问IWDG_RLR)。

函数原型:void IWDG_SetReload(uint16_t Reload);

参数解释:

        uint16_t Reload:指定IWDG的重装载值,即12位递减计数器的初始值

返回值:无返回值

[4] IWDG_ReloadCounter函数:将独立看门狗的计数器重装载为预设值,即执行“喂狗”操作(键寄存器写入0xAAAA)。

函数原型:void IWDG_ReloadCounter(void);

参数解释:无参数

返回值:无返回值

[5] IWDG_Enable函数:使能独立看门狗,启动其12位递减计数器开始运行(键寄存器写入0xCCCC)。

函数原型:void IWDG_Enable(void);

参数解释:无参数

返回值:无返回值

[6] IWDG_GetFlagStatus函数:检查独立看门狗的指定状态标志位是否被置位,从而判断相应的硬件状态是否已就绪。

函数原型:FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG);

参数解释:

        uint16_t IWDG_FLAG:指定要检查的状态标志位

返回值:SET表示指定的标志位被置位(寄存器更新仍在进行中,不能执行新的写操作),RESET表示指定的标志位未被置位(寄存器更新已完成,可以安全地执行后续写操作)

(3)在main.c文件中粘贴以下代码,然后编译,将程序下载到开发板中进行调试。

①正常情况下,将程序下载到开发板后OLED屏会显示一次“RST”,然后会不断闪烁显示“FFED”,如果按下复位按键,OLED屏会再显示一次“RST”,然后接着不断闪烁显示“FFED”。

如果按住按键不放,程序会卡死在Key_GetNum函数的死循环中程序不能及时喂狗,于是看门狗会产生复位信号并置IWDGRST为1,程序从头开始运行,由于是IWDG导致的复位,OLED屏不显示“RST”而显示“IWDGRST”,然后接着不断闪烁显示“FFED”

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

int main()
{
	OLED_Init();
Key_Init();
	
	OLED_ShowString(1,1,"IWDG TEXT");
	
	if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)  //判断IWDG是否产生复位
	{
		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的写保护
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
	
	//配置分频系数(40MHz经过16分频为2.5MHz)
	IWDG_SetPrescaler(IWDG_Prescaler_16);
	
	//配置重装值
	IWDG_SetReload(2500 - 1);  //超时时间为1000ms
	
	//先喂一次狗(键寄存器写入0x5555以外的值,同时开启写保护)
	IWDG_ReloadCounter();
	
	//启动看门狗
	IWDG_Enable();
	
	while(1)
	{
		Key_GetNum();
		//按下按键不放,程序会卡死在Key_GetNum函数的死循环中
		
		IWDG_ReloadCounter();   //喂狗
		
		OLED_ShowString(4,1,"FFED");
		Delay_ms(400);
		OLED_ShowString(4,1,"FFED");
		Delay_ms(200);
	}
}

2、窗口看门狗使用示例

(1)按照下图所示接好电路,并将上例的项目文件夹复制一份作为模板使用。

(2)在stm32f10x_wwdg.h文件中有WWDG模块相关的函数。

[1] WWDG_DeInit函数:将窗口看门狗外设的所有寄存器恢复为默认值(即硬件复位后的状态)。

函数原型:void WWDG_DeInit(void);

参数解释:无参数

返回值:无返回值

[2] WWDG_SetPrescaler函数:设置窗口看门狗的预分频值。

函数原型:void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);

参数解释:

        uint32_t WWDG_Prescaler:指定WWDG的预分频值,决定计数器时钟的分频系数

返回值:无返回值

[3] WWDG_SetWindowValue函数:设置窗口看门狗的上窗口值,也就是设置最早喂狗时间(写访问WWDG_CFR)。

函数原型:void WWDG_SetWindowValue(uint8_t WindowValue);

参数解释:

        uint8_t WindowValue:指定窗口看门狗的上窗口值

返回值:无返回值

[4] WWDG_EnableIT函数:使能窗口看门狗的早期唤醒中断。

函数原型:void WWDG_EnableIT(void);

参数解释:无参数

返回值:无返回值

[5] WWDG_SetCounter函数:设置窗口看门狗的递减计数器(写访问WWDG_CR,即喂狗)。

函数原型:void WWDG_SetCounter(uint8_t Counter);

参数解释:

        uint8_t Counter:要写入递减计数器的7位值

返回值:无返回值

[6] WWDG_Enable函数:使能窗口看门狗外设,并加载计数器的初始值,启动看门狗开始工作。

函数原型:void WWDG_Enable(uint8_t Counter);

参数解释:

        uint8_t Counter:要写入递减计数器的7位初始值

返回值:无返回值

[7] WWDG_GetFlagStatus函数:检查窗口看门狗的早期唤醒中断标志是否被置位。

函数原型:FlagStatus WWDG_GetFlagStatus(void);

参数解释:无参数

返回值:无返回值

[8] WWDG_ClearFlag函数:清除窗口看门狗的早期唤醒中断标志。

函数原型:vvoid WWDG_ClearFlag(void);

参数解释:无参数

返回值:无返回值

(3)在main.c文件中粘贴以下代码,然后编译,将程序下载到开发板中进行调试。

①正常情况下,将程序下载到开发板后OLED屏会显示一次“RST”,然后会不断闪烁显示“FFED”,如果按下复位按键,OLED屏会再显示一次“RST”,然后接着不断闪烁显示“FFED”。

如果按住按键不放,程序会卡死在Key_GetNum函数的死循环中程序不能及时喂狗,于是看门狗会产生复位信号并置WWDGRST为1,程序从头开始运行,由于是WWDG导致的复位,OLED屏不显示“RST”而显示“WWDGRST”,然后接着不断闪烁显示“FFED”

③修改while循环中的延时函数参数,使喂狗时间早于30ms,那么喂狗后一样会产生复位信号并置WWDGRST为1,程序从头开始运行,由于是WWDG导致的复位,OLED屏不显示“RST”而显示“WWDGRST”,然后接着不断闪烁显示“FFED”

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

int main()
{
	OLED_Init();
	Key_Init();
	
	OLED_ShowString(1,1,"WWDG TEXT");
	
	if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)  //判断WWDG是否产生复位
	{
		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);
	}
	
	//开启WWDG时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
	
	//配置分频系数(36MHz经过8×4096分频为1098.63Hz)
	WWDG_SetPrescaler(WWDG_Prescaler_8);
	
	//配置窗口值(这个值多多少少会有误差,但不大)
	WWDG_SetWindowValue(21|0x40);  //最早喂狗时间为30ms,过早喂狗会复位(0x40的作用是置W6为1,21由公式计算而得)
	
	//启动看门狗(这个值多多少少会有误差,但不大)
	WWDG_Enable(54|0x40);          //最晚喂狗时间为50ms,过晚喂狗会复位(0x40的作用是置T6为1,54由公式计算而得)
	
	while(1)
	{
		Key_GetNum();
		//按下按键不放,程序会卡死在Key_GetNum函数的死循环中
		
		OLED_ShowString(4,1,"FFED");
		Delay_ms(20);
		OLED_ShowString(4,1,"    ");
		Delay_ms(20);
		
		WWDG_SetCounter(54|0x40);   //喂狗(0x40的作用是置T6为1)
	}
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐