文章目录

  • 基础知识
  • 简介
  • 作用
  • 工作原理
  • 寄存器
  • 寄存器简介
  • 寄存器操作步骤
  • IWDG溢出时间计算
  • IWDG 配置步骤
  • HAL库相关函数介绍
  • 步骤
  • 实验


基础知识

简介

  • IWDG : Independent watchdog, 独立看门狗
  • IWDG本质是能产生系统复位信号的计数器
  • 特性:
  1. 递减计数器
  2. 时钟由独立的RC振荡器提供(可在待机和停止模式下运行)
  3. 激活后,当递减计数器计数到0x00时产生复位
  • 喂狗 :在计数器计数到0之前,重装载计数器的值,防止复位。

作用

  • 单片机异常:外界电磁干扰或者自身系统异常造成程序跑飞
  • 作用:WDG可以用于检测外侧电磁干扰或者硬件异常导致的程序跑飞
  • 应用:在一些需要高稳定性的产品中,并且对时间精度要求较低
    是异常处理的最后手段,不可依赖

工作原理

Java 看门狗机制原理 看门狗原理和应用_嵌入式硬件

  • 系统框图

    IWDG 有一个输入(时钟 LSI),经过一个 8 位的可编程预分频器提供时钟给一个 12 位递减计数器,满足条件就会输出一个复位信号(iwdg1_out_rst)
    STM32F407 的独立看门狗由内部专门的 32Khz 低速时钟(lsi_ck)驱动,即使主时钟发生故障,它也仍然有效。这里需要注意独立看门狗的时钟是一个内部 RC 时钟,所以并不是准确的 32Khz,而是在 17~47Khz 之间的一个可变化的时钟,只是我们在估算的时候,以 32Khz 的频率来计算,看门狗对时间的要求不是很精确,所以,时钟有些偏差。

寄存器

寄存器简介

  • 关键字寄存器 IWDG_KR
  • Java 看门狗机制原理 看门狗原理和应用_寄存器_02

  • 预分频寄存器 IWDG_PR
  • Java 看门狗机制原理 看门狗原理和应用_寄存器_03

  • 该寄存器用来设置看门狗时钟(LSI)的分频系数,最低为 4,最高为 256,该寄存器是一个 32 位的寄存器,但是我们只用了最低 3 位,其他都是保留位。
  • 重装载寄存器 IWDG_RLR
  • Java 看门狗机制原理 看门狗原理和应用_stm32_04

  • 该寄存器用来保存重装载到计数器中的值。该寄存器也是一个 32 位寄存器,只有低 12 位是有效的。
  • 状态寄存器 IWDG_SR
  • Java 看门狗机制原理 看门狗原理和应用_嵌入式硬件_05

寄存器操作步骤

  1. 通过在键寄存器 (IWDG_KR) 中写入 0xCCCC 来使能 IWDG。
  2. 通过在键寄存器 (IWDG_KR) 中写入 0x5555 来使能寄存器访问。
  3. 通过将预分频器寄存器 (IWDG_PR) 编程为 0~7 中的数值来配置预分频器。
  4. 对重载寄存器 (IWDG_RLR) 进行写操作。
  5. 等待寄存器更新 (IWDG_SR = 0x0000 0000)。
  6. 刷新计数器值为 IWDG_RLR 的值 (IWDG_KR = 0xAAAA)。

IWDG溢出时间计算

  • 计算公式



    ——"Tout"是看门狗溢出时间
    ——f𝐼𝑊𝐷𝐺是看门狗的时钟源频率
    ——psc是看门狗预分频系数
    ——rlr是看门狗重装载值
    ——prer是IWDG_PR 的值
  • IWDG 最短最长的超时时间

IWDG 配置步骤

HAL库相关函数介绍

  • 初始化函数
HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg);
/* 关键结构体 */
typedef struct 
{ 
   IWDG_TypeDef *Instance; /* IWDG 寄存器基地址 */
   IWDG_InitTypeDef Init;       /* IWDG 初始化参数 */
}IWDG_HandleTypeDef;

typedef struct
{ 
    uint32_t Prescaler;  /* 预分频系数 */ 
    uint32_t Reload;       /* 重装载值 */ 
} IWDG_InitTypeDef;
  • 喂狗函数
HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);

步骤

  1. 取消寄存器写保护,设置看门狗预分频系数和重装载值
    取消写保护和设置预分频系数以及重装载值在 HAL 库中是通过函数 HAL_IWDG_Init 实现的。
    看门狗的时钟不是准确的32Khz,所以在喂狗的时候,最好不要太晚了,否则,有可能发生看门狗复位。
  2. 重载计数值喂狗(向 IWDG_KR 写入 0xAAAA)
    在 HAL 中重载计数值的函数是 HAL_IWDG_Refresh
  3. 启动看门狗(向 IWDG_KR 写入 0xCCCC)
    HAL 库函数里面启动独立看门狗是通过宏定义标识符来实现
#define __HAL_IWDG_START(__HANDLE__) 
WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_ENABLE);

实验

Java 看门狗机制原理 看门狗原理和应用_嵌入式硬件_06

  • wdg.h
#ifndef __WDG_H
#define __WDG_H

#include "./SYSTEM/sys/sys.h"


void iwdg_init(uint32_t prer, uint16_t rlr);        /* 初始化IWDG,并使能IWDG */
void iwdg_feed(void);                               /* 喂狗 */

#endif
  • wdg.c
#include "./BSP/WDG/wdg.h"


IWDG_HandleTypeDef iwdg_handler; /*独立看门狗句柄 */

/**
 * @brief       初始化独立看门狗 
 * @param       prer: IWDG_PRESCALER_4~IWDG_PRESCALER_256,对应4~256分频
 *   @arg       分频因子 = 4 * 2^prer. 但最大值只能是256!
 * @param       rlr: 自动重装载值,0~0XFFF. 
 * @note        时间计算(大概):Tout=((4 * 2^prer) * rlr) / 32 (ms). 
 * @retval      无
 */
void iwdg_init(uint32_t prer, uint16_t rlr)
{
    iwdg_handler.Instance = IWDG;
    iwdg_handler.Init.Prescaler = prer; /* 设置IWDG分频系数 */
    iwdg_handler.Init.Reload = rlr;     /* 从加载寄存器 IWDG->RLR 重装载值 */
    HAL_IWDG_Init(&iwdg_handler);       /* 初始化IWDG并使能 */
}

/**
 * @brief       喂独立看门狗
 * @param       无
 * @retval      无
 */
void iwdg_feed(void)
{
    HAL_IWDG_Refresh(&iwdg_handler);    /* 喂狗 */
}
  • main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/KEY.h"
#include "./BSP/WDG/wdg.h"


int main(void)
{    
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    key_init();                             /* 初始化按键 */
    delay_ms(100);                          /* 延时100ms再初始化看门狗,LED0的变化"可见" */
    printf("您还没喂狗,请及时喂狗!\r\n");
    iwdg_init(IWDG_PRESCALER_64, 500);      /* 预分频数为64,重载值为500,溢出时间约为1s */
    LED0(0);                              /* 点亮LED0(红灯) */    
    while(1)
    {
       if (key_scan(1) == WKUP_PRES)        /* 如果WK_UP按下,则喂狗,支持连按 */
        {
            iwdg_feed();                    /* 喂狗 */
            printf("您已喂狗!\r\n");
        }

        delay_ms(10);
    }
}