STM32F1xx官方资料:
《STM32中文参考手册V10》-第17章 独立看门狗
在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环;或者程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果。所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。
简单点说:看门狗的作用就是在一定时间内(通过定时计数器实现)没有接收到喂狗信号(表示MCU已经挂了),便实现处理器的自动复位重启(发送复位信号)。
看门狗的作用和要求:
STM32内置两个看门狗,提供了更高的安全性、时间的精确性和使用的灵活性。两个看门狗设备(独立看门狗、窗口看门狗)可以用来检测和解决由软件错误引起的故障。当计数器达到给定的超时值时,触发一个中断(仅适用窗口看门狗)或者产生系统复位。
注意:IWDG_PR和IWDG_RLR寄存器具有写保护功能。要修改这两个寄存器的值,必须先向IWDG_KR寄存器中写入0x5555。以不同的值写入这个寄存器将会打乱操作顺序,寄存器将重新被保护。重装载操作(即写入0xAAAA)也会启动写保护功能。
作用:写入0xAAAA,不停地喂狗,避免复位;写入0x5555表示允许访问IWDG_PR和IWDG_RLR寄存器;写入0xCCCC,启动看门狗工作(若选择了硬件看门狗则不受此命令字限制)。最起初的复位值是从0xFFF开始递减,喂狗之后再从重装载寄存器的值开始递减。
作用:对时钟频率进行分频。
作用:用于定义看门狗计数器的重装载值,每当向IWDG_KR寄存器写入0xAAAA时,重装载值会被传送到计数器中。随后计数器从这个值开始递减计数。
作用:对预装载值更新、预分频值更新状态进行监控。
超出(溢出)时间计算:
Tout=((4×2^PR) ×RLR)/40
其中:Tout的单位为毫秒。
时钟频率LSI=40K, 一个看门狗时钟周期就是最短超时时间。最长超时时间= (IWDG_RLR寄存器最大值)X看门狗时钟周期。
比如,想要设置超出时间为1s(也就是说,如果1s之内没有喂狗,就直接复位),设置预分频系数为64(也就是说,PR位为4,即[2:0]位为100),由此可以计算出RLR为625。
也就是说,在时钟信号为40kHz,预分频系数为64的时候,从625递减至0的事件是1s。
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);
void IWDG_Enable(void);
作用:前者取消IWDG_PR和IWDG_RLR寄存器具有写保护功能(0x5555使能),后者使能看门狗(写0xCCCC到KR)。
这里需要注意一点:IWDG一旦启动,就不能被关闭!如果想要关闭,只能重启,并且重启之后不能再次打开。
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);
void IWDG_SetReload(uint16_t Reload);
作用:前者设置预分频系数,后者设置重装载值。
void IWDG_ReloadCounter(void);
作用:不断喂狗,避免复位(写0xAAAA到KR)。
下面按照这个一般步骤来进行一个简单的独立看门狗程序:
//初始化独立看门狗
//prer:分频数:0~7(只有低3位有效!)
//分频因子=4*2^prer.但最大值只能是256!
//rlr:重装载寄存器值:低11位有效.
//时间计算(大概):Tout=((4*2^prer)*rlr)/40 (ms).
void IWDG_Init(u8 prer,u16 rlr)
{
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使能对寄存器IWDG_PR和IWDG_RLR的写操作
IWDG_SetPrescaler(prer); //设置IWDG预分频值:设置IWDG预分频值为64
IWDG_SetReload(rlr); //设置IWDG重装载值
IWDG_ReloadCounter(); //按照IWDG重装载寄存器的值重装载IWDG计数器
IWDG_Enable(); //使能IWDG
}
//喂独立看门狗
void IWDG_Feed(void)
{
IWDG_ReloadCounter();//reload
}
int main(void)
{
delay_init(); //延时函数初始化
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init(); //按键初始化
delay_ms(500); //让人看得到灭
IWDG_Init(4,625); //与分频数为64,重载值为625,溢出时间为1s
LED0=0; //点亮LED0
while(1)
{
if(KEY_Scan(0)==WKUP_PRES)
{
IWDG_Feed();//如果WK_UP按下,则喂狗
}
delay_ms(10);
};
}