记得刚开始面试的时候,被面试官问了一个问题:stm32这个项目如果系统跑飞了,死机了,你是怎么处理的?
那时候没听懂他的意思,傻乎乎的回了一句,没遇到过这种情况。事后,才后悔莫及啊,他的意思不就是问我看门狗的作用吗?
然后就没有然后了,一个嵌入式工程师,如果看门狗都不知道人家是不敢用你的。
一、什么是看门狗?
看门狗是一个定时器,我们可以设置一个计数值,当看门狗启动后,计数值在一定的频率下不停的减1,当计数值减到0,看门狗会发出一个复位信号给CPU,这样会造成嵌入式系统复位。
软件系统在正常工作的过程中,需要在看门狗计数值减到0之前,给计数值重新赋值,这个看门狗就不会复位了,这个过程->“喂狗”。
当嵌入式系统,因为受到干扰或者出现错误,可能会造成软件“跑飞”--->死机。当系统发生死机的时候,看门狗喂狗的过程就没有了,看门狗计数值减到0,产生一个复位信号给CPU,造成系统复位,从错误的状态恢复了。
STM32有2个看门狗:独立看门狗和窗口看门狗。
独立看门狗IWDG----独立于系统之外,因为有独立时钟,所以不受系统影响的系统故障探测器,主要用于监视硬件错误。
窗口看门狗WWDG----系统内部的故障探测器,时钟与系统相同。如果系统时钟不走了,这个狗也就失去了作用了,主要用于监视软件错误。
简单的讲,看门狗就是检测系统故障的,如果因为系统故障而没有及时喂狗,则引发复位重启。
独立看门狗拥有自己的时钟源,不依赖PLL时钟输出的分频信号,能够独立运行,这样子的好处就是PLL假如受到干扰,导致运行异常,独立的看门狗还能正常地进行工作,如果没有正常的喂狗动作,就复位CPU。
IWDG程序设计
//用于定时喂狗
void Tim3_Init(void)
{
/* TIM3 clock enable ,定时器3的时钟使能*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* Enable the TIM3 global Interrupt,使能定时器3全局中断 */
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Time base configuration ,定时器的基本配置,用于配置定时器的中断频率为10Hz,也就是说100ms触发一次中断*/
TIM_TimeBaseStructure.TIM_Period = (10000/100)-1; //10000是10KHz,是定时器3的时钟源,100就是中断的频率
TIM_TimeBaseStructure.TIM_Prescaler = 8400-1; //预分频,也就是说第一次分频,降低频率
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //第二次分频,当前是实现1分频,也就是不降低频率
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数方法,从0开始计算
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* TIM Interrupts enable ,使能定时器3的更新中断事件,也代表说定时到达的事件*/
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
/* TIM3 enable counter,使能定时器3工作,开始计数 */
TIM_Cmd(TIM3, ENABLE);
}
void WatchDog_Init(void){
/* Clear reset flags,清空复位标志 */RCC_ClearFlag();/* Enable write access to IWDG_PR and IWDG_RLR registers ,独立看门狗的寄存器都是受到保护的,必须解除写保护*/IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);/* IWDG counter clock: LSI/256 ,设置看门狗的时钟频率 = 32KHz / 256 = 125Hz*/ 1s 计算125次IWDG_SetPrescaler(IWDG_Prescaler_256);//设置看门狗的超时时间,也代表说看门狗必须在2秒内进行喂狗(刷新计数值,不能等于0) IWDG_SetReload(250);/* Reload IWDG counter ,刷新计数值==喂狗*/IWDG_ReloadCounter();/* Enable IWDG ,使能看门狗 */IWDG_Enable();
Tim3_Init();}void TIM3_IRQHandler(void){ //检查当前触发中断事件是否为更新中断事件 if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {//添加用户代码 IWDG_ReloadCounter();//喂狗 //清空标志位 TIM_ClearITPendingBit(TIM3, TIM_IT_Update); }}
喂狗的动作其实就等同于银行取钱的原理,银行正常工作时间为上午9点到下午5点,其他时间都不能取钱。
WWDG程序设计
/* WWDG configuration ,窗口看门狗的配置*/
/* Enable WWDG clock ,使能看门狗的时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
/* 窗口看门狗的时钟 = (PCLK1 (42MHz)/4096)/8 = 1281 Hz (~780 us) */
WWDG_SetPrescaler(WWDG_Prescaler_8);
/* Set Window value to 80; WWDG counter should be refreshed only when the counter
is below 80 (and greater than 0x40) otherwise a reset will be generated 设置窗口的上限值为80*/
WWDG_SetWindowValue(80);
/* 设置计数值的初值为127,则窗口看门狗的最大超时时间 = 780 us * 64 = 49.92 ms
这个时候窗口刷新时间如下
~780 * (127-80) = 36.6ms < refresh window < ~780 * 64 = 49.9ms */
WWDG_Enable(127);
//WWDG NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //窗口看门狗中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//清空提前唤醒中断标志位
WWDG_ClearFlag();
//使能提前唤醒中断
WWDG_EnableIT();
void WWDG_IRQHandler(void)
{
if(WWDG_GetFlagStatus()==SET)
{
//进行喂狗
WWDG_SetCounter(127);
//清空提前唤醒中断标志位
WWDG_ClearFlag();
}
}
思考,如果窗口看门狗的中断优先级是低于定时器中断的抢占优先级出现什么问题?
回答:会出现窗口看门狗喂狗超时,导致发送“IWDG RESET\n”的时候,定时器进入while(1)死循环,由于窗口看门狗的抢占优先级低于定时器,同时独立看门狗的喂狗时间是1秒以内的,而窗口看门狗的喂狗时间40ms以内的,结果导致窗口看门狗喂狗超时直接复位,详细分析如下图。