首页 资讯 应用技术 高压 设计 行业 低压电器 电路图 关于

单片机与DSP

旗下栏目: 可编程逻辑 嵌入式技术 单片机与DSP 光电技术

STM32 独立看门狗定时器IWDG复位

单片机与DSP | 发布时间:2017-07-30 | 人气: | #评论# |本文关键字:STM32,看门狗,IWDG
摘要:无论是什么微控制器,一般都会有看门狗模块。对于STM32来说,它具有两个看门狗:独立看门狗(IWDG)与窗口看门狗(WWDG)。这里就先讲讲独立看门狗。 看门狗能够检测和解决有软件错误引

无论是什么微控制器,一般都会有看门狗模块。对于STM32来说,它具有两个看门狗:独立看门狗(IWDG)与窗口看门狗(WWDG)。这里就先讲讲独立看门狗。

看门狗能够检测和解决有软件错误引起的故障。当一个错误导致看门狗不能及时“喂狗”,那么它就会产生一个系统复位。独立看门狗,之所以“独立”,是因为它由专门的40kHz左右的低速时钟驱动的,及即时主时钟发生故障它也仍然有效。为它提供时钟的低速时钟LSI的频率虽然号称是40KHz,但实际上并不准确,它有MCU内部RC振荡产生,频率会会在30kHz~60kHz之间变化。所以,独立看门狗不能用来精确计时。如果想要实现准确计时,那还需要对LSI进行校准。独立看门狗最适合用于那些在一个主程序之外能够完全独立工作,并且对时间要求较低的场合。

下面就来实现下独立看门狗定时器的系统复位。还是基于我自己的规范工程。

1、工程的修改

1)当然要先添加stm32f10x_iwdg.c文件到STM32F10x_StdPeriph_Driver工程组中。因为这里我们要先校准LSI时钟频率,所以还要添加stm32f103_tim.c文件以便校验LSI时钟。

2)打开stm32f10x_conf.c文件,将其中原先屏蔽着的:#include "stm32f10x_iwdg.h" 与 #include "stm32f10x_tim.h"这两句话的屏蔽去掉。

3)新建IWDGRReset.c和IWDGReset.h两个文件,分别保存在BSP文件下的src与inc中,并将IWDGReset.c文件添加到BSP工作组中。

2、IWDGRReset.c和IWDGReset.h两个文件代码的编写

前面曾经说过,独立看门狗定时器的计数值不准确,因为它的时钟源是低速时钟LSI,所以在初始化独立看门狗定时器之前,需要先校准下LSI。校验LSI的步骤如下:

1)打开TIM5,设置通道4为输入捕获模式

2)设置AFIO_MAPR的TIM5_CH4_IREMAP位为‘1’,在内部吧LSI连接到TIM5的通道4.

3)通过TIM5的捕获/比较4事件或者中断来测量LSI的时钟频率。

所以在初始化独立看门狗定时器之前,需要配置下TIM5,将它的CH4配置成输入捕获,代码如下:

/*************************************************************
 Function   : IWDGReset_TIM5_Init
 Description: 初始化TIM5,用来校准LSI时钟
 Input      : none        
 return     : none    
*************************************************************/
static void IWDGReset_TIM5_Init(void)
{
TIM_ICInitTypeDef  TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);//打开TIM5时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//打开AFIO时钟,引脚重映射一定要时,一定要有这句话
TIM_PrescalerConfig(TIM5, 0, TIM_PSCReloadMode_Immediate);//不对TIM5之中进行分频,所以TIM5这时候的时钟是72M
GPIO_PinRemapConfig(GPIO_Remap_TIM5CH4_LSI, ENABLE);//在内部吧LSI连接到TIM4的CH4上,用CH4来测量LSI时钟频率

TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;//TIM5的CH4
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//检测上升沿
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//管脚与寄存器直接对应
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV8;//输入捕获8预分频,即检测到8个上升沿才生成中断标志
TIM_ICInitStructure.TIM_ICFilter = 0;//不滤波
TIM_ICInit(TIM5, &TIM_ICInitStructure);//初始化TIM5的CH4

TIM_ITConfig(TIM5, TIM_IT_CC4, ENABLE);//打开CH4中断源
TIM5->SR = 0;//清除TIM5的所有标志位
TIM_Cmd(TIM5, ENABLE);//打开TIM5

NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;//设置TIM5的中断优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

