嵌入式开发必掌握:软件定时器与系统服务实战(定时器管理+回调机制+命令队列)
标签:嵌入式开发、RTOS、FreeRTOS、软件定时器、定时器服务、回调函数、定时器队列、STM32、系统服务、底层驱动
前言
摘要:软件定时器是RTOS提供的重要系统服务,无需占用硬件定时器资源即可实现精确定时。本文从软件定时器原理、定时器创建与配置、单次与周期定时器、定时器命令队列、定时器回调函数到多定时器协同实战,提供全套可直接量产的代码示例,同时总结定时器回调限制、命令队列阻塞、定时器精度、资源竞争等踩坑经验,帮助开发者掌握高效可靠的软件定时器编程技术。
软件定时器是RTOS内核提供的重要服务,相比硬件定时器具有资源丰富、配置灵活、使用简单的优势。FreeRTOS的软件定时器基于系统Tick实现,可以在定时器服务任务中执行回调函数,实现精确定时和周期性任务。
文章主要内容:
- 软件定时器工作原理
- 定时器创建与配置
- 单次与周期定时器
- 定时器命令队列
- 定时器回调函数设计
- 多定时器协同实战
- 定时器状态管理
一、软件定时器工作原理
1.1 软件定时器vs硬件定时器
对比分析表:
| 特性 | 硬件定时器 | 软件定时器 |
|---|---|---|
| 数量 | 有限(3-5个) | 丰富(理论无限) |
| 精度 | 高(微秒级) | 较低(Tick级别) |
| 执行上下文 | 中断 | 任务 |
| 配置复杂度 | 高(寄存器配置) | 低(API调用) |
| 资源占用 | 硬件资源 | RAM和CPU |
| 适用场景 | 高精度定时 | 一般定时任务 |
1.2 软件定时器架构
1.3 定时器服务任务配置
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1)
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
void TimerService_Config(void)
{
printf("Timer Service Task Priority: %d\r\n", configTIMER_TASK_PRIORITY);
printf("Timer Command Queue Length: %d\r\n", configTIMER_QUEUE_LENGTH);
printf("Timer Task Stack Depth: %d\r\n", configTIMER_TASK_STACK_DEPTH);
}
二、定时器创建与配置
2.1 定时器基本创建
#include "FreeRTOS.h"
#include "timers.h"
TimerHandle_t one_shot_timer;
TimerHandle_t periodic_timer;
void OneShotTimer_Callback(TimerHandle_t xTimer);
void PeriodicTimer_Callback(TimerHandle_t xTimer);
void Timer_Create(void)
{
one_shot_timer = xTimerCreate(
"OneShot", // 定时器名称
pdMS_TO_TICKS(1000), // 定时周期1000ms
pdFALSE, // 单次定时器
(void*)0, // 定时器ID
OneShotTimer_Callback); // 回调函数
if(one_shot_timer == NULL)
{
printf("One-Shot Timer Create Failed\r\n");
}
else
{
printf("One-Shot Timer Created\r\n");
}
periodic_timer = xTimerCreate(
"Periodic", // 定时器名称
pdMS_TO_TICKS(500), // 定时周期500ms
pdTRUE, // 周期定时器
(void*)1, // 定时器ID
PeriodicTimer_Callback); // 回调函数
if(periodic_timer == NULL)
{
printf("Periodic Timer Create Failed\r\n");
}
else
{
printf("Periodic Timer Created\r\n");
}
}
2.2 定时器参数传递
typedef struct
{
uint8_t timer_id;
char name[16];
uint32_t event_count;
void (*event_handler)(uint8_t);
} TimerParam_t;
TimerParam_t timer1_param = {1, "LED Timer", 0, LED_Handler};
TimerParam_t timer2_param = {2, "Sensor Timer", 0, Sensor_Handler};
TimerHandle_t timer_with_param;
void TimerWithParam_Callback(TimerHandle_t xTimer)
{
TimerParam_t *param = (TimerParam_t*)pvTimerGetTimerID(xTimer);
param->event_count++;
printf("Timer %d: %s, Count: %lu\r\n",
param->timer_id, param->name, param->event_count);
if(param->event_handler != NULL)
{
param->event_handler(param->timer_id);
}
}
void Create_TimerWithParam(void)
{
timer_with_param = xTimerCreate(
"ParamTimer",
pdMS_TO_TICKS(1000),
pdTRUE,
(void*)&timer1_param,
TimerWithParam_Callback);
}
2.3 定时器启动与停止
void Timer_Start(void)
{
if(xTimerStart(one_shot_timer, pdMS_TO_TICKS(100)) == pdPASS)
{
printf("One-Shot Timer Started\r\n");
}
else
{
printf("One-Shot Timer Start Failed\r\n");
}
if(xTimerStart(periodic_timer, pdMS_TO_TICKS(100)) == pdPASS)
{
printf("Periodic Timer Started\r\n");
}
}
void Timer_Stop(void)
{
if(xTimerStop(periodic_timer, pdMS_TO_TICKS(100)) == pdPASS)
{
printf("Periodic Timer Stopped\r\n");
}
}
void Timer_Reset(void)
{
if(xTimerReset(one_shot_timer, pdMS_TO_TICKS(100)) == pdPASS)
{
printf("One-Shot Timer Reset\r\n");
}
}
三、单次与周期定时器
3.1 单次定时器
void OneShotTimer_Callback(TimerHandle_t xTimer)
{
static uint32_t count = 0;
count++;
printf("One-Shot Timer Expired: %lu\r\n", count);
if(count < 5)
{
xTimerStart(xTimer, 0);
}
else
{
printf("One-Shot Timer Finished\r\n");
}
}
void OneShotTimer_Example(void)
{
one_shot_timer = xTimerCreate(
"OneShot",
pdMS_TO_TICKS(2000),
pdFALSE,
0,
OneShotTimer_Callback);
xTimerStart(one_shot_timer, 0);
}
3.2 周期定时器
void PeriodicTimer_Callback(TimerHandle_t xTimer)
{
static uint32_t count = 0;
count++;
printf("Periodic Timer: %lu\r\n", count);
if(count % 10 == 0)
{
printf("Periodic Timer: 10 times reached\r\n");
}
}
void PeriodicTimer_Example(void)
{
periodic_timer = xTimerCreate(
"Periodic",
pdMS_TO_TICKS(500),
pdTRUE,
0,
PeriodicTimer_Callback);
xTimerStart(periodic_timer, 0);
}
3.3 可变周期定时器
TimerHandle_t variable_timer;
uint32_t timer_periods[] = {100, 200, 500, 1000, 2000};
uint8_t period_index = 0;
void VariableTimer_Callback(TimerHandle_t xTimer)
{
printf("Variable Timer: Period = %lums\r\n",
timer_periods[period_index]);
period_index++;
if(period_index >= sizeof(timer_periods)/sizeof(timer_periods[0]))
{
period_index = 0;
}
xTimerChangePeriod(xTimer,
pdMS_TO_TICKS(timer_periods[period_index]),
0);
}
void VariableTimer_Example(void)
{
variable_timer = xTimerCreate(
"Variable",
pdMS_TO_TICKS(timer_periods[0]),
pdFALSE,
0,
VariableTimer_Callback);
xTimerStart(variable_timer, 0);
}
四、定时器命令队列
4.1 命令队列机制
4.2 定时器命令API
void Timer_Commands_Example(void)
{
BaseType_t ret;
ret = xTimerStart(one_shot_timer, pdMS_TO_TICKS(100));
if(ret == pdPASS)
{
printf("Timer Start Command Sent\r\n");
}
ret = xTimerStop(one_shot_timer, pdMS_TO_TICKS(100));
if(ret == pdPASS)
{
printf("Timer Stop Command Sent\r\n");
}
ret = xTimerReset(one_shot_timer, pdMS_TO_TICKS(100));
if(ret == pdPASS)
{
printf("Timer Reset Command Sent\r\n");
}
ret = xTimerChangePeriod(one_shot_timer,
pdMS_TO_TICKS(2000),
pdMS_TO_TICKS(100));
if(ret == pdPASS)
{
printf("Timer Period Changed to 2000ms\r\n");
}
}
void Timer_FromISR_Example(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTimerStartFromISR(one_shot_timer, &xHigherPriorityTaskWoken);
xTimerStopFromISR(one_shot_timer, &xHigherPriorityTaskWoken);
xTimerResetFromISR(one_shot_timer, &xHigherPriorityTaskWoken);
xTimerChangePeriodFromISR(one_shot_timer,
pdMS_TO_TICKS(1000),
&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
4.3 命令队列阻塞
void Timer_QueueBlocking_Example(void)
{
BaseType_t ret;
ret = xTimerStart(one_shot_timer, pdMS_TO_TICKS(100));
if(ret == pdPASS)
{
printf("Timer Started (Wait 100ms for command queue)\r\n");
}
else
{
printf("Timer Start Failed (Queue Full)\r\n");
}
ret = xTimerStart(one_shot_timer, 0);
if(ret == pdPASS)
{
printf("Timer Started (No Wait)\r\n");
}
else
{
printf("Timer Start Failed (Queue Full, No Wait)\r\n");
}
ret = xTimerStart(one_shot_timer, portMAX_DELAY);
if(ret == pdPASS)
{
printf("Timer Started (Wait Forever)\r\n");
}
}
五、定时器回调函数设计
5.1 回调函数执行上下文
5.2 回调函数设计原则
void GoodTimer_Callback(TimerHandle_t xTimer)
{
GPIO_ToggleBits(GPIOA, GPIO_Pin_5);
static uint32_t count = 0;
count++;
if(count % 100 == 0)
{
printf("Timer Count: %lu\r\n", count);
}
}
void BadTimer_Callback(TimerHandle_t xTimer)
{
vTaskDelay(pdMS_TO_TICKS(100));
xSemaphoreTake(some_semaphore, portMAX_DELAY);
while(1)
{
if(CheckCondition())
break;
}
for(int i = 0; i < 10000; i++)
{
ComplexCalculation();
}
}
void RecommendedTimer_Callback(TimerHandle_t xTimer)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(event_semaphore, &xHigherPriorityTaskWoken);
xQueueSendFromISR(event_queue, &event_data, &xHigherPriorityTaskWoken);
vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken);
}
5.3 回调函数与任务协作
TimerHandle_t event_timer;
TaskHandle_t worker_task_handle;
QueueHandle_t worker_queue;
typedef struct
{
uint8_t event_type;
uint32_t timestamp;
uint32_t data;
} TimerEvent_t;
void EventTimer_Callback(TimerHandle_t xTimer)
{
TimerEvent_t event;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
event.event_type = 1;
event.timestamp = xTaskGetTickCount();
event.data = (uint32_t)pvTimerGetTimerID(xTimer);
xQueueSendFromISR(worker_queue, &event, &xHigherPriorityTaskWoken);
}
void Worker_Task(void *pvParameters)
{
TimerEvent_t event;
while(1)
{
if(xQueueReceive(worker_queue, &event, portMAX_DELAY) == pdPASS)
{
printf("Timer Event: Type=%d, Time=%lu, Data=%lu\r\n",
event.event_type, event.timestamp, event.data);
ProcessEvent(&event);
}
}
}
void Timer_TaskCooperation_Init(void)
{
worker_queue = xQueueCreate(10, sizeof(TimerEvent_t));
event_timer = xTimerCreate("EventTimer",
pdMS_TO_TICKS(1000),
pdTRUE,
(void*)100,
EventTimer_Callback);
xTaskCreate(Worker_Task, "Worker", 256, NULL, 3, &worker_task_handle);
xTimerStart(event_timer, 0);
}
六、多定时器协同实战
6.1 LED闪烁控制
typedef struct
{
GPIO_TypeDef* port;
uint16_t pin;
uint32_t on_time_ms;
uint32_t off_time_ms;
uint8_t state;
} LED_Control_t;
TimerHandle_t led_timer;
LED_Control_t led_config = {GPIOA, GPIO_Pin_5, 500, 500, 0};
void LED_Timer_Callback(TimerHandle_t xTimer)
{
LED_Control_t *led = (LED_Control_t*)pvTimerGetTimerID(xTimer);
if(led->state == 0)
{
GPIO_SetBits(led->port, led->pin);
led->state = 1;
xTimerChangePeriod(xTimer, pdMS_TO_TICKS(led->on_time_ms), 0);
}
else
{
GPIO_ResetBits(led->port, led->pin);
led->state = 0;
xTimerChangePeriod(xTimer, pdMS_TO_TICKS(led->off_time_ms), 0);
}
}
void LED_Timer_Init(void)
{
led_timer = xTimerCreate("LED",
pdMS_TO_TICKS(led_config.on_time_ms),
pdFALSE,
&led_config,
LED_Timer_Callback);
xTimerStart(led_timer, 0);
}
6.2 多任务调度器
#define MAX_SCHEDULED_TASKS 10
typedef struct
{
uint8_t active;
uint32_t period_ms;
uint32_t next_time;
void (*task_func)(void);
} ScheduledTask_t;
ScheduledTask_t scheduled_tasks[MAX_SCHEDULED_TASKS];
TimerHandle_t scheduler_timer;
void Scheduler_Callback(TimerHandle_t xTimer)
{
uint8_t i;
uint32_t current_time = xTaskGetTickCount();
for(i = 0; i < MAX_SCHEDULED_TASKS; i++)
{
if(scheduled_tasks[i].active)
{
if(current_time >= scheduled_tasks[i].next_time)
{
scheduled_tasks[i].task_func();
scheduled_tasks[i].next_time = current_time +
pdMS_TO_TICKS(scheduled_tasks[i].period_ms);
}
}
}
}
void Scheduler_Init(void)
{
uint8_t i;
for(i = 0; i < MAX_SCHEDULED_TASKS; i++)
{
scheduled_tasks[i].active = 0;
}
scheduler_timer = xTimerCreate("Scheduler",
pdMS_TO_TICKS(10),
pdTRUE,
0,
Scheduler_Callback);
xTimerStart(scheduler_timer, 0);
}
uint8_t Scheduler_AddTask(void (*task_func)(void), uint32_t period_ms)
{
uint8_t i;
for(i = 0; i < MAX_SCHEDULED_TASKS; i++)
{
if(!scheduled_tasks[i].active)
{
scheduled_tasks[i].active = 1;
scheduled_tasks[i].period_ms = period_ms;
scheduled_tasks[i].next_time = xTaskGetTickCount() +
pdMS_TO_TICKS(period_ms);
scheduled_tasks[i].task_func = task_func;
printf("Task Added: Index=%d, Period=%lums\r\n", i, period_ms);
return i;
}
}
printf("Scheduler Full\r\n");
return 0xFF;
}
void Scheduler_RemoveTask(uint8_t index)
{
if(index < MAX_SCHEDULED_TASKS)
{
scheduled_tasks[index].active = 0;
printf("Task Removed: Index=%d\r\n", index);
}
}
6.3 状态机定时器
typedef enum
{
STATE_IDLE,
STATE_INIT,
STATE_RUNNING,
STATE_ERROR,
STATE_SHUTDOWN
} SystemState_t;
typedef struct
{
SystemState_t current_state;
SystemState_t next_state;
uint32_t state_timer;
uint32_t timeout_ms;
} StateMachine_t;
TimerHandle_t state_timer;
StateMachine_t state_machine = {STATE_IDLE, STATE_IDLE, 0, 5000};
void StateMachine_Callback(TimerHandle_t xTimer)
{
switch(state_machine.current_state)
{
case STATE_IDLE:
printf("State: IDLE\r\n");
state_machine.next_state = STATE_INIT;
break;
case STATE_INIT:
printf("State: INIT\r\n");
if(InitSystem())
{
state_machine.next_state = STATE_RUNNING;
}
else
{
state_machine.next_state = STATE_ERROR;
}
break;
case STATE_RUNNING:
printf("State: RUNNING\r\n");
state_machine.state_timer++;
if(state_machine.state_timer >= 10)
{
state_machine.next_state = STATE_SHUTDOWN;
}
break;
case STATE_ERROR:
printf("State: ERROR\r\n");
state_machine.state_timer++;
if(state_machine.state_timer >= 3)
{
state_machine.next_state = STATE_IDLE;
state_machine.state_timer = 0;
}
break;
case STATE_SHUTDOWN:
printf("State: SHUTDOWN\r\n");
ShutdownSystem();
xTimerStop(xTimer, 0);
break;
}
state_machine.current_state = state_machine.next_state;
}
void StateMachine_Init(void)
{
state_timer = xTimerCreate("StateMachine",
pdMS_TO_TICKS(1000),
pdTRUE,
0,
StateMachine_Callback);
xTimerStart(state_timer, 0);
}
七、定时器状态管理
7.1 定时器状态查询
void Timer_StatusQuery(void)
{
TimerHandle_t timer = periodic_timer;
const char *name = pcTimerGetName(timer);
printf("Timer Name: %s\r\n", name);
TickType_t period = xTimerGetPeriod(timer);
printf("Timer Period: %lu ticks (%lu ms)\r\n",
period, period * portTICK_PERIOD_MS);
TickType_t expiry = xTimerGetExpiryTime(timer);
printf("Timer Expiry Time: %lu ticks\r\n", expiry);
void *id = pvTimerGetTimerID(timer);
printf("Timer ID: %p\r\n", id);
if(xTimerIsTimerActive(timer) != pdFALSE)
{
printf("Timer State: Active (Running)\r\n");
}
else
{
printf("Timer State: Inactive (Stopped)\r\n");
}
}
void Timer_PrintAllTimersStatus(void)
{
printf("\r\n=== Timer Status ===\r\n");
printf("One-Shot Timer:\r\n");
Timer_StatusQuery();
printf("\r\nPeriodic Timer:\r\n");
Timer_StatusQuery();
}
7.2 定时器删除
void Timer_Delete_Example(void)
{
if(xTimerIsTimerActive(one_shot_timer))
{
if(xTimerStop(one_shot_timer, pdMS_TO_TICKS(100)) == pdPASS)
{
printf("Timer Stopped\r\n");
}
}
if(xTimerDelete(one_shot_timer, pdMS_TO_TICKS(100)) == pdPASS)
{
printf("Timer Deleted\r\n");
one_shot_timer = NULL;
}
else
{
printf("Timer Delete Failed\r\n");
}
}
void Timer_Recreate_Example(void)
{
if(one_shot_timer != NULL)
{
xTimerDelete(one_shot_timer, pdMS_TO_TICKS(100));
}
one_shot_timer = xTimerCreate("OneShot",
pdMS_TO_TICKS(1000),
pdFALSE,
0,
OneShotTimer_Callback);
xTimerStart(one_shot_timer, 0);
}
八、软件定时器踩坑总结
8.1 常见问题与解决方案
8.2 踩坑经验汇总
坑点1:回调函数中阻塞
// ❌ 错误:在回调中调用阻塞函数
void BadCallback(TimerHandle_t xTimer)
{
vTaskDelay(pdMS_TO_TICKS(100)); // 错误!会阻塞定时器服务任务
xSemaphoreTake(mutex, portMAX_DELAY); // 错误!可能阻塞
while(!Condition()); // 错误!可能死循环
}
// ✅ 正确:回调中只做轻量操作,通知其他任务
void GoodCallback(TimerHandle_t xTimer)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(worker_task, &xHigherPriorityTaskWoken);
xSemaphoreGiveFromISR(event_sem, &xHigherPriorityTaskWoken);
}
坑点2:定时器精度不足
// ❌ 错误:期望微秒级精度
TimerHandle_t timer = xTimerCreate("Timer",
pdMS_TO_TICKS(1), // 期望1ms
pdTRUE,
0,
Callback);
// 问题:系统Tick默认1ms,定时器精度只能是Tick的整数倍
// ✅ 正确:提高系统Tick频率
#define configTICK_RATE_HZ ((TickType_t)1000) // 1ms Tick
// 或者使用硬件定时器实现高精度定时
坑点3:命令队列阻塞
// ❌ 错误:命令队列满时无限等待
xTimerStart(timer, portMAX_DELAY); // 可能永久阻塞!
// ✅ 正确:使用超时或非阻塞
xTimerStart(timer, pdMS_TO_TICKS(100)); // 最多等待100ms
// 或者增大队列长度
#define configTIMER_QUEUE_LENGTH 20 // 增大队列
坑点4:定时器服务任务优先级过低
// ❌ 错误:定时器服务任务优先级低于应用任务
#define configTIMER_TASK_PRIORITY 1 // 太低!
// 问题:定时器回调可能被延迟执行
// ✅ 正确:定时器服务任务优先级应该足够高
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) // 最高优先级
坑点5:回调函数执行时间过长
// ❌ 错误:回调中执行复杂计算
void BadCallback(TimerHandle_t xTimer)
{
for(int i = 0; i < 10000; i++)
{
ComplexCalculation(); // 执行时间过长
}
}
// ✅ 正确:回调中触发任务处理
void GoodCallback(TimerHandle_t xTimer)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
uint8_t event = TIMER_EVENT;
xQueueSendFromISR(event_queue, &event, &xHigherPriorityTaskWoken);
}
// 在任务中执行复杂计算
void WorkerTask(void *pvParameters)
{
uint8_t event;
while(1)
{
if(xQueueReceive(event_queue, &event, portMAX_DELAY) == pdPASS)
{
for(int i = 0; i < 10000; i++)
{
ComplexCalculation();
}
}
}
}
8.3 性能优化建议
优化1:合理设置定时器服务任务优先级
// 定时器服务任务优先级应该高于大部分应用任务
// 但低于中断处理任务
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 2)
优化2:增大队列长度避免阻塞
// 根据定时器数量和命令发送频率调整队列长度
// 建议队列长度 >= 定时器数量 * 2
#define configTIMER_QUEUE_LENGTH 20
优化3:回调函数轻量化
// 回调函数只做必要的操作
// 复杂处理交给专门的任务
void OptimizedCallback(TimerHandle_t xTimer)
{
static uint32_t count = 0;
count++;
if(count % 100 == 0)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(log_queue, &count, &xHigherPriorityTaskWoken);
}
}
九、总结与互动
9.1 核心要点总结
- 软件定时器原理:理解定时器服务任务和命令队列机制
- 定时器类型:掌握单次和周期定时器的使用场景
- 回调函数设计:回调中不能阻塞,执行时间要短
- 命令队列:理解命令队列机制,避免队列满阻塞
- 定时精度:软件定时器精度受系统Tick限制
9.2 实战经验总结
- 回调函数中不能调用阻塞函数,使用任务通知代替
- 定时器服务任务优先级要足够高
- 命令队列长度要根据定时器数量合理配置
- 复杂操作不要在回调中执行,交给专门任务
- 高精度定时需求使用硬件定时器
投票组件
你对软件定时器的最大困惑是什么?
- 不理解定时器服务任务和命令队列机制
- 回调函数设计不当,经常导致系统异常
- 定时器精度不够,不知道如何提高
- 多个定时器协同工作,不知道如何设计
- 其他问题(请评论区说明)
欢迎在评论区分享你的软件定时器使用经验和遇到的问题!
互动引导
思考题:
- 如何设计一个LED呼吸灯效果,使用软件定时器控制亮度?
- 如何实现多个定时器的优先级调度?
- 如何监控定时器的运行状态和资源使用情况?
实践建议:
- 先从简单的周期定时器开始,理解基本使用
- 逐步尝试单次定时器和可变周期定时器
- 学习回调函数与任务的协作机制
- 实现多定时器协同的复杂场景
下一篇文章预告:《嵌入式开发必掌握:RTOS中断管理实战》,将深入讲解中断优先级配置、中断服务函数设计、延迟中断处理、中断与任务通信等核心内容。
239

被折叠的 条评论
为什么被折叠?



