当前位置:首页 » 《随便一记》 » 正文

单片机裸机实用组件--软件定时器、时间戳_不咸不要钱

17 人参与  2022年03月22日 12:34  分类 : 《随便一记》  评论

点击全文阅读


单片机裸机实用组件–软件定时器、时间戳

之前写过一篇关于单片机定时器延时计时功能的博客 ,刚工作的时候搞得现在看来还是比较糙的,是时候整一个新的了。

base_timer

单片机裸机适用的定时器小组件,通过一个定时器记录系统运行时长,为用户提供一个时间戳信息,当然也有软件定时器功能。

移植教程

  1. 将 base_timer.c base_timer.h base_timer_port.c 添加到工程中
  2. 打开 base_timer_port.c 文件,自行实现里面的函数, 其中最重要的是 btimer_port_init(uint32_t clock, uint16_t ticks)函数和 uint32_t btimer_port_get_ticks(void)函数
/*!
  * @brief    初始化定时器
  *
  * @param    clock:  定时器时钟
  * @param    ticks:  定时器每s中断ticks次
  *
  * @return   无
  *
  * @note     移植时需要根据使用的定时器实现该函数
  *
  * @see      btimer_port_init(72000000, 1000);  
  *
  * @date     2019/07/15
  */
void btimer_port_init(uint32_t clock, uint16_t ticks)
{

}

/*!
  * @brief    获取定时器的tick值
  *
  * @param    无
  *
  * @return   ticks
  *
  * @note     移植时需要根据使用的定时器实现该函数
  *
  * @see      
  *
  * @date     2019/07/15
  */
uint32_t btimer_port_get_ticks(void)
{
    return ;
}
  1. 将 btimer_ticks();函数添加到定时器中断服务函数中,周期性的执行。

使用说明

  1. 初始化定时器
void btimer_init(uint32_t clock, uint16_t ticks);
  1. 定时器初始化后,系统内就存在了一个时基,可以获取系统从定时器初始化到现在的运行 ticks,通过这个时间戳信息,就可以实现各种各样的功能了。
    需要注意的是,us和ms相关的函数都是在调用 uint64_t btimer_get_ticks(void); 函数的基础上使用除法实现的,但是没有double FPU的单片机对 uint64_t 的除法运算比较耗时,实测STM32F103 72MHz时, 一次uint64_t 需要50us左右
uint64_t btimer_get_ticks(void);
uint32_t btimer_get_us(void);
uint32_t btimer_get_ms(void);
void btimer_delay_ticks(uint32_t ticks);
void btimer_delay_us(uint32_t us);
void btimer_delay_ms(uint32_t ms);
  1. 组件内同样也有实现软件定时器,在 base_timer.h 中使能
/*! 是否使能软件定时器 */
#define SOFT_TIMER_ENABLE  1
  • 调用软件定时器初始化函数初始化一个软件定时器
void stimer_init(Stimer_t * stimer, uint16_t ticks, uint8_t count, stimerCallback_t cb);
  • 开启关闭定时器
void stimer_start(Stimer_t * stimer);
void stimer_stop(Stimer_t * stimer);

使用例子

/* 定时器超时回调函数 */
void stimerLed1Callback(void)
{
  //do something
}

void stimerLed2Callback(void)
{
  //do something
}

void stimerLed3Callback(void)
{
  //do something
}

Stimer_t g_stimerLed1, g_stimerLed2, g_stimerLed3;

