MultiTimer|一款可无限扩展的软件定时器
来源:嵌入式大杂烩 发布时间:2022-05-04 分享至微信

嵌入式开源项目精选专栏之前发布过一篇关于MultiTimer的文章,MultiTimer | 一款可无限扩展的软件定时器,这周有小伙伴在群里提醒我 MutilTimer 和文章写的不太一样,第一反应是重构了,大佬们技术水平提升一个段位后都喜欢重构项目,去github看看发生了什么。


master分支上还是之前的v1版本,和文章是一样的:




development分支上果然重构了项目,发布了v2版本:




同步更新下教程。


本期给大家带来的开源项目是 MultiTimer,一款可无限扩展的软件定时器,作者0x1abin,目前收获 399 个 star,遵循 MIT 开源许可协议。


MultiTimer 是一个软件定时器扩展模块,可无限扩展你所需的定时器任务,取代传统的标志位判断方式, 更优雅更便捷地管理程序的时间触发时序。


项目地址:https://github.com/0x1abin/MultiTimer


开源项目在移植过程中主要参考项目的readme文档,一般只需两步:

  • ① 添加源码到裸机工程中;
  • ② 实现需要的接口;


本文中我使用的是小熊派IoT开发套件,主控芯片为STM32L431RCT6:



移植之前需要准备一份裸机工程,我使用STM32CubeMX生成,需要初始化以下配置:

  • 配置一个串口用于打印信息
  • printf重定向


① 复制MultiTimer源码到工程中:


② 在keil中添加 MultiTimer的源码文件:


③ 将MultiTimer头文件路径添加到keil中:


① 复制MultiTimer源码到工程中:



② 在 Makefile 中添加 MultiTimer的源码文件:



③ 添加MultiTimer头文件路径:



使用时包含头文件:

#include"multi_timer.h"


MultiTimer中的时基信号需要安装,API如下:

/**
* @brief Platform ticks function.
*
* @param ticksFunc ticks function.
* @return int 0 on success, -1 on error.
*/
intMultiTimerInstall(PlatformTicksFunction_t ticksFunc);


PlatformTicksFunction_t 函数指针定义如下:

typedefuint64_t(*PlatformTicksFunction_t)(void);


本文中使用的是STM32HAL库,所以通过Systick来提供,无需设置额外的定时器。


编写获取系统 tick 的函数:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint64_tPlatformTicksGetFunc(void)
{
return(uint64_t)HAL_GetTick();
}
/* USER CODE END 0 */


在main函数中安装该tick函数:

/* USER CODE BEGIN 2 */
printf("MultiTimer v2 Port on BearPi board by mculover666!\r\n");
MultiTimerInstall(PlatformTicksGetFunc);
/* USER CODE END 2 */


软件定时器抽象为 MultiTimer 结构体:

structMultiTimerHandle{
MultiTimer* next;
uint64_tdeadline;
MultiTimerCallback_t callback;
void* userData;
};

typedefstructMultiTimerHandleMultiTimer;


所以直接使用 MultiTimer 类型创建软件定时器:

/* USER CODE BEGIN PV */
MultiTimer timer1;
/* USER CODE END PV */


回调函数类型定义如下:

typedefvoid(*MultiTimerCallback_t)(MultiTimer* timer,void* userData);


按照回调函数格式,创建超时回调函数:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
voidtimer1_callback(MultiTimer* timer,void* userData)
{
printf("timer1 timeout!\r\n");
}
/* USER CODE END 0 */


启动定时器的API如下:

/**
* @brief Start the timer work, add the handle into work list.
*
* @param timer target handle strcut.
* @param timing Set the start time.
* @param callback deadline callback.
* @param userData user data.
* @return int 0: success, -1: fail.
*/
intMultiTimerStart(MultiTimer* timer,uint64_ttiming, MultiTimerCallback_t callback,void* userData);


初始化定时器对象,注册定时器回调处理函数,设置超时时间(ms):

/* USER CODE BEGIN 2 */
printf("MultiTimer v2 Port on BearPi board by mculover666!\r\n");
MultiTimerStart(&timer1,1000, timer1_callback,NULL);
/* USER CODE END 2 */


Timer对象处理函数API定义如下:

/**
* @brief Check the timer expried and call callback.
*
* @return int The next timer expires.
*/
intMultiTimerYield(void);


在主循环中调用Timer对象处理函数,处理函数会判断链表上的每个定时器是否超时,如果超过,则拉起注册的回调函数:

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while(1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
MultiTimerYield();
}
/* USER CODE END 3 */


接下来编译下载,看在串口助手中看到打印的日志:



在定时器超时函数中,重启定时器即可。

voidtimer1_callback(MultiTimer* timer,void* userData)
{
printf("timer1 timeout!\r\n");

// restart
MultiTimerStart(&timer1,1000, timer1_callback,NULL);
}


相对于v1版本,v2版本明显涉及简洁很多,c文件实现只有4个函数,82行代码。


v2版本中使用注册机制由用户提供tick,这样设计有个好处是,可移植性更强,无需干预系统tick中断,只有MultiTimer得到调度的时候,它才可以通过我们安装的API获取到系统tick,以此为基准来判断定时器是否超时。


v2版本还优化了链表插入机制,之前是简单粗暴直接单链表插入节点,现在通过超时时间排序插入,更加优雅:




除了插入的更加优雅之外,这样做还有两个对于软件定时器性能的提升,在调度的时候:

  • 超时时间近的定时器总能得到优先处理
  • 前面的定时器还未超时,可以直接结束调度

软件定时器实现思想可以参考之前v1版本的教程。


[ 新闻来源:嵌入式大杂烩,更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj]
存入云盘 收藏
举报
全部评论

暂无评论哦,快来评论一下吧!