细说STM32F407单片机窗口看门狗WWDG的原理及使用方法
目录
一、窗口看门狗的工作原理
1、递减计数器
2、窗口值和比较器
3、看门狗的启动
4、提前唤醒中断
二、窗口看门狗的HAL驱动程序
1、窗口看门狗初始化
2.窗口看门狗刷新
3.EWI中断及其处理
三、不开启EWI的WWDG示例
1、示例功能
2、项目设置
(1) 时钟、Debug
(2)GPIO
(3) WWDG
(4)NVIC
(5)USART、 CodeGenrator
3、软件设计main.c
4、下载并调试
(1)启动后不按任何键,连续更新
(2) 442ms内按键,更新
(3)442ms~1094ms之间按键,不更新
(4)1094ms以上按键,更新
四、开启EWI的WWDG示例
1、示例功能、项目配置
2、软件设计
3、下载并调试
(1)不按按任何键
(2)按S2,800ms
(3) 按S3,1200ms
(4)按S4,200ms
窗口看门狗(Window Watchdog,WWDG)是STM32F407上的另一个看门狗,通常用来监测由外部干扰或不可预见的逻辑条件造成的应用程序软件故障。
一、窗口看门狗的工作原理
窗口看门狗的内部结构如图所示。
1、递减计数器
窗口看门狗内部有一个7位递减计数器,控制寄存器WWDG_CR中的T[6:0]位,是计数器的计数值。7位计数器的时钟信号来源于PCLK1,看门狗内部首先对PCLK1进行4096分频,然后再经过可配置的预分频器分频,因此7位递减计数器的时钟频率是:
=/(4096×DIV)
是时钟信号PCLK1的频率,4096是看门狗的固定分频系数,DIV是可设置的分频系数,由寄存器WWDG_CFR的WDGTB[1:0]位决定,DIV可取值为1、2、4、8。
7位递减计数器在T6位由1变为0时,就会使系统产生复位(看门狗必须是激活的,也就是控制寄存器WWDG_CR中的WDGA位是1),也就是计数值由0x40变为0x3F时,产生复位。要避免系统复位,就必须在计数值变为0x3F之前重置计数器,重置计数器的值必须大于0x3F。
窗口看门狗的递减计数器是自由运行计数器,即使没有开启看门狗,这个计数器也是在计数的。所以,在启动看门狗之前,应该重置计数器的值,以避免因为T6位是0而立刻复位。
2、窗口值和比较器
在配置寄存器WWDG_CFR中,有个7位的窗口值W[6:0],这个值用来与计数器的当前值T[6:0]进行比较。
窗口看门狗的工作时序图如图所示。当T[6:0]>W[6:0]时,比较器输出为1,这时不允许重置计数器的值,也就是不允许写WWDG_CR,否则系统复位。只有当T[6:0]≤W[6:0]时,才可以重置计数器的值,如果在T[6:0]变化到0x3F之前没有重置计数器,就会产生系统复位信号。所以,只能在这样一个窗口期重置看门狗计数器,这也是称为“窗口看门狗”的原因。
根据窗口看门狗的工作特点,在初始化设置时,窗口值W[6:0]必须小于或等于递减计数器的重置值。窗口看门狗的超时(timeout)就是计数器重置后,计数值变化为0x3F的这段时间长度,也就是图中不允许刷新和允许刷新两段的时间长度之和。用户可以根据计数器的时钟信号频率和T[6:0]的重置值计算超时。例如,设置计数器重置值为最大值0x7F,变化到0x3F时的计数周期个数是:
∆=0x7F-0x3F=0x40。
计数器的时钟周期是:
所以,看门狗的超时是:
同样,也可以计算出不允许刷新的时间段的长度。
3、看门狗的启动
控制寄存器WWDG_CR中的位WDGA用于启动看门狗。系统复位后WDGA被硬件清零,通过向WDGA写1可启动看门狗。此外,启动看门狗后就无法再停止,除非系统复位。
根据窗口看门狗的特点,可以使用软件使系统立刻复位。具体的操作方法是将WDGA位置1(启动窗口看门狗),并将T6位清零(使看门狗立刻产生复位),也就是设置一个小于0x3F的重置值即可。
4、提前唤醒中断
窗口看门狗有一个提前唤醒中断(Early Wakeup Interrupt,EWI)事件,如果已开启此中断事件源,且启动了看门狗,在递减计数器的值变为0x40时,就会触发此中断。
可在此中断服务程序里执行系统复位之前的一些关键操作,但是执行时间有限,只有一个计数器时钟周期。当然,也可以在此中断服务程序里重置计数器的值,避免系统复位,但是这样似乎就违背了使用窗口看门狗的初衷。
二、窗口看门狗的HAL驱动程序
窗口看门狗的驱动程序的头文件是stm32f4xx_hal_wwdg.h,WWDG的驱动函数不多。
1、窗口看门狗初始化
使用函数HAL_WWDG_Init()进行窗口看门狗初始化,其原型定义如下:
WWDG_HandleTypeDef hwwdg; //WWDG外设对象变量
结构体WWDG_HandleTypeDef的定义如下:
typedef struct
{
WWDG_TypeDef *Instance; //寄存器基址
WWDG_InitTypeDef Init; //WWDG的参数
}WWDG_HandleTypeDef;
其成员变量Init是结构体类型WWDG_InitTypeDef,包含WWDG的参数。该结构体定义如下,各成员变量意义见注释:
typedef struct
{
uint32_t Prescaler; //WWDG时钟预分频系数
uint32_t Window; //WWDG窗口值,设定值范围为0x40~0x7E
uint32_t Counter; //WWDG自由运行递减计数器的重载值,设定值范围为0×40~0×7F
uint32_t EWIMode; //WWDG的EWI中断模式,开启或禁止
}WWDG_InitTypeDef;
2.窗口看门狗刷新
函数HAL_WWDG_Refresh()用于刷新窗口看门狗,其原型定义如下:
HAL_StatusTypeDef HAL_WWDG_Refresh(WWDG_HandleTypeDef *hwwdg);
其功能就是将计数器重置值加载到看门狗的递减计数器,以避免看门狗触发系统复位。但只能在允许刷新时间段才能刷新看门狗。
3.EWI中断及其处理
WWDG有一个全局中断,只有一个提前唤醒中断(EWI)事件。驱动程序头文件定义了EWI中断事件使能位的宏,也作为中断事件类型定义。
#define WWDG_IT_EWI WWDG_CFR_EWI //EWI中断事件使能位,也作为中断事件类型
有一个宏函数用于开启EWI中断事件,即
__HAL_WWDG_ENABLE_IT(__HANDLE__,__INTERRUPT__)
参数__HANDLE__是WWDG对象指针,__INTERRUPT__就使用WWDG_IT_EWI作为参数值。EWI中断事件开启后就不能关闭,只能在硬件复位时才关闭,所以没有关闭EWI中断事件的函数。
WWDG全局中断ISR里调用的通用处理函数是HAL_WWDG_IRQHandler(),对应于EWI事件中断的回调函数是HAL_WWDG_EarlyWakeupCallback(),其原型定义如下:
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg);
若要对EWI事件中断做出处理,重新实现这个回调函数即可。
三、不开启EWI的WWDG示例
1、示例功能
本文作者旨在介绍如何WWDG及用法。继续使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。示例功能:看门狗在启动或上次刷新后,在442ms之内不能再刷新,在443ms至1049ms之内可以刷新看门狗,如果超过1049ms没有刷新看门狗,看门狗就会使系统复位。
使用开发板上的3个按键,按下案件后分别执行:在允许刷新的时间段内刷新、超时后刷新、在不允许刷新的时间段内刷新。LED1~LED3分别用于动作指示。
//显示菜单
[S2]KeyUp = 在允许刷新时间段内,看门狗不会触发复位. LED1闪烁
[S3]KeyDown = 超时,看门狗会触发系统复位. LED2闪烁
[S4]KeyLeft = 在不允许刷时间段内,刷新看门狗会导致系统复位. LED3闪烁
本示例不开启EWI中断。
部分内容需要参考本文作者写的其他文章。参考文章:细说STM32F407单片机独立看门狗IWDG的原理及使用方法-CSDN博客 https://wenchm.blog.csdn.net/article/details/145159637
2、项目设置
(1) 时钟、Debug
设置Debug接口为Serial Wire,设置HSE为Crystal/Ceramic Resonator
配置时钟树,设置HSE为8MHz,HCLK为32MHz,设置APB1 Prescaler为16,使PCLK1为2MHz。这是因为窗口看门狗要用到PCLK1时钟,使PCLK1为2MHz是为得到一个较低频率时钟信号,用于看门狗的递减计数器,便于观察程序运行效果。
(2)GPIO
本示例要用到两个LED,配置PA6和PA4引脚,设置初始输出为高电平,两个LED的GPIO引脚的配置结果如图所示,仍然使用keyled.h中的LED驱动函数。
(3) WWDG
只需激活WWDG即可,参数设置部分的几个参数决定看门狗的特性。
- WWDG counter clock prescaler,看门狗计数器预分频系数,有1、2、4、8几个可选值。
- WWDG window value,窗口值,也就是W[6:0]的值。这个值必须小于计数器的重置值,也必须大于0x3F(十进制值63)。
- WWDG free-running downcounter value,递减计数器T[6:0]的重置值,最大值为127(也就是0x7F),必须大于W[6:0]的值。根据设置的参数以及PCLK1为2MHz,可以计算出看门狗的超时为
计数器重置后不允许刷新的时间段长度是
对照WWDG工作原理图,这两个时间对看门狗的意义:看门狗在启动或上次刷新后,在442ms之内不能再刷新,在443ms至1049ms之内可以刷新看门狗,如果超过1049ms没有刷新看门狗,看门狗就会使系统复位。
- Early wakeup interrupt,是否开启提前唤醒中断。这里开启EWI中断。还需要在WWDG的NVIC Settings页面开启WWDG的全局中断,使用默认的优先级即可。
(4)NVIC
在WWDG的NVIC Settings页面开启WWDG的全局中断,使用默认的优先级。
(5)USART、 CodeGenrator
与参考文章相同。
3、软件设计main.c
/* USER CODE BEGIN Includes */
#include "keyled.h"
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */
//系统复位、初始状态
printf("Demo21_WWDG: Windows Watchdog\r\n\r\n");
//显示菜单
printf("[S2]KeyUp = 在允许刷新时间段内,看门狗不会触发复位.\r\n");
printf("[S3]KeyDown = 超时,看门狗会触发系统复位.\r\n");
printf("[S4]KeyLeft = 在不允许刷时间段内,刷新看门狗会导致系统复位.\r\n\r\n");
// MCU output low level LED light is on
LED1_OFF();
LED2_OFF();
LED3_OFF();
LED4_OFF();
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
KEYS curKey = ScanPressedKey(KEY_WAIT_ALWAYS);
switch(curKey)
{
//1. 在允许刷新时间段内,看门狗不会触发复位,LED1闪烁
case KEY_UP:
{
LED1_Toggle();
LED2_OFF();
LED3_OFF();
HAL_Delay(800);
HAL_WWDG_Refresh(&hwwdg); //刷新看门狗,也就是重置计数器的值
printf("The watchdog will not trigger a reset during the allowed refresh period.\r\n\r\n");
break;
}
//2. 超时,看门狗会触发系统自动复位,LED2亮
case KEY_DOWN:
{
LED1_OFF();
LED2_ON();
LED3_OFF();
printf("Timeout, the watchdog will trigger a reset automatically.\r\n\r\n");
HAL_Delay(1200);
HAL_WWDG_Refresh(&hwwdg);
break;
}
//3. 在不允许刷时间段内,刷新看门狗会导致系统复位,LED3亮
case KEY_LEFT:
{
HAL_Delay(200);
LED1_OFF();
LED2_OFF();
LED3_ON();
HAL_Delay(200);
HAL_WWDG_Refresh(&hwwdg); //刷新看门狗,也就是重置计数器的值
printf("During the period of time when refresh is not allowed,\r\n");
printf("refreshing the watchdog will cause the system to reset.\r\n\r\n");
break;
}
default:
{
printf("Keystroke error, refreshing watchdog will not cause system reset.\r\n\r\n");
LED1_OFF();
LED2_OFF();
LED3_OFF();
}
}
HAL_Delay(500); //消除按键抖动影响
}
/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);
return ch;
}
/* USER CODE END 4 */
4、下载并调试
调试的时候,按键按下后不要抬起。
(1)启动后不按任何键,连续更新
启动后不按任何键,系统连续启动,串口助手连续显示启动信息,这是因为WWDG的窗口值100,重置值127,当减计数到100后,系统自动重启。
(2) 442ms内按键,更新
在执行LED3_ON()前后各延时200ms是为了看到LED3的闪烁效果,这两个延时合计400ms,还在不允许刷新时间段内(小于442ms),这时调用HAL_WWDG_Refresh()刷新看门狗会使系统复位,后面如果有程序也是不会被执行的。
(3)442ms~1094ms之间按键,不更新
看门狗在允许刷新的时间段内及时刷新。如果在while循环内调用HAL_Delay(800)延时800ms,然后调用HAL_WWDG_Refresh()刷新看门狗,则可以观察到LED1一直闪烁。因为延时800ms后进入允许刷新的时间段(大于442ms),也没有超过看门狗的超时时间(1049ms),这时候调用HAL_WWDG_Refresh()是可以刷新看门狗的,程序能一直正常运行,所以LED1闪烁,闪烁周期为800ms。
(4)1094ms以上按键,更新
看门狗超时是自动复位的。如果将程序中的延时改为1200ms,则运行时会看到LED1一直亮着。因为在延时1200ms的过程中,看门狗已经超时导致系统复位,while循环里使LED1输出翻转的代码不会被执行。
四、开启EWI的WWDG示例
1、示例功能、项目配置
除WWDG开启EWI中断外,其余都与三相同。
2、软件设计
在不开启EWI的时候,程序下载后,即使不按任何键,WWDG的减计数器减到阈值后也会自动重置,现象就是串口助手连续不断底显示更新,一般情况下,这不是设计者的设计目的。
开启EWI后,在EWI的回调函数里执行计数器重置。那么久解决了上面不需要的连续刷新WWDG的现象了。
/* USER CODE BEGIN 4 */
// EWI中断事件回调函数
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
LED4_Toggle();
HAL_WWDG_Refresh(hwwdg); //在此刷新看门狗,看门狗也是能被刷新的
}
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);
return ch;
}
/* USER CODE END 4 */
看门狗的EWI中断是在递减计数器的值变为0x40时触发的,而递减计数器的值变为0x3F时就会导致系统自动复位。所以,EWI中断相当于在系统复位之前的一个预警,用户可以在此中断里做一些紧急处理,例如关闭某个开关,但是处理时间只有1个计数周期。
3、下载并调试
(1)不按按任何键
(2)按S2,800ms
(3) 按S3,1200ms
(4)按S4,200ms