int main(void)
{

  /* 初始化定时器 定时器时钟 72MHz 每秒中断1000次 */
  btimer_init(72000000, 1000);
  
  /* 获取时间戳 */
  uint64_t ticks1 = btimer_get_ticks();
  
  /* 延时 10000us */
  btimer_delay_us(10000);
  
  /* 获取时间戳 通过 ticks2 - ticks1 可以获得 btimer_delay_us(10000); 运行时长 */
  uint64_t ticks2 = btimer_get_ticks();
  
  /* 延时 10ms */
  btimer_delay_ms(10);
  
  stimer_init(&g_stimerLed1, 100,  COUNTER_MAX, stimerLed1Callback);
  stimer_init(&g_stimerLed2, 1000, 1,           stimerLed2Callback);
  stimer_init(&g_stimerLed3, 1000, COUNTER_MAX, stimerLed3Callback);
  
  stimer_start(&g_stimerLed1);
  stimer_start(&g_stimerLed2);
  stimer_start(&g_stimerLed3);

  while (1)
  {
    if(stimer_is_timeout(&g_stimerLed2))
    {
      stimer_init(&g_stimerLed2, 1000, 1,           stimerLed2Callback);
      stimer_start(&g_stimerLed2);
    }
  }
}

base_timer.c

/*!
  * @file     base_timer.c
  *
  * @brief    系统计时器
  *
  * @company   
  *
  * @author   不咸不要钱
  *
  * @note     
  *
  * @version  V1.2
  *
  * @Software  
  *
  * @par URL  
  *           
  *
  * @date     2019/07/15
  */ 
#include "base_timer.h"

/** 
  * @brief 一个时间结构体
  * @note  内部调用
  */
typedef struct{
    uint32_t ulTickPerMicrosecond;                  /*!<每us tick计数值         */
    uint32_t ulTickPerMillisecond;                  /*!<每ms tick计数值         */
    uint32_t ulTickPerHandler;                      /*!<每?ticks 定时器中断一次*/
    volatile uint64_t ullTicks;                     /*!<当前ticks               */
    uint32_t ulDelayOffSet;                         /*!<函数零偏                */
}BaseTimerParame_t;

/** 保存定时器参数 */
static BaseTimerParame_t s_tTimer;

#if defined(SOFT_TIMER_ENABLE) && (SOFT_TIMER_ENABLE == 1)
static void stimer_ticks(void);
#endif

/*!
  * @brief    定时器tick自增
  *
  * @param    无
  *
  * @return   无
  *
  * @note     需要在定时器中断服务函数中调用 btimer_ticks(); 
  *
  * @see      无
  *
  * @date     2019/07/15
  */
void btimer_ticks(void)
{
    s_tTimer.ullTicks     += s_tTimer.ulTickPerHandler;
    
    #if defined(SOFT_TIMER_ENABLE) && (SOFT_TIMER_ENABLE == 1)
        stimer_ticks();    
    #endif
}

/*!
  * @brief    初始化定时器
  *
  * @param    clock:  定时器时钟 clock要大于等于 1M
  * @param    ticks:  定时器每s中断ticks次 
  *
  * @return   无
  *
  * @note     
  *
  * @see      btimer_init(72000000, 1000);  
  *
  * @date     2019/07/15
  */
void btimer_init(uint32_t clock, uint16_t ticks)
{
    
    if(clock < 1000000ul)
    {
        /* 定时器频率 小于1MHz  每us tick计数值 固定为1 
           注意:定时器频率过低,使用us相关函数时会不准确 */
        s_tTimer.ulTickPerMicrosecond = 1;
    }
    else
    {
        s_tTimer.ulTickPerMicrosecond = clock / 1000000ul;
    }
    
    if(clock < 1000)
    {
        /* 定时器频率 小于1KHz  
           注意:定时器频率过低 */
        while(1);
    }
    else
    {
        s_tTimer.ulTickPerMillisecond = clock / 1000;
    }
    
    s_tTimer.ullTicks = 0;
    s_tTimer.ulTickPerHandler = clock / ticks;
    s_tTimer.ulDelayOffSet = 0;
    
    btimer_port_init(clock, ticks);
    
    /* 去零偏 */
    uint32_t currentTicks1 = 0, currentTicks2 = 0;
    for(int i = 0; i < 1000; i++)
    {
        currentTicks1 = btimer_get_ticks();
        btimer_delay_us(1);
        currentTicks2 = btimer_get_ticks();
        s_tTimer.ulDelayOffSet += (currentTicks2 - currentTicks1 - s_tTimer.ulTickPerMicrosecond);
    }
    s_tTimer.ulDelayOffSet = (s_tTimer.ulDelayOffSet + 500)/1000;
}


