STM32/STM8技术论坛
直播中

正点原子运营官

5年用户 1793经验值
擅长:模拟技术 嵌入式技术 控制/MCU
私信 关注
[资料]

正点原子 STM32F4/F7水星 开发板资料连载第十二章 窗口门狗实验

1)实验平台:正点原子水星 STM32F4/F7 开发板
2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子


第十二章 窗口门狗(WWDG)实验
这一章,我们将向大家介绍如何使用 STM32F7 的另外一个看门狗,窗口看门狗(以下简称 WWDG)。在本章中,我们将使用窗口看门狗的中断功能来喂狗,通过 DS0 和 DS1 提示程序的运行状态。本章分为如下几个部分:
11.1 STM32F7 窗口看门狗简介
11.2 硬件设计
11.3 软件设计
11.4 下载验证
11.5 STM32CubeMX 配置 WWDG
12.1 STM32F7 窗口看门狗简介
窗口看门狗(WWDG)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。除非递减计数器的值在 T6 位(WWDG->CR 的第六位)
变成 0 前被刷新,看门狗电路在达到预置的时间周期时,会产生一个 MCU 复位。在递减计数
器达到窗口配置寄存器(WWDG->CFR)数值之前,如果 7 位的递减计数器数值(在控制寄存器中)
被刷新, 那么也将产生一个 MCU 复位。这表明递减计数器需要在一个有限的时间窗口中被刷
新。他们的关系可以用图 12.1.1 来说明:


图 12.1.1 窗口看门狗工作示意图
图 12.1.1 中,T[6:0]就是 WWDG_CR 的低七位,W[6:0]即是 WWDG->CFR 的低七位。T[6:0]
就是窗口看门狗的计数器,而 W[6:0]则是窗口看门狗的上窗口,下窗口值是固定的(0X40)。
当窗口看门狗的计数器在上窗口值之外被刷新,或者低于下窗口值都会产生复位。
上窗口值(W[6:0])是由用户自己设定的,根据实际要求来设计窗口值,但是一定要确保
窗口值大于 0X40,否则窗口就不存在了。
窗口看门狗的超时公式如下:
Twwdg=(4096×2^WDGTB×(T[5:0]+1)) /Fpclk1;
其中:
Twwdg:WWDG 超时时间(单位为 ms)
Fpclk1:APB1 的时钟频率(单位为 Khz)
WDGTB:WWDG 的预分频系数
T[5:0]:窗口看门狗的计数器低 6 位
根据上面的公式,假设 Fpclk1=54Mhz,那么可以得到最小-最大超时时间表如表 12.1.1 所示:


表 12.1.1 54M 时钟下窗口看门狗的最小最大超时表
接下来,我们介绍窗口看门狗的 3 个寄存器。首先介绍控制寄存器(WWDG_CR),该寄
存器的各位描述如图 12.1.2 所示:


图 12.1.2 WWDG_CR 寄存器各位描述
可以看出,这里我们的 WWDG_CR 只有低八位有效,T[6:0]用来存储看门狗的计数器值,随时更新的,每个窗口看门狗计数周期(4096×2^ WDGTB)减 1。当该计数器的值从 0X40 变为 0X3F 的时候,将产生看门狗复位。
WDGA 位则是看门狗的激活位,该位由软件置 1,以启动看门狗,并且一定要注意的是该位一旦设置,就只能在硬件复位后才能清零了。
窗口看门狗的第二个寄存器是配置寄存器(WWDG_CFR),该寄存器的各位及其描述如图12.1.3 所示:


