由于单片机会受到来自外界电磁场的干扰,造成程序跑飞使系统陷入死循环,造成不可预知的后果
于是有了用于检测单片机程序运行状态的模块或芯片,叫做”看门狗”(watchdog)
看门狗作用:检测系统,当系统跑飞时复位系统,重新执行程序
STM32有两个看门狗:独立看门狗和窗口看门狗
独立看门狗:
驱动:由LSI驱动,即使主时钟发生故障它仍有效
适用场景:
作为主程序之外能够完全独立工作
对时间精度要求较低
窗口看门狗:
驱动:由APB1时钟分频后得到时钟驱动
适用场景:
在精确计时窗口起作用的程序
启动:键值寄存器IWDG_KR写入0xCCCC开始启用独立看门狗
过程:计时器从重装载值(由寄存器IWDG_RLR配置,默认0xFFF)开始递减,当递减到0x000时产生复位信号IWDG_RESET
喂狗:键值寄存器IWDG_KR写入0xAAAA,重装载寄存器IWDG_RLR的值会重新加载到递减计数器,避免看门狗复位
复位:如果程序异常跑飞,不能完成喂狗,系统复位
如图:
独立看门狗时钟LSI通过预分频(IWDG_PR)
向减值寄存器IWDG_KR写入0xCCCC,开启独立看门狗,此时重装载值写入递减计数器
在任何时间向IWDG_KR写入0xAAAA完成喂狗,重装载寄存器IWDG_RLR的值会重新加载到递减计数器,避免看门狗复位
如果在0xFFF递减到0x000前未完成喂狗,系统复位
键值寄存器IWDG_KR: 0~15位有效
写入0x5555取消IWDG_PR和IWDG_RLR写保护
写入0xCCCC开启独立看门狗
写入0xAAAA喂狗
预分频寄存器IWDG_PR:0~2位有效。具有写保护功能,要操作先取消写保护
重装载寄存器IWDG_RLR:0~11位有效。具有写保护功能,要操作先取消写保护。
0-11位[11:0]有效,[12:31]位保留 默认值,最大值为0xFFF
当IWDG_KR写入0xAAAA是,重装载值会被传送到计数器中
IWDG_SR寄存器RVU位为0时才能对IWDG_RLR寄存器进行修改
状态寄存器IWDG_SR:0~1位有效
看门狗时钟源 : 由LSI经过IWDG_PR分频提供
LSI时钟频率=40MHz
IWDG_PR预分频系数 P = 4 * 2^prer
所以看门狗时钟频率 = 40/P
IWDG_RLR计数器重装载值,每经过一个看门狗时钟周期-1
看门狗时钟周期 = P/40
看门狗移除时间:
Tout = ((4 * 2^prer) * RLR) / 40
最短时间 : 一个看门狗时钟周期 即IWDG_RLR=0x001
最长时间 : IWDG_RLR寄存器最大值(0xFFF) * 看门狗时钟周期
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess); // 取消写保护:0x5555使能
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); // 设置预分频系数:写PR
void IWDG_SetReload(uint16_t Reload); // 设置重装载值:写RLR
void IWDG_ReloadCounter(void); // 喂狗:写0xAAAA到KR
void IWDG_Enable(void); // 使能看门狗写0xCCCC到KR
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG); // 状态:重装载/预分频 更新
//取消寄存器写保护:
IWDG_WriteAccessCmd();
//设置独立看门狗的预分频系数,确定时钟:
IWDG_SetPrescaler();
//设置看门狗重装载值,确定溢出时间:
IWDG_SetReload();
//使能看门狗
IWDG_Enable();
//应用程序喂狗:
IWDG_ReloadCounter();
//溢出时间计算:
Tout=((4×2^prer)×rlr) /40
功能:
独立看门狗设置1秒溢出,按下WK_UP按键喂狗
如果不喂狗可以看到每1秒程序重启一次
现象LED0闪烁,因为每次启动程序有500毫秒延时
若连续快速按下WK_UP,使看门狗不进入溢出,LED0常亮
HAEDWARE/IWDG下新建Iwdg.h头文件
#ifndef __WDG_H
#define __WDG_H
#include “sys.h” //因为使用了u8 u16
/**
* 看门狗初始化
* prer 预分频系数
* rlr 重装载值
*/
void IWDG_Init(u8 prer,u16 rlr);
void IWDG_Feed(void); // 喂狗
#endif
HAEDWARE/IWDG下新建Iwdg.c实现函数
在stm32f10x_iwdg.h中找到:
/** @defgroup IWDG_Exported_Functions
* @{
*/
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess); // 取消写保护
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); // 设置预分频系数
void IWDG_SetReload(uint16_t Reload); // 设置重装载值
void IWDG_ReloadCounter(void); // 喂狗
void IWDG_Enable(void); // 使能独立看门狗
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG); // 获取状态
#include "wdg.h"
//看门狗初始化
void IWDG_Init(u8 prer,u16 rlr)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); // 取消IWDG_PR WDG_RLR写保护
IWDG_SetPrescaler(prer); // 设置预分频系数
IWDG_SetReload(rlr); // 设置重装载值
IWDG_ReloadCounter(); // 喂狗-使第一次从重装载值开始计数,默认首次0xFFF
IWDG_Enable(); // 使能看门狗
}
// 喂狗
void IWDG_Feed(void)
{
IWDG_ReloadCounter(); // reload
}
USER/main.编写主函数
#include "led.h" // LED
#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); // NVIC中断优先级分组
uart_init(115200); // 设置USART波特率115200
LED_Init(); // LED端口初始化 - 默认LED0熄灭
KEY_Init(); // 按键端口初始化
delay_ms(500); // 延时500ms - 复位时LED闪烁
IWDG_Init(4, 625); // 独立看门狗初始化-设置溢出时间1S-预分频系数4,重装载值625
LED0=0; // LED0点亮
while(1)
{
if(KEY_Scan(0)==WKUP_PRES) // 按下按键WK_UP
{
IWDG_Feed(); // 喂狗
}
delay_ms(10);
};
}
以上代码实现:
独立看门狗设置1秒溢出,按下WK_UP按键喂狗
如果不喂狗可以看到每1秒程序重启一次
现象LED0闪烁,因为每次启动程序有500毫秒延时
若连续快速按下WK_UP,使看门狗不进入溢出,LED0常亮