单片机裸机实用组件--软件定时器时间戳
Posted 不咸不要钱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单片机裸机实用组件--软件定时器时间戳相关的知识,希望对你有一定的参考价值。
单片机裸机实用组件–软件定时器、时间戳
之前写过一篇关于单片机定时器延时计时功能的博客 ,刚工作的时候搞得现在看来还是比较糙的,是时候整一个新的了。
base_timer
单片机裸机适用的定时器小组件,通过一个定时器记录系统运行时长,为用户提供一个时间戳信息,当然也有软件定时器功能。
移植教程
- 将 base_timer.c base_timer.h base_timer_port.c 添加到工程中
- 打开 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 ;
- 将 btimer_ticks();函数添加到定时器中断服务函数中,周期性的执行。
使用说明
- 初始化定时器
void btimer_init(uint32_t clock, uint16_t ticks);
- 定时器初始化后,系统内就存在了一个时基,可以获取系统从定时器初始化到现在的运行 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);
- 组件内同样也有实现软件定时器,在 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
以上是关于单片机裸机实用组件--软件定时器时间戳的主要内容,如果未能解决你的问题,请参考以下文章