目录

一、前言

1、mx_timlib是什么?

2、特性一览

3、支持的场景

4、项目地址:

二、快速开始

1、移植临界区宏

2、初始化

3、创建和启动定时器

4、驱动定时器

5、完整示例:LED 闪烁 + 传感器采样 + 单次任务 + 动态修改

三、API一览

1、初始化

2、生命周期

3、查询

4、修改

5、驱动

6、配置宏

7、回调函数

四、开源协议


一、前言

做嵌入式开发的时候,你是否遇到过这样的困扰?

  • 项目里需要同时管理 LED 闪烁、传感器定时采样、按键消抖、UART 心跳包……四五个定时任务
  • 不想引入 RTOS,觉得太重
  • 又不想自己手写一堆 HAL_TIM_Base_StartHAL_TIM_PeriodElapsedCallback
  • 于是代码里充斥着 if (HAL_GetTick() - last_led_tick > 500) 这样的裸写判断

如果你有同感,那么 mx_timlib 就是为你设计的。

mx_timlib 是一款专为嵌入式场景打造的轻量级定时器轮片(timer wheel)组件。核心实现 300+ 行代码,零外部依赖,支持裸机和 RTOS 环境。支持两种使用模式:池化模式(Classic API)和用户自有模式(Extended API),可按需选择。

1、mx_timlib是什么?

mx_timlib 是一个时间驱动组件,v3.1 版本采用了双队列 + 有序链表架构,实现了 O(1) 的到期检查:

┌─────────────────────────────────────────────┐
│         1ms 定时器中断 (mx_timer_irq_handler) │
│                                              │
│  tick++                                      │
│  检查 active_list 头部(只检查一个!)      │
│  到期 → 移入 expired_queue (头插法)         │
│  PERIODIC: 预计算下次 expiry                 │
└─────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────┐
│     主循环 (mx_timer_task_handler)           │
│                                              │
│  从 expired_queue 弹出(头删法)            │
│  执行 callback(临界区外,允许耗时)         │
│  PERIODIC: 回调执行完后重新插入 active_list │
└─────────────────────────────────────────────┘

关键设计:

  • ISR 只检查 active_head 一个节点是否为最新到期,不是遍历全表
  • ISR 和主循环操作不同的队列,通过 expired_queue 解耦
  • 临界区宏只在主循环中使用,ISR 由硬件保证原子性
  • PERIODIC 定时器的 re-arm 在回调执行之后完成,消除了 zombie 定时器问题
2、特性一览

特性

说明

O(1) 到期检查

有序链表,ISR 只检查表头,不遍历全表

双队列设计

active_list(计时)+ expired_queue(待执行),ISR 与主循环完全解耦

两种模式

PERIODIC

周期模式(自动重载)和 ONCE

单次模式(触发后自动停止)

分布式定时器

用户可静态定义 mx_timer_t

,不占用内部池,数量无限制

丰富的查询 API

查剩余时间、暂停/恢复、动态修改周期、低功耗唤醒间隔

中断安全

active_list 和 expired_queue 分别由 ISR 和主循环操作,flag 同步

无外部依赖

纯 C 代码,编译后仅数 KB

MIT 协议

可商用,可修改,开源无忧


3、支持的场景
  • 裸机项目 — STM32、GD32、NXP 等 MCU,无需操作系统
  • RTOS 环境 — FreeRTOS、RT-Thread 等,timer 作为系统心跳或任务内延时
  • 低功耗场景mx_timer_next_expiry() 返回距下次唤醒的 tick 数,配合深度睡眠使用
  • 任何有定时器外设的平台 — 只需提供 1ms 周期中断
4、项目地址:

觉得项目还可以的请帮博主点点⭐,后续有bug可以直接反馈给我

https://github.com/jin-le-le/mx_timlib.git

二、快速开始

1、移植临界区宏

在包含 mx_timlib.h之前定义两个宏,这是唯一的移植工作。

推荐方案:只屏蔽目标定时器中断(不影响其他外设)

// STM32,只屏蔽 TIM2,不影响 UART、SPI 等其他外设中断
#define MX_ENTER_CRITICAL()  NVIC_DisableIRQ(TIM2_IRQn)
#define MX_EXIT_CRITICAL()   NVIC_EnableIRQ(TIM2_IRQn)
#include "mx_timlib.h"

备选方案:全局关中断(简单兼容)

// STM32 HAL
#define MX_ENTER_CRITICAL()  __disable_irq()
#define MX_EXIT_CRITICAL()   __enable_irq()

// 裸机 ARM GCC
#define MX_ENTER_CRITICAL()  __asm__("cpsid i")
#define MX_EXIT_CRITICAL()   __asm__("cpsie i")
临界区宏只在主循环中使用,ISR 不需要临界区保护(由硬件保证原子性)。
2、初始化
#include "mx_timlib.h"

int main(void)
{
    mx_timer_init();  // 只需调用一次
    // ... 创建和启动定时器 ...
}
3、创建和启动定时器

方式一:Classic API(池化模式) :

固定数量定时器池,mx_timer_create() 返回 ID,简单直接:

// 周期定时器:LED 每秒翻转一次
mx_timer_id_t id = mx_timer_create(1000, MX_TIMER_MODE_PERIODIC, led_toggle, NULL);
mx_timer_start(id);

