在嵌入式实时系统开发中,定时器是不可或缺的工具。软件定时器(Software Timer) 提供了一种无需创建独立任务、便可在特定延时后执行回调函数的机制。它适用于那些不要求高精度、但需要周期性或一次性延时执行操作的场景。
一、什么是软件定时器?
软件定时器是在 RTOS 内核中由专门的“定时器服务任务”统一管理的一类定时机制。不同于硬件定时器依赖 MCU 的定时器外设,软件定时器完全由软件实现,基于 RTOS 的节拍时钟(tick)驱动。
本质上:软件定时器就是一个在若干 tick 后触发的回调函数。
二、软件定时器的典型用途
应用场景 | 示例 |
---|---|
周期性任务 | LED 闪烁、数据上传 |
延时执行操作 | 延迟关闭设备 |
替代硬件定时器节省资源 | GPIO 超时检测 |
防抖处理、状态切换延迟等 | 按键防抖、状态复位 |
优势:
-
不占用额外任务资源
-
系统统一调度,调试方便
-
可动态创建、删除、启动、停止
-
支持周期性和一次性定时
三、FreeRTOS 中软件定时器核心结构与源码解读
FreeRTOS 软件定时器的核心结构体是 Timer_t
,但对用户公开的类型为 TimerHandle_t
。管理定时器的关键组件包括:
1. 创建定时器:xTimerCreate
TimerHandle_t xTimerCreate(const char * const pcTimerName,const TickType_t xTimerPeriodInTicks,const UBaseType_t uxAutoReload,void * const pvTimerID,TimerCallbackFunction_t pxCallbackFunction );
参数说明:
-
pcTimerName
:定时器名称(可调试用) -
xTimerPeriodInTicks
:周期时间(tick) -
uxAutoReload
:是否自动重装(周期性) -
pvTimerID
:用户定义的数据指针 -
pxCallbackFunction
:定时器超时时调用的回调函数
TimerHandle_t myTimer = xTimerCreate("LEDTimer", pdMS_TO_TICKS(1000), pdTRUE, NULL, vLEDCallback);
2. 启动定时器:xTimerStart
xTimerStart(myTimer, 0);
将定时器放入活动链表,等待 Tick 累加触发。
3. 停止定时器:xTimerStop
xTimerStop(myTimer, 0);
可用于手动取消定时操作。
4. 回调函数定义
void vLEDCallback(TimerHandle_t xTimer) {// 执行定时任务,比如切换 LED 状态
}
注意:回调函数中不能调用阻塞操作(如
vTaskDelay
),应尽可能快速完成逻辑。
5. 定时器服务任务机制
FreeRTOS 中有一个专门的任务叫做 定时器服务任务(Timer Service Task),用于处理所有软件定时器的事件。其本质是一个优先级较高、等待定时器消息队列的任务。
源码入口在 timers.c
中的 prvTimerTask
:
static void prvTimerTask( void *pvParameters )
{for( ;; ){// 等待来自定时器命令队列的消息(如启动/停止)xQueueReceive(xTimerQueue, &xMessage, portMAX_DELAY);switch(xMessage.xMessageID) {case tmrCOMMAND_START:// 添加定时器到活动链表break;case tmrCOMMAND_EXECUTE_CALLBACK:// 执行用户回调函数pxTimer->pxCallbackFunction((TimerHandle_t) pxTimer);break;// ...其他命令处理}}
}
其调度机制由 tick 中断驱动,在 xTaskIncrementTick()
中判断是否触发回调。
四、完整使用示例:LED 闪烁控制
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"#define LED_PIN 13TimerHandle_t ledTimer;void vLEDCallback(TimerHandle_t xTimer) {static int led_state = 0;led_state = !led_state;gpio_set_level(LED_PIN, led_state);
}void app_main() {gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);ledTimer = xTimerCreate("LEDTimer", pdMS_TO_TICKS(500), pdTRUE, NULL, vLEDCallback);xTimerStart(ledTimer, 0);
}
五、常见问题与注意事项
-
为什么不在任务中直接用
vTaskDelay
?
因为软件定时器可以统一调度,不占用额外任务资源,更适合事件驱动模型。 -
能不能在中断中操作软件定时器?
是的,FreeRTOS 提供 ISR 安全版本,如xTimerStartFromISR
。 -
软件定时器精度如何?
精度取决于系统 tick 周期,一般不适合要求微秒级精度的应用。 -
最大支持多少个定时器?
由系统内存和队列大小决定,可配置,但通常可支持上百个。
六、小结
特性 | 软件定时器 |
---|---|
精度 | Tick 周期决定 |
触发方式 | 回调函数 |
ISR 安全性 | 提供 FromISR 版本支持 |
占用资源 | 少,无需额外任务 |
适用场景 | 周期任务、延时处理、状态切换 |