HONG-看门狗—IWDG&窗口看门狗—WWDG

独立看门狗—IWDG&窗口看门狗—WWDG
  • 看门狗可以监控程序的运行状态,当程序因为设计漏洞、硬件故障、电磁干扰等原因,出现卡死或者跑飞现象时,看门狗能及时复位程序,避免程序陷入长时间的罢工状态,保证系统的可靠性和安全性
  • 看门狗本质上是一个定时器,当指定时间范围内,程序没有执行喂狗(重置计数器)操作时,看门狗硬件电路就会自动产生复位信号
  • STM32内置两个看门狗
    独立看门狗(IWDG):独立工作,对时间精度要求较低
    窗口看门狗(WWDG):要求看门狗在精确计时窗口起作用
独立看门狗
独立看门狗结构图

上半部分是寄存器核心,下半部分是工作流程
在这里插入图片描述

①时钟:独立看门狗的时钟源来自LSI(内部低速时钟),意味着不受外部晶振电路影响,同时就算系统主时钟发生故障时,也可以正常工作。使用内部晶振,也就以为i精度并不高,因此只适合应用在对时间精度要求比较低的场合。

②计数:独立看门狗的计数器是一个12位的递减计数器,计数最大值为0xFFF,当计数器递减到0时,就会产生一个复位信号,重启整个系统。如果在递减到0之前,将重装载数值写入递减计数器,就会由重装载数值开始递减到0,如此反复,就永远不会到0,也就不会产生复位信号,这个重装载计数值写入递减计数器的过程叫”喂狗“。

重装载数值来自重装载寄存器(IWDG_RLR),这个值大小决定独立看门狗的溢出时间(复位倒计时),溢出时间为:
在这里插入图片描述

其中,pre为预分频寄存器的值,范围:0<=pre<=6,rlr为重载寄存器的值,范围:0~0xFFF。

键值寄存器(IWDG_KY),往该写一些关键值,以实现不同的效果,这里关键值共有三个:

  • 写0xAAAA:将重载寄存器(IWDG_RLR)的值更新到计数器;

  • 写0x5555:默认预分频寄存器(IWDG_PR)和重装载寄存器(IWDG_RLR)有写保护,不允许直接写值,需要先向键值寄存器(IWDG_KY)写0x5555去保护,才能写预分频寄存器(IWDG_PR)和重装载寄存器(IWDG_RLR)。写完后,向键值寄存器(IWDG_KY)写入其它任意值,将重新启用写保护;

  • 写0xCCCC:启动独立看门狗,一旦启动,无法通过其它方式关闭,只能复位后恢复默认关闭状态;

③独立看门狗复位:当独立看门狗的递减计数器递减到0,将发生复位。假设当前预分频寄存器值为6(256分频),重装载寄存器的值为15,则溢出时间为:

在这里插入图片描述

即启动独立看门狗,需要载0.96s内喂狗一次,否则系统将复位。

状态寄存器:预分频器和重装载数值的工作状态,可通过状态寄存器(IWDG_SR)获取。该寄存器只有最低两位有效,含义如下:

  • Bit[1] Reload Value Update,RVU:独立看门狗计数器重载值更新状态,读出为1表示重装载值正在更新,读出为0表示更新完毕;
  • Bit[0] Prescaler Value Update,PVU: 独立看门狗预分频值更新状态,出为1表示预分频值正在更新,读出为0表示更新完毕;

因此,只有在RVU和PVU为0的情况下,才能分别设置重装载寄存器和预分频寄存器的值。

软件设计

通过按键切换自动喂狗模式和不自动喂狗模式

  • 初始独立看门狗相关参数:时钟预分频、设置重装载值等;
  • 主函数编写控制逻辑:通过按键切换不自动喂狗和自动喂狗模式。不自动喂狗模式中,不做任何操作,观察打印,何时复位重启。自动喂狗模式中,不断喂狗,观察打印,是否复位重启。

Step1:初始化独立看门狗喂狗时间

driver_iwdg.c

#include <stdio.h>
#include "main.h"
#include "driver_iwdg.h"

IWDG_HandleTypeDef hiwdg;

