STM32-自学笔记(15.窗口看门狗)

概述:

再由单片机为核心构成的微型计算机系统中,单片机常常会受到来自外界电磁场的干扰,造成程序跑飞,致使程序的正常运行状态被打断陷入死循环,使得由单片机控制的系统无法继续正常工作,造成整个系统的停滞,发生不可预料的后果。

所以,出于对单片机运行状态进行实时检测的考虑,便产生了一种专门用于检测单片机程序运行状态的硬件结构,俗称“看门狗”。STM32微控制配备了2只看门狗,分别是窗口看门狗独立看门狗

窗口看门狗简称WWDG,是Window Watch Dog的缩写。WWDG的核心是一个6位定时计数器,其特性如下:

  • 内置一个可编程的、自由运行的递减计数器。
  • 复位条件:当递减计数器的值小于0x40,(若看门狗已被启用)则产生复位;当递减计数器在窗口外被重新装载,(若看门狗被启动)则产生复位。
  • 如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),此中断服务可以被用于重装载计数器以避免发生WWDG复位。

WWDG结构简图

看门狗控制寄存器中的T[6:0]存放的是WWDG当前计数值,其会在PCLK1经过分频器之后所产生的时钟驱动下进行递减计数。当即数值递减至0x40,则会请求一次看门狗早期唤醒中断(可以在该中断服务中进行喂狗操作)。而当计数值继续递减至0x3F时,就会产生一次WWDG复位。而W[6:0]存放的是WWDG计数比较值,当T[6:0]中存放的值大于W[6:0]中存放的值时进行喂狗操作,同样会产生一次看门狗中断。这就是“窗口”的含义:喂狗操作必须是当前计数值在W[6:0]与0x3F之间进行才不会发生看门狗复位。从程序的角度来说,即无论是过早还是过晚的进行喂狗操作,都将引发一次看门狗复位。这正是STM32的WWDG最大的特点。

WWDG的驱动时钟来自PCLK1。这就是WWDG正常运行的必要条件——当PCLK1发生故障,则看门狗就停止了工作。因此WWDG一般用于整个程序中某个局部的检测。

实验设计

验证STM32微控制器窗口看门狗的复用功能。初始化各个设备之后,在看门狗早期唤醒中断服务中进行喂狗操作。同时配置一个外部中断EXTI0,并赋予其比窗口看门狗早期唤醒中断更为高级的先占优先级。当EXTI0触发即可停止喂狗操作,则理应很快发生一次窗口看门狗复位事件。以上信息使用串口向上位机打印。

硬件电路

软件设计(程序设计)

注意要点

  • 配置RCC寄存器组,设置PCLK1频率为36MHz(即PLL输出72MHz后进行2分频)。
  • 打开WWDG时钟,注意WWDG属于APB1总线设备(最大速度36MHz)。
  • 配置WWDG,预分频值为8,并写入初始计数值(本次实验写入0x7F)。
  • 配置GPIO、EXTI、USART等外设。
  • 给WWDG的早期唤醒中断赋予较低先占优先级,同时给予EXTI中断赋予较高先占优先级。

对于WWDG的配置来说,最重要的无疑是其溢出时间初始计数值之间的关系。现基于以上提出的几点要点来进行一次计算的示例。

  1. 上述要点提及WWDG属于APB1总线设备,即表示其时钟来自于PCLK1,最大为36MHz,因此PCLK1为36MHz。
  2. 在PCLK1驱动看门狗计时之前,首先要经过既定的4096分频,在经过Prescaler=8分频(上述第3点),由此不难得到看门狗的计数频率为:          f = PCLK / 4096 / Prescaler = 36MHz / 4096 / 8 = 244Hz
  3. 则可以得到进行一次计数的时间约为:T = 1 / f = 4ms
  4. 上述第三点还提及将初始计数值设为0x7F,则由前面所知,当看门狗计数值从0x40跳变至0x3F时发生看门狗复位,则计算出了看门狗从启动计数到发生溢出复位的时间为:T1=4ms*(0x7F-0x3F)=264ms
  5. T1便是本次软件设计所设定的看门狗溢出复位时间,所以用户程序的喂狗周期不能大于264ms,否则将发生看门狗复位。

