《STM32H7R7开发指南 V1.1 》第十八章 窗口门狗(WWDG)实验

第十八章 窗口门狗(WWDG)实验


       本章我们学习如何使用STM32H7R7的另外一个看门狗,窗口看门狗(以下简称WWDG)。我们将使用窗口看门狗的中断功能来喂狗。

       本章分为如下几个小节:

       18.1 WWDG简介

       18.2 硬件设计

       18.3 程序设计

       18.4 下载验证


        18.1 WWDG简介

       窗口看门狗(WWDG)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。窗口看门狗跟独立看门狗一样,也是一个递减计数器,不同的是它们的复位条件不一样。窗口看门狗产生复位信号有两个条件:

       1) 当递减计数器的数值从0x40减到0x3F时(T6位跳变到0)。

       2) 当喂狗的时候如果计数器的值大于W[6:0]时,此数值在WWDG_CFR寄存器定义。

       上述的两个条件详细解释是,当计数器的值减到0X40时还不喂狗的话,到下一个计数就会产生复位,这个值称为窗口的下限值,是固定的值,不能改变。这个跟独立看门狗类似,不同的是窗口看门狗的计数器的值在减到某一个数之前喂狗的话也会产生复位,这个值叫窗口的上限,上限值W[6:0]由用户设置。窗口看门狗计数器的上限值和下限值就是窗口的含义,喂狗也必须在窗口之内,否则就会复位。


       18.1.1 WWDG框图

       下面先来学习WWDG框图,通过学习WWDG框图会有一个很好的整体掌握,同时对之后的编程也会有一个清晰的思路。


图18.1.1.1 WWDG框图


       从WWDG框图整体认知就是,WWDG有一个输入(时钟pclk),经过一个除4096的分频器,再经过一个分频系数可选(1、2、4、8…128)的可编程预分频器提供时钟给一个7位递减计数器,这里有两个输出信号。具体如下表:


表18.1.1.1 WWDG内部输入/输出信号


       这里需要注意的就是pclk时钟是由PPRE1提供,由前面十一章的内容知道,我们的例程在系统时钟设置函数中把PPRE1的时钟频率设置为150Mhz。

       结合寄存器分析窗口看门狗的上限值和下限值。W[6:0] 是WWDG_CFR寄存器的低7位,用于与递减计数器比较的窗口值,也就是我们说的上限值,由用户设置。0x40就是下限值,递减计数器达到这个值就会产生复位。T6位就是WWDG_CR寄存器的位6,即递减计数器T[6:0]的最高位。他们的关系可以用图18.1.1.2来说明:


图18.1.1.2 窗口看门狗工作示意图


       图18.1.1.2可以看出,递减计数器的值递减过程中,当T[6:0]>W[6:0]是不允许刷新T[6:0]的值,即不允许喂狗,否则会产生复位。只有在W[6:0]

       上限值W[6:0]是由用户自己设置,但是一定要确保大于0X40,否则就不存在上图的窗口了,下限值0x40是固定的,不可修改。

       知道了窗口看门狗的工作原理,下面学习如何计算窗口看门狗的超时公式:

Twwdg=(4096×2^WDGTB[2:0]×(T[5:0]+1)) /Fppre1

       其中:

       Twwdg:WWDG超时时间(单位为ms)

       Fppre1:APB1的时钟频率(单位为Mhz)

       WDGTB[2:0]:WWDG的预分频系数,0~7

       T[5:0]:窗口看门狗的计数器低6位

       根据以上公式,假设Fppre1=150Mhz,那么可以得到最小—最大超时时间表如下表所示:


表18.1.1.2 150M时钟下窗口看门狗的最小最大超时表


       18.1.2 WWDG寄存器

       WWDG只有3个寄存器,具体如下:


       l 控制寄存器(WWDG_CR)

       窗口看门狗的控制寄存器描述如图18.1.2.1所示:


图18.1.2.1 WWDG_CR寄存器


       该寄存器只有低八位有效,其中T[6:0]用来存储看门狗的计数器的值,随时更新的,每隔(4096×2^ WDGTB[2:0])PCLK个周期减1。当该计数器的值从0X40变为0X3F的时候,将产生看门狗复位。

       WDGA位则是看门狗的激活位,该位由软件置1,启动看门狗,并且一定要注意的是该位一旦设置,就只能在硬件复位后才能清零了。


       l 配置寄存器(WWDG_CFR)

       配置寄存器描述如图18.1.2.2所示:


