前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MultiTimer v2 重构版本 | 一款可无限扩展的软件定时器

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

作者头像
Mculover666
发布2022-05-23 09:39:06
7660
发布2022-05-23 09:39:06
举报
文章被收录于专栏:TencentOS-tiny

前言

嵌入式开源项目精选专栏之前发布过一篇关于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

使用时包含头文件:

代码语言:javascript
复制
#include "multi_timer.h"

1. 提供Timer时基信号

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

代码语言:javascript
复制
/**
 * @brief Platform ticks function.
 * 
 * @param ticksFunc ticks function.
 * @return int 0 on success, -1 on error.
 */
int MultiTimerInstall(PlatformTicksFunction_t ticksFunc);

PlatformTicksFunction_t 函数指针定义如下:

代码语言:javascript
复制
typedef uint64_t (*PlatformTicksFunction_t)(void);

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

编写获取系统 tick 的函数:

代码语言:javascript
复制
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint64_t PlatformTicksGetFunc(void)
{
  return (uint64_t)HAL_GetTick();
}
/* USER CODE END 0 */

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

代码语言:javascript
复制
/* USER CODE BEGIN 2 */
printf("MultiTimer v2 Port on BearPi board by mculover666!\r\n");
MultiTimerInstall(PlatformTicksGetFunc);
/* USER CODE END 2 */

2. 创建Timer对象

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

代码语言:javascript
复制
struct MultiTimerHandle {
    MultiTimer* next;
    uint64_t deadline;
    MultiTimerCallback_t callback;
    void* userData;
};

typedef struct MultiTimerHandle MultiTimer;

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

代码语言:javascript
复制
/* USER CODE BEGIN PV */
MultiTimer timer1;
/* USER CODE END PV */

3. Timer回调函数

回调函数类型定义如下:

代码语言:javascript
复制
typedef void (*MultiTimerCallback_t)(MultiTimer* timer, void* userData);

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

代码语言:javascript
复制
/* 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如下:

代码语言:javascript
复制
/**
 * @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):

代码语言:javascript
复制
/* 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定义如下:

代码语言:javascript
复制
/**
 * @brief Check the timer expried and call callback.
 * 
 * @return int The next timer expires.
 */
int MultiTimerYield(void);

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

代码语言:javascript
复制
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
  /* USER CODE END WHILE */

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

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

四、如何循环触发

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

代码语言:javascript
复制
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版本的教程。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Mculover666 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、MultiTimer
  • 二、移植MultiTimer
    • 1. 移植思路
      • 2.MDK移植
        • 3. gcc移植
        • 三、使用MultiTimer
          • 1. 提供Timer时基信号
            • 2. 创建Timer对象
              • 3. Timer回调函数
                • 4. 初始化并启动Timer
                • 5. Timer对象处理
                • 四、如何循环触发
                • 五、设计思想解读
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档