WWDG — 窗口看门狗
1、WWDG 简介
窗口看门狗通常被用来监测,由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。除非递减计数器的值在 T6 位变成 0 前被刷新,看门狗电路在达到预置的时间周期时,会产生一个 MCU 复位。如果在递减计数器达到窗口寄存器值之前刷新控制寄存器中的 7 位递减计数器值,也会产生 MCU 复位。这意味着必须在限定的时间窗口内刷新计数器。
STM32 有两个看门狗,一个是独立看门狗,一个是窗口看门狗。我们知道独立看门狗的工作原理就是一个递减计数器不断的往下递减计数,当减到 0之前如果没有喂狗的话,产生复位。窗口看门狗跟独立看门狗一样,也是一个递减计数器不断的往下递减计数,当减到一个固定值 0X40 时还不喂狗的话,产生复位,这个值叫窗口的下限,是固定的值,不能改变。这个是跟独立看门狗类似的地方,不同的地方是窗口看门狗的计数器的值在减到某一个数之前喂狗的话也会产生复位,这个值叫窗口的上限,上限值由用户独立设置。窗口看门狗计数器的值必须在上窗口和下窗口之间才可以喂狗,这就是窗口看门狗中窗口两个字的含义。
RLR是重装载寄存器,用来设置独立看门狗的计数器的值。
TR是窗口看门狗的计数器的值,由用户独立设置,WR是窗口看门狗的上窗口值,由用户独立设置。
2、 WWDG功能框图剖析
①窗口看门狗时钟
窗口看门狗时钟来自 PCLK1,PCLK1 最大是 45M,由 RCC时钟控制器开启。
②计数器时钟
计数器时钟由 CK 计时器时钟经过预分频器分频得到,分频系数由配置寄存器 CFR的位 8:7 WDGTB[1:0]配置,可以是[0,1,2,3],其中 CK计时器时钟=PCLK1/4096,除以 4096是手册规定的,没有为什么。所以计数器的时钟 CNT_CK=PCLK1/4096/(2^WDGTB),这就可以算出计数器减一个数的时间 T= 1/CNT_CK = Tpclk1 * 4096 * (2^WDGTB)。
③计数器
窗口看门狗的计数器是一个递减计数器,共有 7 位,其值存在控制寄存器 CR 的位 6:0,即 T[6:0],当 7 个位全部为 1 时是 0X7F,这个是最大值,当递减到 T6 位变成 0时,即从0X40 变为 0X3F时候,会产生看门狗复位。这个值 0X40 是看门狗能够递减到的最小值,所以计数器的值只能是:0X40~0X7F之间,实际上用来计数的是 T[5:0]。当递减计数器递减到 0X40 的时候,还不会马上产生复位,如果使能了提前唤醒中断:CFR 位 9 EWI 置 1,则产生提前唤醒中断,如果真进入了这个中断的话,就说明程序肯定是出问题了,那么在中断服务程序里面我们就需要做最重要的工作,比如保存重要数据,或者报警等,这个中断我们也叫它死前中断。
④窗口值
我们知道窗口看门狗必须在计数器的值在一个范围内才可以喂狗,其中下窗口的值是固定的 0X40,上窗口的值可以改变,具体的由配置寄存器 CFR的位 6:0 W[6:0]设置。其值必须大于 0X40,如果小于或者等于 0X40就是失去了窗口的价值,而且也不能大于计数器的值,所以必须得小于 0X7F。那窗口值具体要设置成多大?这个得根据我们需要监控的程序的运行时间来决定。如果我们要监控的程序段 A运行的时间为 Ta,当执行完这段程序之后就要进行喂狗,如果在窗口时间内没有喂狗的话,那程序就肯定是出问题了。一般计数器的值 TR设置成最大 0X7F,窗口值为 WR,计数器减一个数的时间为 T,那么时间:(TR-WR)*T应该稍微大于 Ta即可,这样就能做到刚执行完程序段 A之后喂狗,起到监控的作用,这样也就可以算出 WR的值是多少。
⑤计算看门狗超时时间
这个图来自数据手册,从图我们知道看门狗超时时间:Twwdg = Tpclk1 x 4096 x2^wdgtb x (T[5:0] + 1) ms,当 PCLK1 = 30MHZ时,WDGTB取不同的值时有最小和最大的超时时间,那这个最小和最大的超时时间该怎么理解,又是怎么算出来的?
讲起来有点绕,这里我稍微讲解下 WDGTB=0 时是怎么算的。递减计数器有 7位 T[6:0] ,当位 6 变为 0 的时候就会产生复位,实际上有效的计数位是 T[5:0],而且 T6 必须先设置为 1。如果T[5:0]=0 时,递减计数器再减一次,就产生复位了,那这减一的时间就等于计数器的周期
=1/CNT_CK = Tpclk1 * 4096 * (2^WDGTB) = 1/30 * 4096 *2^0 = 136.53us,这个就是最短的超时时间。如果 T[5:0]全部装满为 1,即 63,当他减到 0X40变成 0X3F时,所需的时间就是最大的超时时间=113.7*2^5=136.53*64=8.74ms。同理,当 WDGTB等于 1/2/3 时,代入公式即可。
3、 怎么用 WWDG
WWDG 一般被用来监测,由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。比如一个程序段正常运行的时间是 50ms,在运行完这个段程序之后紧接着进行喂狗,如果在规定的时间窗口内还没有喂狗,那就说明我们监控的程序出故障了,跑飞了,那么就会产生系统复位,让程序重新运行。
4 、WWDG喂狗实验
硬件设计
1、WWDG一个
2、LED 两个
WWDG属于单片机内部资源,不需要外部电路,需要两个 LED 来指示程序的运行状态。
软件设计
配置IWDG的超时时间为T,如果在T之内没有及时喂狗的话,产生系统复位,并用LED灯的状态变化来指示。
1-如何配置WWDG的超时时间T?
2-如果编写喂狗函数?
3-在main函数里面的什么地方喂狗比较合适?
能不能在中断函数里面喂狗?
//用于记录看门狗 递减计数器的值,方便喂狗函数直接使用
static uint8_t wwdg_cnt ;
/* WWDG 配置函数 * tr :递减计时器的值, 取值范围为:0x7f~0x40,超出范围会直接复位 * wr :窗口值,取值范围为:0x7f~0x40 * prv:预分频器值,取值可以是 * @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1(45MHz)/4096)/1 约10968Hz 91us * @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1(45MHz)/4096)/2 约5484Hz 182us * @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1(45MHz)/4096)/4 约2742Hz 364us * @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1(45MHz)/4096)/8 约1371Hz 728us * * 例:tr = 127(0x7f,tr的最大值) wr = 80(0x50, 0x40为最小wr最小值) prv = WWDG_Prescaler_8 * ~728 * (127-80) = 34.2ms < 刷新窗口 < ~728 * 64 = 46.6ms * 也就是说调用WWDG_Config进行这样的配置,若在之后的34.2ms前喂狗,系统会复位,在46.6ms后没有喂狗,系统也会复位。 * 需要在刷新窗口的时间内喂狗,系统才不会复位。 */ void WWDG_Config(uint8_t tr, uint8_t wr, uint32_t prv) { wwdg_cnt = tr;//保存CNT配置,用在喂狗函数 // 开启 WWDG 时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // 设置预分频器的值 WWDG_SetPrescaler( prv ); // 设置上窗口值 WWDG_SetWindowValue( wr ); // 设置计数器的值,使能WWDG WWDG_Enable(tr); // 清除提前唤醒中断标志位 WWDG_ClearFlag(); // 配置WWDG中断优先级 WWDG_NVIC_Config(); // 开WWDG 中断 WWDG_EnableIT(); }
// 喂狗 void WWDG_Feed(void) { // 喂狗,刷新递减计数器的值,设置成最大WDG_CNT=0X7F WWDG_SetCounter( wwdg_cnt ); }
int main(void) { uint8_t wwdg_tr, wwdg_wr; /* LED 端口初始化 */ LED_GPIO_Config(); // BLUE 蓝色灯亮 LED3(ON); Delay(0XFFFFFF); // WWDG配置 /* WWDG 配置函数 * tr :递减计时器的值, 取值范围为:0x7f~0x40,超出范围会直接复位 * wr :窗口值,取值范围为:0x7f~0x40 * prv:预分频器值,取值可以是 * @arg WWDG_Prescaler_1: WWDG counter clock = (PCLK1(45MHz)/4096)/1 约10968Hz 91us * @arg WWDG_Prescaler_2: WWDG counter clock = (PCLK1(45MHz)/4096)/2 约5484Hz 182us * @arg WWDG_Prescaler_4: WWDG counter clock = (PCLK1(45MHz)/4096)/4 约2742Hz 364us * @arg WWDG_Prescaler_8: WWDG counter clock = (PCLK1(45MHz)/4096)/8 约1371Hz 728us * * 例:tr = 127(0x7f,tr的最大值) * wr = 80(0x50, 0x40为最小wr最小值) * prv = WWDG_Prescaler_8 * 窗口时间为728 * (127-80) = 34.2ms < 刷新窗口 < ~728 * 64 = 46.6ms * 也就是说调用WWDG_Config进行这样的配置,若在之后的34.2ms前喂狗, * 系统会复位,在46.6ms后没有喂狗,系统也会复位。 * 需要在刷新窗口的时间内喂狗,系统才不会复位。 */ // 初始化WWDG:配置计数器初始值,配置上窗口值,启动WWDG,使能提前唤醒中断 WWDG_Config(127,80,WWDG_Prescaler_8); // 窗口值我们在初始化的时候设置成0X5F,这个值不会改变 wwdg_wr = WWDG->CFR & 0X7F; while(1) { // BLUE 蓝色灯 LED3(OFF); //----------------------------------------------------- // 这部分应该写需要被WWDG监控的程序,这段程序运行的时间 // 决定了窗口值应该设置成多大。 //----------------------------------------------------- // 计时器值,初始化成最大0X7F,当开启WWDG时候,这个值会不断减小 // 当计数器的值大于窗口值时喂狗的话,会复位,当计数器减少到0X40 // 还没有喂狗的话就非常非常危险了,计数器再减一次到了0X3F时就复位 // 所以要当计数器的值在窗口值和0X40之间的时候喂狗,其中0X40是固定的。 wwdg_tr = WWDG->CR & 0X7F; if( wwdg_tr < wwdg_wr ) { // 喂狗,重新设置计数器的值为最大0X7F WWDG_Feed(); } } }
tr :递减计时器的值, 取值范围为:0x7f~0x40,超出范围会直接复位。并进入下列中断。
WWDG中断服务程序,如果发生了此中断,表示程序已经出现了故障,这是一个死前中断。在此中断服务程序中应该干最重要的事,比如保存重要的数据等。
void WWDG_IRQHandler(void) { // 清除中断标志位 WWDG_ClearFlag(); //黄灯亮,点亮LED只是示意性的操作, //真正使用的时候,这里应该是做最重要的事情 LED_YELLOW; }
欢迎加入作者的小圈子
扫描下方左边二维码加入QQ交流群,扫描下方右边二维码关注个人微信公众号并,获取更多隐藏干货,QQ交流群:859800032 微信公众号:Crystal软件学堂
作者:Liu_Jing bilibili视频教程地址:https://space.bilibili.com/5782182 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在转载文章页面给出原文连接。 如果你觉得文章对你有所帮助,烦请点个推荐,你的支持是我更文的动力。 文中若有错误,请您务必指出,感谢给予我建议并让我提高的你。 |