单片机裸机实用组件--软件定时器、时间戳
生活随笔
收集整理的这篇文章主要介绍了
单片机裸机实用组件--软件定时器、时间戳
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
单片机裸机实用组件–软件定时器、时间戳
之前写过一篇关于单片机定时器延时计时功能的博客 ,刚工作的时候搞得现在看来还是比较糙的,是时候整一个新的了。
base_timer
单片机裸机适用的定时器小组件,通过一个定时器记录系统运行时长,为用户提供一个时间戳信息,当然也有软件定时器功能。
移植教程
使用说明
需要注意的是,us和ms相关的函数都是在调用 uint64_t btimer_get_ticks(void); 函数的基础上使用除法实现的,但是没有double FPU的单片机对 uint64_t 的除法运算比较耗时,实测STM32F103 72MHz时, 一次uint64_t 需要50us左右
- 调用软件定时器初始化函数初始化一个软件定时器
- 开启关闭定时器
使用例子
/* 定时器超时回调函数 */ 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; } #endifbase_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#endifbase_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 《新程序员》:云原生和全面数字化实践50位技术专家共同创作,文字、视频、音频交互阅读总结
以上是生活随笔为你收集整理的单片机裸机实用组件--软件定时器、时间戳的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: FreeRtos学习笔记(11)查找就绪
- 下一篇: 单片机裸机实用组件--LED