看门狗概述
看门狗定时器是一个计数器,其基本功能是发生软件问题及程序跑飞后使系统重新启动。看门狗正常启动时自动计数,程序定期将其复位清零,如果系统某处跑飞或者卡死,定时器溢出并进入中断,在定时器中断中执行复位操作,使系统恢复正常工作状态。
stm32有两个看门狗,独立看门狗和窗口看门狗,下面会较为详细地分别讲解两者。
独立看门狗
STM32 的独立看门狗由内部专门的 40Khz(因为使用的是内部RC时钟,所以并不是准确的40Khz) 低速时钟驱动,即使主时钟发生故障,它也仍然有效。其限制喂狗时间在0-x内,x由相关寄存器决定。喂狗的时间不能过晚。
独立看门狗主要用到下面几个寄存器:
键值寄存器 IWDG_KR 有效位0-15
预分频寄存器 IWDG_PR 有效位0-2 //具有写保护功能,要操作先取消写保护
重装载寄存器 IWDR_RLR 有效位0-11 //具有写保护功能,要操作先取消写保护
状态寄存器 IWDG_SR 有效位0-1 //读取一些看门狗的状态 重装载值更新,预分频值更新
下面放几个手册里的寄存器描述图(对描述图的理解能帮助我们明白如何使用库函数以及使用哪些)
键值寄存器IWDG_KR
预分频寄存器 IWDG_PR
重装载寄存器
独立看门狗溢出时间计算
Tout =(4*2^prer)*rlr / 40
具体怎么算如果还是没法理解的话可以看看这个博客,讲解的很详细:
stm32系列芯片独立看门狗(IWDG)溢出时间计算原理
独立看门狗操作步骤
取消寄存器写保护
设置独立看门狗的预分频系数,确定时钟
设置看门狗重装载值,确定溢出时间
使能看门狗
应用程序喂狗
程序示例:
iwdg.h文件
#ifndef __IWDG_H__
#define __IWDG_H__
#include "sys.h"
//看门狗初始化
void IWDG_Init(u8 prer, u16 value);
//喂狗
void IWDG_Feed(void);
#endif
iwdg.c文件
#include "iwdg.h"
/*看门狗初始化*/
void IWDG_Init(u8 prer, u16 value)
{
//取消写访问保护 写入0X5555
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
//设置预分频
IWDG_SetPrescaler(prer);
//设置IWDG装载值
IWDG_SetReload(value);
//重新装载 写入0XAAAA
IWDG_ReloadCounter();
//使能看门狗 写入0XCCCC
IWDG_Enable();
}
//喂狗
void IWDG_Feed(void)
{
//重新装载
IWDG_ReloadCounter();
}
main.c文件
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "iwdg.h"
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M
delay_init(72); //初始化延时函数
uart_init(115200); //初始化串口
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
delay_ms(100); //延时100ms再初始化看门狗,可以看到LED0的变化
IWDG_Init(IWDG_PRESCALER_64,500); //分频数为64,重载值为500,溢出时间为1s
LED0 = 0;
while(1)
{
f(KEY_Scan(0)==WKUP_PRES) //如果WK_UP按下,喂狗
{
IWDG_Feed(); //喂狗
}
delay_ms(10);
}
}
窗口看门狗
大体功能与独立看门狗类似。窗口是因为其喂狗时间在一个有上下限的范围内,通过设定相关寄存器,设定其上限时间,下限时间是固定的。喂狗的时间不能过早也不能过晚。
直线最顶端的点T[6:0]是我们要设置的递减计数器值
中间的W[6:0]是我们通过配置寄存器 WWDG_CFR设置的 上窗口边界(未达上窗口边界喂狗会导致系统复位)
下面的3Fh处其实是一个与3Fh接近的固定值0x40,也就是下窗口边界,当计数值下降到0x40时仍不重装载,系统便会复位
窗口看门狗主要用到下面几个寄存器:
控制寄存器 WWDG_CR
配置寄存器 WWDG_CFR
状态寄存器 WWDG_SR //硬件置1 喂狗
同样贴寄存器描述图
窗口看门狗溢出时间计算
Twwdg = 4096 * (2^WDGTB)*(T[5:0] +1)/Fpclk1
窗口看门狗操作步骤
使能看门狗时钟
设置预分频系数
设置上窗口值
开启提前唤醒中断并分组(也可以不写)
使能看门狗
喂狗
编写中断服务函数
程序示例:
/*保存WWDG计数器的设置值,默认为最大. */
static u8 WWDG_CNT=0x7f;
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); //WWDG时钟使能
WWDG_SetPrescaler(fprer); //设置IWDG预分频值
WWDG_SetWindowValue(wr); //设置窗口值
WWDG_CNT=tr&WWDG_CNT; //初始化WWDG_CNT,tr :T[6:0],计数器值
WWDG_Enable(WWDG_CNT); //使能看门狗 , 设置 counter
WWDG_ClearFlag(); //清除提前唤醒中断标志位
WWDG_NVIC_Init(); //初始化窗口看门狗 NVIC
WWDG_EnableIT(); //开启窗口看门狗中断
}
/*窗口看门狗中断服务程序*/
void WWDG_NVIC_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG中断
/* 抢占2,子优先级3 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_Init(&NVIC_InitStructure);//NVIC初始化
}
/*重设置WWDG计数器的值*/
void WWDG_Set_Counter(u8 cnt)
{
WWDG_Enable(cnt); //使能看门狗 , 设置 counter
}
/*看门狗中断服务程序 */
void WWDG_IRQHandler(void)
{
WWDG_Set_Counter(WWDG_CNT);
WWDG_ClearFlag(); //清除提前唤醒中断标志位
LED1 = ~LED1; //LED状态翻转 ,检测效果
}
其它
从这个图可以更深入地理解看门狗工作过程
对于窗口看门狗中其为地递减计数器T[6:0],在下述两种情况之一时产生看门狗复位:
1.当喂狗时如果计数器值大于某一设定数值W[6:0]时。此设定数值在配置寄存器 WWDG_CFR中定义。
2.当计数器数值从0x40减到0x3F时(T6位变跳到0)由1000000变为0000000
ps:上窗口值(W[6:0])是由用户自己设定的,确保窗口值大于 0X40.
如果启动看门狗且允许终端,0x40时会产生早期唤醒中断(EWI),用于喂狗。
独立看门狗适用于需要看门狗作为一个主程序之外能够独立工作,并且对时间精度要求低的场合
窗口看门狗适用于要求看门狗在精确计时窗口起作用的程序