以下内容全部来自正点原子,本人只是对主要知识点进行整理,方便以后查看。
一、独立看门狗
独立看门狗本质上是一个定时器,这个定时器有一个输出端,可以输出复位信号。该定时 器是一个 12 位的递减计数器,当计数器的值减到 0 的时候,就会产生一个复位信号。如果在计 数没减到 0 之前,重置计数器的值的话,那么就不会产生复位信号,这个动作我们称为喂狗。 看门狗功能由 VDD 电压域供电,在停止模式和待机模式下仍然可以工作。
STM32F407 的独立看门狗由内部专门的 32Khz 低速时钟(lsi_ck)驱动,即使主时钟发生故障,它也仍然有效。独立看门狗的时钟不是准确 的 32Khz,而是在 17~47Khz 之间的一个可变化的时钟,我们在估算的时候,以 32Khz 的 频率来计算,看门狗对时间的要求不是很精确,所以,时钟有些偏差,都是可以接受的。
二、独立看门狗寄存器
IWDG 主要用到其中 3 个寄存器:关键字寄存器(IWDG_KR)、预分频寄存器(IWDG_PR)和重载寄存器(IWDG_ RLR)。
2.1、关键字寄存器(IWDG_KR)
关键字寄存器可以看作是独立看门狗的控制寄存器
在键寄存器(IWDG_KR)中写入 0xCCCC,开始启用独立看门狗;此时计数器开始从其复位值 0xFFF 递减计数。当计数器计数到末尾 0x000 时,会产生一个复位信号(IWDG_RESET)。
无 论何时,只要关键字寄存器 IWDG_KR 中被写入 0xAAAA,IWDG_RLR 中的值就会被重新加 载到计数器中从而避免产生看门狗复位。
IWDG_PR 和 IWDG_RLR 寄存器具有写保护功能。要修改这两个寄存器的值,必须先向 IWDG_KR 寄存器中写入 0x5555。将其他值写入这个寄存器将会打乱操作顺序,寄存器将重新 被保护。重装载操作(即写入 0xAAAA)也会启动写保护功能。
2.2、预分频寄存器(IWDG_PR)
2.3、重载寄存器(IWDG_ RLR)
该寄存器用来保存重装载到计数器中的值。该寄存器也是一个 32 位寄存器,只有低 12 位 是有效的。
三、相关函数
IWDG 的初始化函数,其声明如下:
HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg);
参数如下
typedef struct
{
IWDG_TypeDef *Instance; /* IWDG 寄存器基地址 */
IWDG_InitTypeDef Init; /* IWDG 初始化参数 */
}IWDG_HandleTypeDef;
1)Instance:指向 IWDG 寄存器基地址。
2)Init:IWDG 初始化结构体,用于配置计数器的相关参数。
IWDG_InitTypeDef 这个结构体类型定义如下:
typedef struct
{
uint32_t Prescaler; /* 预分频系数 */
uint32_t Reload; /* 重装载值 */
} IWDG_InitTypeDef;
1)Prescaler:预分频系数,IWDG_PRESCALER_4 到 IWDG_PRESCALER_256。
2)Reload:重装载值,范围:0 到 0x0FFF。
HAL_IWDG_Refresh 函数是独立看门狗的喂狗函数。
HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);
函数返回值: HAL_StatusTypeDef 枚举类型的值。
独立看门狗配置步骤
1)取消寄存器写保护,通过函数 HAL_IWDG_Init 实现。
看门狗的喂狗时间(也就是看门狗溢 出时间)的计算方式为:
Tout=((4×2^prer) ×rlr) /32
其中 Tout 为看门狗溢出时间(单位为 ms)。
prer 为看门狗时钟预分频值(IWDG_PR 值),范围为 0~7。
rlr 为看门狗的重装载值(IWDG_RLR 的值)。
比如我们设定 prer 值为 4(因为4×2^4=64,所以 prer =4代表的是 64 分频,HAL 库中可以使用宏定义标识符 IWDG_PRESCALER_64),rlr 值为 500,那么就可以得到 Tout=64×500/32=1000ms,这样,看 门狗的溢出时间就是 1s,只要在一秒钟之内,有一次写入 0xAAAA 到 IWDG_KR,就不会导 致看门狗复位。
注:看门狗的时钟不是准确的 32Khz,最好不要太晚喂狗,否则,有可能发生看门狗复位。
2)重载计数值喂狗(向 IWDG_KR 写入 0xAAAA) 在 HAL 中重载计数值的函数是 HAL_IWDG_Refresh,把值 0xAAAA 写入 到 IWDG_KR 寄存器,从而触发计数器重载,即实现独立看门狗的喂狗操作。
3) 启动看门狗(向 IWDG_KR 写入 0xCCCC) HAL 库函数里面启动独立看门狗是通过宏定义标识符来实现的:
#define __HAL_IWDG_START(__HANDLE__)
WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_ENABLE);
我们只需要调用宏定义标识符__HAL_IWDG_START 即可实现看门狗使能。实际上,当我 们调用了看门狗初始化函数 HAL_IWDG_Init 之后,在内部已经调用了该宏启动看门狗。
注:调用了看门狗初始化函数 HAL_IWDG_Init 之后,在内部会自动启动看门狗。
四、实战
#include "stm32f4xx.h"
#include "core_cm4.h"
#include "stm32f4xx_hal.h"
#include "stdio.h"
IWDG_HandleTypeDef g_iwdg_handle;
uint8_t keyval = 0; /* 保存按键状态 */
void KEY_int(void)
{
GPIO_InitTypeDef gpio_init_struct; /* 定义结构体 */
gpio_init_struct.Pin = GPIO_PIN_0; /* LED1引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 初始化KEY引脚 */
}
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓看门狗参数配置↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
void iwdg_init(uint8_t prer, uint16_t rlr)
{
g_iwdg_handle.Instance = IWDG;
g_iwdg_handle.Init.Prescaler = prer; /* 设置 IWDG 分频系数 */
g_iwdg_handle.Init.Reload = rlr; /* 重装载值 */
HAL_IWDG_Init(&g_iwdg_handle);
}
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓喂狗↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
void iwdg_feed(void)
{
HAL_IWDG_Refresh(&g_iwdg_handle); /* 重装载计数器 */
}
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓检测按键↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
void key(void)
{
if(keyval=1) keyval=0;
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==0)
{
delay_ms(10); /* 消抖 */
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)==0) keyval=1;
}
}
int main()
{
HAL_Init(); /* 初始化HAL库 */
__HAL_RCC_GPIOA_CLK_ENABLE(); /* 端口A的时钟使能 */
delay_init(168); /* 延时初始化 */
KEY_init(); /* 初始化LED */
iwdg_init(IWDG_PRESCALER_64,500);
keyval = 0;
while(1)
{
key();
if(keyval==1)
{
iwdg_feed();
}
delay_ms(10);
}
}
注:delay_ms(500); 在后面时钟才能学到,这里只是表示一下。