/*!
  * @brief    关闭定时器
  *
  * @param    无
  *
  * @return   无
  *
  * @note     
  *
  * @see      BaseTimerDeInit();  
  *
  * @date     2019/07/15
  */
void btimer_deinit(void)
{
    btimer_port_deinit();
}

/*!
  * @brief    获取当前tick值
  *
  * @param    无
  *
  * @return   从定时器初始化到当前的ticks
  *
  * @note     
  *
  * @see      
  *
  * @date     2019/07/15
  */
uint64_t btimer_get_ticks(void)
{
    uint64_t ticks = 0;
    uint32_t timerValue = 0;
    do
    {
        ticks = s_tTimer.ullTicks;
        timerValue = btimer_port_get_ticks();
        
    }while(ticks != s_tTimer.ullTicks);
    return ticks + timerValue;
}

/*!
  * @brief    获取当前us值
  *
  * @param    无
  *
  * @return   从定时器初始化到当前的us
  *
  * @note     uint64 除法非常耗时  stm32F1xx 做一次 uint64除法大概需要50us
  *
  * @see      
  *
  * @date     2019/07/15
  */
uint32_t btimer_get_us(void)
{
    return btimer_get_ticks()/s_tTimer.ulTickPerMicrosecond;
}



/*!
  * @brief    获取当前ms值
  *
  * @param    无
  *
  * @return   从定时器初始化到当前的ms
  *
  * @note     uint64 除法非常耗时  stm32F1xx 做一次 uint64除法大概需要50us
  *
  * @see      
  *
  * @date     2019/07/15
  */
uint32_t btimer_get_ms(void)
{
    return btimer_get_ticks()/s_tTimer.ulTickPerMillisecond;
}



/*!
  * @brief    延时
  *
  * @param    ticks : 要延时的ticks
  *
  * @return   无
  *
  * @note     
  *
  * @see      
  *
  * @date     2019/07/15
  */
void btimer_delay_ticks(uint32_t ticks)
{
    uint64_t end = btimer_get_ticks() + ticks - s_tTimer.ulDelayOffSet;
    
    while(btimer_get_ticks() <= end);
}

/*!
  * @brief    延时
  *
  * @param    us : 要延时的us
  *
  * @return   无
  *
  * @note     
  *
  * @see      
  *
  * @date     2019/07/15
  */
void btimer_delay_us(uint32_t us)
{
    uint64_t end = btimer_get_ticks() + us * s_tTimer.ulTickPerMicrosecond - s_tTimer.ulDelayOffSet;
    
    while(btimer_get_ticks() <= end);
}

/*!
  * @brief    延时
  *
  * @param    ms : 要延时的ms
  *
  * @return   无
  *
  * @note     
  *
  * @see      
  *
  * @date     2019/07/15
  */
void btimer_delay_ms(uint32_t ms)
{
    uint64_t end = btimer_get_ticks() + ms * s_tTimer.ulTickPerMillisecond - s_tTimer.ulDelayOffSet;
    
    while(btimer_get_ticks() <= end);
}


#if defined(SOFT_TIMER_ENABLE) && (SOFT_TIMER_ENABLE == 1)

/** 指向软件定时器驱动链表头节点 */
static Stimer_t * s_stimer_head = 0;

/*!
  * @brief    软件定时器节拍处理
  *
  * @param    无
  *
  * @return   无
  *
  * @note     需要周期性调用该函数  软件定时器ticks均以该周期为单位 
  *
  * @see      
  *
  * @date     2019/07/15
  */
static void stimer_ticks(void)
{
    Stimer_t * node = s_stimer_head;
    
    while(node)
    {
        if(node->ucCounter > 0)
        {  
            if(-- node->ulTicks == 0)
            {
                if(node->ucCounter != COUNTER_MAX)
                {
                    node->ucCounter--;
                }

                node->ulTicks = node->ulPerLoad;
                node->ucFlag  = 1;
                
                if(node->stimerCallback != 0)
                {
                    node->stimerCallback((void *)node);
                }
                
                if(node->ucCounter == 0)
                {
                    stimer_stop(node);
                }
            }
        }
        else
        {
            stimer_stop(node);
        } 
        
        node = node->ptNextTimer;
    }
}    

/*!
  * @brief    软件定时器初始化
  *
  * @param    stimer:软件定时器
  * @param    ticks :软件定时器定时时间
  * @param    count :软件定时器定时次数  如果为0xFF 则为永久
  * @param    cb    :软件定时器溢出回调函数
  *
  * @return   无
  *
  * @note     cb 回调函数会在 btimer_ticks() 中执行,而 btimer_ticks() 一般在中断服务函数中,因此定时器溢出回调函数尽量快进快出
  *
  * @see      
  *
  * @date     2019/07/15
  */
void stimer_init(Stimer_t * stimer, uint16_t ticks, uint8_t count, stimerCallback_t cb)
{
    stimer_port_lock();
    
    stimer->ulTicks   = ticks;
    stimer->ulPerLoad = ticks;
    stimer->ucCounter = count;
    stimer->ucFlag    = 0;
    stimer->stimerCallback = cb;
    
    stimer_port_unlock();
}

/*!
  * @brief    开启软件定时器
  *
  * @param    stimer:软件定时器
  *
  * @return   无
  *
  * @note     将软件定时器添加到链表上
  *
  * @see      
  *
  * @date     2019/07/15
  */
void stimer_start(Stimer_t * stimer)
{
    stimer_port_lock();
  
    /* 防止重复 */
    Stimer_t * node = s_stimer_head;

    while(node)
    {
        if(node == stimer)
        {
            stimer_port_unlock();
            return;
        }
        else
        {
            node = node->ptNextTimer;
        }   
    }

    stimer->ptNextTimer  = s_stimer_head;
    s_stimer_head        = stimer;
    
    stimer_port_unlock();
}

/*!
  * @brief    停止软件定时器
  *
  * @param    stimer:软件定时器
  *
  * @return   无
  *
  * @note     将软件定时器从定时器链表上移除
  *
  * @see      
  *
  * @date     2019/07/15
  */
void stimer_stop(Stimer_t * stimer)
{
    Stimer_t** curr;
	  for(curr = &s_stimer_head; *curr; ) 
    {
        Stimer_t* entry = *curr;
        if (entry == stimer) 
        {
            stimer_port_lock();
                
            *curr = entry->ptNextTimer;
                
            stimer_port_unlock();
            return;
        } 
        else
        {
            curr = &entry->ptNextTimer;
        }   	
	  }
}

/*!
  * @brief    停止软件定时器超时
  *
  * @param    stimer:软件定时器
  *
  * @return   无
  *
  * @note     调用后会自动清除超时标志位
  *
  * @see      
  *
  * @date     2019/07/15
  */
uint8_t stimer_is_timeout(Stimer_t * stimer)
{
    uint8_t flag = stimer->ucFlag;
    
    stimer_port_lock();
    
    stimer->ucFlag = 0;
    
    stimer_port_unlock();
    
    return flag;
}
#endif



base_timer.h

/*!
  * @file     base_timer.h
  *
  * @brief    系统计时器
  *
  * @company   
  *
  * @author   不咸不要钱
  *
  * @note     
  *
  * @version  V1.2
  *
  * @Software C99
  *
  * @par URL  
  *           
  *
  * @date     2019/07/15
  */ 
