看门狗的作用:防止MCU跑飞或死机,检测MCU运行状态。
STM32内置了两个看门狗(独立看门狗和窗口看门狗)
独立看门狗适用在需要看门狗作为一个在主程序之外能够完全独立工作的,由专用的LSI时钟驱动。
独立看门狗的计数器是一个 12 位的递减计数器,最大值为 0xFFF,当计数器减到 0时,会产生一个复位信号 IWDG RESET,让程序重新启动运行,如果在计数器减到0之前刷新了计数器的值的话,就不会产生复位信号,重新刷新计数器值的这个动作我们俗称喂狗。(值都由键寄存器写入,然后递交重装载数值)
接下来我们看一下详细的寄存器
键寄存器(IWDG_KR):0~15位有效
写入0x5555表示允许访问IWDG_PR和IWDG_RLR寄存器(取消写保护)
写入0xCCCC,启动看门狗工作
预分频寄存器(IWDG_PR):0~2位有效
预分频因子有:4,8,16,32,64,128,256这7种
预分频因子和PR[2:0]对应的关系:
预分频因子 PR[2:0]
4 0
8 1
16 2
32 3
64 4
128 5
256 6/7
重装载寄存器IWDG_RLR:0~11位有效
每当向IWDG_KR寄存器写入0xAAAA时,重装载值会被传送到计数器中。
状态寄存器(IWDG_SR):0~1位有效
RVU:看门狗计数器重装载值更新
PVU:看门狗预分频值更新
看门狗溢出时间计算
单位是毫秒,其中prer代表的是预分频寄存器的值(0,1,2,3,4,5,6/7),rlr是重装载值
举个例子:
比如溢出时间是1000毫秒,预分频系数是64,那要输入的rlr值就等于1000*40/4/4/4=625
关于独立看门狗的库函数
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);//状态:重装载/预分频 更新
独立看门狗操作步骤
1.取消寄存器写保护:
IWDG_WriteAccessCmd();
2.设置独立看门狗的预分频系数,确认时钟:
IWDG_SetPrescaler();
3.设置看门狗重装载值,确认溢出时间:
IWDG_SetReload();
4.使能看门狗:
IWDG_Enable();
5.应用程序喂狗:
IWDG_ReloadCounter();
现在我们已经完成了理论学习,接下来完成一个实验做练习:
1.使能一个周期为1s(因为IWDG时钟不精准,所以只是大约1s)的IWDG。
2.在主函数里,每20ms喂狗一次,使用软件定时器
3.每500ms翻转DS1来提示系统运行
4.通过按键WKUP来停止每20ms一次的喂狗行为,最终会导致系统因为独立看门狗复位而复位
5.通过按键RESET来复位,通过串口打印的信息来判断是复位还是独立看门狗引起的复位
我们现在开始建立工程,在之前的基础上建立文件bsp_iwdg.c和bsp_iwdg.h,并加入头文件模板和源文件模板,然后在bsp.h里加入#include "bsp_iwdg.h"
和之前一样,我们在.c里还是先写初始化
void bsp_InitIwdg(uint8_t _uIWDGPrescaler,uint16_t _uIWDGRlr)
{
//使能LSI
RCC_LSICmd(ENABLE);
//等待LSI就绪
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) {};
//检测系统的复位情况,IWDG复位,还是按键RESET复位(上电复位)
if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
{
/*清楚复位标志*/
RCC_ClearFlag();
printf("**独立看门狗复位********\r\n");
}
else
{
printf("不是独立看门狗复位,可能是上电复位,也可能是RESET按键复位************\r\n");
}
//允许操作寄存器RLR,PR
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
//设置预分频系数
IWDG_SetPrescaler(_uIWDGPrescaler);
//设置RLR重装载值
IWDG_SetReload(_uIWDGRlr);
//重装载计数
IWDG_ReloadCounter();
//使能IWDG
IWDG_Enable();
}
然后是喂狗函数
void IWDG_Feed(void)
{
IWDG_ReloadCounter();
}
最后在.h里声明这两个函数,看门狗部分就写完了
接下来是在main里实现功能:
int main(void)
{
uint8_t ucKeyCode;
bsp_Init(); /* 硬件初始化 */
printf("串口1的实例/r/n");
bsp_InitIwdg(IWDG_Prescaler_64,625);//溢出一秒 64分频,RLR是625
bsp_StartAutoTimer(0,500); //启动一个500ms的自动重装载定时器
bsp_StartAutoTimer(1,20); //启动一个20ms的周期定时器
while(1)
{ if(bsp_CheckTimer(0))
{
bsp_LedToggle(2);//翻转DS1的状态
}
if(bsp_CheckTimer(1))
{
IWDG_Feed(); //喂狗
}
/*处理按键事件*/
ucKeyCode = bsp_GetKey();
if(ucKeyCode != KEY_NONE)
{
switch(ucKeyCode)
{
case WKUP_DOWN:
{
bsp_StopTimer(1); //停止喂狗
} break;
default:
/*其他按键不处理*/
break;
}
}
}
}