背景:
项目中要使用485通信,而485通信中采用的主呼从应的方式需要一个超时时间,在呼叫指令发出时将定时器置0并开始计时,并在数据接收函数中判断是否超时。
正文:
设计思路:首先,需要一个定时产生中断的定时器(比如滴答定时器,CubeMX生成的工程滴答定时器默认为1ms中断一次),然后可以在这个定时器中断函数中做多个计数器,每次进入后计数器加1,并判断是否到达溢出条件。
代码实现:
timer.h
#ifndef __DELAY_H__
#define __DELAY_H__
#include "stdint.h"
#include "stm32f4xx.h"
typedef struct __timer
{
uint8_t overFlow;
uint32_t cnt;
uint32_t overFlowValue;
void (*timerReset) (struct __timer* p_timer, uint32_t overFlagValue);
void (*timerRun) (struct __timer* p_timer);
}timer_t;
/*
滴答定时器进入中断的时间间隔,这个值理论上越大越好,当然这个值
的设置还取决于你需要多长时间的溢出Flag,假如你需要一个25ms溢出
的Flag,那么将中断时间间隔必须设置为25的约数,并且取最大值。
另外,你可以添加新的心跳,例如下面的EnterITPer_2Ms,但记得在
heartBeatInit()函数中添加相关的心跳计算因数设置代码。
还有,心跳不要超过50ms,超过50ms请自测心跳是否正常
*/
typedef enum __SysTickITPeriod
{
EnterITPer_1Ms = 1000,
/*EnterITPer_2Ms = 500,*/
EnterITPer_5Ms = 200,
EnterITPer_10Ms = 100,
EnterITPer_50Ms = 20,
}SysTickITPeriod_t;
/*------------全局变量-------------*/
extern timer_t SecondPulse;
extern timer_t RS485_Polling;
extern timer_t LEDBlinkTimer;
/*-------------全局函数-----------------*/
extern void globalTimerRun(void);
extern void heartBeatInit(SysTickITPeriod_t factor);
#endif
timer.c
/*
@>说明:定时器的创建见下方 New_Timer 出现的位置
@>说明:定时器的使用:
使用 New_Timer.overFlow 判断是否溢出
使用 New_Timer.timerReset(&New_Timer, xxx) 来设置定时器溢出时间
*/
#include "delay.h"
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv心跳相关vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
static uint16_t heartBeatFactor = 1;
static void timerInit(timer_t* p_timer, uint32_t overFlagValue);
timer_t SecondPulse;
timer_t LEDBlinkTimer;
timer_t RS485_Polling;
/*timer_t New_Timer */
/*---------------------------------------------------------------------
@功能:更新所有的定时器计数值
-----------------------------------------------------------------------*/
void globalTimerRun(void)
{
SecondPulse.timerRun(&SecondPulse);
LEDBlinkTimer.timerRun(&LEDBlinkTimer);
RS485_Polling.timerRun(&RS485_Polling);
/*timer_t New_Timer.timerRun(&New_Timer) */
}
/*-----------------------------------------------------------------
@>功能:初始化滴答定时器进入中断的时间间隔
可选参数参见 SysTickITPeriod_t
并且设置 heartBeatFactor 心跳计算
因数,该数值为1ms的整数倍。
------------------------------------------------------------------*/
void heartBeatInit(SysTickITPeriod_t factor)
{
switch(factor)
{
case EnterITPer_1Ms :
heartBeatFactor = 1;
break;
case EnterITPer_5Ms :
heartBeatFactor = 5;
break;
case EnterITPer_10Ms :
heartBeatFactor = 10;
break;
case EnterITPer_50Ms :
heartBeatFactor = 50;
break;
}
HAL_SYSTICK_Config(SystemCoreClock /factor);
timerInit(&SecondPulse, 1000); /*---初始化定时器---*/
timerInit(&RS485_Polling, 2000);
timerInit(&LEDBlinkTimer, 100);
/*timerInit(&New_Timer, 1000);*/
}
/*-----------------------------------------------------------------
@>功能:更新定时器计数,被滴答定时器中断调用
@>参数:p_timer 定时器指针
-------------------------------------------------------------------*/
static void timerRun(timer_t* p_timer)
{
if(p_timer->cnt >= p_timer->overFlowValue / heartBeatFactor)
{
p_timer->overFlow = 1;
}
else
{
p_timer->cnt++;
}
}
/*-----------------------------------------------------------------
@>功能:重新启动定时器
@>参数:p_timer 指定定时器,overFlagValue指定溢出值单位1ms
-------------------------------------------------------------------*/
static void timerReset(timer_t* p_timer, uint32_t overFlagValue)
{
p_timer->overFlowValue = overFlagValue;
p_timer->cnt = 1;
p_timer->overFlow = 0;
}
/*-----------------------------------------------------------------
@>功能:初始化一个定时器
@>参数:p_timer 定时器指针
overFlagValue 设定时器溢出值
-------------------------------------------------------------------*/
static void timerInit(timer_t* p_timer, uint32_t overFlagValue)
{
p_timer->overFlowValue = overFlagValue;
p_timer->cnt = 1;
p_timer->overFlow = 0;
p_timer->timerReset = timerReset;
p_timer->timerRun = timerRun;
}
将void globalTimerRun(void)函数置于定时器的中断中即可。
主循环进入While(1)之前调用,也可以不调用,默认的值为配置定时器后进入中断的时间间隔。
heartBeatInit(EnterITPer_1Ms );
判断定时是否到达:
if(timer.overFlow){
...
}
重置定时器:
timer.Reset(&timer, time);