在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。
简单来说,看门狗就是在不停地对我们正在运行的程序进行监视,我们必须在规定的时间的有效的时间内去进行“喂狗”操作,这样看门狗就知道我们的程序正在正常的运行,如果我们程序由于外界原因跑飞了,那么就无法进行喂狗操作,那么看门狗由于饥饿难耐,他就会使我们的单片机进行复位,以便让程序重新开始运行。
看门狗分为独立看门狗和窗口看门狗,独立看门狗比较简单,当然它的安全性没有窗口看门狗高。下面就简单介绍一下这两个狗狗。
1、独立看门狗(IWDOG)
独立看门狗是由专门的低速总线进行驱动,即LSI总线(时钟频率40KHz),它可以在主时钟故障的情况下仍然可以工作(或许这就是把这只狗成为独立看门狗的原因吧)。独立看门狗适合应用于需要看门狗作为一个在主程序之外 能够完全独立工作,并且对时间精度要求低的场合。
在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用独立看门狗。此时计数器开始从其复位值0xFFF递减,当计数器值计数到尾值0x000时会产生一个复位信号(IWDG_RESET)。
无论何时,只要在键值寄存器IWDG_KR中写入0xAAAA(通常说的喂狗,但是0XAAAA并不是装入计数器中的值,真正装入计数器中的值是我们根据分频系数和我们需要的超时时间来计算好的), 自动重装载寄存器IWDG_RLR的值就会重新加载到计数器,从而避免看门狗复位。
如果程序异常,就无法正常喂狗,从而系统复位。
下面是独立看门狗所用到的库函数
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();
溢出时间计算:
Tout=((4×2^prer) ×rlr) /40 (M3)
相关代码如下
//初始化独立看门狗
//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_SetReload(rlr); //设置IWDG重装载值
IWDG_ReloadCounter(); //按照IWDG重装载寄存器的值重装载IWDG计数器
IWDG_Enable(); //使能IWDG
}
//喂独立看门狗
void IWDG_Feed(void)
{
IWDG_ReloadCounter();//reload
}
这里面所谓的时间计算就是让我们设置必须在多长的时间内进行喂狗,我们根据上面的公式,选择好与分频系数和重装载的值,就可以得到这个喂狗时间了,我们必须在这个时间内进行喂狗至少一次(哪怕你一直不停的喂,但是必须至少喂一次,否则它就会使单片机复位)
2、窗口看门狗(WWDOG)
窗口看门狗是用APB1总线驱动的,时钟频率最大36MHz
说起窗口看门狗,其实它的基本原理和独立看门狗是一样的,只不过我们又在喂狗的时间上做了一些手脚,在独立看门狗中,我们可以从计数器的值一直减到0的中间的任何时刻都可以进行喂狗,但是在窗口看门狗中,我们规定只能在某一个时间段内进行喂狗,举个栗子,假如说我们计数器是从100减到0,我们现在规定只能在计数器为80—30这个段内进行喂狗(这也就是窗口这个名词的来源),否则我们看门狗就会产生一个复位信号,从而使单片机复位,从这可以看出来,窗口看门狗的安全性比独立看门狗要高一些,毕竟它喂狗的时间段缩短了,这样就会降低由于程序跑飞而误打误撞的去执行喂狗操作的几率了。
对于窗口看门狗,我们不能太早喂狗,也不能太晚喂狗,最晚喂狗时间所对应的计数器值为0X3FH
我们来看一张图
当计数器的值T6:0>W6:0的值,也就是此时我们还处于上窗口,假如说我们现在喂狗,也就是把WWDG_CR置1,这时与门输出1,经过一个或门,输出1,然后我们又使能了WDGA位(使能这一位就相当于开启了窗口看门狗),然后又经过一个与门输出1,所以此时就会产生复位,即我们不能在上窗口之前进行喂狗(即不能太早喂狗)。第二种情况是我们不能等到计数器减到0X3FH之后再喂狗,因为到达0X3FH的时候,T6位就会由高跳变为低(0X40是0100 0000,0X3F是0011 1111),此时他经过一个非门和或门,再加上WDGA位也置1了(即开启了窗口看门狗),此时就会复位,即我们也不能太晚喂狗。
对于窗口看门狗,我们可以建立一个提前唤醒中断EWI,用来提醒我们去喂狗,即当计数器减到0X40的时候,产生一个中断,我们在这个中断里面去执行喂狗操作,这样就能够保持这个狗狗不闹腾了(即它不会让单片机复位了)。关于此中断,有一个中断标志位EWIF,这个标志位是当计数器减到0X40的时候,由硬件置1,当我们进入到中断服务函数并且执行完喂狗之后,需要用软件把这一位清零。
窗口看门狗配置过程如下
1、使能看门狗时钟:
RCC_APB1PeriphClockCmd();
2、 设置分频系数:
WWDG_SetPrescaler();
3、 设置上窗口值:
WWDG_SetWindowValue();
4、 开启提前唤醒中断并分组(可选):
WWDG_EnableIT();
NVIC_Init();
5、 使能看门狗:
WWDG_Enable();
6、 喂狗:
WWDG_SetCounter();
7、 编写中断服务函数
WWDG_IRQHandler();
部分代码如下
//保存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); //使能看门狗,设置计数器初值
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(); //清除提前唤醒中断标志位
}
这里面我们注意到这样的语句
u8 WWDG_CNT=0x7f;
WWDG_CNT=tr&WWDG_CNT; //初始化WWDG_CNT.
我们把送入的初值的最高位利用与的操作给清零了,这是因为在窗口看门狗的控制寄存器中(也就是计数器中)的最高位是控制窗口看门狗是否使能的控制位,这一位不参与计数,所以我们需要把最高位给清零,然后再在后面使能这个最高位,即开启窗口看门狗
关于使能和设置计数器初值是使用一个函数来完成的,即WWDG_Enable(WWDG_CNT); //使能看门狗,并设置计数器初值
关于两只小狗狗就先说到这吧
上一篇:STM32中的串口通信
下一篇:STM32之AHB与APB总线
推荐阅读最新更新时间:2024-03-16 16:25