图 12.1.3 WWDG_ CFR 寄存器各位描述
该位中的 EWI 是提前唤醒中断,也就是在快要产生复位的前一段时间(T[6:0]=0X40)来
提醒我们,需要进行喂狗了,否则将复位!因此,我们一般用该位来设置中断,当窗口看门狗
的计数器值减到 0X40 的时候,如果该位设置,并开启了中断,则会产生中断,我们可以在中
断里面向 WWDG_CR 重新写入计数器的值,来达到喂狗的目的。注意这里在进入中断后,必
须在不大于 1 个窗口看门狗计数周期的时间(在 PCLK1 频率为 54M 且 WDGTB 为 0 的条件下,
该时间为 75.85us)内重新写 WWDG_CR,否则,看门狗将产生复位!
最后我们要介绍的是状态寄存器(WWDG_SR),该寄存器用来记录当前是否有提前唤醒
的标志。该寄存器仅有位 0 有效,其他都是保留位。当计数器值达到 40h 时,此位由硬件置 1。
它必须通过软件写 0 来清除。对此位写 1 无效。即使中断未被使能,在计数器的值达到 0X40
的时候,此位也会被置 1。
在介绍完了窗口看门狗的寄存器之后,我们介绍要如何启用 STM32F7 的窗口看门狗。这
里我们介绍 HAL 中用中断的方式来喂狗的方法,窗口看门狗 HAL 库相关源码和定义分布在文
件 stm32f7xx_hal_wwdg.c 文件和头文件 stm32f7xx_hal_wwdg.h 中。步骤如下:
1)使能 WWDG 时钟
WWDG 不同于 IWDG,IWDG 有自己独立的 32Khz 时钟所以不存在使能问题。而 WWDG
使用的是 PCLK1 的时钟,需要先使能时钟。方法是:
__HAL_RCC_WWDG_CLK_ENABLE(); //使能窗口看门狗时钟
2)设置窗口值,分频数和计数器初始值
在 HAL 库中,这三个值都是通过函数 HAL_WWDG_Init 来设置的。该函数声明如下:
HAL_StatusTypeDef HAL_WWDG_Init(WWDG_HandleTypeDef *hwwdg);该函数只有一个入口参数,就是 WWDG_HandleTypeDef 结构体类型指针变量。这里我们
来看看 WWDG_HandleTypeDef 结构体定义:
typedef struct
{
WWDG_TypeDef *Instance;
WWDG_InitTypeDef Init;
}WWDG_HandleTypeDef;该结构体和前面我们讲解的 WWDG_HandleTypeDef 类似,Instance 成员变量设置为值
WWDG 即可。这里我们就主要讲解成员变量 Init,它是 WWDG_InitTypeDef 结构体类型,该结
构体定义如下:
typedef struct
{
uint32_t Prescaler;
//预分频系数
uint32_t Window;
//窗口值
uint32_t Counter;
//计数器值
uint32_t EWIMode; //提前唤醒中断使能
}WWDG_InitTypeDef;该结构体有 4 三个成员变量,分别用来设置 WWDG 的预分频系数,窗口值,计数器值以
及是否开启提前唤醒中断。函数 HAL_WWDG_Init 的使用范例如下:
WWDG_HandleTypeDef WWDG_Handler; //窗口看门狗句柄
WWDG_Handler.Instance=WWDG; //窗口看门狗
WWDG_Handler.Init.Prescaler=WWDG_PRESCALER_8;//设置分频系数为 8
WWDG_Handler.Init.Window=0X5F;
//设置窗口值 0X5F
WWDG_Handler.Init.Counter=0x7F;
//设置计数器值 0x7F
WWDG_Handler.Init.EWIMode=WWDG_EWI_ENABLE;//使能窗口看门狗提前唤醒中断
HAL_WWDG_Init(&WWDG_Handler);
//初始化 WWDG3)开启 WWDG
HAL 库中开启 WWDG 是通过宏定义标识符实现:
#define __HAL_WWDG_ENABLE(__HANDLE__)
SET_BIT((__HANDLE__)->Instance->CR, WWDG_CR_WDGA)这里需要说明一下的是,在调用函数 HAL_WWDG_Init 之后,该函数会开启窗口看门狗,
所以不需要再重复开启。
4)使能中断通道并配置优先级(如果开启了 WWDG 中断)
这一步相信大家已经非常熟悉了,我们这里仅仅列出两行实现代码,如下:
HAL_NVIC_SetPriority(WWDG_IRQn,2,3); //抢占优先级 2,子优先级为 3
HAL_NVIC_EnableIRQ(WWDG_IRQn); //使能窗口看门狗中断这里大家要注意, 跟串口一样, HAL 库 同 样 为 看 门 狗 提 供 了 MSP 回 调 函 数
HAL_WWDG_MspInit,一般情况下,步骤 1 和步骤 4 的步骤,是与 MCU 相关的,我们均放在
该回调函数中。关于 MSP 回调函数的使用方法,前面多次讲解,这里我们就不累赘了。
5)编写中断服务函数
在最后,还是要编写窗口看门狗的中断服务函数,通过该函数来喂狗,喂狗要快,否则当
窗口看门狗计数器值减到 0X3F 的时候,就会引起软复位了。在中断服务函数里面也要将状态
寄存器的 EWIF 位清空。
窗口看门狗中断服务函数为:
void WWDG_IRQHandler(void);
在 HAL 库中,喂狗函数为:
HAL_StatusTypeDef HAL_WWDG_Refresh(WWDG_HandleTypeDef *hwwdg, uint32_t cnt);WWDG 的喂狗操作实际就是往 CR 寄存器重写计数器值,这里的第二个入口函数就是重写
的计数器的值。
6) 重写窗口看门狗唤醒中断处理回调函数 HAL_WWDG_EarlyWakeupCallback
跟串口和外部中断一样,首先,HAL 库定义了一个中断处理共用函数
HAL_WWDG_IRQHandler,我们在 WWDG 中断服务函数中会调用该函数。同时该函数内部,
会经过一系列判断,最后调用回调函数 HAL_WWDG_EarlyWakeupCallback,所以提前唤醒中
断逻辑我们一般些在回调函数 HAL_WWDG_EarlyWakeupCallback 中。 回调函数声明为:
__weak void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg);
完成了以上 6 个步骤之后,我们就可以使用 STM32F7 的窗口看门狗了。这一章的实验,
我们将通过 DS0 来指示 STM32F7 是否被复位了,如果被复位了就会点亮 300ms。DS1 用来指
示中断喂狗,每次中断喂狗翻转一次。
12.2 硬件设计
本实验用到的硬件资源有:
1) 指示灯 DS0 和 DS1
2) 窗口看门狗
其中指示灯前面介绍过了,窗口看门狗属于 STM32F767 的内部资源,只需要软件设置好
即可正常工作。我们通过DS0和DS1来指示STM32F767的复位情况和窗口看门狗的喂狗情况。
12.3 软件设计
打开窗口看门狗实验可以看到,我们增加了窗口看门狗相关的库函数支持文件
stm32f7xx_hal_wwdg.c 和 stm32f7xx_hal_wwdg.h,同时新建 wwdg.c 和对应的头文件 wwdg.h 用
来编写窗口看门狗相关的函数代码。
接下来我们看看 wwdg.c 文件内容如下:
WWDG_HandleTypeDef WWDG_Handler; //窗口看门狗句柄
//初始化窗口看门狗
//tr :T[6:0],计数器值
//wr :W[6:0],窗口值
//fprer:分频系数(WDGTB),仅最低 2 位有效
//Fwwdg=PCLK1/(4096*2^fprer). 一般 PCLK1=54Mhz
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{
WWDG_Handler.Instance=WWDG;
WWDG_Handler.Init.Prescaler=fprer;
//设置分频系数
WWDG_Handler.Init.Window=wr;
//设置窗口值
WWDG_Handler.Init.Counter=tr;
//设置计数器值
WWDG_Handler.Init.EWIMode=WWDG_EWI_ENABLE;//使能看门狗提前唤醒中断
HAL_WWDG_Init(&WWDG_Handler);
//初始化 WWDG
}
//WWDG 底层驱动,时钟配置,中断配置
//此函数会被 HAL_WWDG_Init()调用
//hwwdg:窗口看门狗句柄
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{
__HAL_RCC_WWDG_CLK_ENABLE(); //使能窗口看门狗时钟
HAL_NVIC_SetPriority(WWDG_IRQn,2,3); //抢占优先级 2,子优先级为 3
HAL_NVIC_EnableIRQ(WWDG_IRQn); //使能窗口看门狗中断
}
//窗口看门狗中断服务函数
void WWDG_IRQHandler(void)
{
HAL_WWDG_IRQHandler(&WWDG_Handler);
}
//中断服务函数处理过程
//此函数会被 HAL_WWDG_IRQHandler()调用
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg)
{
HAL_WWDG_Refresh(&WWDG_Handler);//更新窗口看门狗值
LED1_Toggle;
}wwdg.c 文件一共包含四个函数。第一个函数 WWDG_Init()实现的是前面讲解的步骤 1 和步骤 3,主要作用是调用函数 HAL_WWDG_Init 设置 WWDG 的分频系数,窗口值和计数器初
始值,同时还使能看门狗提前唤醒中断。第二个函数 HAL_WWDG_MspInit 是 WWDG 的 MSP
回调函数,该函数主要作用是使能 WWDG 时钟,以及设置 NVIC,实现的是前面讲解的步骤 2
和 4。第三个函数 WWDG_IRQHandler 也就是中断服务函数,该函数在前面步骤 5 有讲解,一
般情况下,在该函数内部会调用中断共用处理函数 HAL_WWDG_IRQHandler。第四个函数
HAL_WWDG_EarlyWakeupCallback 是提前唤醒中断回调函数,该函数内部我们主要编写了喂
狗操作,以及 LED1 翻转。
wwdg.h 头文件内容比较简单,这里我们就不做过多讲解。
在完成了以上部分之后,我们就回到主函数,代码如下:
int main(void)
{
Cache_Enable(); //打开 L1-Cache
HAL_Init();
//初始化 HAL 库
Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz
delay_init(216);
//延时初始化
uart_init(115200);
//串口初始化
LED_Init(); //初始化 LED
KEY_Init();
//初始化按键
LED0(0);
//点亮 LED0
delay_ms(300);
//延时 300ms 再初始化看门狗,LED0 的变化"可见"
WWDG_Init(0X7F,0X5F,WWDG_PRESCALER_8);
//计数器值为 7F,窗口寄存器为 5F,分频数为 8
while(1)
{
LED0(1); //熄灭 LED 灯
}
}该函数通过 LED0(DS0)来指示是否正在初始化。而 LED1(DS1)用来指示是否发生了中断。
我们先让 LED0 亮 300ms,然后关闭以用于判断是否有复位发生了。在初始化 WWDG 之后,
我们回到死循环,关闭 LED1,并等待看门狗中断的触发/复位。
在编译完成之后,我们就可以下载这个程序到水星 STM32F7 开发板上,看看结果是不是
和我们设计的一样。
12.4 下载验证
将代码下载到水星 STM32F7 后,可以看到 DS0 亮一下之后熄灭,紧接着 DS1 开始不停的
闪烁。每秒钟闪烁 20 次左右,和我们预期的一致,说明我们的实验是成功的。
12.5 STM32CubeMX 配置 WWDG
上一讲我们讲解了使用 STM32CubeMX 配置 IWDG 步骤,而 WWDG 配置过程和 IWDG
配置过程基本是一模一样的,这里我们就直接列出配置图,对配置过程不做过多讲解。首先进
入 Pinout 选项界面,使能 WWDG,如下图 12.5.1:


图 12.5.1 使能 WWDG
接下来配置 WWDG 的参数,进入 Configuration->WWDG 界面,如下图所示:


图 12.5.2 WWDG Configuration 配置界面
配置栏 Watchdog Clocking 有三个配置参数,顾名思义,第一个参数是配置分频系数,这里
我们配置为 8。第二个参数是配置窗口值,第三个参数是配置计数器初始值。配置栏 Watchdog
Interrupt 只有一个配置项 EWI Mode,也就是是否使能 WWDG 的提前唤醒中断,这里我们选择
Enable。配置好上述参数之后,因为我们开启了提前唤醒中断,所以这里我们要配置 NVIC 中
断优先级。进入 Configuration->NVIC 界面,参考第九章配置方法配置 WWDG 中断优先级即可。
配置方法如下图 12.5.3 所示:


图 12.5.3 配置 WWDG 中断
配置完成之后直接生成工程源码。在 main.c 文件中生成的 MX_WWDG_Init 函数和本实验
的 WWDG_Init 函数实现功能类似。在 stm32f7xx_it.c 中生成的中断服务函数和我们实验一致。
在 stm32f7xx_hal_msp.c 文件中生成的 MSP 回调函数内容核我们实验内容一致。

回帖(1)

leslie_aqiang

2021-1-7 20:17:39
很棒棒哦,值得学习
举报

更多回帖

发帖
×
20
完善资料,
赚取积分