/*
 *  函数名:void IWDG_Init(uint16_t period)
 *  输入参数:period-设置喂狗周期,单位ms
 *  输出参数:无
 *  返回值:无
 *  函数作用:初始化独立看门狗的喂狗时间
 *  刷新时间计算:Prescaler/LSI*Reload
*/
void IWDG_Init(uint16_t period)
{
    hiwdg.Instance = IWDG;                     // 选择独立看门狗        
    hiwdg.Init.Prescaler = IWDG_PRESCALER_256; // 设置预分频
    hiwdg.Init.Reload = 40000/256*period/1000; // 设置重装载值
    if (HAL_IWDG_Init(&hiwdg) != HAL_OK)       // 初始化独立看门狗
    {
        Error_Handler();
    }
}

喂狗:HAL库提供刷新独立看门狗函数”HAL_IWDG_Refresh()“

driver_iwdg.c

/*
 *  函数名:void ClearIWDG(void)
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
 *  函数作用:刷新独立看门狗的计数器,俗称“喂狗”
*/
void ClearIWDG(void)
{
    if (HAL_IWDG_Refresh(&hiwdg) != HAL_OK)  // 将重载寄存器的值更新到计数器
    {
        Error_Handler();
    }
	printf("--------- 喂狗 --------\n\r");
}

Step2:主函数控制逻辑,初始化设置喂狗周期时间,接着通过按键切换喂狗/不喂狗模式

main.c

#include <stdio.h>
#include "main.h"
#include "driver_usart.h"
#include "driver_iwdg.h"
#include "driver_key.h"


int main(void)
{    
    uint32_t run_cnt = 1;
    // 初始化HAL库函数必须要调用此函数
    HAL_Init();

    /*
     * 系统时钟即AHB/APB时钟配置    
	 * 使用外部高速时钟HSE(8MHz)配置系统时钟,经过PLL放大9倍,得到72MHz
    */
    SystemClock_Config();
    
    // 初始化USART1,设置波特率为115200 bps
    UsartInit(115200);
    
	printf("**********************************************\n\r");
    printf("-->独立看门狗IWDG实验\n\r");
    printf("**********************************************\n\r");
	
    // 初始化按键
    KeyInit();
    
    // 初始化IWDG,设置喂狗周期1000ms=1s
    IWDG_Init(1000);
    
    while(1)
    {
        if(up_flag != true)  // 不喂狗,喂狗周期结束后将复位重启  
			printf("*当前模式:不自动喂狗模式   %d \n\r", run_cnt);
		else
		{
            printf("*当前模式:自动喂狗模式   %d \n\r", run_cnt);
            ClearIWDG();    // 喂狗周期内喂狗,不会复位重启 
        }
		
        run_cnt++;
        HAL_Delay(100);     
    }
}

/*
 *  函数名:void Error_Handler(void)
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
*  函数作用:程序错误处理函数,此处暂时设为死循环,不做任何动作
*/
void Error_Handler(void)
{
    while(1)
    {
    }
}

窗口看门狗—WWDG

独立看门狗和窗口看门狗的效果类似,都是检测系统发生软件错误或死机时,通过复位重启使系统重新正常工作。

独立看门狗包含一个12位递减计数器,从用户定义的t开始递减到0,必须载t~0之间喂狗,否则复位重启。

窗口看门狗包含一个7位递减计数器,从用户定义的t开始递减到64,必须在t~64之间喂狗,在t之前或者64之后喂狗,也会导致复位重启。这里的t值,称之为窗口上限,由用户自定义设置;这里的64,称之为窗口下限,是系统固定的。窗口看门狗计数器必须在上窗口和下窗口之间被刷新(喂狗),不能过早,也不能过晚,这也就窗口看门狗中的“窗口”含义。

窗口看门狗的窗口下限值为63,而窗口看门狗递减计数器又是7位,因此用户可设置的窗口上限范围为63<x<128。假设递减计数器初值为127,随着时间的递增,它的值将逐渐减少。现在还需要定一个窗口上边界,这个上边界位于下边界和递减计数器初值之间,假设为80。那么计数器从127递减到80这个时间段,是不允许喂狗的,一旦喂狗将复位重启;80递减到63这个时间段,则必须喂狗;63这个下边界是固定的,一旦递减计数器小于等于这个值,系统将复位。可以看到,整个过程,有两个关键数值,一个是递减计数器当前计数值,对应寄存器WWDG_CR[6:0],一个是窗口上边界值,对应寄存器WWDG_CFG[6:0]。

