什么是看门狗?它的原理?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是看门狗?它的原理?相关的知识,希望对你有一定的参考价值。
看门狗,又叫 watchdog timer,是一个定时器电路, 一般有一个输入,叫喂狗,一个输出到MCU的RST端,MCU正常工作的时候,每隔一端时间输出一个信号到喂狗端,给 WDT 清零,如果超过规定的时间不喂狗,(一般在程序跑飞时),WDT 定时超过,就回给出一个复位信号到MCU,是MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。工作原理:在系统运行以后也就启动了看门狗的计数器,看门狗就开始自动计数,如果到了一定的时间还不去清看门狗,那么看门狗计数器就会溢出从而引起看门狗中断,造成系统复位。所以在使用有看门狗的芯片时要注意清看门狗。
硬件看门狗是利用了一个定时器,来监控主程序的运行,也就是说在主程序的运行过程中,我们要在定时时间到之前对定时器进行复位如果出现死循环,或者说PC指针不能回来。那么定时时间到后就会使单片机复位。常用的WDT芯片如MAX813 ,5045, IMP 813等,价格4~10元不等.
软件看门狗技术的原理和这差不多,只不过是用软件的方法实现,我们还是以51系列来讲,我们知道在51单片机中有两个定时器,我们就可以用这两个定时器来对主程序的运行进行监控。我们可以对T0设定一定的定时时间,当产生定时中断的时候对一个变量进行赋值,而这个变量在主程序运行的开始已经有了一个初值,在这里我们要设定的定时值要小于主程序的运行时间,这样在主程序的尾部对变量的值进行判断,如果值发生了预期的变化,就说明T0中断正常,如果没有发生变化则使程序复位。对于T1我们用来监控主程序的运行,我们给T1设定一定的定时时间,在主程序中对其进行复位,如果不能在一定的时间里对其进行复位,T1 的定时中断就会使单片机复位。在这里T1的定时时间要设的大于主程序的运行时间,给主程序留有一定的的裕量。而T1的中断正常与否我们再由T0定时中断子程序来监视。这样就够成了一个循环,T0监视T1,T1监视主程序,主程序又来监视T0,从而保证系统的稳定运行。
51 系列有专门的看门狗定时器,对系统频率进行分频计数,定时器溢出时,将引起复位.看门狗可设定溢出率,也可单独用来作为定时器使用.
凌阳61的看门狗比较单一,一个是时间单一,第二是功能在实际的使用中只需在循环当中加入清狗的指令就OK了。
C8051Fxxx单片机内部也有一个21位的使用系统时钟的定时器,该定时器检测对其控制 寄存器的两次特定写操作的时间间隔。如果这个时间间隔超过了编程的极限值,将产生一个WDT复位。
--------------------------------------------------------------------------------
看门狗使用注意:大多数51 系列单片机都有看门狗,当看门狗没有被定时清零时,将引起复位。这可防止程序跑飞。设计者必须清楚看门狗的溢出时间以决定在合适的时候,清看门狗。清看门狗也不能太过频繁否则会造成资源浪费。程序正常运行时,软件每隔一定的时间(小于定时器的溢出周期)给定时器置数,即可预防溢出中断而引起的误复位。
看门狗运用:看门狗是恢复系统的正常运行及有效的监视管理器(具有锁定光驱,锁定任何指定程序的作用,可用在家庭中防止小孩无节制地玩游戏、上网、看录像)等具有很好的应用价值.
系统软件"看门狗"的设计思路:
1.看门狗定时器T0的设置。在初始化程序块中设置T0的工作方式,并开启中断和计数功能。系统Fosc=12 MHz,T0为16位计数器,最大计数值为(2的10次方)-1=65 535,T0输入计数频率是.Fosc/12,溢出周期为(65 535+1)/1=65 536(μs)。
2.计算主控程序循环一次的耗时。考虑系统各功能模块及其循环次数,本系统主控制程序的运行时间约为16.6 ms。系统设置"看门狗"定时器T0定时30 ms(T0的初值为65 536-30 000=35 536)。主控程序的每次循环都将刷新T0的初值。如程序进入"死循环"而T0的初值在30 ms内未被刷新,这时"看门狗"定时器T0将溢出并申请中断。
3.设计T0溢出所对应的中断服务程序。此子程序只须一条指令,即在T0对应的中断向量地址(000BH)写入"无条件转移"命令,把计算机拖回整个程序的第一行,对单片机重新进行初始化并获得正确的执行顺序。 参考技术A 看门狗用于监视、管理单片机、DSP等CPU的复位电路。CPU正常运行时,没隔一定时间给看门狗一个脉冲信号,俗称“喂狗”,看门狗超过时间没有收到“喂狗”信号,输出一个脉冲给CPU的复位引脚,使CPU复位。现在有许多CPU内置了看门狗电路。本回答被提问者采纳
STM32F103五分钟入门系列(十四)窗口看门狗WWDG
学习板:STM32F103ZET6
参考:STM32F103五分钟入门系列(十三)独立看门狗IWDG
一、窗口看门狗(WWDG)简介
1、什么是窗口看门狗
看门狗的概念上一博客已经总结过了,之所以被称为“窗口”,是因为“喂狗”时间有一个范围,可以通过相关寄存器设置上限时间,而下限是固定的。喂狗时间不能过早,也不能过晚。
2、窗口看门狗原理
上图所示,随着时间的推移,计数器的值一直在减小。窗口看门狗的喂狗上限为W[6:0](因为寄存器是低7位有效),这个上限值需要自己定义,但是不能小于0x3F,不能大于0x7F。
当前计数器的值大于自定义的W[6:0],且“喂狗”时,会发生中断;当计数值到达0x3F时,不管有没有“喂狗”,都发生中断;所以“喂狗”的计数范围为W[6:0]~0x3F,在W[6:0]之前、0x3F之后都不能“喂狗”,有上下限,谓之“窗口”。
当“可喂狗区间”没有喂狗,计数到0x40,即:0x01000000时,再倒计数一次,就变为0x00111111,即0x3F,此时会产生唤醒中断,产生复位信号。所以不难理解上图中当计数到3F时,T6为由1——>0,即0x01000000——>0x00111111。
3、窗口看门狗与独立看门狗区别
①喂狗范围不同
窗口看门狗“喂狗”范围有个上限和下限,独立看门狗没有下限,只有上限。
②产生复位信号时间不同
窗口看门狗在大于W[6:0]、倒计数到0x3F都产生复位信号;独立看门狗只有倒计数到0才产生复位信号
③时钟不同
窗口看门狗的时钟来源为APB1预分频后的PCLK1,为36MHZ。独立看门狗的时钟为40KHZ的LSI时钟
④窗口看门狗有自己的中断服务函数WWDG_IRQHandler,而独立看门狗没有
4、窗口看门狗应用场合
窗口看门狗(WWDG)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。因为窗口看门狗计数值寄存器只有7位,即最大为127,而还有下限0x40,即64,所以可喂狗计数区间在64~127(不一定能到达127,看这个窗口上限设置在哪里),所以很短时间内就需要喂狗,在某些时候,程序虽然跑飞,但是短暂时间后,又回归正常,如果用独立看门狗,可能无法检测出来,此时使用窗口看门狗比较合适。
二、窗口看门狗的相关寄存器
1、控制寄存器(WWDG_CR)
该寄存器是低8位有效的32位寄存器。位6:0用来存储看门狗的计数器值。当计数值从0x40变为3F时(到达下限),产生一个复位信号。位7为1时启动看门狗
2、配置寄存器(WWDG_CFR)
该寄存器是低10位有效的32位寄存器,位6:0包含与计数器比较的窗口值,即这7位是用来存储窗口看门狗上限值的。
位8:7设置窗口看门狗的时钟,将窗口看门狗时钟设置为(PCLK1/4096)/(2^位[8:7])
位9设置提前唤醒中断,当该位设置为1时,计数器达到0x40,跳变到0x3F时产生复位信号。即设置下限产生复位信号。
3、状态寄存器(WWDG_SR)
当计数器到达下限后无论是否产生复位信号(即无论CFR寄存器位9是否置1允许中断),SR寄存器的位0都会被硬件置1。所以可以根据该寄存器位0是否被置1来获取计数器是否到达下限。
三、窗口看门狗编程顺序
1、使能WWDG时钟
WWDG时钟用的是APB1,所以使能APB1时钟即可。
由之前博客STM32F103五分钟入门系列(七)SystemInit()函数、SetSysClock()函数总结,可以知道在72MHZ默认系统时钟下,APB1预分频系数为2、AHB预分频系数为1,所以PCLK1的时钟为36MHZ。
故WWDG时钟为36MHZ
2、设置窗口值和分频因子
3、开启 WWDG 中断并分组
4、 设置计数器初始值并使能看门狗
5、编写中断服务函数
四、例子(寄存器版+测试)
1、led代码
例:用LED0来指示程序复位,复位后LED0亮0.5s。在中断服务函数中清除中断标志,同时用LED1来指示进入中断服务函数情况。
LED代码上一博客STM32F103五分钟入门系列(十三)独立看门狗IWDG用到过,直接附代码:
//led.h
#ifndef LED_H
#define LED_H
void LED_Init(void);
#endif
//led.c
#include "sys.h"
#include "stm32f10x.h"
#include "led.h"
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct_B;
GPIO_InitTypeDef GPIO_InitStruct_E;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE ,ENABLE);
GPIO_InitStruct_B.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct_B.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStruct_B.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct_B);
GPIO_InitStruct_E.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct_E.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStruct_E.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStruct_E);
GPIO_SetBits(GPIOB, GPIO_Pin_5);//PB5置高电平
//GPIO_WriteBit(GPIOB, GPIO_Pin_5,1);
//GPIO_Write(GPIOB,0x0020); //慎用
//PBout(5)=1;
GPIO_SetBits(GPIOE, GPIO_Pin_5);//PE5置高电平
//GPIO_WriteBit(GPIOE, GPIO_Pin_5,1);
//GPIO_Write(GPIOE,0x0020); //慎用
//PEout(5)=1;
}
接下来就是窗口看门狗代码编写。
2、wwdg.h
wwdg.h文件编写,固定格式:
//wwdg.h
#ifndef WWDG_H
#define WWDG_H
void WWDG_Init(void);
#endif
wwdg.c文件代码编写:
3、wwdg.c
(1)使能WWDG时钟
RCC->APB1ENR|=1<<11;//使能WWDG时钟
(2)设置窗口值和分频数(有个重要总结)
窗口值下限为0x40(0x3F),为固定值,不需要设置。上限值设置:
PCLK1时钟为36MHZ,为了在有限的计数次数下,可喂狗时间间隔尽量长一点,WWDG时钟可以小一点,即分频系数可以大一点。
选用PCLK1除以4096再除以8位WWDG的时钟,此时时钟为:36MHZ/(4096*8)≈1.1KHZ
因为CR寄存器位6:0储存计数器的值、CFR的位6:0储存窗口上限值,所以这俩个值最大为:2^7-1=127(0x7F)。所以上限值的范围是127~65(>0x40),范围很小。当WWDG时钟最小时,可计数时间(最大可不喂狗时差)最长,为(127-65)/1100≈0.056s,这个时间已经是WWDG喂狗的最大时间了。
因为"可喂狗时间"范围比较小,所以就不变强求0.5s喂狗还是1s为喂狗了,如窗口上限0x6F
综上:
预分频器采用:CK计时器时钟(PCLK1除以4096)除以8
窗口上限赋值:0x6F
故CFR寄存器的值:0x1eF
WWDG->CFR|=0x1ef;//设置 CK计时器时钟(PCLK1除以4096)除以8、0x6f窗口上限
(3)开启 WWDG 中断并分组
WWDG->CFR|=1<<9;//设置提前唤醒中断
在主函数中设置中断优先级分组
//主函数中
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
再回到wwdg.c中设置优先级分组初始化函数:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
(4)设置计数器初始值并使能看门狗
最大装载值上限为1111111=0x7F,不妨设成这个:同时使能看门狗
WWDG->CR=0xff;//设置计数器初始值并使能看门狗
(5)编写中断服务函数
在中断服务函数中重装载初值和清除中断标志:
void WWDG_IRQHandler(void)
{
WWDG->CR=0x7f;
WWDG->SR=0;
PEout(5)=~PEout(5);
}
4、完整代码+测试
(1)不唤醒提前中断
//wwdg.c
#include"wwdg.h"
#include "stm32f10x.h"
#include "led.h"
#include "sys.h"
void WWDG_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC->APB1ENR|=1<<11;//使能WWDG时钟
//WWDG->CFR&=0xfc00;//清空
WWDG->CFR|=0x1ef;//设置 CK计时器时钟(PCLK1除以4096)除以8、0x6f窗口上限
//WWDG->CFR|=1<<9;//设置提前唤醒中断
NVIC_InitStructure.NVIC_IRQChannel=WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
WWDG->CR=0x7f;
WWDG->CR|=1<<7;//设置计数器初始值并使能看门狗
}
void WWDG_IRQHandler(void)
{
WWDG->CR=0x7f;
WWDG->SR=0;
PEout(5)=~PEout(5);
}
//main.c
#include "stm32f10x.h"
#include "led.h"
#include "wwdg.h"
#include "sys.h"
#include "delay.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init();
delay_init();
PBout(5)=0;
delay_ms(500);//通过LED0的亮灭来指示程序是否复位
WWDG_Init();
while(1)
{
PBout(5)=1;
}
}
注意:在wwdg.c中这行代码:WWDG->CFR|=1<<9;//设置提前唤醒中断
被注释掉,此时没有唤醒提前中断,即会计数到0x40,并到0x3f时产生复位信号,程序不会进入中断服务函数,程序直接复位了,所有只有LED0每隔0.5s闪烁一次。
测试效果:
(2)唤醒提前中断
当wwdg.c中代码WWDG->CFR|=1<<9;//设置提前唤醒中断
未被注释,即允许提前唤醒中断时,快要计数到0x40时,唤醒中断,进入中断服务函数,此时执行中断服务函数的重装载、清除中断标志代码,程序不会复位,并且中断服务函数中的LED1一直取反。表现为手动复位后,LED0闪烁一次,LED1一直闪烁,而之后不会再不复位,所以LED0一直处于熄灭状态。
注意是计数到0x40——>0x3f跳变时,程序复位,提前唤醒中断后,进入中断服务函数重装载初值,就不会计数到0x3F了,程序也不会复位!
去掉注释后代码效果:
五、窗口看门狗常用库函数
窗口看门狗库函数定义在stm32f10x_wwdg.h中:
1、失能窗口看门狗函数WWDG_DeInit()
参数: 无
返回值: 无
从函数体中也能看到,该函数只是对WWDG的时钟失能。
2、设置预分频系数函数WWDG_SetPrescaler()
参数:
表示PLCK1/4096/(1、2、4、8)
然后将该值赋值给CFR寄存器。
返回值: 无
3、窗口上限值设置函数WWDG_SetWindowValue()
参数:
自定义的一个小于0x7f的值,因为装载寄存器只有7位,且这个值不能小于0x40,所以应该在0x41~0x7f之间
返回值: 无
操作:
对CFR寄存器赋值
4、唤醒提前中断函数WWDG_EnableIT()
参数: 无
返回值: 无
操作:
直接把1赋值给CFR寄存器的位9,这里用的是基地址+偏移地址来访问该位,可以把库函数改为以下代码,更直观:
void WWDG_EnableIT(void)
{
WWDG->CFR|=1<<9;
}
5、设置重装载值函数WWDG_SetCounter()
参数:
该参数是自定义参数,必须小于窗口值上限,大于窗口值下限。
操作:
对CR寄存器赋值
6、窗口看门狗使能函数WWDG_Enable()(加一个注意事项)
参数:
这里传递了一个参数Counter,其实是没有用的,用的时候可以直接把参数置为0。
不过仔细看这个参数:
如果把这个参数设置为重装载值,那么这个函数即使能了WWDG,又同时重装载了初值!这样的话WWDG_SetCounter()
函数就可以用本函数替代了。
操作:
将0x80与Counter或运算后赋值给CR寄存器,不仅对CR寄存器位7置1,使能WWDG,还对CR寄存器的位6:0赋值,操作了装载值。
注意这里对CR寄存器的操作是赋值,不是位或运算,甚至就算是位或运算,对低6:0操作后,会对WWDG的重装载值产生影响,如果在本函数之前使用了WWDG_SetCounter()
函数进行重装载值,执行本函数后,重装载值会发生变化!
所以为了防止程序出现错误,本函数的参数最好是我们自定义的那个重装载值。这也是这个库函数不好的地方,库函数修改为如下代码就好用多了:
void WWDG_Enable()
{
WWDG->CR|=0x80;
//WWDG->CR|=CR_WDGA_Set
}
7、获取中断状态标志函数WWDG_GetFlagStatus()
参数: 无
返回值: RESET 或SET
操作:
直接返回SR寄存器的值,由于该寄存器只有位0有效,所以上电后其它位为0,若发生中断,SR寄存器位0变为1,该函数强制类型转换为FlagStatus类型,即{RESET = 0, SET = !RESET}
8、清除中断标志函数WWDG_ClearFlag()
参数: 无
返回值: 无
操作: 对SR寄存器直接赋0,清除中断标志
六、例子(库函数版+测试)
就前面的那个例子,用库函数写一遍,直接把源代码注释掉换成库函数:
只在wwdg.c中修改代码,其它代码保持不变
//wwdg.c
#include"wwdg.h"
#include "stm32f10x.h"
#include "led.h"
#include "sys.h"
void WWDG_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//RCC->APB1ENR|=1<<11;//使能WWDG时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
//WWDG->CFR|=0x1ef;//设置 CK计时器时钟(PCLK1除以4096)除以8、0x6f窗口上限
WWDG_SetPrescaler(WWDG_Prescaler_8);
WWDG_SetWindowValue(0x6f);
//WWDG->CFR|=1<<9;//设置提前唤醒中断/**********注意注释本行,可以进入中断服务函数******/
WWDG_EnableIT();//设置提前唤醒中断/**********注意注释本行,可以进入中断服务函数******/
NVIC_InitStructure.NVIC_IRQChannel=WWDG_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
//WWDG->CR=0x7f;
WWDG_SetCounter(0x7f);
//WWDG->CR|=1<<7;//设置计数器初始值并使能看门狗
WWDG_Enable(0x7f);//注意参数,为重装载值,或者把上一行代码注释掉
}
void WWDG_IRQHandler(void)
{
//WWDG->CR=0x7f;
WWDG_SetCounter(0x7f);
//WWDG->SR=0;
WWDG_ClearFlag();
PEout(5)=~PEout(5);
}
测试效果与之前例子一致:
注意:在wwdg.c中这行代码:WWDG_EnableIT();
被注释掉,此时没有唤醒提前中断,即会计数到0x40,并到0x3f时产生复位信号,程序不会进入中断服务函数,程序直接复位了,所有只有LED0每隔0.5s闪烁一次
测试效果:
当wwdg.c中代码WWDG_EnableIT()
未被注释,即允许提前唤醒中断时,快要计数到0x40时,唤醒中断,进入中断服务函数,此时执行中断服务函数的重装载、清除中断标志代码,程序不会复位,并且中断服务函数中的LED1一直取反。表现为手动复位后,LED0闪烁一次,LED1一直闪烁,而之后不会再不复位,所以LED0一直处于熄灭状态。
注意是计数到0x40——>0x3f跳变时,程序复位,提前唤醒中断后,进入中断服务函数重装载初值,就不会计数到0x3F了,程序也不会复位!
去掉注释后代码效果:
以上是关于什么是看门狗?它的原理?的主要内容,如果未能解决你的问题,请参考以下文章