【嵌入式系统】独立看门狗原理+看门狗实验分析

【嵌入式系统】独立看门狗原理+看门狗实验分析

1、看门狗模块概述

在由单片机构成的微机系统中,由于单片机工作常常会受到来自外界电磁场干扰导致程序跑飞,陷入死循环——即程序正常运行被打断,系统无法继续工作。这种情况下会造成系统陷入停滞状态,发生不可预料的后果。因此出于对单片机运行状态进行实时监测的考虑,产生了一种专门用于监测单片机程序运行状态的模块或芯片,称为看门狗。STM32F10xxx内置两个看门狗:独立看门狗(IWDG, Independent WatchDoG)和窗口看门狗(WWDG, Windows WatchDoG),提供了更高的安全性、时间的精确性和使用的灵活性。
【嵌入式系统】独立看门狗原理+看门狗实验分析_第1张图片

图1 STM32基本看门狗类型
WWDG和IWDG原理有所区别,本篇文章仅对IWDG作分析

2、IWDG原理

【嵌入式系统】独立看门狗原理+看门狗实验分析_第2张图片

图2 IWDG结构框图

IWDG由独立时钟LSI驱动且处于VDD供电区,因此可在睡眠、停机、待机模式下运行。当IWDG_KR写入0xCCCC时IWDG递减计数器启动,从0xFFFF开始减计数,当计数器计数为0时系统就进行复位。在正常工作状态下,每隔一段时间会向IWDG_KR写入0xAAAA,即将一个寄存在IWDG_RLR的12bits数值装载到IWDG计数器中——可见正常工作下IWDG永远不会触发系统复位;当系统跑飞时将无法正常喂狗,从而一段时间后产生系统复位,维护系统运行稳定。

看门狗溢出时间如下,当计数器第一次重装载后,超过 T o u t T_{out} Tout不重装载将产生系统复位。在战舰版中,LSI如图2为40kHz,因此令 f L S I f_{LSI} fLSI=40即可
T o u t = ( 4 × 2 P r e ) × R L R f L S I T_{out}=\frac{(4×2^{Pre})×RLR}{f_{LSI}} Tout=fLSI(4×2Pre)×RLR

【嵌入式系统】独立看门狗原理+看门狗实验分析_第3张图片

图3 IWDG主要寄存器

3、IWDG实验分析

本实验基于STM32NANO(HAL库),结合KEY、LED与IWDG观察看门狗的监视复位功能。

int main(void)
{
    HAL_Init();                    		//初始化HAL库    
    Stm32_Clock_Init(RCC_PLL_MUL9); 		//设置时钟,72M
    delay_init(72);                 		//初始化延时函数
	uart_init(115200);					//初始化串口
    LED_Init();                     		//初始化LED 
	KEY_Init();						//初始化按键
	delay_ms(100);                  		//延时100ms再初始化看门狗
    IWDG_Init(IWDG_PRESCALER_64,625);  	//分频数为64,重载值为625
    LED0=0;
	while(1)
	{
		if(KEY_Scan(1)==WKUP_PRES)  	//如果WK_UP按下,喂狗
		{
			IWDG_Feed();    			//喂狗
		}
			delay_ms(10); 
	}
}

IWDG_Init(IWDG_PRESCALER_64,625)设置了溢出时间为:
T o u t = 64 × 625 40 m s = 1 s T_{out}=\frac{64×625}{40}ms=1s Tout=4064×625ms=1s
即超过1s没有喂狗,系统将执行复位。

LED0=0使用位带操作进行点灯,位带操作点灯的原理可以参考:位带操作原理+LED实验分析while(1)循环内将喂狗函数和WK_UP按键绑定,即按下WK_UP就执行了重装载操作(KEY_Scan(1)即选用支持连按)。IWDG初始化前的延时delay_ms(100)是为了使LED0的闪烁可见:如图4所示,若没有delay(),两次点灯时间间隔很短,无法观察到因为复位导致的LED0灭灯现象。
在这里插入图片描述

图4

下面讲解封装过的独立看门狗初始化函数void IWDG_Init(u8,u16)原理:

IWDG_HandleTypeDef IWDG_Handler;	 //独立看门狗句柄
void IWDG_Init(u8 prer,u16 rlr)
{
    IWDG_Handler.Instance=IWDG;
    IWDG_Handler.Init.Prescaler=prer;	 //设置IWDG分频系数
    IWDG_Handler.Init.Reload=rlr;		 //重装载值
    HAL_IWDG_Init(&IWDG_Handler);	 //初始化IWDG,默认会开启独立看门狗
	HAL_IWDG_Start(&IWDG_Handler);	 //启动独立看门狗
}

首先在IWDG头文件中定义了全局的IWDG句柄,句柄定义如下,包含的是IWDG寄存器组基址以及预分频系数、重装载值等资源信息。

typedef struct
{
  IWDG_TypeDef         *Instance;  /*!< Register base address    */

  IWDG_InitTypeDef       Init;      /*!< IWDG required parameters */

}IWDG_HandleTypeDef;

接下来在函数中对这个全局句柄进行初始化,将输入参数Prep、Rlr以及IWDG基址赋给IWDG_Handle,之后使用此句柄进行实质性的IWDG初始化和启动。下面给出了IWDG的初始化封装HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *)中与IWDG配置有关的代码

HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg)
{
  ……
  /* Enable IWDG. LSI is turned on automaticaly */
  __HAL_IWDG_START(hiwdg);

  /* Enable write access to IWDG_PR and IWDG_RLR registers by writing 0x5555 in KR */
  IWDG_ENABLE_WRITE_ACCESS(hiwdg);

  /* Write to IWDG registers the Prescaler & Reload values to work with */
  hiwdg->Instance->PR = hiwdg->Init.Prescaler;
  hiwdg->Instance->RLR = hiwdg->Init.Reload;

  /* Reload IWDG counter with value defined in the reload register */
  __HAL_IWDG_RELOAD_COUNTER(hiwdg);

  /* Return function status */
  return HAL_OK;
}

大多数配置都是通过宏定义函数进行的,例如

#define WRITE_REG(REG, VAL)   ((REG) = (VAL))
#define IWDG_ENABLE_WRITE_ACCESS(__HANDLE__)
WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_WRITE_ACCESS_ENABLE)

其中IWDG_KEY_WRITE_ACCESS_ENABLE就是0x5555,即往KR写入0x5555,参照图3知此时取消了RLR与PR的写保护,可以向其中写入预分频因子和重装载值了。

将IWDG初始化并启动后,若不进行喂狗系统将在溢出时间到达后自动复位。IWDG_Feed()是对宏定义函数__HAL_IWDG_RELOAD_COUNTER(hiwdg)的封装,即往KR中写入0xAAAA对计数器进行重装载。

你可能感兴趣的:(嵌入式系统)