STM32—窗口看门狗(WWDG)


窗口看门狗(WWDG)

  • STM32—窗口看门狗(WWDG)
  • 窗口看门狗(WWDG)
  • WWDG 简介
  • WWDG 功能框图剖析
  • 窗口看门狗时钟
  • 计数器时钟
  • 计数器
  • 窗口值
  • 计算看门狗超时时间
  • 怎么用 WWDG
  • 独立看门狗所用到的库函数
  • 窗口看门狗操作步骤
  • 相关代码如下

  • 窗口看门狗(WWDG)

    大家想要了解独立看门狗(IWDG)的也可以去看我的另一篇文章——》 独立看门狗(IWDG)

    WWDG 简介

    STM32 有两个看门狗,一个是独立看门狗,一个是窗口看门狗。我们知道独立看门狗的工作原
    理就是一个递减计数器不断的往下递减计数,当减到 0 之前如果没有喂狗的话,产生复位。窗口
    看门狗跟独立看门狗一样,也是一个递减计数器不断的往下递减计数,当减到一个固定值 0X40 时还不喂狗的话,产生复位,这个值叫窗口的下限,是固定的值,不能改变。这个是跟独立看门
    狗类似的地方,不同的地方是窗口看门狗的计数器的值在减到某一个数之前喂狗的话也会产生 复位,这个值叫窗口的上限,上限值由用户独立设置。窗口看门狗计数器的值必须在上窗口和下
    窗口之间才可以喂狗,这就是窗口看门狗中窗口两个字的含义

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

    WWDG 功能框图剖析

    窗口看门狗时钟

    窗口看门狗是用APB1总线驱动的,时钟频率最大36MHz
    说起窗口看门狗,其实它的基本原理和独立看门狗是一样的,只不过我们又在喂狗的时间上做了一些手脚,在独立看门狗中,我们可以从计数器的值一直减到0的中间的任何时刻都可以进行喂狗,但是在窗口看门狗中,我们规定只能在某一个时间段内进行喂狗,举个栗子,假如说我们计数器是从100减到0,我们现在规定只能在计数器为80—30这个段内进行喂狗(这也就是窗口这个名词的来源),否则我们看门狗就会产生一个复位信号,从而使单片机复位,从这可以看出来,窗口看门狗的安全性比独立看门狗要高一些,毕竟它喂狗的时间段缩短了,这样就会降低由于程序跑飞而误打误撞的去执行喂狗操作的几率了。

    计数器时钟

    计数器时钟由 CK 计时器时钟经过预分频器分频得到,分频系数由配置寄存器 CFR 的位8:7 WDGTB[1:0] 配置,可以是 [0,1,2,3],其中 CK 计时器时钟 =PCLK1/4096,除以 4096 是手册规定的,没有为什么。所以计数器的时钟 CNT_CK=PCLK1/4096/(2^WDGTB),这就可以算出计数器减一个数的时间 T= 1/CNT_CK = Tpclk1 * 4096 *(2^WDGTB)。

    计数器

    窗口看门狗的计数器是一个递减计数器,共有 7 位,其值存在控制寄存器 CR 的位 6:0,即 T[6:0],
    当 7 个位全部为 1 时是 0X7F,这个是最大值,当递减到 T6 位变成 0 时,即从 0X40 变为 0X3F 时候,会产生看门狗复位。这个值 0X40 是看门狗能够递减到的最小值,所以计数器的值只能是:0X40~0X7F 之间,实际上真正用来计数的是 T[5:0]。当递减计数器递减到 0X40 的时候,还不会马上产生复位,如果使能了提前唤醒中断: CFR 位 9EWI 置 1,则产生提前唤醒中断,如果真进入了这个中断的话,就说明程序肯定是出问题了,那么在中断服务程序里面我们就需要做最重要的工作,比如保存重要数据,或者报警等,这个中断我们也叫它死前中断。

    窗口值

    我们知道窗口看门狗必须在计数器的值在一个范围内才可以喂狗,其中下窗口的值是固定的
    0X40,上窗口的值可以改变,具体的由配置寄存器 CFR 的位 6:0 W[6:0] 设置。其值必须大于
    0X40,如果小于或者等于 0X40 就是失去了窗口的价值,而且也不能大于计数器的值,所以必须
    **得小于 0X7F。**那窗口值具体要设置成多大?这个得根据我们需要监控的程序的运行时间来决定。
    如果我们要监控的程序段 A 运行的时间为 Ta,当执行完这段程序之后就要进行喂狗,如果在窗
    口时间内没有喂狗的话,那程序就肯定是出问题了。一般计数器的值 TR 设置成最大 0X7F,窗
    口值为 WR,计数器减一个数的时间为 T,那么时间: (TR-WR)*T 应该稍微小于 Ta 即可,这样
    就能做到刚执行完程序段 A 之后喂狗,起到监控的作用,这样也就可以算出 WR 的值是多少。

    计算看门狗超时时间

    这个图来自数据手册,从图我们知道看门狗超时时间: Twwdg = Tpclk1 x 4096 x 2^wdgtb x (T[5:0]+ 1) ms,当PCLK1 = 36MHZ 时, WDGTB 取不同的值时有最小和最大的超时时间,那这个最小和最大的超时时间该怎么理解,又是怎么算出来的?讲起来有点绕,这里我稍微讲解下 WDGTB=0时是怎么算的。递减计数器有 7 位 T[6:0] ,当位 6 变为 0 的时候就会产生复位,实际上有效的计数位是 T[5:0],而且 T6 必须先设置为 1。如果 T[5:0]=0 时,递减计数器再减一次,就产生复位了,那这减一的时间就等于计数器的周期 =1/CNT_CK = Tpclk1 * 4096 2^WDGTB) = 1/36 * 40962^0 =113.7us,这个就是最短的超时时间。如果 T[5:0] 全部装满为 1,即 63,当他减到 0X40 变成 0X3F 时,所需的时间就是最大的超时时间 =113.72^5=113.764=7.2768ms。同理,当 WDGTB等于 1/2/3 时,代入公式即可。

    怎么用 WWDG

    WWDG 一般被用来监测,由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。比如一个程序段正常运行的时间是 50ms,在运行完这个段程序之后紧接着进行喂狗,如果在规定的时间窗口内还没有喂狗,那就说明我们监控的程序出故障了,跑飞了,那么就会产生系统复位,让程序重新运行。

    独立看门狗所用到的库函数

    void WWDG_DeInit(void);
    void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
    void WWDG_SetWindowValue(uint8_t WindowValue);
    void WWDG_EnableIT(void);
    void WWDG_SetCounter(uint8_t Counter);
    void WWDG_Enable(uint8_t Counter);
    FlagStatus WWDG_GetFlagStatus(void);
    void WWDG_ClearFlag(void);
    
    

    窗口看门狗操作步骤

    1) 使能 WWDG 时钟
    WWDG 不同于 IWDG, IWDG 有自己独立的 40Khz 时钟,不存在使能问题。而 WWDG
    使用的是 PCLK1 的时钟,需要先使能时钟。 方法是:

    void RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WWDG 时钟使能
    

    2) 设置窗口值和分频数
    设置窗口值的函数是:

    void WWDG_SetWindowValue(uint8_t WindowValue);
    

    这个函数的入口参数 WindowValue 用来设置看门狗的上窗口值。
    设置分频数的函数是:

    void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
    

    这个函数同样只有一个入口参数,用来设置看门狗的分频值。
    3) 开启 WWDG 中断并分组
    开启 WWDG 中断的函数为:

    void WWDG_EnableIT(); //开启窗口看门狗中断
    

    接下来是进行中断优先级配置,这里就不重复了,使用 NVIC_Init()函数即可。
    4) 设置计数器初始值并使能看门狗
    这一步在库函数里面是通过一个函数实现的:

    void WWDG_Enable(uint8_t Counter);
    

    该函数既设置了计数器初始值,同时使能了窗口看门狗。
    5) 编写中断服务函数
    在最后,还是要编写窗口看门狗的中断服务函数,通过该函数来喂狗,喂狗要快,否则当
    窗口看门狗计数器值减到 0X3F 的时候,就会引起软复位了。在中断服务函数里面也要将状态
    寄存器的 EWIF 位清空。
    完成了以上 5 个步骤之后,我们就可以使用 STM32 的窗口看门狗了。

    相关代码如下

    wdg.h

    #ifndef __WDG_H
    #define __WDG_H
    #include "sys.h"
    //独立看门狗
    void IWDG_Init(u8 prer,u16 rlr);
    void IWDG_Feed(void);
    //窗口看门狗
    void WWDG_Init(u8 tr,u8 wr,u32 fprer);//初始化WWDG
    void WWDG_Set_Counter(u8 cnt);       //设置WWDG的计数器
    void WWDG_NVIC_Init(void);
    #endif
    
    

    wdg.c

    #include "wdg.h"
    #include "led.h"
    
    //窗口看门狗
    //保存WWDG计数器的设置值,默认为最大. 
    u8 WWDG_CNT=0x7f; 
    //初始化窗口看门狗 	
    //tr   :T[6:0],计数器值 
    //wr   :W[6:0],窗口值 
    //fprer:分频系数(WDGTB),仅最低2位有效 
    //Fwwdg=PCLK1/(4096*2^fprer). 
    void WWDG_Init(u8 tr,u8 wr,u32 fprer)
    { 
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);  //   WWDG时钟使能
    
    	WWDG_CNT=tr&WWDG_CNT;   //初始化WWDG_CNT.   
    	WWDG_SetPrescaler(fprer);设置IWDG预分频值
    
    	WWDG_SetWindowValue(wr);//设置窗口值
    
    	WWDG_Enable(WWDG_CNT);	 //使能看门狗 ,	设置 counter .                  
    
    	WWDG_ClearFlag();//清除提前唤醒中断标志位 
    
    	WWDG_NVIC_Init();//初始化窗口看门狗 NVIC
    
    	WWDG_EnableIT(); //开启窗口看门狗中断
    } 
    //重设置WWDG计数器的值
    void WWDG_Set_Counter(u8 cnt)
    {
        WWDG_Enable(cnt);//使能看门狗 ,	设置 counter .	 
    }
    //窗口看门狗中断服务程序
    void WWDG_NVIC_Init()
    {
    	NVIC_InitTypeDef NVIC_InitStructure;
    	NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;    //WWDG中断
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;   //抢占2,子优先级3,组2	
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;	 //抢占2,子优先级3,组2	
      NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; 
    	NVIC_Init(&NVIC_InitStructure);//NVIC初始化
    }
    
    void WWDG_IRQHandler(void)
    {
    
    	WWDG_SetCounter(WWDG_CNT);	  //当禁掉此句后,窗口看门狗将产生复位
    
    	WWDG_ClearFlag();	  //清除提前唤醒中断标志位
    
    	LED1=!LED1;		 //LED状态翻转
    }
    
    
    

    main.c

    通过 LED0 来指示 STM32 是否被复位了,如果被复位了就会点亮 500ms。LED0 用来指示中
    断喂狗,每次中断喂狗翻转一次。

    #include "led.h"
    #include "delay.h"
    #include "key.h"
    #include "sys.h"
    #include "usart.h"
    #include "wdg.h"
     
    int main(void)
    {		
    	delay_init();	    	 //延时函数初始化	  
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
    	uart_init(115200);	 //串口初始化为115200
     	LED_Init();
    	KEY_Init();          //按键初始化	 
    	LED0=0;
    	delay_ms(500);	  
    	WWDG_Init(0X7F,0X5F,WWDG_Prescaler_8);//计数器值为7f,窗口寄存器为5f,分频数为8	   
     	while(1)
    	{
    		LED0=1;			  	   
    	}   
    }
    

    给那些看完的朋友,奖励一个 赤赤博客-后端+前端,觉得不错的话可以推荐给身边的朋友哟!

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32—窗口看门狗(WWDG)

    发表评论