看门狗全称:WatchDog,简称WDG,本质作用是是自动复位电路;
WDG主要是监控程序的运行状态,当程序因为设计漏洞,硬件故障或者电磁干扰等原因时,造成程序出现卡死或者跑飞时,看门狗可以自动复位电路,可以避免程序陷入长时间的罢工状态,保证了系统的可靠性和安全性;
看门狗DWG本质是定时器,在指定时间范围内,如果没有执行自动重装计数器(简称喂狗),就会产生复位信号,使程序复位;
看门狗分为IWDG独立看门狗和WWDG窗口看门狗;
独立工作(WDGd使用的是内部LSI低速时钟,即使内部时钟出现了问题,独立看门狗也可以独立工作),对时间精度要求不高(只有一个最晚的喂狗期限,只要不超过该期限,就不会产生复位信号);
要求看门狗在精确的计时窗口内起作用(使用PCLK1,即APB1作为时钟驱动,可配置时间窗口,即最早喂狗时间,和最晚喂狗时间,组成喂狗窗口,需要在窗口时间内实现喂狗,才不会产生复位信号);
分析执行逻辑:使用LSI内部40MHz低速时钟,经过8位的预分频器分配后(最大256分配,通过配置预分频寄存器IWDG_PR(Prescale)配置分配系数),产生的时钟信号驱动12位递减计数器工作,每来一个时钟,计数器自减一次,自减为0时候,产生IWDG复位信号,通过配置IWDG_RLR(Aotu Reloader)重装载寄存器的值,设置12位重装载数值,通过载键寄存器IWDG_KR中,写入特定的值,将重装载数值复制到递减计数器中,实现计数器值回到重装载数值,继续工作,即喂狗操作,通过状态寄存器IWDG_SR读取当前电路状态;
寄存器位于1.8v供电区,独立看门狗位于VDD供电区,所以独立看门狗可以用于唤醒STM32的低功耗模式;
*与定时器时基单元的区别:定时器使用内部系统时钟SYSCLK经过预分频器分频后(IWDG使用的是LSI),作为CNT计数器的时钟驱动,每来一个时钟,计数器自增或者自减(IWDG自减),自增自减到设置值后(减到0之前,手动更新计数器),自动产生更新时间,重装计数器的值,并产生中断(IWDG产生复位信号);
键寄存器本质是控制寄存器,用于控制硬件电路,,一般来说通过写入控制寄存器的某一位来实现对硬件电路的控制,但是在可能存在干扰的情况下键寄存器通过写入特定的数据来代替控制寄存器中的某一位的功能,降低了硬件电路受到干扰的概率;
TIWDG=TLSI*预分频系数*(RL+1);
其中TLSI=1/FLSI;
分析:LSI的时钟频率是40KHz,使用LSI驱动计数器自减,每1/40KHz计数一次,即0.025ms计数一次,预分频系数为扩大计数时间(例如时钟频率为1KHz,每1ms计数1次,分频系数为1时,每1个数采样1次,即每1ms计数1次,但是当分频系数为2时,即每2s采样1次,每2ms计数1次;),RL+1为目标数(例如RL为99,即计数从0计数到99,为99+1个数);所以IWDG超时时间为TIWDG=TLSI*预分频系数*(RL+1);
最短时间和最长时间计算:
例如PR位置0,预分频系数为/4,40KHZ1ms计数1/40KHZ=0.025ms,最短时间即计数为0x000,最长时间计数为0xFFF;
最短时间即计数为0x000=LSI*预分频系数*(RL+1)=0.025*4*1=0.1ms;
最长时间计数为0xFFF=LSI*预分频系数*(RL+1)=0.025*4*4095=409.6ms;
分类逻辑:APB1的时钟PCKL1经过一个4096的预分频后,进入到看门狗的预分频器,经过预分频器分频后,驱动6位递减计数器CNT递减计数,每来一个时钟信号,计数器递减一次,计数器的值通过看门狗控制寄存器WWDG_CR进行配置,寄存器的8位分为WDGA(看门狗使能位,位1时使能,为0时失能),T6(溢出标志位,为0时代表计数器溢出,为1时代表计数器没有溢出),T5~T0(计数器有效位数),例如写入0XFF,即1 1 111 111,每来一个时钟信号,递减一次,1 1 111 111,每来一个时钟频率,计数器减一从1 1 111 110->1 1 111 101->1 1 111 100->......->1 1 000 000,当下一个时钟驱动到来时,1 1 000 000—>1 0 111 111,此时计数器溢出,产生信号通过或门,与使能信号通过与门组成复位信号;这就是看门狗最晚喂食时间的生成;
看门狗最早喂食时间生成:通过配置看门狗配置寄存器WWDG_CFR来设置最早喂食时间W[6:0]
,如果T[6:0]>W[6:0]那么输出1到与门(提前喂狗),与写入计数器CNT操作(喂狗操作)WWDG_CR,组称信号到达或门,在和使能信号组成到达与门产生复位信号;
递减计数器的值T[6:0]小于值0x40时,WWDG产生复位;
递减计数器的值T[6:0]在窗口W[6:0]外被重新装时,WWDG产生复位;
递减计数器的值T[6:0]等于值0x40时可以产生早期唤醒中断(EWI),用于重装载计数器以避免WWDG复位;
定期写入WWDG_CR(喂狗可以避免WWDG复位);
分析:当CNT递减时,在大于W[6:0]时候,不允许刷新(喂狗),在CNT在W[6:0]和0x3F之间-0111 111,刷新窗口(可以喂狗),如果超过0X3F,此时产生复位信号;
分析:Tpclk1(时钟频率,为APB1的PCLK1默认为36MHZ)=1/36MHZ;(2.7126736111111111111111111111111e-5)
WWDG预分频系数等于
计数目标:T[5:0]的数+1;
最小时间:为T[5:0]=000 000;?
最大时间:为TT[5:0]=111 111;
API实现:
检验独立看门狗复位:
首先判断,是由独立看门狗造成的复位,还是因为上电或者复位键复位造成的复位;通过OLED显示;
配置好独立看门狗的时间,然后在主循环中,进行独立看门狗喂食(在喂食时间内),设置一个按键获取键码,放在主循环中,按下按键不放,导致系统阻塞,独立看门狗产生复位(判断是不是独立看门狗复位);
首先根据结构图分析看门狗过程:开启LSI时钟,设置预分频器,设置重装寄存器(设置喂狗时间),设置键寄存器,使能看门狗;
:
打开.H文件;
IWDG库函数(初始化IWDG):
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);//关闭写保护,可以配置各个寄存器
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);//配置预分频器
void IWDG_SetReload(uint16_t Reload);//配置重装值
void IWDG_ReloadCounter(void);//配置键寄存器(写入重装值,喂狗)
void IWDG_Enable(void);//使能独立看门狗
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG)//获取看门狗状态;
打开RCC.H找到获取状态标志位;(通过获取标志位来判断是什么原因导致的复位)
RCC标志位可以查看的事件主要有两部分组成,第一个是一些时钟的准备就绪,另一些各种复位;
??* ????@arg RCC_FLAG_PINRST: Pin reset
按复位键复位;
??* ????@arg RCC_FLAG_PORRST: POR/PDR reset
PVD可编程电压检测器复位;
??* ????@arg RCC_FLAG_SFTRST: Software reset
软件复位;
??* ????@arg RCC_FLAG_IWDGRST: Independent Watchdog reset
IWDG复位;
??* ????@arg RCC_FLAG_WWDGRST: Window Watchdog reset
WWDG复位;
??* ????@arg RCC_FLAG_LPWRRST: Low Power reset
低功耗复位;
1.首先查看是什么导致的复位:
初始化OLED,OLED显示名称为独立看门狗测试->“LWDG TEST”;
判断复位标志位:如果是独立看门狗产生的复位,OLED闪烁显示“LWDG REST”;
如果是上电或者复位按键按下产生的复位,则OLED闪烁显示“REST”
在获取IWDG标志位后要进行清除,因为该位不会自动清除,也不会在按键复位时清除,所以如果不清楚后面的判断全为IWDG,会造成错误判断;
验证上电复位或者按键复位;
之后配置独立看门狗:
2.开启看门狗时钟;
所以不需要配置;
3.解除写保护;
4.写入入预分频器的值和自动重装器的值(根据超时执行)
?
根据公式计算超时时间,例如设置超时时间位1000ms,从表中可以得知,预分频系数为/4或者/8时,最长时间也达不到1000ms,所以不能使用,其他都可以使用,选取原则是尽可能的让预分频系数小,因为这样可以更多的计数,因为有时,会出现计数器次数为小数,但是计数器系数,只能设置为整数,所以次数需要四舍五入,会出现误差,计数器次数越多误差越小;
所以我们选择16分频,根据计算1000=0.025*16*(RL+1);
得到RL=2499;
5.启动独立看门狗
启动之前执行一次喂狗,确保启动后从新开始计);
6.在主循环里不断执行喂狗操作;
每隔500ms喂食一次;
每隔1200ms喂食一次;
7。完整程序,接入按键模块(见STM3GPIO模块,封装的按键模块,执行逻辑是按键按下时,延时20ms,设置while循环检测按键是否松手,如果松手延迟20ms,读出按键值),如果一直不松手,那么按键一直检测松手与否,此时进入死循环,不能及时喂狗,查看看门狗是否可以执行复位;
);
WWDG窗口看门狗实现API:
与独立看门狗框架和思路类似,删除独立看门狗部分,得到如下程序:
void WWDG_DeInit(void);//复位WWDG
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);//预分频值
void WWDG_SetWindowValue(uint8_t WindowValue);//窗口时间
void WWDG_EnableIT(void);//使能中断
void WWDG_SetCounter(uint8_t Counter);//写入计数器
void WWDG_Enable(uint8_t Counter);//使能WWDG
FlagStatus WWDG_GetFlagStatus(void);//判断标志位
void WWDG_ClearFlag(void);//清除标志位
根据WWDG框图,初始化WWDG:
1.开启APBIWWDG的时钟;
2.配置预分频,配置窗口值;
与IWDG类似通过计算公式计算预分频系数和计数值(参考IWDG计算方式);
例如设置超时时间为50ms,窗口时间为30ms;
预分频系数最大是才能满足最大超时时间为50ms,所以只能选择预分频系数为3,即2的三次方,为8;
50=1/36*8*4096*计数值;
计数值=54.9,四舍五入为55值;
所以T[5:0]给54,对应的超时时间就是50ms;
计算窗口值:
30=1/36*8*4096*(T-W);T-W=33,T-54;所以W=21;
为什么要 | 0x40,原因如下:窗口时间和超时时间寄存器都包括标志位,但是递减计数器处于自由运行状态,所以需要在使能前进行喂狗,同时设置标志位,防止立刻产生复位;
通过或|上0X40实现(按位与按位或改变某几位的值,实现读改写;
读改写操作:第一步把寄存器读到临时变量中;
第二步用&|改变临时变量的某几位;
第三步把临时变量写入寄存器中;
优点:可以单独改变寄存器的某几位而不影响寄存器的值,连续更改多次效率高,并且写入时,同时生效);
3.写入控制寄存器CR,不断喂狗;
4.问题:使能标志位的设置
进入使能函数:是对CR寄存器进行配置的,通过将喂狗值+CR_WDGA_SET进行或操作;
查看CR_WDGA_SET为0x00000080;
将0x00000080->0000.....10000000
对应将第八位使能位置1;此时看门狗使能;
查看喂狗操作,是对CR寄存器进行配置的,通过将喂狗值+BIT_Mask进行或操作;
查看BIT_Maks值;
对应01111111,将第八位使能位改为0,但是第八位是无效操作,因为: