使用STM32实现心跳包,非阻塞延迟

背景:

项目中要使用485通信,而485通信中采用的主呼从应的方式需要一个超时时间,在呼叫指令发出时将定时器置0并开始计时,并在数据接收函数中判断是否超时。

正文:

设计思路:首先,需要一个定时产生中断的定时器(比如滴答定时器,CubeMX生成的工程滴答定时器默认为1ms中断一次),然后可以在这个定时器中断函数中做多个计数器,每次进入后计数器加1,并判断是否到达溢出条件。

<!>: 代码中有临界资源问题,p_timer->cnt;p_timer->overFlow 这两个变量可能在中断和主线任务中竞争,可能导致实际值不符合预期,引起低概率的定时器重设失败。

代码实现:

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)函数置于定时器的中断中即可,以STM32的滴答定时器为例(该函数位于 stm32f4xx_it.c)

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */
  extern void globalTimerRun(void);
  globalTimerRun();
  /* USER CODE END SysTick_IRQn 1 */
}

主循环进入While(1)之前调用。

heartBeatInit(EnterITPer_1Ms);

判断定时是否到达:

if(timer.overFlow){
    /* do something... */
}

重置定时器:

timer.timerReset(&timer, timeval);

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值