【STM32】看门狗

1.看门狗简介

看门狗起始就是一个定时器,从功能上说它可以让微控制器在程序发生意外(程序进入死循环或跑飞)的时候,能重新恢复到系统刚上电状态,以保障系统出问题的时候可以重启一次。说的简单一点,看门狗就是能让程序出问题时能重新启动系统。STM32有两个看门狗,独立看门狗和窗口看门狗。

1.1独立看门狗(Independent Watchdog,IWDG)

独立看门狗号称宠物狗,它有一个12位的递减计数器,当计数器的值从某个值一直减到0的时候,系统就会产生一个复位信号,即IWDG_RESET。如果在计数器没有减到0之前,刷新了计数器的值,那么就不会产生复位信号,这个动作就是我们经常说的喂狗。

1.2独立看门狗时钟

独立看门狗的时候由独立的RC振荡器LSI提供,即使主时钟发生故障它仍然有效,非常独立。LSI的频率一般在30~60KHz之间,所以独立看门狗的定时时间并不一定非常准确,只适用于对时间精度要求比较低的场合。

上图中,配置的IWDG时钟CK_IWDG=32KHz。

1.3独立看门狗计数器时钟

递减计数器的时钟由LSI经过一个8位的预分频器得到,我们可以操作预分频器寄存器IWDG_PR来设置分频因子,分频因子可以是:[4,8,16,32,64,128,256],计数器时钟CK_CNT=CK_IWDG/IWDG_PR。

1.4重装载寄存器

重装载寄存器是一个12位的寄存器,里面装着要刷新到计数器的值,这个值的大小决定着独立看门狗的溢出时间。超时时间Tout=1/CK_CNT*rlv,rlv是重装载寄存器的值。

若IWDG_PR=32,rlv=2000,所以CK_CNT=32KHz/32=1KHz,Tout=1/1000*2000=2s。意味着2s之内我们就得喂狗,不然系统就会重启。

1.5标准库演示
c 复制代码
#ifndef __BSP_IWDG_H
#define __BSP_IWDG_H

#ifdef __cplusplus
extern "C"{

#endif

#include "stm32f4xx.h"

void Init_IWDG(uint8_t prv,uint16_t rlv);
void IWDG_Feed(void);
	
#ifdef __cplusplus
}
#endif

#endifc
c 复制代码
#include "bsp_iwdg.h"

/*
* 设置 IWDG 的超时时间
* Tout = prv/LSICLK * rlv (s)
*      prv可以是[4,8,16,32,64,128,256]
* prv:预分频器值,取值如下:
*     @arg IWDG_Prescaler_4: IWDG prescaler set to 4
*     @arg IWDG_Prescaler_8: IWDG prescaler set to 8
*     @arg IWDG_Prescaler_16: IWDG prescaler set to 16
*     @arg IWDG_Prescaler_32: IWDG prescaler set to 32
*     @arg IWDG_Prescaler_64: IWDG prescaler set to 64
*     @arg IWDG_Prescaler_128: IWDG prescaler set to 128
*     @arg IWDG_Prescaler_256: IWDG prescaler set to 256
*
*    独立看门狗使用LSI作为时钟。
*    LSI 的频率一般在 30~60KHZ 之间,根据温度和工作场合会有一定的漂移,我
*    们的STM32F407中为32KHz,所以独立看门狗的定时时间并不一定非常精确,只适用于对时间精度
*    要求比较低的场合。
*
* rlv:预分频器值,取值范围为:0-0XFFF
* 函数调用举例:
* Init_IWDG(IWDG_Prescaler_32 ,1000);  // IWDG 1s 超时溢出
*            (32/LSICLK)*1000 = 1s
*/
void Init_IWDG(uint8_t prv,uint16_t rlv)
{
	// 使能 预分频寄存器PR和重装载寄存器RLR可写
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
	
	// 设置预分频器值
	IWDG_SetPrescaler(prv);
	
	// 设置重装载寄存器值
	IWDG_SetReload(rlv);
	
	// 把重装载寄存器的值放到计数器中
	IWDG_ReloadCounter();
	
	// 使能 IWDG
	IWDG_Enable();
}

void IWDG_Feed(void)
{
    // 把重装载寄存器的值放到计数器中,喂狗,防止IWDG复位
    // 当计数器的值减到0的时候会产生系统复位
    IWDG_ReloadCounter();
}
c 复制代码
int main(void)
{
	Init_LED();
	
//	//设置中断分组
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//	
//	Init_USART();
	
	Init_IWDG(IWDG_Prescaler_32,1000);
	
  /* Infinite loop */
  while (1)
  {
  }
}

上述代码思路为:Init_LED()中会将LED点亮,然后开启独立看门狗,设置的溢出时间为1s,那么1s后由于没有喂狗,mcu会复位,LED也会灭,但是由于时间太快了,肉眼应该无法看到。所以,我们使用了示波器进行测试LED的输入电平。

c 复制代码
int main(void)
{
	Init_LED();
	
//	//设置中断分组
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//	
//	Init_USART();
	
	Init_IWDG(IWDG_Prescaler_32,1000);
	
  /* Infinite loop */
  while (1)
  {
	  IWDG_Feed();
  }
}

如果我们在巡航中一直喂狗,那么mcu就不会复位。

2.窗口看门狗(Window Watchdog,WWDG)

窗口看门狗号称警犬,它也有一个递减计数器不断的往下递减计数,当减到一个固定值0x40时还不喂狗的话,就会产生复位,这个值叫窗口的下限,是固定值,不能改变。不同的是,窗口看门狗的计数器在减到某一个数之前喂狗也会产生复位,这个值叫窗口的上限,上限值由用户独立设置。窗口看门狗计数器的值必须在上窗口和下窗口之间才可以喂狗,这就是窗口看门狗中窗口两个字的含义。

