STM32独立看门狗

本文最后更新于 2025年1月7日 晚上

一、IWDG简介

IWDG全称(Independent watchdog)独立看门狗。

本质:一个能够产生复位信号的计数器。

那么什么是复位信号呢?复位就是使MCU回到初始状态。对于单片机来说,开机的时候需要复位,以便使得CPU和其他功能部件处于一个正确的初始状态,并以此为起点开始工作,当出现死机的情况也应当对其进行复位,用以摆脱死机状态。

系统复位方式共有5种,分别为:

1、硬件复位。

2、WWGD复位。

3、IWDG复位。

4、软件复位。

5、低功耗复位。

独立看门狗复位:这种方式使用独立的看门狗来监控单片机系统的工作状态,当单片机工作异常时,看门狗会产生复位信号,将单片机系统复位。

特征:

  • 独立看门狗是一个递减计数器产生的复位。
  • 时钟信号是有独立RC振荡器产生。
  • 可以在待机和停止模式下运行。
  • 当看门狗被激活后,当递减为0是产生复位。

喂狗:如果在计数没减到 0 之前,重置计数器的值的话,那么就不会产生复位信号,这个动作我们称为喂狗。

作用:检测外界电磁干扰或硬件导致程序跑飞的问题。

二、工作原理

其本质还是一个计数器;
1736263044980.png

根据其工作原理图知:IWDG有一个输入(时钟LSI),为内部专门的 40Khz 低速时钟(LSI)驱动 ,经过分频器进行预分频,分频为工作时钟,然后提供给递减计数器,当递减计数器减为0时产生复位信号。为递减计数器赋值的过程称为喂狗,喂狗的作用就是不产生复位信号。

三、相关寄存器

3.1键寄存器(IWDG_KR)

1736263044980.png

该寄存器共有16位,其作用为控制看门狗的相关操作,由软件进行设置:

主要的操作有三个:

  • 1、0XAAAA进行喂狗,只有这样才能将重装载寄存器的值进行喂狗;
  • 2、0X5555解除写保护,表示允许访问IWDG_PR和IWDG_RLR寄存器;
  • 3、写入入0xCCCC,启动看门狗工作。

3.2预分频寄存器(IWDG_PR)

1736263044980.png

此寄存器只有三位有效,其主要作用就是将来自时钟信号进行分频,从而供给看门狗使用。

3.3重装载寄存器(IWDG_RLR)

1736263044980.png

该寄存器的有效位数为12位,其作用为看门狗计数器的重装载值。每当向IWDG_KR 寄存器写入0xAAAA时,重装载值会被传送到计数器中。随后计数器从这个值开始递减计数。所以说最大的重装载值为2^12,最小的重装载值为1。

3.4状态寄存器(IWDG_SR)

1736263044980.png

该寄存器只有两位,其主要的功能为: RVU: 看门狗计数器重装载值更新;PVU: 看门狗预分频值更新 。

3.5寄存器配置步骤

上述寄存器以及相关的位的配置步骤如下:

1、通过IWDG_KR 置为0xCCCC 使能看门狗;

2、通过IWDG_KR 置为0x5555 使能寄存器访问,可以访问PR与RLT寄存器;

3、通过IWDG_PR 的0-7位为预分频器;

4、通过IWDG_RLR 设置喂狗的值;

5、通过IWDG_SR 判断重装载值以及预分频系数是否更新完;

6、刷新计数器的值IWDG_RLR,再将IWDG_KR置为0XAAA进行反复的喂狗。

四、IWDG配置步骤

4.1、IWDG溢出时间计算

1736263044979.png

寄存器设置分频系数的方法: psc=4 * 2^prer,但最大值只能是256;

4.2、IWDG 的配置步骤

1、取消PR/RLR寄存器写保护。

2、IWDG设置预分频系数和重装载值,启动IWDG。

3、及时喂狗,写入0XAAAA到IWDG_KR。

4.3、IWDG的HAL库驱动

HAL_IWDG_Init 函数,涉及主要的寄存器IWDG_PR/RL/KP,使能IWDG设置预分频系数和重装载值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* @brief Initialize the IWDG according to the specified parameters in the
* IWDG_InitTypeDef and start watchdog. Before exiting function,
* watchdog is refreshed in order to have correct time base.
* @param hiwdg pointer to a IWDG_HandleTypeDef structure that contains
* the configuration information for the specified IWDG module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg)
{
uint32_t tickstart;

/* Check the IWDG handle allocation */
if (hiwdg == NULL)
{
return HAL_ERROR;
}

/* Check the parameters */
assert_param(IS_IWDG_ALL_INSTANCE(hiwdg->Instance));
assert_param(IS_IWDG_PRESCALER(hiwdg->Init.Prescaler));
assert_param(IS_IWDG_RELOAD(hiwdg->Init.Reload));

/* Enable IWDG. LSI is turned on automatically */
__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;

/* Check pending flag, if previous update not done, return timeout */
tickstart = HAL_GetTick();

/* Wait for register to be updated */
while ((hiwdg->Instance->SR & IWDG_KERNEL_UPDATE_FLAGS) != 0x00u)
{
if ((HAL_GetTick() - tickstart) > HAL_IWDG_DEFAULT_TIMEOUT)
{
if ((hiwdg->Instance->SR & IWDG_KERNEL_UPDATE_FLAGS) != 0x00u)
{
return HAL_TIMEOUT;
}
}
}

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

/* Return function status */
return HAL_OK;
}

