STM32 的独立看门狗由内部专门的 40Khz 低速时钟驱动,即使主时钟发生故障,它也仍然有效。这里需要注意独立看门狗的时钟是一个内部 RC 时钟,所以并不是准确的 40Khz,而是在 30~60Khz 之间的一个可变化的时钟,只是我们在估算的时候,以 40Khz 的频率来计算,看门狗对时间的要求不是很精确,所以,时钟有些偏差,都是可以接受的。
独立看门狗用通俗一点的话来解释就是一个 12 位的递减计数器,当计数器的值从某个值一直减到 0 的时候,系统就会产生一个复位信号,即 IWDG_RESET。如果在计数没减到 0 之前,刷新了计数器的值的话,那么就不会产生复位信号,这个动作就是我们经常说的喂狗。
独立看门狗由 VDD 电压域供电,在停止模式和待机模式下仍能工作。
独立看门狗相关的库函数操作函数在文件 stm32f1xx_hal_iwdg.c 和对应的头文件 stm32f1xx_hal_iwdg.h 中。
独立看门狗的作用
独立看门狗一般用来检测和解决由程序引起的故障。
比如一个程序正常运行的时间是 50ms,在运行完这个段程序之后紧接着进行喂狗,我们设置独立看门狗的定时溢出时间为 60ms,比我们需要监控的程序 50ms 多一点,如果超过 60ms 还没有喂狗,那就说明我们监控的程序出故障了,跑飞了,那么就会产生系统复位,让程序重新运行。
理解独立看门狗框图
本节内容来自野火《STM32 HAL 库开发实战指南—基于F103霸道_V2》
1. 独立看门狗时钟
独立看门狗的时钟由独立的 RC 振荡器 LSI 提供, 即使主时钟发生故障它仍然有效,非常独立。 LSI 的频率一般在 30~60KHZ 之间,根据温度和工作场合会有一定的漂移,我们一般取 40KHZ,所以独立看门狗的定时时间并不一定非常精确,只适用于对时间精度要求比较低的场合。
2. 预分频器寄存器 Prescaler register (IWDG_PR)
递减计数器的时钟由 LSI 经过一个 8 位的预分频器得到,我们可以操作预分频器寄存器 IWDG_PR 来设置分频因子,分频因子可以是: [4,8,16,32,64,128,256,256],计数器时钟 CK_CNT= 40/ 4*2^PRV,一个计数器时钟计数器就减一。
3. 计数器
独立看门狗的计数器是一个 12 位的递减计数器,最大值为 0XFFF,当计数器减到 0 时,会产生一个复位信号:IWDG_RESET,让程序重新启动运行,如果在计数器减到 0 之前刷新了计数器的值的话,就不会产生复位信号,重新刷新计数器值的这个动作我们俗称喂狗,喂狗的方式为往键寄存器 IWDG_KR 中写入0xAAAA,IWDG_RLR 中的值就会被重新加载到计数器,从而避免产生看门狗复位。
4. 重装载寄存器 Reload register (IWDG_RLR)
重装载寄存器是一个 12 位的寄存器,里面装着要刷新到计数器的值,这个值的大小决定着独立看门狗的溢出时间。超时时间 Tout = (42^prv) / 40 rlv (s) , prv 是预分频器寄存器的值, rlv 是重装载寄存器的值。
5. 键寄存器 Key register (IWDG_KR)
键寄存器 IWDG_KR 可以说是独立看门狗的一个控制寄存器,主要有三种控制方式,往这个寄存器写入下面三个不同的值有不同的效果。
通过往键寄存器写 0XCCC 来启动看门狗是属于软件启动的方式,一旦独立看门狗启动,它就关不掉,只有复位才能关掉。
6. 状态寄存器 Status register (IWDG_SR)
状态寄存器 SR 只有位 0: PVU 和位 1: RVU 有效,这两位只能由硬件操作,软件操作不了。
- RVU:看门狗计数器重装载值更新,硬件置 1 表示重装载值的更新正在进行中,更新完毕之后由硬件清 0。
- PVU::看门狗预分频值更新,硬件置‘1’指示预分频值的更新正在进行中,当更新完成后,由硬件清 0。所以只有当 RVU/PVU 等于 0 的时候才可以更新重装载寄存器/预分频寄存器。
通过 HAL 库函数来配置独立看门狗的步骤
文字主要来自正点原子《STM32F1开发指南-HAL库版本_V1.0》
1. 取消寄存器写保护(向 IWDG_KR 写入 0X5555)
首先我们必须取消 IWDG_PR 和 IWDG_RLR 寄存器的写保护, 这样才可以设置寄存器 IWDG_PR 和 IWDG_RLR 的值。取消写保护和设置预分频系数以及重装载值在 HAL 库中是通过函数 HAL_IWDG_Init
实现的。该函数声明为:HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg);
该函数只有一个入口参数 hiwdg,该参数是 IWDG_HandleTypeDef 结构体指针类型。结构体IWDG_HandleTypeDef 定义如下:
typedef struct
{
IWDG_TypeDef *Instance;
IWDG_InitTypeDef Init;
}IWDG_HandleTypeDef;
- 成员变量 Instance 用来设置看门狗寄存器基地址,实际上在 HAL 库中已经通过标识符定义了,这里对于独立看门狗直接设置为标识符 IWDG 即可。
- 成员变量 Init 是一个 IWDG_InitTypeDef 结构体类型,该结构体只有 2 个成员变量,分别用来设置独立看门狗的预分频系数和重装载值,定义如下:
HAL_IWDG_Init 函数使用的一般方法为: ```c IWDG_HandleTypeDef IWDG_Handler; //独立看门狗句柄typedef struct
{
uint32_t Prescaler;
uint32_t Reload;
}IWDG_InitTypeDef;
IWDG_Handler.Instance = IWDG; //独立看门狗 IWDG_Handler.Init.Prescaler = IWDG_PRESCALER_64; //设置 IWDG 分频系数 IWDG_Handler.Init.Reload = 500; //重装载值
HAL_IWDG_Init(&IWDG_Handler); //初始化看门狗 ``` 上面程序的作用是初始化 IWDG,设置分频系数为 64,重装载值为 500。
喂狗时间计算方式
设置完预分频系数和重装载值后,我们就可以知道看门狗的喂狗时间(也就是看门狗溢出时间),该时间的计算方式为:
T**out**=((4× 2^prer) × rlr) /32
其中 T 为看门狗溢出时间(单位为 ms); prer 为看门狗时钟预分频值(IWDG_PR 值),范围为 0~7; rlr 为看门狗的重装载值(IWDG_RLR 的值)。
比如我们设定 prer 值为 4( 4 代表的是 64 分频, HAL 库中可以使用宏定义标识符 IWDG_PRESCALER_64), rlr 值为 500,那么就可以得到 T=64× 500/32=1000ms,这样,看门狗的溢出时间就是 1s,只要你在一秒钟之内,有一次写入 0XAAAA 到 IWDG_KR,就不会导致看门狗复位(当然写入多次也是可以的)。这里需要提醒大家的是,看门狗的时钟不是准确的 32Khz,所以在喂狗的时候,最好不要太晚了,否则,有可能发生看门狗复位。
2. 重载计数值喂狗(向 IWDG_KR 写入 0XAAAA)
在 HAL 中重载计数值的函数是 HAL_IWDG_Refresh
,该函数声明为:HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);
该函数有一个入口参数为前面讲解的 IWDG_HandleTypeDef 结构体类型指针,它的作用是把值 0xAAAA 写入到IWDG_KR 寄存器,从而触发计数器重载, 即实现独立看门狗的喂狗操作。
3. 启动看门狗(向 IWDG_KR 写入 0XCCCC)
HAL 库函数里面启动独立看门狗的函数是 HAL_IWDG_START:
`HAL_IWDG_START(hiwdg);`
通过上面 3 个步骤,我们就可以启动 STM32F1 的独立看门狗了,使能了看门狗,在程序里面就必须间隔一定时间喂狗,否则将导致程序复位。