图18.1.2.2 WWDG_CFR寄存器


       该寄存器中的EWI位是提前唤醒中断,如果该位置1,当递减计数器等于0x40时产生提前唤醒中断,我们就可以及时喂狗以避免WWDG复位。因此,我们一般都会用该位来设置中断,当窗口看门狗的计数器值减到0X40的时候,如果该位设置,并开启了中断,则会产生中断,我们可以在中断里面向WWDG_CR重新写入计数器的值,来达到喂狗的目的。注意这里在进入中断后,必须在不大于1个窗口看门狗计数周期的时间(在ppre1频率为150M且WDGTB[2:0]为0的条件下,该时间为27.3us)内重新写WWDG_CR,否则,看门狗将产生复位!


       l 状态寄存器(WWDG_SR)

       该寄存器用来记录当前是否有提前唤醒的标志。该寄存器仅有位0有效,其他都是保留位。当计数器值达到0x40时,此位由硬件置1。它必须通过软件写0来清除。对此位写1无效。即使中断未被使能,在计数器的值达到0X40的时候,此位也会被置1。 状态寄存器描述如图18.1.2.3所示:


图18.1.2.3 WWDG_SR寄存器


        18.2 硬件设计


       1. 例程功能

       LED1用来指示中断喂狗,每次中断喂狗翻转一次。


       2. 硬件资源


       1)LED灯

              LED1 – PD14


       2)窗口看门狗


       3. 原理图

       窗口看门狗属于STM32H7R7的内部资源,只需要软件设置好即可正常工作。我们通过LED1来指示窗口看门狗的喂狗情况。


        18.3 程序设计 


       18.3.1 WWDG的HAL库驱动

       WWDG在HAL库中的驱动代码在stm32h7rsxx_hal_wwdg.c文件(及其头文件)中。 


       1. HAL_WWDG_Init函数

       WWDG的初始化函数,其声明如下:

HAL_StatusTypeDef HAL_WWDG_Init(WWDG_HandleTypeDef *hwwdg);


       l 函数描述:

       用于初始化WWDG。


       l 函数形参:

       形参1是WWDG句柄,WWDG_HandleTypeDef结构体类型,其定义如下:

typedef struct
{
  WWDG_TypeDef               *Instance; /* WWDG寄存器基地址 */
  WWDG_InitTypeDef           Init;      /* WWDG初始化参数 */
}WWDG_HandleTypeDef;


       1)Instance:指向WWDG寄存器基地址。


       2)Init:WWDG初始化结构体,用于配置计数器的相关参数。

       WWDG_InitTypeDef这个结构体类型定义如下:

typedef struct
{
  uint32_t Prescaler;   /* 预分频系数 */
  uint32_t Window;      /* 窗口值 */
  uint32_t Counter;     /* 计数器值 */
  uint32_t EWIMode;     /* 提前唤醒中断使能 */
} WWDG_InitTypeDef;

       1)Prescaler:预分频系数,WWDG_PRESCALER_1到WWDG_PRESCALER_128。

       2)Window:窗口值,即上限值。

       3)Counter:计数器值,用于保存要设置计数器的值。

       4)EWIMode:提前唤醒中断使能。


       l 函数返回值:

       HAL_StatusTypeDef枚举类型的值。


       2. HAL_WWDG_Refresh函数

       HAL_WWDG_Refresh函数是窗口看门狗的喂狗函数。其声明如下:

HAL_StatusTypeDef HAL_WWDG_Refresh(WWDG_HandleTypeDef *hwwdg);

       l 函数描述:

       该函数实际就是往CR寄存器重写Counter这个预先保存的计数器值。


       l 函数形参:

       形参1是WWDG_HandleTypeDef结构体指针类型的WWDG句柄。


       l 函数返回值:

       HAL_StatusTypeDef枚举类型的值。


       窗口看门狗配置步骤 


       1)使能WWDG时钟

       WWDG不同于IWDG,IWDG有自己独立的32Khz时钟。而WWDG使用的是PPRE1的时钟,需要先使能时钟。方法是:

__HAL_RCC_WWDG_CLK_ENABLE();


       2)设置窗口值,分频数和计数器初始值

       在HAL库中,这三个值都是通过函数HAL_WWDG_Init来设置的,详见本例程源码。


       3)开启WWDG

       通过设置WWDG_CR寄存器的WDGA(bit7)位为1来实现开启窗口看门狗,同样是在HAL_WWDG_Init函数里面实现。


       4)使能中断通道并配置优先级(如果开启了WWDG中断)

       WWDG的中断也是通过:HAL_NVIC_EnableIRQ函数使能,通过HAL_NVIC_SetPriority函数设置优先级。

       HAL库同样为看门狗提供了MSP回调函数HAL_WWDG_MspInit,一般情况下,步骤1和步骤4的步骤,我们均放在该回调函数中。


       5)编写中断服务函数

       在最后,还是要编写窗口看门狗的中断服务函数,通过该函数来喂狗,喂狗要快,否则当窗口看门狗计数器值减到0X3F的时候,就会引起软复位了。在中断服务函数里面也要将状态寄存器的EWIF位清空。

       窗口看门狗中断服务函数为:WWDG_IRQHandler,喂狗函数为:HAL_WWDG_Refresh。


       6)重写窗口看门狗唤醒中断处理回调函数HAL_WWDG_EarlyWakeupCallback

       HAL库定义了一个WWDG中断处理共用函数HAL_WWDG_IRQHandler,我们在WWDG中断服务函数中会调用该函数。同时该函数会调用回调函数HAL_WWDG_EarlyWakeupCallback,提前唤醒中断逻辑(喂狗、闪灯)我们写在回调函数HAL_WWDG_EarlyWakeupCallback中。


       18.3.2 程序解析


       1. WWDG驱动代码

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。窗口看门狗(WWDG)驱动源码包括两个文件:wdg.c和wdg.h。 

       wdg.h头文件只有函数的声明,就不解释了。下面我们直接解析wdg.c的程序,先看WWDG的初始化函数,其定义如下:

/**
 * @brief   初始化窗口看门狗
 * @param   counter: 计数器值
 * @param   window: 窗口值
 * @param   prescaler: 预分频器系数
 * @arg     WWDG_PRESCALER_1  : 1分频
 * @arg     WWDG_PRESCALER_2  : 2分频
 * @arg     WWDG_PRESCALER_4  : 4分频
 * @arg     WWDG_PRESCALER_8  : 8分频
 * @arg     WWDG_PRESCALER_16 : 16分频
 * @arg     WWDG_PRESCALER_32 : 32分频
 * @arg     WWDG_PRESCALER_64 : 64分频
 * @arg     WWDG_PRESCALER_128: 128分频
 * @retval  无
 */
void wwdg_init(uint32_t counter, uint32_t window, uint32_t prescaler)
{
    g_wwdg_handler.Instance = WWDG;
    g_wwdg_handler.Init.Prescaler = prescaler;
    g_wwdg_handler.Init.Window = window;
    g_wwdg_handler.Init.Counter = counter;
    g_wwdg_handler.Init.EWIMode = WWDG_EWI_ENABLE;
    HAL_WWDG_Init(&g_wwdg_handler);
}

       wwdg_init是独立看门狗初始化函数,主要设置预分频数、窗口值和计数器的值,以及选择是否使能窗口看门狗提前唤醒中断。

       因为用到中断,我们用HAL_WWDG_MspInit函数来编写窗口看门狗中断的初始化代码。当然大家也可以HAL_WWDG_MspInit函数的代码放到wwdg_init函数里面。这个初始化框架就是HAL库的特点。

/**
 * @brief       WWDG底层驱动,时钟配置,中断配置
                此函数会被HAL_WWDG_Init()调用
 * @param       hwwdg:窗口看门狗句柄
 * @retval      无
 */
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{
    /* 使能窗口看门狗时钟 */
    __HAL_RCC_WWDG_CLK_ENABLE();
    
    /* 配置中断优先级并使能中断 */
    HAL_NVIC_SetPriority(WWDG_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(WWDG_IRQn);
}

       HAL_WWDG_MspInit函数会被HAL_WWDG_Init函数调用。该函数使能窗口看门狗的时钟,并设置窗口看门狗中断的抢占优先级为0,响应优先级为0。

/**
 * @brief       窗口看门狗中断服务程序
 * @param       无
 * @retval      无
 */
void WWDG_IRQHandler(void)
{
    HAL_WWDG_IRQHandler(&g_wwdg_handler);
}

       WWDG_IRQHandler函数是窗口看门狗中断服务函数,而这个函数实际上就是调用HAL库的中断处理函数HAL_WWDG_IRQHandler。逻辑程序在下面的这个回调函数中:

/**
* @brief       窗口看门狗喂狗提醒中断服务回调函数
 * @param       wwdg句柄
 * @note        此函数会被HAL_WWDG_IRQHandler()调用
 * @retval      无
 */
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg)
{
    HAL_WWDG_Refresh(&g_wwdg_handler); /* 更新窗口看门狗值 */
    LED1_TOGGLE();                     /* LED1闪烁 */
}

       在回调函数内部调用HAL_WWDG_Refresh函数喂狗,并翻转LED1。


       2. main.c代码

       在main.c里面编写如下代码:

int main(void)
{
    uint8_t key;
    
    sys_mpu_config();                           /* 配置MPU */
    sys_cache_enable();                         /* 使能Cache */
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(300, 6, 2);            /* 配置时钟,600MHz */
    delay_init(600);                            /* 初始化延时 */
    usart_init(115200);                         /* 初始化串口 */
    led_init();                                 /* 初始化LED */
    wwdg_init(0x7F, 0x5F, WWDG_PRESCALER_16);   /* 初始化WWDG */
    
    while (1)
    {
        
    }
}

       在main函数里,先初始化系统和用户的外设代码,调用wwdg_init(0X7F, 0X5F, WWDG_PRESCALER_16)这个语句,就设置计数器值为7f,窗口寄存器为5f,分频数为16,然后可由前面的公式得到Twwdg=4096×16×64/150000=27.96ms,即超时时间就是27.96ms。27.96ms进中断喂狗一次,并翻转LED1。


        18.4 下载验证

       下载代码后,可以看到LED1开始不停的闪烁。每秒钟闪烁28次左右,说明程序在中断不停的喂狗,和我们预期的一致。


请使用浏览器的分享功能分享到微信等