看门狗结构图
在这里插入图片描述

①时钟:窗口看门狗的时钟来自PCLK1(最高36HMz),经过4096分频,再经过WWDG_CFG的Bits[8 :7]位WDGTB分频得到,WDGTB支持2^n分频(0<=n<=3),由此可以得出计数器时钟为:
在这里插入图片描述

其中,pre为预分频寄存器的值,范围:0<=pre<=3。

②计数:窗口看门狗的计数器是一个7位的递减计数器,计数最大值为0x7F(01111111)(127),当计数器递减到0x3F(00111111)(63)时,就会产生一个复位信号,重启整个系统。当递减计数器递减到0x40(01000000)(64)时,如果使能了提前唤醒中断(WWDG_CFG的Bits[9]位EWI设置为1),则会产生提前唤醒中断,在该中断可以保存重要数据或者向WWDG_CR重新写入新计数器值,完成喂狗操作。一旦0x40变为0x39,系统将进行复位,因此必须在一个窗口看门狗计数周期内完成喂狗操作。WWDG_CR的Bits[7]位WDGA为窗口看门狗使能位,当为1时,窗口看门狗才工作。
在这里插入图片描述

③窗口:窗口看门狗的WWDG_CFG的Bits[6 :0]位为窗口上边界值,该值应小于计数器最大值0x7F,大于窗口下边界值0x3F。

④逻辑与或

独立/窗口看门狗的相同点和不同点
在这里插入图片描述

软件设计

通过按键切换不同的场景:不自动喂狗模式、自动喂狗模式和中断喂狗模式

  • 初始窗口看门狗相关参数:时钟预分频、设置窗口值和计数值等;
  • 初始化窗口看门狗硬件相关参数:时钟使能、设置中断优先级和使能;
  • 编写窗口看门狗提前唤醒中断回调函数;
  • 主函数编写控制逻辑:通过按键切换三种模式。不自动喂狗模式中,不做任何操作,观察打印,何时复位重启;自动喂狗模式中,先延时一段时间,再喂狗,观察打印,是否复位重启;中断喂狗模式中,使能看门狗中断,再提前唤醒中断里喂狗,观察打印,是否复位重启。

Step1:初始化窗口看门狗

driver_wwdg.c

#include <stdio.h>
#include "main.h"
#include "driver_wwdg.h"

WWDG_HandleTypeDef hwwdg;

/*
 *  函数名:void WWDG_Init(void)
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
 *  函数作用:初始化窗口看门狗
 *  计算方式:窗口看门狗时钟:PCLK1(36MHz)/4096/Prescaler(8) ≈ 1098Hz ≈ 0.9ms
			                       |--------|----------|-------|
              窗口看门狗计数器:  120       80        63      0
			                           36ms    15.3ms  
	          即:启动看门狗后,36ms内不能喂狗,36ms~15.3ms需要喂狗
*/
void WWDG_Init(void)
{
    hwwdg.Instance = WWDG;                     // 指定窗口看门狗
    hwwdg.Init.Prescaler = WWDG_PRESCALER_8;   // 时钟预分频, 支持2^n分频(0≤n≤3)
    hwwdg.Init.Window = 80;                    // 设置窗口上边界值,范围:0x40~0x7F
    hwwdg.Init.Counter = 120;                  // 设置计数值,范围:0x40~0x7F
    hwwdg.Init.EWIMode = WWDG_EWI_ENABLE;      // 提前唤醒中断使能
    if (HAL_WWDG_Init(&hwwdg) != HAL_OK)
    {
        Error_Handler();
    }
}

“HAL_WWDG_Init()”函数会回调“HAL_WWDG_MspInit()”进行硬件相关初始化

driver_wwdg.c