其形参为结构体指针:IWDG_HandleTypeDef *hiwdg;进一步看主要由三个参数进行定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @brief IWDG Init structure definition
*/
typedef struct
{
uint32_t Prescaler; /*!< Select the prescaler of the IWDG.
This parameter can be a value of @ref IWDG_Prescaler */

uint32_t Reload; /*!< Specifies the IWDG down-counter reload value.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x0FFF */

} IWDG_InitTypeDef;

/**
* @brief IWDG Handle Structure definition
*/
typedef struct
{
IWDG_TypeDef *Instance; /*!< Register base address */

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

HAL_IWDG_Refresh 函数,涉及主要的寄存器IWDG_KR,把重装载寄存器的值重载到计数器中,也就是喂狗。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @brief Refresh the IWDG.
* @param hiwdg pointer to a IWDG_HandleTypeDef structure that contains
* the configuration information for the specified IWDG module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg)
{
/* Reload IWDG counter with value defined in the reload register */
__HAL_IWDG_RELOAD_COUNTER(hiwdg);

/* Return function status */
return HAL_OK;
}

该库函数的参数 结构体指针:IWDG_HandleTypeDef *hiwdg;

五、源码及验证

5.1实验说明

​ 可以看到 LED0 不停的闪烁,证明系统在不停的复位,否则 LED0 常亮。这时我们不停的按 KEY_0按键,可以看到 LED0 就常亮了,不会再闪烁。也就是说不按下KEY0按键时,程序不停地在看门狗复位,按下按键的过程也就是喂狗的过程,及时喂狗打断复位,让LED0长亮。

5.2源码

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/led/led.h"
#include "./BSP/iwdg/IWDG.h"
#include "./BSP/key/key.h"

int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
led_init(); /* LED初始化 */
key_init(); /* KEY初始化 */
delay_ms(100); /* 延时1s,再初始化看门狗,作用是为了使LED0的灯进行可视化 */
iwdg_init(IWDG_PR_PR_2,625); /* 预分频系数为64,重载值为625,看门狗的时间约为1s */
LED1(0); /* 点亮LED0 */
while(1)
{

if (key_scan()==1) /* 如果KEY0 按下,则喂狗 */
{
iwdg_feed(); /* 喂狗 */
}
}
}

IWDG.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "./BSP/iwdg/IWDG.h"
IWDG_HandleTypeDef g_iwdg_handle;//定义独立看门狗句柄;
/**;
* @brief 初始化独立看门狗
* @param prer:预分频系数;
* @arg 分频因子 = 4 * 2^prer. 但最大值只能是256!
* @param rlr: 自动重装载值,0~0XFFF.
* @note 时间计算(大概):Tout=((4 * 2^prer) * rlr) / 40 (ms).
* @retval 无
*/
void iwdg_init(uint8_t prer,uint16_t rlr)
{
g_iwdg_handle.Instance=IWDG;
g_iwdg_handle.Init.Prescaler=prer;
g_iwdg_handle.Init.Reload=rlr;
HAL_IWDG_Init(&g_iwdg_handle);
}
/**;
* @brief 喂狗函数
* @param 无
* @retval 无
*/
void iwdg_feed(void)
{
HAL_IWDG_Refresh(&g_iwdg_handle);

}

key.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "./BSP/key/key.h"
#include "./SYSTEM/delay/delay.h"
/**
* @brief 初始化KEY相关IO口, 并使能时钟
* @param 无
* @retval 无
*/
void key_init(void)
{
GPIO_InitTypeDef gpio_init_struct; //定义GPIO结构体
__HAL_RCC_GPIOE_CLK_ENABLE();//使能GPIOB的时钟;
gpio_init_struct.Pin=GPIO_PIN_4;//设置IO口的引脚;
gpio_init_struct.Pull=GPIO_PULLUP;//设置IO口的工作模式
gpio_init_struct.Mode=GPIO_MODE_INPUT;
gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;//设置IO口的输入速度。
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
}

/**
* @brief 判断按键是否按下
* @param 无
* @retval keyval如果是1就是按键按下,如果是0就是按键未按下
*/

uint8_t key_scan()
{
uint8_t keyval=0;//定义一个返回值,用于判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==0)//判断按键是否按下
{
delay_ms(20);
while(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==1);//判断按下后是否松开
keyval=1;
}
return keyval;

}

led.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "./BSP/led/led.h"

/**;
* @brief 初始化LED函数
* @param 无
* @retval 无
*/
void led_init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIOB的时钟;
GPIO_InitTypeDef gpio_init_struct;//定义GPIO结构体
gpio_init_struct.Mode=GPIO_MODE_OUTPUT_PP; //设置IO口的工作模式
gpio_init_struct.Pin=GPIO_PIN_5;//设置IO口的引脚;
gpio_init_struct.Pull=GPIO_PULLUP;//设置IO口上下;
gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;//设置IO的速度;
HAL_GPIO_Init(GPIOB, &gpio_init_struct);
LED1(1);//关闭LED灯;
}

led.h

1
2
3
4
5
6
7
8
9
10
11
#ifndef __LED_H
#define __LED_H

#include "./SYSTEM/sys/sys.h"
void led_init(void);//初始化LED函数

#define LED1(x) do{ x ? \
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET): \
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET); \
}while(0)//进行宏定义,通过LED1(x),设置一个三目运算符,如果是1就关闭LED,如果是0就打开LED.
#endif

本文作者: zhangJinLong
本文链接: https://zhang426fly.github.io/2025/07/05/stm32/IWDG简介/
版权声明: 本博客所有文章除特别声明外,均采用BY-NC-SA许可协议。转载请注明出处!


STM32独立看门狗
https://zhang426fly.github.io/2025/07/05/stm32/IWDG简介/
作者
zhangJinLong
发布于
2025年7月5日
许可协议