主函数  main.c

#include "stm32f10x_lib.h"

#include "stdio.h"

void RCC_Configuration (void);

void NVIC_Configuration (void);

void GPIO_Configuration (void);

void EXTI_Configuration (void);

void USART_Configuration (void);

void WWDG_Configuration (void);

int main (void)

{

RCC_Configuration ();         //设置系统时钟

NVIC_Configuration ();        //设置GPIO端口

GPIO_Configuration ();        //设置NVIC

EXTI_Configuration ();        //设置EXTI

USART_Configuration ();       //设置USART

//检查是否发生过窗口看门狗复位,是则进入if()内部,否则进入else内部

if (RCC_GetFlagStatus(RCC_FLAG_WWDGRST)!=RESET)

{

printf("\r\n The STM32 has been reset by WWDG  \r\n");

RCC_ClearFlag();     //清除看门狗复位标志

}

else

{

WWDG_Configuration ();      //设置WWDG

printf("\r\n The STM32 has been reset by WWDG before /r/n");

}

while(1);

}



设置系统各部分时钟   RCC_Configuration

void RCC_Configuration(void)

{

ErrorStatus HSEStartUpStatus;      //定义枚举类型变量 HSEStartUpStatus

RCC_DeInit();                     //复位系统时钟设置

RCC_HSEConfig(RCC_HSE_ON);         //开启HSE

HSEStatrtUpStatus=RCC_WaitForHSEStartUp();   //等待HSE起振并稳定

if(HSEStatrtUpStatus==SUCCESS)     //判断HSE是否起振成功,是则进入if()内部

{

RCC_HCLKConfig(RCC_SYSCLK_Div1);   //选择HCLK(AHB)时钟源为SYSCLK分频

RCC_PCLK2Config(RCC_HCLK_Div1);    //选择PCLK2时钟源为HCLK(AHB)1分频

RCC_PCLK1Config(RCC_HCLK_Div2);    //选择PCLK1时钟源为HCLK(AHB)2分频

FLASH_SetLatency(FLASH_Latency_2);  //设置Flash延时周期数为2

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);   //使能Flash预取缓存

//选择PLL时钟源为 HSE 1 分频,倍频数为9,则PLL=8MHz *9=72MHz

RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);

RCC_PLLCmd(ENABLE);                  //使能PLL

while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);  //等待PLL输出稳定

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);    //选择SYSCLK时钟源为PLL

while(RCC_GetSYSCLKSource()!=0x08);      //等待PLL成为SYSCLK时钟源

}

RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);     //打开APB1总线上的窗口看门狗时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);    //打开APB2总线上的GPIOA和USART1时钟

}

设置各GPIO端口功能    GPIO_Configuration (void)

void GPIO_Configuration(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

//设置PA.0为上拉输入(EXTI Line0)

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode__IPU;

GPIO_Init(GPIOA,&GPIO_InitStructure);

//定义PA.0为外部中断0输入通道(EXTI0)

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);

//设置USART1的Tx脚(PA.9)为第2功能推挽输出功能

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;

GPIO_Init(GPIOA,&GPIO_InitStructure);

//设置USART1的Rx脚(PA.10)为浮空输入脚

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_10;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOA,&GPIO_InitStructure);

}

设置EXTI参数    EXTI_Configuration

void EXTI_Configuration(void)

{

//定义EXTI初始化结构体EXTI_InitStructure

EXTI_InitTypeDef EXTI_InitStructure;

//设置外部中断0通道(EXTI_Line0)在下降沿时触发中断

EXTI_InitStructure.EXTI_Line=EXTI_Line0;

EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;

EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;

EXTI_InitStructure.EXTI_LineCmd=ENABLE;

EXTI_Init(&EXTI_InitStructure);

//定义PA.0为外部中断0输入通道(EXIT0)

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);

}