/*
 *  函数名:void HAL_WWDG_MspInit(WWDG_HandleTypeDef* wwdgHandle)
 *  输入参数:wwdgHandle-WWDG句柄
 *  输出参数:无
 *  返回值:无
 *  函数作用:HAL_WWDG_Init回调硬件初始化
*/
void HAL_WWDG_MspInit(WWDG_HandleTypeDef* wwdgHandle)
{
    if(wwdgHandle->Instance==WWDG)
    {
        __HAL_RCC_WWDG_CLK_ENABLE();           // 使能WWDG时钟
        
        HAL_NVIC_SetPriority(WWDG_IRQn,0,0);   // 设置WWDG中断优先级
        HAL_NVIC_EnableIRQ(WWDG_IRQn);         // 使能WWDG中断   
    }
}

Step2:窗口看门狗中断处理

使能中断后,当窗口看门狗计数到0x40时,会进去提前唤醒中断,在该中断处理函数里,用户可以保存数据或喂狗

driver_wwdg.c

/*
 *  函数名:void WWDG_IRQHandler(void)
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
 *  函数作用:WWDG的中断处理函数
*/
void WWDG_IRQHandler(void)
{
	HAL_WWDG_IRQHandler(&hwwdg);
}
 
/*
 *  函数名:void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg)
 *  输入参数:hwwdg-WWDG句柄
 *  输出参数:无
 *  返回值:无
 *  函数作用:提前唤醒中断回调函数
*/
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg)
{
	ClearWWDG();
	printf("-------复位前保存数据------\n\r");
	printf("--------- 软件喂狗 --------\n\r");
}

Step3:窗口看门狗喂狗

调用HAL库提供的“HAL_WWDG_Refresh()”刷新计数器值,实现喂狗操作

driver_wwdg.c

/*
 *  函数名:void ClearIWDG(void)
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
 *  函数作用:刷新独立看门狗的计数器,俗称“喂狗”
*/
void ClearWWDG(void)
{
    if (HAL_WWDG_Refresh(&hwwdg) != HAL_OK)
    {
        Error_Handler();
    }
}

Step4:主函数控制逻辑

main.c

#include <stdio.h>

#include "main.h"
#include "driver_key.h"
#include "driver_usart.h"
#include "driver_wwdg.h"


int main(void)
{    
    uint32_t run_cnt = 1;
    uint8_t windows_value = 0;
    uint8_t counter_value = 0;
	uint8_t delay_value = 0;
    // 初始化HAL库函数必须要调用此函数
    HAL_Init();

    /*
     * 系统时钟即AHB/APB时钟配置    
	 * 使用外部高速时钟HSE(8MHz)配置系统时钟,经过PLL放大9倍,得到72MHz
    */
    SystemClock_Config();
    
    // 初始化USART1,设置波特率为115200 bps
    UsartInit(115200);
    
	printf("\n\r");
	printf("**********************************************\n\r");
    printf("-->窗口看门狗WWDG实验\n\r");
    printf("**********************************************\n\r");
	
	
    KeyInit();                           // 初始化按键
    WWDG_Init();                         // 初始化WWDG
	
	counter_value = WWDG->CR & 0X7F;     // 获取计数值
    windows_value = WWDG->CFR & 0x7F;    // 获取窗口值
	delay_value = (counter_value - windows_value)*0.9; // 计算多少ms后可喂狗
	
    while(1)
    {
		if(up_flag == 0)  
		{
			printf("*当前模式:不自动喂狗模式   %d \n\r", run_cnt); 
		} 
		else if(up_flag == 1)  
		{   
			HAL_Delay(delay_value);
			ClearWWDG();
			printf("*当前模式:自动喂狗模式   %d \n\r", run_cnt);
		}
		else if(up_flag == 2) 
		{
			HAL_NVIC_EnableIRQ(WWDG_IRQn);         // 使能WWDG中断   
			printf("*当前模式:中断喂狗模式   %d \n\r", run_cnt);
		}
		HAL_NVIC_DisableIRQ(WWDG_IRQn);            // 去能WWDG中断
		run_cnt++;
		HAL_Delay(1);
    }
}

/*
 *  函数名:void Error_Handler(void)
 *  输入参数:无
 *  输出参数:无
 *  返回值:无
*  函数作用:程序错误处理函数,此处暂时设为死循环,不做任何动作
*/
void Error_Handler(void)
{
    while(1)
    {
    }
}
  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值