目录
看门狗
在单片机工作时常常会收到外界的干扰,造成程序异常,陷入死循环,也有可能是我们程序代码编写不正确,导致代码出现BUG,是单片机无法正常工作,于是便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog)
IWDG简介
IWDG用通俗一点的话来解释就是一个 12 位的递减计数器,当计数器的值从某个值一直减到 0 的时候,系统就会产生一个复位信号,即 IWDG_RESET。如果在计数没减到 0 之前,刷新了计数器的值的话,那么就不会产生复位信号,这个动作就是我们经常说的喂狗。看门狗功能由 VDD 电压域供电,在停止模式和待机模式下仍能工作。
IWDG功能框图
1.独立看门狗时钟
独立看门狗的时钟由独立的 RC 振荡器 LSI 提供, 即使主时钟发生故障它仍然有效,
非常独立。 LSI 的频率一般在 30~60KHZ 之间,根据温度和工作场合会有一定的漂移,我
们一般取 40KHZ,所以独立看门狗的定时时间并一定非常精确,只适用于对时间精度
要求比较低的场合,在这里固定为LSI=40KHZ。
2.计数器时钟
递减计数器的时钟由 LSI 经过一个 8 位的预分频器得到,我们可以操作预分频器寄存
器 IWDG_PR 来设置分频因子,分频因子可以是: [4,8,16,32,64,128,256,256],
IWDG_PR寄存器框图如下
由PR[2:0]三位的数值,我们得到计数器时钟CK_CNT和振荡器LSI的关系为 CK_CNT=40 / 4*2^PR[2:0],每过一个计数器时钟计数器就减一。
3.计数器
独立看门狗的计数器是一个 12 位的递减计数器,最大值为 0XFFF,当计数器减到 0 时,
会产生一个复位信号:IWDG_RESET,让程序重新启动运行,如果在计数器减到 0 之前刷新
了计数器的值的话,就不会产生复位信号,重新刷新计数器值的这个动作我们俗称喂狗。
4.重装载寄存器(IWDG_RLR)
重装载寄存器是一个 12 位的寄存器,里面装着要刷新到计数器的值,这个值的大小决
定着独立看门狗的溢出时间。计数器时钟为第二点中的表达式CK_CNT=40 / 4*2^PR[2:0],
那么记一个数的时间为它的倒数,即为 4*2^PR[2:0] / 40,那么总的超时时间就是记一个数的时间再乘以重装载寄存器的值,由此我们得到超时时间的表达式Tout = 4*2^PR[2:0] / 40 * rlv (s),rlv 是重装载寄存器的值。
5.键寄存器(IWDG_KR)
直接看芯片手册描述就好
6.状态寄存器(IWDG_SR)
状态寄存器 SR 只有位 0: PVU 和位 1: RVU 有效,这两位只能由硬件操作,软件操
作不了。 RVU:看门狗计数器重装载值更新,硬件置 1 表示重装载值的更新正在进行中,
更新完毕之后由硬件清 0。 PVU: 看门狗预分频值更新,硬件置‘1‘指示预分频值的更新正在
进行中,当更新完成后,由硬件清 0。所以只有当 RVU/PVU 等于 0 的时候才可以更新重装
载寄存器/预分频寄存器。
IWDG HAL 库驱动程序
1.初始化函数 HAL_IWDG_Init()
该函数用于初始化独立看门狗,其函数原型定义如下
HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg),其中hiwdg是IWDG_HandleTypeDef结构体指针,是看门狗对象指针。独立看门狗初始化后就自动启动了,无法关闭。
使用CubeIDE生成的外设代码中,会定义一个独立看门狗外设对象变量,即
IWDG_HandleTypeDef hiwdg;
结构体IWDG_HandleTypeDef定义如下:
typedef struct
{
IWDG_TypeDef *Instance; /*IWDG寄存器基地址 */
IWDG_InitTypeDef Init; /* IWDG参数结构体 */
} IWDG_HandleTypeDef;
其中成员变量Init是结构体类型IWDG_InitTypeDef ,定义如下
typedef struct
{
uint32_t Prescaler; /*!< IWDG预分频系数,就是预分频寄存器IWDG_PR里面的PR[2:0] */
uint32_t Reload; /*!< IWDG计数器重装载值,也就是重装载寄存器IWDG_RLR的值,该值需要在0-
0XFF之间 */
} IWDG_InitTypeDef;
2.喂狗函数 HAL_IWDG_Refresh()
该函数的作用就是将重载寄存器IWDG_RLR的值重新载入看门狗计数器,避免超时产生复位。
函数原型为HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);
3.一些宏函数
#define IWDG_KEY_RELOAD 0x0000AAAAu /*!< IWDG 重装载寄存器启用 */
#define IWDG_KEY_ENABLE 0x0000CCCCu /*!< IWDG 看门狗启动工作 */
#define IWDG_KEY_WRITE_ACCESS_ENABLE 0x00005555u /*!< IWDG KR 启用写入访问 */
#define IWDG_KEY_WRITE_ACCESS_DISABLE 0x00000000u /*!< IWDG KR 关闭写入访问 */
#define __HAL_IWDG_START(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_ENABLE) //启用看门狗,就是向IWDG_KR寄存器写入0x0000CCCC。
#define __HAL_IWDG_RELOAD_COUNTER(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_RELOAD) //向IWDG_KR写入0x0000AAAA,并使重装载寄存器IWDG_RLR中的值载入看门狗计数器。
#define IWDG_ENABLE_WRITE_ACCESS(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_WRITE_ACCESS_ENABLE) //启用对IWDG_PR和IWDG_RLR寄存器的写访问。
#define IWDG_DISABLE_WRITE_ACCESS(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_WRITE_ACCESS_DISABLE) //禁用对IWDG_PR和IWDG_RLR寄存器的写访问。
简单示例代码
1.时钟树,默认就是40KHZ,默认配置即可
2.IWDG配置
选择激活即可,这里我选择的分频系数为4,重装载值为最大的4095,及0xFF
2.LED灯GPIO口配置
我的板子的灯连接到PB1,低电平点亮,这里设置GPIO为推挽输出。
3.Main函数代码
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_IWDG_Init();
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, 1);//先拉高,LED熄灭
HAL_Delay(100);//延时100ms
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, 0); //低电平点亮LED
HAL_Delay(300);//延时300ms
HAL_IWDG_Refresh(&hiwdg); //喂狗
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
应为我们选择是4095的重装载值,根据公式,超时复位为0.4095秒,在上述代码中我们延时了400ms后喂狗,未达到超时时间,所以可以看到LED一直亮灯。
先将延时函数时间改长一点,
总共延时了500ms,超过超时时间,喂狗函数未成功执行,系统复位,所以会看到LED灯闪烁。