STM32看门狗实验
独立看门狗实验
在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的芯片,俗称"看门狗"。
建议:看门狗是定时器的一种,学习看门狗对于理解定时器有着借鉴作用
实验目的:
1.分析和学习固件库
2.理解固件库的结构
3.通过stm32f10x_iwdg.c/.h文件,熟悉IWDG(独立看门狗)的控制和工作原理
4.复习按键中断的使用方法
实验要求:
1.使用LED灯LED1来指示程序是否重启(IWDG)
2.使用按键WAKEUP来不断地喂狗,并用LED4灯指示
硬件分析:
看门狗原理:
看门狗又叫watchdogtimer(WDT),是一个定时器电路。
一个输入端:叫喂狗引脚;
一个输出端:连接到MCU的RESET引脚;
在系统运行以后,启动了看门狗的计数器,看门狗就开始自动计数;MCU正常工作时,每隔一段时间输出一个信号到喂狗端,将WDT清零;一旦单片机由于干扰造成程序跑飞后,而进入死循环状态时,在超过规定的时间内“喂狗”程序不能被执行,看门狗计数器就会溢出,从而引起看门狗中断,就会输出一个复位信号到MCU,造成系统复位。
在使用看门狗时,要注意适时喂狗。
STM32看门狗简介:
启动方式:
通过选项字设定:硬件或软件启动;
基本特色:
自由运行的递减计数器;
内部独立的低功耗时钟LSI提供时钟,即使主时钟失效,看门狗仍处于激活状态;
一旦启动独立看门狗,就不能停止(LSI也不能被禁止);
看门狗被激活后,则在计数器计数至0x000时产生复位;在电源稳定期间,即使系统进入STOP和STANDBY模式,独立看门狗复位能将系统从STANDBY模式唤醒。
最适合应用于要求看门狗运行时,完全独立与主应用之外的项目
硬件电路分析:
这里的核心是在STM32内部进行,并不需要外部电路。但是考虑到指示当前状态和喂狗等操作,我们需要2个IO口,一个用来输入喂狗信号,另外一个用来指示程序是否重启。喂狗我们采用板上的WAKEUP键来操作,而程序重启,则是通过LED4来指示的。LED4和WAKEUP
的连接在前面跑马灯实验已经介绍了,这里我们不再多说
STM32的独立看门狗由内部专门的40Khz低速时钟驱动,即使主时钟发生故障,它也仍然有效。这里需要注意独立看门狗的时钟并不是准确的40Khz,而是在30~60Khz之间变化的一个时钟,只是我们在估算的时候,以40Khz的频率来计算,看门狗对时间的要求不是很精确,所以,时钟有些偏差,都是可以接受的。
通过对LSI进行校准可获得相对精确的看门狗超时时间。有关LSI校准的问题,详见数据手册LSI时钟一节。IWDG的功能框图如上所示。
可以看出,IWDG主要由4个寄存器控制。
Prescalerregister:预分频寄存器
Statusregister:状态寄存器
Reloadregister:重装载寄存器
KeyRegister:键值寄存器
IWDG的工作流程如下:
40KHz的LSI时钟信号发送至8位预分频器进行分频,分频后的时钟信号发送至12位的递减计数器,重装载寄存器把12位的重装载数值发送至递减计数器,如果12位的递减计数器没有得到12位的重装载数值,当计数减至0x000时IWDG则复位
具体的寄存器的使用和特点见下。
IWDG寄存器介绍:
IWDG寄存器结构,IWDG_TypeDeff,在文件“stm32f10x_map.h”中定义如下:
typedefstruct
{
vu32KR;//KeyRegister键值寄存器
vu32PR;//Prescalerregister预分频寄存器
vu32RLR;//Reloadregiste重装载寄存器
vu32SR;//Statusregister状态寄存器
}IWDG_TypeDef;
IWDG外设声明于文件“stm32f10x_map.h”:
#definePERIPH_BASE((u32)0x40000000)
#defineAPB1PERIPH_BASEPERIPH_BASE
#defineAPB2PERIPH_BASE(PERIPH_BASE+0x10000)
#defineAHBPERIPH_BASE(PERIPH_BASE+0x20000)
#defineIWDG_BASE(APB1PERIPH_BASE+0x3000)
#ifndefDEBUG
...
#ifdef_IWDG
#defineIWDG((IWDG_TypeDef*)IWDG_BASE)
#endif/*_IWDG*/
...
#else/*DEBUG*/
...
#ifdef_IWDG
EXTIWDG_TypeDef*IWDG;
#endif/*_IWDG*/
...
#endif
”中定义如下:为了访问IWDG寄存器,_IWDG必须在文件“stm32f10x_conf.hstm32f10x_conf.h”
#define_IWDG
键寄存器(IWDG_KR)键寄存器(IWDG_KR)
位31:16保留,始终读为0。
位15:0KEY[15:0]:键值(只写寄存器,读出值为0x0000)(Keyvalue)
软件必须以一定的间隔写入0xAAAA,否则,当计数器为0时,看门狗会产生复位。
写入0x5555表示允许访问IWDG_PR和IWDG_RLR寄存器。
写入0xCCCC,启动看门狗工作(若选择了硬件看门狗则不受此命令字限制)。
在WDG_KR中写入0xCCCC,开始启用独立看门狗;此时计数器开始从其复位值0xFFF递减计数。当计数器计数到末尾0x000时,会产生一个复位信号(IWDG_RESET)。无论何时,只要键寄存器IWDG_KR中被写入0xAAAA,IWDG_RLR中的值就会被重新加载到计数器中从而避免产生看门狗复位。
IWDG_PR和IWDG_RLR寄存器具有写保护功能。要修改这两个寄存器的值,必须先向IWDG_KR寄存器中写入0x5555。以不同的值写入这个寄存器将会打乱操作顺序,寄存器将重新被保护。重装载操作(即写入0xAAAA)也会启动写保护功能。对于寄存器的典型值,在stm32f10x.h中有如下定义
#defineIWDG_WriteAccess_Enable((uint16_t)0x5555)
#defineIWDG_WriteAccess_Disable((uint16_t)0x0000)
(IWDG_PR)预分频寄存器预分频寄存器(IWDG_PR)
预分频寄存器(IWDG_PR),该寄存器用来设置看门狗时钟的分频系数,最低为4,最高位256,该寄存器是一个32位的寄存器,但是我们只用了最低3
位,其他都是保留位。预分频寄存器各位定义如下:
位31:3保留,始终读为0。
位2:0PR[2:0]:预分频因子(Prescalerdivider)这些位具有写保护设置.
通过设置这些位来选择计数器时钟的预分频因子。要改变预分频因子,IWDG_SR寄存器的PVU位必须为0。000:预分频因子=4
001:预分频因子=8
010:预分频因子=16
011:预分频因子=32
100:预分频因子=64
101:预分频因子=128
110:预分频因子=256
111:预分频因子=256
注意:对此寄存器进行读操作,将从VDD电压域返回预分频值。如果写操作正在进行,则读回的值可能是无效的。因此,只有当IWDG_SR寄存器的PVU位为0时,读出的值才有效。
预分频值的设定,在stm32f10x.h中有如下定义
#defineIWDG_Prescaler_4((uint8_t)0x00)
#defineIWDG_Prescaler_8((uint8_t)0x01)
#defineIWDG_Prescaler_16((uint8_t)0x02)
#defineIWDG_Prescaler_32((uint8_t)0x03)
#defineIWDG_Prescaler_64((uint8_t)0x04)
#defineIWDG_Prescaler_128((uint8_t)0x05)
#defineIWDG_Prescaler_256((uint8_t)0x06)
(IWDG_RLR)重装载寄存器重装载寄存器(IWDG_RLR)
在介绍完IWDG_PR之后,我们介绍一下重装载寄存器。该寄存器用来保存重装载到计数器中的值。该寄存器也是一个32位寄存器,但是只有低12
位是有效的,该寄存器的各位描述如下:
位31:12保留,始终读为0。
位11:0RL[11:0]:
看门狗计数器重装载值(Watchdogcounterreloadvalue)这些位具有写保护功能。
用于定义看门狗计数器的重装载值,每当向IWDG_KR寄存器写入0xAAAA时,重装载值会被传送到计数器中。随后计数器从这个值开始递减计数。看门狗超时周期可通过此重装载值和时钟预分频值来计算。只有当IWDG_SR寄存器中的RVU位为0时,才能对此寄存器进行修改。
注:对此寄存器进行读操作,将从VDD电压域返回预分频值。如果写操作正在进行,则读回的值可能是无效的。因此,只有当IWDG_SR寄存器的RVU位为0时,读出的值才有效。
(IWDG_SR)状态寄存器状态寄存器(IWDG_SR)
位31:2保留。
位1RVU:
看门狗计数器重装载值更新(Watchdogcounterreloadvalueupdate)此位由硬件置’1’用来指示重装载值的更新正在进行中。当在VDD域中的重装载更新结束后,此位由硬件清’0’(最多需5个40kHz的RC周期)。重装载值只有在RVU位被清’0’后才可更新。
位0PVU:
看门狗预分频值更新(Watchdogprescalervalueupdate)此位由硬件置’1’用来指示预分频值的更新正在进行中。当在VDD域中的预分频值更新结束后,此位由硬件清’0’(最多需5个40kHz的RC周期)。预分频值只有在PVU位被清’0’后才可更新。
IWDG寄存器映像
IWDG
寄存器映像和复位值
有关寄存器的起始地址,参见数据手册.
程序分析:
固件库库函数分析:
IWDG
的库函数如下所示:
函数IWDG_WriteAccessCmd
IWDG_WriteAccess
该参数使能或者失能对寄存器IWDG_PR和IWDG_RLR的写操作
函数原型如下:
voidIWDG_WriteAccessCmd(uint16_tIWDG_WriteAccess)
{
/*Checktheparameters*/
assert_param(IS_IWDG_WRITE_ACCESS(IWDG_WriteAccess));
IWDG->KR=IWDG_WriteAccess;
}
可以看出,该函数的作用就是把输入参数传递到IWDG_KR中去.
在stm32f10x.h中我们找到输入参数的定义,如下所示
#defineIWDG_WriteAccess_Enable((uint16_t)0x5555)
#defineIWDG_WriteAccess_Disable((uint16_t)0x0000)
#defineIS_IWDG_WRITE_ACCESS(ACCESS)(((ACCESS)==IWDG_WriteAccess_Enable)||\
((ACCESS)==IWDG_WriteAccess_Disable))
由硬件分析可知,写入0x5555表示允许访问IWDG_PR和IWDG_RLR寄存器。以不同的值写入这个寄存器将会打乱操作顺序,寄存器将重新被保护。重装载操作(即使写入0xAAAA)也会启动写保护功能。
所以IWDG_WriteAccess_Enable和IWDG_WriteAccess_Disable的含义与字面含义不同,作用分别为使能和失能IWDG_PR和RWDG_RLR的写操作.
比如IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//关闭IWDG_PR和IWDG_RLR的写保护IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);//打开IWDG_PR和IWDG_RLR的写保护
函数IWDG_SetPrescaler
IWDG_Prescaler
该参数设置了IWDG
的预分频值
这些参数被定义在stm32f10x.h中.
#defineIWDG_Prescaler_4
#defineIWDG_Prescaler_8
#defineIWDG_Prescaler_16
#defineIWDG_Prescaler_32
#defineIWDG_Prescaler_64
#defineIWDG_Prescaler_128
#defineIWDG_Prescaler_256
详见硬件分析中的预分频寄存器.
函数原型如下:((uint8_t)0x00)((uint8_t)0x01)((uint8_t)0x02)((uint8_t)0x03)((uint8_t)0x04)((uint8_t)0x05)((uint8_t)0x06)
voidIWDG_SetPrescaler(uint8_tIWDG_Prescaler)
{
/*Checktheparameters*/
assert_param(IS_IWDG_PRESCALER(IWDG_Prescaler));
IWDG->PR=IWDG_Prescaler;
}
可以看出该函数的作用也仅仅为把IWDG_Prescaler的值传递至预分频寄存器中.
使用方法,例如:
/*设置预分频为8分频*/
IWDG_SetPrescaler(IWDG_Prescaler_8);
函数IWDG_SetReload
函数原型如下:
voidIWDG_SetReload(uint16_tReload)
{
/*Checktheparameters*/
assert_param(IS_IWDG_RELOAD(Reload));
IWDG->RLR=Reload;
}
如上面的函数一样,该函数的作用一样简单清晰,仅仅为把需要设定的重装载值传递到IWDG_RLR寄存器中.
喂狗时间(单位ms)=(预分频系数/4)*0.1*RLR(重装载值)
比如,我想达到喂狗时间越为1S的效果,为此我们选择32分频,带入计算可得
1000=(32/4)*0.1*RLR
RLR=1000/(32/4*0.1)=1250=0x4E2
即当设置了预分频为32后,我们使用IWDG_SetReload(0x4E2);后,喂狗时间约为1S
实际上,喂狗时间不可能无穷大,或者无穷小。下图为看门狗超时时间:
函数IWDG_ReloadCounter
/*喂狗*/
IWDG_ReloadCounter();
该函数原型为
voidIWDG_ReloadCounter(void)
{
IWDG->KR=KR_KEY_Reload;
}
在stm32f10x.c中我们可以找到以下的定义
#defineKR_KEY_Reload((uint16_t)0xAAAA)//初始的喂狗值,格段时间喂一次
KR_KEY_Reload的值为0xAAA,对照着函数代码可知,该函数的作用仅仅是向IWDG_KR中传输0xAAA,由上面的寄存器分析可知,向KR寄存器写入0XAAA后,IWDG_RLR中的值就会被重新加载到计数器中从而避免产生看门狗复位。这个也就是我们说的喂狗操作.
函数IWDG_Enable
该函数的原型如下:
voidIWDG_Enable(void)
{
IWDG->KR=KR_KEY_Enable;
}
和上个函数相同,该函数的作用为向IWDG_KR中写入KR_KEY_Enable的值.
我们可以在stm32f10x.c中找出KR_KEY_Enable的定义
#defineKR_KEY_Enable((uint16_t)0xCCCC)
通过硬件分析中的KR寄存器分析可知,向IWDG_KR中写入0xCCCC的效果为启用看门狗.
直接调用这个函数就可以启用看门狗.
函数IWDG_Enable
该函数原型如下:
FlagStatusIWDG_GetFlagStatus(uint16_tIWDG_FLAG)
{
FlagStatusbitstatus=RESET;
/*Checktheparameters*/
assert_param(IS_IWDG_FLAG(IWDG_FLAG));
if((IWDG->SR&IWDG_FLAG)!=(uint32_t)RESET)
{
bitstatus=SET;
}
else
{
bitstatus=RESET;
}
/*Returntheflagstatus*/
returnbitstatus;
}
在stm32f10x.h中有如下定义
typedefenum{RESET=0,SET=!RESET}FlagStatus,ITStatus;
可以看出RESET和SET的值分别为0,1.
由此可以看出,该函数的作用为,通过读取IWDG_SR寄存器的相应位,来检测IWDG当前的的状态,并返回。详见上面状态寄存器(IWDG_SR)介绍.
程序设计简要分析:
看门狗是定时器的一种,一般定时器的使用过程如下:
1.初始化定时器的设置。包括工作方式等,并开启中断和计数功能。
2.启用寄存器后,经过一定的耗时,如果在定时器溢出之前没有刷新定时器的数值,则定时器将溢出,并申请中断。
3.定时器中断后执行相应的中断服务程序.
与一般定时器不同的是,看门狗溢出所对应的中断服务程序只须一条指令,即在中断向量地址写入"无条件转移"命令,把计算机拖回整个程序的第一行,对单片机重新进行初始化并获得正确的执行顺序
通过硬件分析可知,事实上我们只要三个寄存器进行相应的设置,就可以启动STM32的独立看门狗,启动过程可以按如下步骤实现:
IWDG_KR写入0X5555。1)向)向IWDG_KRIWDG_KR写入写入0X55550X5555。
通过这步,我们取消IWDG_PR和IWDG_RLR的写保护,使后面可以操作这两个寄存器。
设置IWDG_PR和IWDG_RLR的值。
这两步设置看门狗的分频系数,和重装载的值。由此,就可以知道看门狗的喂狗时间,该时间的计算方式为:Tout=40Khz/((4*2^prer)*rlr);当然这个值是个粗略的计算值,因为时钟不准确,所以无法得到准确的喂狗时间。
2)向IWDG_KR写入0XAAAA。
通过这步操作,将使STM32重新加载IWDG_RLR的值到看门狗计数器里面。也可以用该命令来喂狗。
3)向IWDG_KR写入0XCCCC通过这步操作,我们就可以启动STM32的看门狗。
IWDG_KR写入0XAAAA,则程序复位,调回整个程序的第一行.4)如果一段时间内,不向如果一段时间内,不向IWDG_KR0XAAAA,则程序复位,调回整个程序的第一行,则程序复位,调回整个程序的第一行.
TB开发板LED程序流程图
TB开发板程序源代码:
Main.c
/*
*Jason
*jiangjj@emsym
**/
/*iwdg*/
#include"stm32f10x.h"
#include"stm32f10x_iwdg.h"
#include"TB_LED.h"
#include"TB_KEY.h"
voidiwdg_init();
voiddelay();
/*--------------------------------------------------------------------------------------------------
//函数名称:intmain()
//入口参数:无
//输出:无
//函数功能:主函数
--------------------------------------------------------------------------------------------------*/
intmain(void){
//初始化LED1和LED3
TB_LEDInit(LED1);
TB_LEDInit(LED3);
//立即熄灭LED3
TB_LEDOff(LED3);
//延时一段时间后,熄灭LED1
delay();
TB_LEDOff(LED1);
//初始化按键WAKEUP按键的中断
TB_PBInit(BUTTON_WAKEUP,BUTTON_MODE_EXTI);//PA0
//初始化看门狗
iwdg_init();
while(1);
return0;
}
/*--------------------------------------------------------------------------------------------------
//函数名称:voidiwdg_init()
//入口参数:无
//输出:无
//函数功能:初始化看门狗
--------------------------------------------------------------------------------------------------*/
voidiwdg_init(){
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//关闭IWDG_PR和IWDG_RLR的写保护IWDG_SetReload(0xfff);//设置重装载值为0xfff
IWDG_SetPrescaler(IWDG_Prescaler_32);//设置预分频系数为32
IWDG_ReloadCounter();
IWDG_Enable();//使能看门狗//
}
/*--------------------------------------------------------------------------------------------------
//函数名称:voiddelay()
//入口参数:无
//输出:无
//函数功能:延时函数
--------------------------------------------------------------------------------------------------*/
voiddelay()
{
uint32_ti=0xfffff;
for(;i>0;i--);
}
中断服务程序
voidEXTI0_IRQHandler(void)
{
//while(1);
TB_LEDOn(LED3);
IWDG_ReloadCounter();
TB_PBClearInterrupt(BUTTON_WAKEUP);delay();
TB_LEDOff(LED3);
}//使LED3亮,可以证明成功进入了中断//把重装载值写入看门狗中,俗称喂狗//清除按键的中断标志位//延时一段时间//使LED3灭,表示退出了中断服务程序