1、看门狗模块概述
在由单片机构成的微机系统中,由于单片机工作常常会受到来自外界电磁场干扰导致程序跑飞,陷入死循环——即程序正常运行被打断,系统无法继续工作。这种情况下会造成系统陷入停滞状态,发生不可预料的后果。因此出于对单片机运行状态进行实时监测的考虑,产生了一种专门用于监测单片机程序运行状态的模块或芯片,称为看门狗。STM32F10xxx内置两个看门狗:独立看门狗(IWDG, Independent WatchDoG)和窗口看门狗(WWDG, Windows WatchDoG),提供了更高的安全性、时间的精确性和使用的灵活性。
WWDG和IWDG原理有所区别,本篇文章仅对IWDG作分析
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} T
out
不重装载将产生系统复位。在战舰版中,LSI如图2为40kHz,因此令 f L S I f_{LSI} f
LSI
=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}}
T
out
=
f
LSI
(4×2
Pre
)×RLR
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
T
out
=
40
64×625
ms=1s
即超过1s没有喂狗,系统将执行复位。
LED0=0使用位带操作进行点灯,位带操作点灯的原理可以参考:位带操作原理+LED实验分析while(1)循环内将喂狗函数和WK_UP按键绑定,即按下WK_UP就执行了重装载操作(KEY_Scan(1)即选用支持连按)。IWDG初始化前的延时delay_ms(100)是为了使LED0的闪烁可见:如图4所示,若没有delay(),两次点灯时间间隔很短,无法观察到因为复位导致的LED0灭灯现象。
下面讲解封装过的独立看门狗初始化函数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对计数器进行重装载。