首先要将TIM5的时钟打开。除此之外还要将AFIO的时钟打开,这个很重要,要不然就TIM5的引脚重映射就没有效果了。接下去就要将TIM5的CH4重映射到LSI上,也就是上面的GPIO_PinRemapConfig(GPIO_Remap_TIM5CH4_LSI, ENABLE)语句。再下去则要将TIM5的CH4配置成输入捕获,上升沿捕获,8预分频,不滤波。这里对CH4输入捕获进行8与分频是有目的,LSI的时钟频率在30kHz~60kHz,所以如果不分频的话,CH4的计数值变化范围为:(72M/30k)=1200~(72M/60k)=2400,它的变化才1200左右,相对于16为计数值而言只是小数目,这样计算出来的LSI频率误差就很大;而如果进行8预分频,CH4捕获到8次上升沿才产生中断标志位,也就是说让CH4采集8个周期,CH4的计数值变化范围为:9600~19200,变化有将近10000了,然后将计算出的频率处除与8,就是LSI的频率了,这样计算出LSI的频率误差就小了。接下去打开TIM5,打开CH4的苏茹捕获中断,同时清除TIM5所有的标志位。最后配置下TIM5的中断优先级为1。

TIM5配置好了,就要开始校准LSI时钟了,代码如下:

u32 LsiFreq = 40000; //LSI时钟频率默认为40kHz
extern u16 CaptureNumber; //捕获次数

/*************************************************************
 Function   : IWDGReset_LSICalibration
 Description: 校准LSI时钟频率
 Input      : none      
 return     : none    
*************************************************************/
static void IWDGReset_LSICalibration(void)
{
RCC_LSICmd(ENABLE);//打开LSI时钟
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY)==RESET); //等待LSI时钟稳定

IWDGReset_TIM5_Init();//初始化TIM5
while(CaptureNumber != 2);//等待检测到LSI的2个上升沿
PRINTF("LSI freq: %dHz\r\n", LsiFreq);//将测量的LSI频率打印出来

TIM_ITConfig(TIM5, TIM_IT_CC4, DISABLE);//关闭CH4中断源
}

要校准LSI时钟,首先当然是先将LSI时钟打开,让内部的RC振荡器开始工作,等到振荡稳定后,再配置TIM5的CH4开始校准LSI。计算LSI频率的代码在stm32f10x_it.c的TIM5中断服务程序中,我之后再讲。等待知道捕获计数值CaptureNumber(在stm32f10x_it.c中定义)等于2,表示LSI的频率LsiFreq(默认情况下位40k)已经计算出来了。接下去将计算出来的LSI频率打印出来。最后在关闭TIM5 CH4的输入捕获中断。

接下去,就开始讲独立看门狗的初始化了,代码如下:

/*************************************************************
 Function   : IWDGReset_Init
 Description: 独立看门狗初始化
 Input      : none        
 return     : none    
*************************************************************/
void IWDGReset_Init(void)
{
IWDGReset_LSICalibration(); //打开LSI,并校准LSI时钟

IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//允许访问IWDG相关寄存器
IWDG_SetPrescaler(IWDG_Prescaler_32); //32分频
IWDG_SetReload(LsiFreq/128);   //设置看门狗喂狗时间为250ms,装载值=0.25/(LsiFreq/32)=LsiFreq/128
IWDG_Enable(); //打开IWDG
}/*************************************************************
 Function   : IWDGReset_Init
 Description: 独立看门狗初始化
 Input      : none        
 return     : none    
*************************************************************/
void IWDGReset_Init(void)
{
IWDGReset_LSICalibration();//打开LSI,并校准LSI时钟

IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//允许访问IWDG相关寄存器
IWDG_SetPrescaler(IWDG_Prescaler_32);//32分频
IWDG_SetReload(LsiFreq/128);//设置看门狗喂狗时间为250ms,装载值=0.25/(LsiFreq/32)=LsiFreq/128
IWDG_Enable();//打开IWDG
}

这里先调用刚编写好的IWDGReset_LSICalibration()函数校准下LSI时钟,然后就可以开始配置看门狗了。调用库函数IWDG_WriteAccessCmd()让它允许范围IWDG的相关寄存器,再调用IWDG_SetPrescaler()设置32预分频,分频之后的频率为LsiFreq/32,再下去则要设置看门狗的超时时间了,调用IWDG_SetReload()函数来设置,看门狗的转载值设定(因为IWDG为12位计数器,所以最大的转载值为4095)可以使用公式:转载值=超时时间/(LsiFreq/32),上面代码中,我设定超时时间为250ms,所以要设定的看门狗转载值=0.25s/(LsiFreq/32)=LsiFreq/128。最后再打开独立看门狗。

接下去在编写IWDGReset.h的程序,程序非常简单,只是声明下独立看门狗的小初始化函数,代码如下:

#ifndef __IWDGRESET_H__
#define __IWDGRESET_H__
#include "stm32f10x.h"

void IWDGReset_Init(void);

#endif

3、stm32f10x_it.c的修改

需要在stm32f10x_it.c文件中添加TIM5的中断服务函数,代码如下:

