当前位置:网站首页>MultiTimer v2 重构版本 | 一款可无限扩展的软件定时器

MultiTimer v2 重构版本 | 一款可无限扩展的软件定时器

2022-04-23 15:22:00 Mculover666

前言

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

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

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

同步更新下教程。

一、MultiTimer

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

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

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

二、移植MultiTimer

1. 移植思路

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

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

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

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

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

2.MDK移植

① 复制MultiTimer源码到工程中:

② 在keil中添加 MultiTimer的源码文件:
在这里插入图片描述

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

3. gcc移植

① 复制MultiTimer源码到工程中:

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

③ 添加MultiTimer头文件路径:

三、使用MultiTimer

使用时包含头文件:

#include "multi_timer.h"

1. 提供Timer时基信号

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

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

PlatformTicksFunction_t 函数指针定义如下:

typedef uint64_t (*PlatformTicksFunction_t)(void);

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

编写获取系统 tick 的函数:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint64_t PlatformTicksGetFunc(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 */

2. 创建Timer对象

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

struct MultiTimerHandle {
    
    MultiTimer* next;
    uint64_t deadline;
    MultiTimerCallback_t callback;
    void* userData;
};

typedef struct MultiTimerHandle MultiTimer;

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

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

3. Timer回调函数

回调函数类型定义如下:

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

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

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

4. 初始化并启动Timer

启动定时器的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. */
int MultiTimerStart(MultiTimer* timer, uint64_t timing, 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 */

5. Timer对象处理

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

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

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

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

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

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

四、如何循环触发

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

void timer1_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版本的教程。

接收更多精彩文章及资源推送,欢迎订阅我的微信公众号:『mculover666』。

版权声明
本文为[Mculover666]所创,转载请带上原文链接,感谢
https://mculover666.blog.csdn.net/article/details/124359206