STM32单片机的看门狗有独立看门狗和窗口看门狗之分,这两者的工作原理却完全不同,今天来看一下他们的具体区别和配置方法。
STM32独立看门狗 由专门的低速时钟(LSI)驱动,即便是主时钟发生故障它仍能够有效,所以此狗狗可以工作在与主时钟无关的要求下,或者待机模块下等,所以它叫独立看门狗,注意一旦开启此看门狗则只能由MCU复位后才清除,让它不再工作。 它的时钟是一个内部RC时钟,它会在30KHZ到60KHZ之间变化,并非是精确的40KHZ,而只是一般计算时取40KHZ。 独立看门狗需设置四个寄存器如下:
其中,预分频寄存器(IWDG_PR),最低三位PR[2:0](Prescaler divider)有效,可设置有8种不同的计数器时钟预分频因子。 重装载寄存器(IWDG_RLR)低12位RL[11:0]: 看门狗计数器重装载值 (Watchdog counter reload value) 有效,用来设置计数器的重装载值。 注意要设置以上两个寄存器的值需满足两个条件,详见如下: 键寄存器(IWDG_KR),用来控制去除IWDG_PR和IWDG_RLR写保护功能以便正常写值,向此寄存器写入0x5555则暂时去除IWDG_PR和IWDG_RLR的写保护功能才可向两个寄存器中写值。 当向此寄存器写入0xAAAA则IWDG_RLR的值会重装载,防止MCU复位,向入0xCCCC是开启狗立看门狗动作。 状态寄存器(IWDG_SR)最低两位有效RVU: 看门狗计数器重装载值更新 (Watchdog counter reload value update) 标识位和PVU: 看门狗预分频值更新 (Watchdog prescaler value update) 标识位,分别用来指示此时是否可向IWDG_RLR 和 IWDG_PR写值,此寄存器由硬件置1与清0,只有当为0时才可向上面两个寄存器写值。 它的初始化过程大致如下 :
//时间计算(大概):Tout=((4*2^prer)*rlr)/40 (ms) void IWDG_Init(u8 prer,u16 rlr) { IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); IWDG_SetPrescaler(prer); IWDG_SetReload(rlr); IWDG_ReloadCounter(); IWDG_Enable(); }
喂狗可通过调用如下函数进行:
IWDG_ReloadCounter();//reload另外要注意不要使用硬件时钟中断喂狗,因为硬件时钟中断一般都有较高优先级且独立于主控程序,这样有时会出现主控程序虽然跑飞了,但仍能够正常喂狗的现象。 独立看门狗能够在一定程度上监控着程序正常运行,然而我认为更加强大,应用更灵活及更能保证程序稳定运行的还属窗口看门狗,虽然它开始时不太好理解。 ▍STM32窗口看门狗 共三个寄存器,如下图:
看似简单,但设置及应用起来有不少玄机。 控制寄存器(WWDG_CR)中的值必须在0xFF与0xC0之间, 因为它的第0至第6位为递减计数器CNT,在它的第6位变为0时将产生复位,所以在初始化时需要为1,第7位WDGA是用来设置启动或禁止窗口看门狗的,当为1进才会启动窗口看门狗,所以第6和第7位都需为1,即WWDG_CR 的值需要大于等于0xC0 。 配置寄存器(WWDG_CFR) 第0至第6位 是设置窗口边界值用的,只有当递减计数器CNT的值小于边界值时才可以喂狗,过早不行,狗还不饿,撑死了。 并且7位递减计数器CNT减少到0x3F时即T6位变为0,此时MCU也会复位,过晚了,狗饿死了。 所以必须在指定的时间范围喂狗,过早或过晚都将产生复位,而这样设计可以减少软件跑飞了却仍能够歪打正着地喂狗的发生概率。 状态寄存器(WWDG_CFR) 只用到了第0位,EWIF(Early wakeup interrupt flag )是提前唤醒中断标识,当递减计数器CNT的值到达0X40(若再减少一次则T6位变为0,产生复位)时此位由硬件置1,且需用软件清0,注意无论中断是否使能此位都会被硬件置1。 而提前唤醒中断使能设置是在配置寄存器(WWDG_CFR)第9位EWI(Early wakeup interrupt),此位需由软件置1,则会在当递减计数器CNT的值到达0X40时产生中断,并且与EWIF不同,此位是由硬件清0。 另外控制寄存器(WWDG_CR)中第7位WDGA(Activation bit)激活位,需用软件来置1,以启动窗口看门狗,并且一旦启动后,只能在复位或重启后由硬件来清0。 配置寄存器(WWDG_CFR)的第8位和第7位WDGTB[1:0]用来设置时基(Timer base)预分频数。 以上描述可参考下图以更清晰的理解:
窗口看门狗应用时还要注意算准最小与最大喂狗时间,以便正确地喂狗,如下:
在PCLK1频率为36MHz 时,则 上窗口时间:T_min = 4096 * (2^WDGTB)*(WWDG_CR[6:0] - WWDG_CFR[6:0])/36 (us) 下窗口时间:T_max = 4096 * (2^WDGTB)*(WWDG_CR[6:0] - 0x40)/36 (us) 。 喂狗动作需在这段时间之间进行,而喂狗动作为向控制寄存器(WWDG_CR)中写值。 窗口看门狗中断函数void WWDG_IRQHandler(void);是用来做什么的呢。 窗口看门狗中断函数是在递减计数器减少到0x40是被调用,因为它本身计数就比较慢,所以离数到0x3F复位还有一段时间,我认为这样设计是为MCU复位之前留下一点时间,能够使工程设计人员根据需要在中断函数保存一些重要的数据,这样在复位后MCU可知道系统因异常复位的某此状态,以使系统有更高稳定性。 并且我觉得在窗口看门狗中断函数中喂狗没有什么意义,程序本来已经不按正常运行了,还在中断函数中喂狗防止复位只会错上加错,不好好利用它干点正事, 更是浪费资源。 这点上我个人认为不要被点原子示例代码误导哦,但其还是有部分借鉴意义的,以下为初始化相关代码:
//窗口看门狗中断服务设置程序 void WWDG_NVIC_Init() { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG 中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占2 子优先级3 组2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //抢占2,子优先级3,组2 NVIC_Init(&NVIC_InitStructure); //NVIC 初始化 }//保存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); //使能看门狗,设置counter WWDG_ClearFlag(); //清除提前唤醒中断标志位 (注:若没有此句则会在初始化后先进入中断一次) WWDG_NVIC_Init(); //初始化窗口看门狗NVIC WWDG_EnableIT(); //开启窗口看门狗中断 }
以上代码朋友们也可以跳到库函数代码中自己研究下,另外要说明下的是WWDG_EnableIT(); 函数相关代码
#define CFR_EWI_BB (PERIPH_BB_BASE + (CFR_OFFSET * 32) + (EWI_BitNumber * 4))
用到位带操作,具体理解可参照《Cortex-M3权威指南》第五章的位带操作相关介绍(具体89页)。