在嵌入式开发中,“定时”几乎无处不在——LED 闪烁、任务轮询、事件触发、超时重试、UI动画、通信心跳……每一个场景都在向你提出一个问题:你如何高效地管理多个异步时间事件?
STM32 的硬件定时器固然强大,但数量有限,且每次使用都要配置寄存器、时钟源、回调中断,工程复杂度指数级增长。有没有一种更轻量、灵活、高可复用的解决方案?答案就是:软件定时器(Soft Timer)!
今天我们就来解析一个极具工程价值的开源项目:
https://gitee.com/stanley_shen/soft_timer,看它如何帮助开发者轻松实现多定时任务调度。
什么是软件定时器?
软件定时器不是硬件资源,而是一种基于统一时基进行时间管理的软件策略,通常由系统时钟节拍(如 SysTick 每 1ms 中断一次)驱动,维护一个定时器链表或数组,按需触发用户定义的回调函数。
它最大的优势就是:用一个定时器,实现多个逻辑上的“定时任务”。
soft_timer 的设计
soft_timer 项目的核心是极简、清晰、可移植,适合所有裸机或 RTOS 环境下的 MCU 项目。它通过以下模块组织完成整个定时器生命周期管理:
核心数据结构(soft_timer_t)
typedef struct soft_timer {
uint32_t timeout; // 定时超时时间
uint32_t counter; // 当前计数值
void (*callback)(void*); // 到时执行的回调函数
void* user_data; // 回调函数参数
bool repeat; // 是否为周期定时
struct soft_timer* next; // 链表指针
bool active; // 是否处于运行状态
} soft_timer_t;
每个软件定时器都是一个结构体实例,包含:
- timeout:定时时间(单位一般为 ms)
- counter:当前计数值
- callback:时间到后执行的函数
- user_data:传入回调的参数
- repeat:是否周期运行
- active:是否启用
- next:链表管理(多个定时器的组织方式)
这一结构设计具备高度拓展性,也适用于任何 MCU 平台。
API 函数声明:
void soft_timer_init(void);
soft_timer_t* soft_timer_create(uint32_t timeout, bool repeat, void (*callback)(void*), void* user_data);
void soft_timer_start(soft_timer_t* timer);
void soft_timer_stop(soft_timer_t* timer);
void soft_timer_delete(soft_timer_t* timer);
void soft_timer_update(void); // 通常在 SysTick 或 1ms 中断中调用
使用流程概览
一个典型的软件定时器使用流程如下:
soft_timer_init(); // 初始化链表
// 创建一个500ms周期定时器
soft_timer_t* my_timer = soft_timer_create(500, true, led_toggle, NULL);
soft_timer_start(my_timer);
在系统时钟中断中(比如 1ms SysTick)调用:
void SysTick_Handler(void) {
soft_timer_update(); // 每1ms调用一次,驱动全部定时器运行
}
示例:LED 闪烁不再繁琐
传统的 HAL 定时器实现 LED 闪烁,至少需要:
- 配置 TIMx 寄存器
- 编写中断服务函数
- 开启中断优先级
- 注册 HAL 回调或用户 ISR
使用 soft_timer 后,仅需几行:
void led_blink(void* arg) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
soft_timer_t* blink_timer = soft_timer_create(500, true, led_blink, NULL);
soft_timer_start(blink_timer);
优雅、解耦、可重用,项目可读性也大大提升。
场景应用拓展
场景 | 使用建议 |
UI动画/按键消抖 | 使用 repeat=false 的一次性定时器 |
报文超时机制 | 回调中重置定时器实现“自动重传” |
网络心跳包 | 使用周期定时器发送 ping |
多任务调度(非RTOS) | 多个定时器绑定不同任务函数 |
这种模式可以轻松取代状态机中复杂的超时判断逻辑,让主循环代码更“干净”。
和 RTOS 配合使用?
当然可以!即便你用了 FreeRTOS 等系统,soft_timer 依旧能在任务级别独立管理时间任务,尤其适合:
- low_priority 后台逻辑管理
- 在不便使用 RTOS 的中断上下文中触发定时任务
- 替代 vTaskDelay 更精细地控制短延迟
注意:只需保证 soft_timer_update() 运行在一个固定节拍中(RTOS 中通常放在 tick hook 或后台任务)。