// 单次定时器:500ms 后触发一次,然后自动停止
mx_timer_id_t id2 = mx_timer_create(500, MX_TIMER_MODE_ONCE, uart_send, NULL);
mx_timer_start(id2);

方式二:Extended API(用户自有模式)

用户自己在 .c 文件中定义 mx_timer_t,不占用内部池:

static mx_timer_t my_sensor;  // 用户自有

mx_timer_init_ex(&my_sensor, 200, MX_TIMER_MODE_PERIODIC, sensor_sample, NULL);
mx_timer_attach(&my_sensor);
mx_timer_start_ex(&my_sensor);
4、驱动定时器

在 1ms 定时器中断中调用:

void TIM2_IRQHandler(void)
{
    mx_timer_irq_handler();
    // 清除中断标志(根据具体芯片)
}

在主循环中调用:

while (1)
{
    mx_timer_task_handler();
    // 其他业务逻辑可以放在这里
}
5、完整示例:LED 闪烁 + 传感器采样 + 单次任务 + 动态修改
#include "mx_timlib.h"

// ===== 移植临界区宏(STM32)=====
#define MX_ENTER_CRITICAL()  NVIC_DisableIRQ(TIM2_IRQn)
#define MX_EXIT_CRITICAL()   NVIC_EnableIRQ(TIM2_IRQn)

// ===== 回调函数 =====

/* LED 每秒翻转一次(PERIODIC)*/
void led_toggle(void *param)
{
    (void)param;
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}

/* 传感器每 100ms 采样一次(PERIODIC)*/
void sensor_sample(void *param)
{
    (void)param;
    uint32_t value = read_adc();
    process_data(value);
}

/* 系统启动后 2 秒打印一次(ONCE)*/
void startup_task(void *param)
{
    (void)param;
    printf("System started!\n");
}

// ===== 主函数 =====

int main(void)
{
    mx_timer_init();

    /* Classic API */
    mx_timer_id_t id_led  = mx_timer_create(1000, MX_TIMER_MODE_PERIODIC, led_toggle,     NULL);
    mx_timer_id_t id_once = mx_timer_create(2000, MX_TIMER_MODE_ONCE,     startup_task,  NULL);
    mx_timer_start(id_led);
    mx_timer_start(id_once);

    /* Extended API(用户自有传感器定时器)*/
    static mx_timer_t sensor_timer;
    mx_timer_init_ex(&sensor_timer, 100, MX_TIMER_MODE_PERIODIC, sensor_sample, NULL);
    mx_timer_attach(&sensor_timer);
    mx_timer_start_ex(&sensor_timer);

    /* 动态修改 LED 周期(1秒改为2秒)*/
    mx_timer_set_period(id_led, 2000);

    /* 暂停/恢复演示 */
    mx_timer_pause(id_led);    // 暂停
    mx_timer_resume(id_led);  // 恢复

    while (1)
    {
        mx_timer_task_handler();
    }
}

三、API一览

1、初始化

函数

说明

mx_timer_init()

初始化组件(池化模式)

mx_timer_init_ex(timer, ms, mode, cb, param)

初始化用户自有定时器

2、生命周期

函数

说明

mx_timer_create(ms, mode, cb, param)

创建池化定时器,返回 ID

mx_timer_start(id)

/ mx_timer_start_ex(timer)

启动

mx_timer_stop(id)

/ mx_timer_stop_ex(timer)

停止

mx_timer_delete(id)

删除(池化)

mx_timer_detach(timer)

脱离(用户自有)

mx_timer_attach(timer)

接入系统(用户自有)

3、查询

函数

说明

mx_timer_get_remaining(id)

剩余 tick 数(池化)

mx_timer_get_remaining_ex(timer)

剩余 tick 数(用户)

mx_timer_is_running(id)

是否运行中

mx_timer_is_running_ex(timer)

是否运行中

mx_timer_next_expiry()

距下次到期的 tick 数(低功耗用)

4、修改

函数

说明

mx_timer_set_period(id, ms)

修改周期(池化)

mx_timer_set_period_ex(timer, ms)

修改周期(用户)

mx_timer_pause(id)

暂停

mx_timer_pause_ex(timer)

暂停

mx_timer_resume(id)

恢复

mx_timer_resume_ex(timer)

恢复

mx_timer_reset_ex(timer)

重置为完整周期

5、驱动

函数

说明

mx_timer_irq_handler()

在 1ms ISR 中调用(O(1) 检查)

mx_timer_task_handler()

在主循环中调用(执行回调)

6、配置宏

默认值

说明

MX_TIMER_TASK_MAX

8

池化模式最大定时器数量

MX_ENTER_CRITICAL

未定义

临界区进入(必须定义)

MX_EXIT_CRITICAL

未定义

临界区退出(必须定义)

7、回调函数

签名:void callback(void *param)

回调在 mx_timer_task_handler() 中被调用,执行在主循环上下文,不在 ISR 中,允许阻塞操作。回调函数指针在调用前被拷贝到局部变量,因此可以在回调内部安全地停止或删除当前定时器。

zombie guard 机制:如果用户在回调中调用了 detach,定时器不会被重新插入 active_list,因为 re-arm 前会检查 callback != NULL

四、开源协议

本项目采用 MIT License 开源。你可以:

  • 商用,无需付费
  • 修改代码,适配你的项目
  • 分发,包括闭源产品
  • 唯一要求:保留原作者版权声明
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