#ifndef __BASE_TIMER_H
#define __BASE_TIMER_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

/* 用户函数 */
void btimer_init(uint32_t clock, uint16_t ticks);
void btimer_deinit(void);
uint64_t btimer_get_ticks(void);
uint32_t btimer_get_us(void);
uint32_t btimer_get_ms(void);
void btimer_delay_ticks(uint32_t ticks);
void btimer_delay_us(uint32_t us);
void btimer_delay_ms(uint32_t ms);
void btimer_ticks(void);


/*! 是否使能软件定时器 */
#define SOFT_TIMER_ENABLE  1

#if defined(SOFT_TIMER_ENABLE) && (SOFT_TIMER_ENABLE == 1)


/*! 定时器重复最大次数 */
#define COUNTER_MAX  (0xFF)

/*! 函数指针 指向软件定时器超时回调函数 */
typedef void (*stimerCallback_t)(void *);

/** 
  * @brief stimer 软件定时器结构体
  * @note  无
  */
typedef struct Stimer
{
    volatile uint8_t  ucCounter;
    volatile uint8_t  ucFlag;
    volatile uint32_t ulTicks;
             uint32_t ulPerLoad;
    stimerCallback_t  stimerCallback;
    struct Stimer * ptNextTimer;
}Stimer_t;

void stimer_init(Stimer_t * stimer, uint16_t ticks, uint8_t count, stimerCallback_t cb);
void stimer_start(Stimer_t * stimer);
void stimer_stop(Stimer_t * stimer);
uint8_t stimer_is_timeout(Stimer_t * stimer);

void stimer_port_lock(void);
void stimer_port_unlock(void);
#endif




/* 移植时需用户实现 */
void btimer_port_init(uint32_t clock, uint16_t ticks);
void btimer_port_deinit(void);
uint32_t btimer_port_get_ticks(void);





#ifdef __cplusplus
}
#endif

#endif


base_timer_port.c

/*!
  * @file     base_timer_port.c
  *
  * @brief    系统计时器用户接口
  *
  * @company   
  *
  * @author   不咸不要钱
  *
  * @note     
  *
  * @version  V1.2
  *
  * @Software C99
  *
  * @par URL  
  *           
  *
  * @date     2019/07/15
  */ 
#include "base_timer.h"


/*!
  * @brief    初始化定时器
  *
  * @param    clock:  定时器时钟
  * @param    ticks:  定时器每s中断ticks次
  *
  * @return   无
  *
  * @note     移植时需要根据使用的定时器实现该函数
  *
  * @see      btimer_port_init(72000000, 1000);  
  *
  * @date     2019/07/15
  */
void btimer_port_init(uint32_t clock, uint16_t ticks)
{

}

/*!
  * @brief    关闭定时器
  *
  * @param    无
  *
  * @return   无
  *
  * @note     
  *
  * @see      btimer_port_deinit();  
  *
  * @date     2019/07/15
  */
void btimer_port_deinit(void)
{

}

/*!
  * @brief    获取定时器的tick值
  *
  * @param    无
  *
  * @return   ticks
  *
  * @note     移植时需要根据使用的定时器实现该函数
  *
  * @see      
  *
  * @date     2019/07/15
  */
uint32_t btimer_port_get_ticks(void)
{
    return ;
}


#if defined(SOFT_TIMER_ENABLE) && (SOFT_TIMER_ENABLE == 1)
/*!
  * @brief    加锁
  *
  * @param    
  *
  * @return   无
  *
  * @note     
  *
  * @see      
  */
void stimer_port_lock(void)
{

}

/*!
  * @brief    开锁
  *
  * @param    无
  *
  * @return   无
  *
  * @note     
  *
  * @see      
  */
void stimer_port_unlock(void)
{

}

#endif


点击全文阅读


本文链接:http://zhangshiyu.com/post/36504.html

定时器  函数  软件  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1