正点原子stm32F407学习笔记7——看门狗实验

一、什么是看门狗

在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。
就是在程序执行异常情况下系统复位,程序重新开始执行。
看门狗解决的问题是什么?
在启动正常运行的时候,系统不能复位。在系统跑飞(程序异常执行)的情况,系统复位,程序重新执行。

二、独立看门狗

通过库函数来配置独立看门狗的步骤:
1)取消寄存器写保护(向 IWDG_KR 写入 0X5555)
通过这步,我们取消 IWDG_PR 和 IWDG_RLR 的写保护,使后面可以操作这两个寄存器,设置IWDG_PR 和 IWDG_RLR 的值。这在库函数中的实现函数是:IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
2)设置独立看门狗的预分频系数和重装载值
设置看门狗的分频系数的函数是:
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler); //设置 IWDG 预分频值
设置看门狗的重装载值的函数是:
void IWDG_SetReload(uint16_t Reload); //设置 IWDG 重装载值
设置好看门狗的分频系数 prer 和重装载值就可以知道看门狗的喂狗时间(也就是看门狗溢出时间),该时间的计算方式为:Tout=((4×2^prer) ×rlr) /40
其中 Tout 为看门狗溢出时间(单位为 ms);prer 为看门狗时钟预分频值(IWDG_PR 值),范围为 0~7;rlr 为看门狗的重装载值(IWDG_RLR 的值)
比如我们设定 prer 值为 4,rlr 值为 625,那么就可以得到 Tout=64×625/40=1000ms,这样,看门狗的溢出时间就是 1s,只要你在一秒钟之内,有一次写入 0XAAAA 到 IWDG_KR,就不会导致看门狗复位(当然写入多次也是可以的)。这里需要提醒大家的是,看门狗的时钟不是准确的 40Khz,所以在喂狗的时候,最好不要太晚了,否则,有可能发生看门狗复位。
3)重载计数值喂狗(向 IWDG_KR 写入 0XAAAA)
库函数里面重载计数值的函数是:
IWDG_ReloadCounter(); //按照 IWDG 重装载寄存器的值重装载 IWDG 计数器
通过这句,将使 STM32 重新加载 IWDG_RLR 的值到看门狗计数器里面。即实现独立看门
狗的喂狗操作。
4) 启动看门狗(向 IWDG_KR 写入 0XCCCC)
库函数里面启动独立看门狗的函数是:
IWDG_Enable(); //使能 IWDG
通过这句,来启动 STM32F4 的看门狗。注意 IWDG 在一旦启用,就不能再被关闭!想要关闭,只能重启,并且重启之后不能打开 IWDG,否则问题依旧,所以在这里提醒大家,如果不用 IWDG 的话,就不要去打开它,免得麻烦。
通过上面 4 个步骤,我们就可以启动 STM32F4 的看门狗了,使能了看门狗,在程序里面就必须间隔一定时间喂狗,否则将导致程序复位。利用这一点,我们本章将通过一个 LED 灯来指示程序是否重启,来验证 STM32F4 的独立看门狗。
在配置看门狗后,DS0 将常亮,如果 KEY_UP 按键按下,就喂狗,只要 KEY_UP 不停的
按,看门狗就一直不会产生复位,保持 DS0 的常亮,一旦超过看门狗定溢出时间(Tout)还没
按,那么将会导致程序重启,这将导致 DS0 熄灭一次。
iwdg.c代码如下

#include "iwdg.h"
//初始化独立看门狗
//prer:分频数:0~7(只有低 3 位有效!) rlr:自动重装载值,0~0XFFF.
//分频因子=4*2^prer.但最大值只能是 256!
//rlr:重装载寄存器值:低 11 位有效.
//时间计算(大概):Tout=((4*2^prer)*rlr)/32 (ms).
void IWDG_Init(u8 prer,u16 rlr)
{
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //取消寄存器写保护
	IWDG_SetPrescaler(prer); //设置 IWDG 分频系数
	IWDG_SetReload(rlr); //设置 IWDG 装载值
	IWDG_ReloadCounter(); //reload
	IWDG_Enable(); //使能看门狗
}
//喂独立看门狗
void IWDG_Feed(void)
{
	IWDG_ReloadCounter();//reload
}

iwdg.h代码如下

#ifndef _IWDG_H
#define _IWDG_H

#include "stm32f4xx.h"

void IWDG_Init(u8 prer,u16 rlr);
//喂独立看门狗
void IWDG_Feed(void);

#endif

主函数代码如下:

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2
	delay_init(168); //初始化延时函数
	LED_Init(); //初始化 LED 端口
	KEY_Init(); //初始化按键
	delay_ms(100); //延时 100ms 
	IWDG_Init(4,500); //与分频数为 64,重载值为 500,溢出时间为 1s
	LED0=0; //先点亮红灯
	while(1)
	{
		if(KEY_Scan(0)==WKUP_PRES)//如果 WK_UP 按下,则喂狗
		{
			IWDG_Feed();//喂狗
		}
		delay_ms(10);
	}
}

三、窗口看门狗