u16 CaptureNumber = 0;
extern u32 LsiFreq;
/*************************************************************
 Function   : TIM5_IRQHandler
 Description: TIM5中断服务程序
 Input      : none        
 return     : none    
*************************************************************/
void TIM5_IRQHandler(void)
{
static u16 capture = 0;
static u16 IC1ReadValue1 = 0, IC1ReadValue2 = 0;
if (TIM_GetITStatus(TIM5, TIM_IT_CC4) != RESET)
{    
if(CaptureNumber == 0)//第一次捕获到上升沿
{
IC1ReadValue1 = TIM_GetCapture4(TIM5);//获取当前计数值
}
else if(CaptureNumber == 1)//第二次捕获到上升沿
{
IC1ReadValue2 = TIM_GetCapture4(TIM5);//获取当前计数值

if (IC1ReadValue2 > IC1ReadValue1)//没有超过最大计数值
{
capture = (IC1ReadValue2 - IC1ReadValue1);//计算两个上升沿之间的计数值
}
else//超过了最大计数值
{
capture = ((0xFFFF - IC1ReadValue1) + IC1ReadValue2);//计算两个上升沿之间的计数值
}
LsiFreq = (uint32_t) SystemCoreClock / capture;
LsiFreq *= 8;//计算LSI频率=72M/capture*8 (乘上8是因为TIM5的CH4 8预分频)
}
CaptureNumber++;//捕获上升沿次数自增

TIM_ClearITPendingBit(TIM5, TIM_IT_CC4);//清除标志位
}
}

在这段代码中,计算出了LSI的时钟频率,它的思路是这样的:通过计算两次上升沿之间计数值差,进而计算出LSI的频率。由于之前配置CH4为8分频,这里的第一次检测到上升沿实际上是检测到了LSI时钟的第8个上升沿;这里的第二次检测到上升沿实际上是检测到LSI时钟的第16个上升沿。然后保存这两次检测到上升沿之间的计数值,分别用变量IC1ReadValue1和IC1ReadValue2保存,接着计算他们之间的差值。这里有两种情况:一种是,IC1ReadValue1<IC1ReadValue2,这样的话直接capture保存他们之间的差值;另一种情况是IC1ReadValue1>IC1ReadValue2,这说明计数值已经溢出一次了,所以要用((0xFFFF - IC1ReadValue1) + IC1ReadValue2);这个计算公式计算得到,并保存在capture中。得到计数值差值后,才能计算LSI频率。调用公式:LsiFreq=SystemCoreClock/capture=72M/capture,这里得到的LsiFreq是8个周期所对应频率,所以实际上LSI时钟频率LsiFreq还要乘于8,即LsiFreq *=8这才是LSI的真实频率。

4、main函数的编写

main函数的代码很简单,如下:

/*************************************************************
 Function   : main
 Description: main入口
 Input      : none        
 return     : none    
*************************************************************/
int main(void)
{  
BSP_Init();
PRINTF("\nmain() is running!\r\n");
IWDGReset_Init();
while(1)
{
LED1_Toggle(); //LED闪烁
Delay_ms(200); //延时大改250ms以上,就会看门狗复位
IWDG_ReloadCounter();//喂狗
}
}

IWDGReset_Init()初始化独立看门狗,然后在while(1)中每次延时200ms后就调用喂狗函数IWDG_ReloadCounter()来重新转载下看门狗的转载值。

5、测试

  依上见面的mian函数代码,每次都及时喂狗,程序只会执行一次而一直不会复位,在串口调试软件输出的信息中可以看到,如下图所示:

STM32 独立看门狗定时器IWDG复位 - ziye334 - ziye334的博客

 

 在将main函数中的延时改成Delay_ms(300),这样的话,看门 狗复无法及时喂狗,到时一直复位,如下图所示:

STM32 独立看门狗定时器IWDG复位 - ziye334 - ziye334的博客

   下面在做做其他实验,看不看CH4的预分频对LSI频率的影响。

将配置TIM5 CH4的代码中的CH4d的8预分频改成不分频,即改成:

TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;

同时在stm32f10x_it.c中,将LsiFreq *= 8;这就话屏蔽掉,然后编译下载,有如下现象:

STM32 独立看门狗定时器IWDG复位 - ziye334 - ziye334的博客

  可以发现修改后的LSI时钟频率为53097Hz,跟原先的43920Hz相差甚大。要问哪个准确,当然是43920Hz比较准确了,所以在编写程序的时候,最好要将TIM5的CH4进行8分频。

责任编辑:电气自动化网
首页 | 资讯 | 应用技术 | 高压 | 设计 | 行业 | 低压电器 | 电路图 | 关于

Copyright 2017-2018 电气自动化网 版权所有 辽ICP备17010593号-1

电脑版 | 移动版

Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。