设置WWDG参数   WWDG_Configuration

void WWDG_Configuration(void)

{

设置WWDG预分频值为8,WWDG时钟频率=(PCLK1/4096)/8=244Hz(约4ms)

WWDG_SetPrescaler(WWDG_Prescaler_8);

//设置WWDG初始计数值为0x7F并启动WWDG,此时WWDG超时时间为4ms*(0x7F-0x3F)=264ms

WWDG_Enable(0x7F);

WWDG_ClearFlag();    //清除WWDG早期唤醒中断(EWI)标志

WWDG_EnableIT();     //使能WWDG早期唤醒中断(EWI)

}

设置NVIC参数   NVIC_Configuration

void NVIC_Configuration(void)

{

//定义NVIC初始化结构体NVIC_InitStructure

NVIC_InitTypeDef NVIC_InitStructure;

//使用优先级分组2

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//使能外部中断0通道(EXIT0),0级先占优先级,0级次占优先级

NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;

NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;

NVIC_Init(&NVIC_InitStructure);

//使能窗口看门狗(WWDG)中断,1级先占优先级

NVIC_InitStructure.NVIC_IRQChannel=WWDG_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;

NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;

NVIC_Init(&NVIC_InitStructure);

}

设置USART1  USART_Configuration

void USART_Configuration(void)

{

USART_InitTypeDef USART_InitStructure;       //定义USART初始化结构体USART_InitStructure

USART_ClockInitTypeDef USART_ClockInitStructure;  //定义USART初始化结构体USART_ClockInitStructure

//波特率为9600bps;8位数据长度,1个停止位,无检验位;禁用硬件流控制;禁止USART时钟;时钟极性低;在第2个边沿捕获数据;最后一位数据的时钟脉冲不从SCLK输出

USART_InitStructure.USART_BaudRate=9600;

USART_InitStructure.USART_WordLength=USART_WordLength_8b;

USART_InitStructure.USART_StopBits=USART_StopBits_1;

USART_InitStructure.USART_Parity=USART_Parity_NO;

USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;

USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;

USART_Init(USART1,&USART_InitStructure);

USART_Cmd(USART1,ENABLE);         //使能USART1

}

将printf函数重定位到USART1     fputc

int fputc (int ch,FILE*f)

UASRT_SendData(USART1,(u8)ch);

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);

return ch;

中断服务程序

头文件

#include "stm32f10x_it.h"

#include"stdio.h"

看门狗早期唤醒中断服务函数  WWDG_IRQHandler

void WWDG_IRQHandler (void)

{

WWDG_SetCounter(0x7F);      //更新WWDG计数器

WWDG_ClearFlag();           //清除WWDG早期唤醒中断(EWI)标志

printf("\r\n The Windows Watch Dog Has been flash \r\n");

}

外部中断0中断服务函数     EXTI0_IRQHandler

void EXTI0_IRQHandler (void)

{

while(1)

}

注意事项:

  1. 窗口看门狗是否产生复位操作,取决于定时计数器的值是否小于0x40,也就是窗口看门狗控制寄存器中的T6位是否为0,因此写入小于0x40的初始计数会马上发生一次复位操作。
  2. 窗口看门狗的复位相当于一次软复位,复位前WWDG各个寄存器的状态都将得到保留,因此在复位后,要首先将看门狗复位标志清除掉。
  3. 注意窗口看门狗启用之后在下一次复位事件产生之前不可以被禁用。
  4. 默认情况下,即使STM32进入调试状态窗口看门狗仍然会运行,这将导致调试出错。在开启窗口看门狗的情况下进行程序跟踪调试的读者应该注意这点。
  5. 如果程序中需要处理比较多的中断服务,请合理安排窗口看门狗的中断优先级。推荐的做法是:设置比较长的喂狗周期,同时赋予看门狗比较高的中断优先级。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值