窗口看门狗(WWDG)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。
之所以称为窗口就是因为其喂狗时间是一个有上下限的范围内(窗口),你可以通过设定相关寄器,设定其上限时间(下限固定)。喂狗的时间不能过早也不能过晚。
窗口看门狗工作示意图:
正点原子stm32F407学习笔记7——看门狗实验_第1张图片
窗口看门狗框图:
正点原子stm32F407学习笔记7——看门狗实验_第2张图片
窗口看门狗工作过程总结:
STM32F的窗口看门狗中有一个7位的递减计数器T[6:0],它会在出现下述2种情况之一时产生看门狗复位:
a、当喂狗的时候如果计数器的值大于某一设定数值W[6:0]时,此设定数值在WWDG_CFR寄存器定义。
b、当计数器的数值从0x40减到0x3F时【T6位跳变到0】。
如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可以用于喂狗以避免WWDG复位。

窗口看门狗库函数相关源码和定义分布在文件 stm32f4xx_wwdg.c 文件和头文件 stm32f4xx_wwdg.h 中。步骤如下:
1)使能 WWDG 时钟
WWDG 不同于 IWDG,IWDG 有自己独立的 32Khz 时钟,不存在使能问题。而 WWDG使用的是 PCLK1 的时钟,需要先使能时钟。方法是:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WWDG 时钟使能
2)设置窗口值和分频数
设置窗口值的函数是:void WWDG_SetWindowValue(uint8_t WindowValue);
这个函数就一个入口参数为窗口值,很容易理解。
设置分频数的函数是:
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
这个函数同样只有一个入口参数就是分频值。
3)开启 WWDG 中断并分组
开启 WWDG 中断的函数为:WWDG_EnableIT(); //开启窗口看门狗中断
接下来是进行中断优先级配置,这里就不重复了,使用 NVIC_Init()函数即可。
4)设置计数器初始值并使能看门狗
这一步在库函数里面是通过一个函数实现的:void WWDG_Enable(uint8_t Counter);
该函数既设置了计数器初始值,同时使能了窗口看门狗。
这里还需要说明一下,库函数还提供了一个独立的设置计数器值的函数为:
void WWDG_SetCounter(uint8_t Counter);
5)编写中断服务函数
在最后,还是要编写窗口看门狗的中断服务函数,通过该函数来喂狗,喂狗要快,否则当
窗口看门狗计数器值减到 0X3F 的时候,就会引起软复位了。在中断服务函数里面也要将状态
寄存器的 EWIF 位清空。

完成了以上 4 个步骤之后,我们就可以使用 STM32F4 的窗口看门狗了。这一章的实验,
我们将通过 DS0 来指示 STM32F4 是否被复位了,如果被复位了就会点亮 300ms。DS1 用来指
示中断喂狗,每次中断喂狗翻转一次。
wwdg.c文件:

#include "exti.h"
#include "led.h"
#include "wwdg.h"
#include "sys.h"
u8 WWDG_CNT=0X7F;
//初始化窗口看门狗
//tr :T[6:0],计数器值 wr :W[6:0],窗口值
//fprer:分频系数(WDGTB),仅最低 2 位有效
//Fwwdg=PCLK1/(4096*2^fprer). 一般 PCLK1=42Mhz
void WWDG_Init(u8 tr,u8 wr,u32 fprer)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE); 
	//使能窗口看门狗时钟
	WWDG_CNT=tr&WWDG_CNT; //初始化 WWDG_CNT. 
	WWDG_SetPrescaler(fprer); //设置分频值
	WWDG_SetWindowValue(wr); //设置窗口值
	WWDG_SetCounter(WWDG_CNT);//设置计数值
	WWDG_Enable(WWDG_CNT); //开启看门狗
	NVIC_InitStructure.NVIC_IRQChannel=WWDG_IRQn; //窗口看门狗中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x02; //抢占优先级为 2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //响应优先级为 3
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能窗口看门狗
	NVIC_Init(&NVIC_InitStructure);
	WWDG_ClearFlag();//清除提前唤醒中断标志位
	WWDG_EnableIT();//开启提前唤醒中断
}
//窗口看门狗中断服务程序
void WWDG_IRQHandler(void)
{
	WWDG_SetCounter (WWDG_CNT); //重设窗口看门狗值
	WWDG_ClearFlag();//清除提前唤醒中断标志位
	LED1 = !LED1;
}

wwdg.h文件:

#ifndef _WWDG_H
#define _WWDG_H

#include "stm32f4xx.h"
void WWDG_Init(u8 tr,u8 wr,u32 fprer);

#endif

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "wwdg.h"
int main(void)
{ 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2
	delay_init(168); //初始化延时函数
	LED_Init(); //初始化 LED 端口
	KEY_Init(); //初始化按键
	LED0 = 0; //点亮 LED0
	delay_ms(300);
	WWDG_Init(0x7F,0X5F,WWDG_Prescaler_8); 
	//计数器值为 7f,窗口寄存器为 5f,分频数为 8 
	while(1)
	{
		LED0 = 1; //熄灭 LED 灯
	}
}

你可能感兴趣的:(stm32单片机,stm32,学习,笔记)