RLR是重装载寄存器,用来设置独立看门狗的计数器的值。TR是窗口看门狗的计数器的值,由用户独立设置,WR是窗口看门狗的上窗口值,由用户独立设置。

在出现下述两种情况之一时产生看门狗复位:

  1. 当喂狗的时候如果计数器的值大于窗口上限值。
  2. 当计数器的数值从0x40减到0x3F。

如果启动了看门狗并且使能中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),这个中断我们称它为死前中断或者叫遗嘱中断, 在中断函数里面我们应该出来最重要的事情,而且必须得快,因为递减计数器再减一次,就会产生系统复位。

注意事项:

  1. 上限值必须大于0x40,否则就无窗口了。
  2. 窗口看门狗时钟来源PCLK1(APB1总线时钟)分频后。
2.1标准库演示
c 复制代码
#ifndef __BSP_WWDG_H
#define __BSP_WWDG_H

#ifdef __cplusplus
extern "C"{

#endif

#include "stm32f4xx.h"

#define WWDG_CNT    0x7F

void Init_WWDG(uint8_t tr, uint8_t wr, uint32_t prv);
void WWDG_Feed(void);

#ifdef __cplusplus
}
#endif

#endif
c 复制代码
#include "bsp_wwdg.h"
#include "stdio.h"

/* WWDG 配置函数
* tr :递减计时器的值, 取值范围为:0x7f~0x40
* wr :窗口值,取值范围为:0x7f~0x40
* prv:预分频器值,取值可以是
*      @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1/4096)/1
*      @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1/4096)/2
*      @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1/4096)/4
*      @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1/4096)/8
*/
void Init_WWDG(uint8_t tr, uint8_t wr, uint32_t prv)
{
	//使能窗口看门狗时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
	
	// 设置预分频器值
	WWDG_SetPrescaler(prv);
	
	// 设置重装载寄存器值
	WWDG_SetWindowValue(wr);
	
	// 使能 WWDG
	WWDG_Enable(WWDG_CNT&tr);
	
	//配置中断控制器并使能中断
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=WWDG_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStruct);
	
	WWDG_ClearFlag();//清楚标志位
	WWDG_EnableIT();//使能中断
}

void WWDG_IRQHandler(void)
{
	WWDG_SetCounter(WWDG_CNT);
	WWDG_ClearFlag();
}

void WWDG_Feed(void)
{
	printf("WWDG_Feed\r\n");
    // 把重装载寄存器的值放到计数器中,
    WWDG_SetCounter(WWDG_CNT);
}
c 复制代码
int main(void)
{
	Init_USART();
	
	Init_LED();
	
	//设置中断分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
		
	Init_WWDG(0x7f,0x5f,WWDG_Prescaler_8);
	
	printf("hello,this is stm32f407\r\n");
  /* Infinite loop */
  while (1)
  {
        //-----------------------------------------------------
        // 这部分应该写需要被WWDG监控的程序,这段程序运行的时间
        // 决定了窗口值应该设置成多大。
        //-----------------------------------------------------
      
	    // 计时器值,初始化成最大0X7F,当开启WWDG时候,这个值会不断减小
        // 当计数器的值大于窗口值时喂狗的话,会复位,当计数器减少到0X40
        // 还没有喂狗的话就非常非常危险了,计数器再减一次到了0X3F时就复位
        // 所以要当计数器的值在窗口值和0X40之间的时候喂狗,其中0X40是固定的。
        if ( (WWDG->CR & 0X7F) < 0x5f )
		{
            // 喂狗,重新设置计数器的值为最大0X7F
            WWDG_Feed();
        }
  }
}

注意,我们试过了在死前中断中喂狗,但是好像来不及,mcu还是重启了。所以,我们在main函数中进行了计数判断喂狗。

主函数中我们把WWDG的计数器的值设置 为0X7F,上窗口值设置为0X5F,分频系数为8分频。在while死循环中,我们不断读取计数器的值, 当计数器的值减小到小于上窗口值的时候,我们喂狗,让计数器重新计数。

在while死循环中,一般是我们需要监控的程序,这部分代码的运行时间,决定了上窗口值应该设置为多少,当监控的程序运行完毕之后, 我们需要执行喂狗程序,比起独立看门狗,这个喂狗的窗口时间是非常短的,对时间要求很精确。如果没有在这个窗口时间内喂狗的话, 那就说明程序出故障了,会产生提前唤醒中断,最后系统复位。

相关推荐
嵌入式大圣7 分钟前
单片机结合OpenCV
单片机·嵌入式硬件·opencv
日晨难再2 小时前
嵌入式:STM32的启动(Startup)文件解析
stm32·单片机·嵌入式硬件
yufengxinpian2 小时前
集成了高性能ARM Cortex-M0+处理器的一款SimpleLink 2.4 GHz无线模块-RF-BM-2340B1
单片机·嵌入式硬件·音视频·智能硬件
__基本操作__3 小时前
历遍单片机下的IIC设备[ESP--0]
单片机·嵌入式硬件
网易独家音乐人Mike Zhou9 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
zy张起灵9 小时前
48v72v-100v转12v 10A大功率转换电源方案CSM3100SK
经验分享·嵌入式硬件·硬件工程
PegasusYu12 小时前
STM32CUBEIDE FreeRTOS操作教程(九):eventgroup事件标志组
stm32·教程·rtos·stm32cubeide·free-rtos·eventgroup·时间标志组
lantiandianzi16 小时前
基于单片机的多功能跑步机控制系统
单片机·嵌入式硬件
文弱书生65616 小时前
输出比较简介
stm32
哔哥哔特商务网17 小时前
高集成的MCU方案已成电机应用趋势?
单片机·嵌入式硬件