FreeRTOS:软件定时器(Software Timers)与时间管理

软件定时器(Software Timers)与时间管理

本课目标:掌握 FreeRTOS 软件定时器的概念、常用 API 与最佳实践,以及与任务延时(vTaskDelay / vTaskDelayUntil)的比较。附带实验示例:用软件定时器触发周期性处理并给出代码与流程图。


0. 为什么需要软件定时器?

软件定时器是 RTOS 提供的一种在内核定时服务(Timer Daemon Task)上下文中运行的回调机制,用于把时间驱动的动作从任务中解耦出来。它适合:

  • 想要在将来某个时间点执行短小操作
  • 不想为定时唤醒写一个单独的高优先级任务
  • 需要"轻量级的延时回调"而不涉及硬件定时器复杂性

软件定时器的回调在定时器服务任务里运行(即任务上下文),不是 ISR。回调中不应长时间阻塞;若需要长时间处理,应使用回调来通知一个任务(例如通过信号量或任务通知)。


1. 常用 Timer API(精讲)

1.1 xTimerCreate

c 复制代码
TimerHandle_t xTimerCreate(
    const char * const pcTimerName,
    const TickType_t xTimerPeriodInTicks,
    const UBaseType_t uxAutoReload,
    void * pvTimerID,
    TimerCallbackFunction_t pxCallbackFunction
);
  • pcTimerName:调试用名字
  • xTimerPeriodInTicks:以 tick 为单位的周期
  • uxAutoReloadpdTRUE 表示周期性(auto-reload),pdFALSE 表示一次性(one-shot)
  • pvTimerID:用户自定义指针(可在回调中通过 pvTimerGetTimerID 取回)
  • pxCallbackFunction:回调函数,签名为 void vTimerCallback(TimerHandle_t xTimer)

1.2 xTimerStart / xTimerReset / xTimerStop / xTimerChangePeriod

  • xTimerStart(xTimer, xTicksToWait):启动计时器(若已启动可返回成功或错误,视实现而定)
  • xTimerReset(xTimer, xTicksToWait):把计时器余时重置为原始周期(常用于 one-shot)
  • xTimerStop(xTimer, xTicksToWait):停止计时器
  • xTimerChangePeriod(xTimer, xNewPeriod, xTicksToWait):改变周期(常用于动态调整)

所有这些 API 都可以在任务上下文中阻塞一段 xTicksToWait,但最好不要在回调中阻塞

1.3 FromISR 版本

FreeRTOS 提供 ISR 安全的版本(如 xTimerStartFromISRxTimerStopFromISR 等),允许在中断中控制软件定时器。

1.4 xTimerDelete

删除计时器并释放资源:

c 复制代码
BaseType_t xTimerDelete(TimerHandle_t xTimer, TickType_t xTicksToWait);

2. 回调上下文的重要注意点

  • 回调执行于定时器服务任务 ,它有自己的优先级(由 configTIMER_TASK_PRIORITY 指定)。
  • 回调不是 ISR,不应使用 FromISR 版本的 API
  • 回调中不要做长时间工作 或调用可能阻塞的 API(例如 xSemaphoreTake(..., portMAX_DELAY)
  • 回调里安全可用的策略:发信号、给队列/任务通知(注意使用非阻塞 xQueueSendFromISR 的替代;在回调中使用标准版本 xQueueSend 是可以的,因为回调是任务上下文)

3. 软件定时器 vs vTaskDelay / vTaskDelayUntil

特性 软件定时器 vTaskDelay / vTaskDelayUntil
运行上下文 定时器服务任务回调 普通任务上下文
精度 受 tick 精度影响 受 tick 精度影响
适用 异步回调、一次性定时;减少任务数量 任务内周期性工作、需要复杂逻辑时
可阻塞 回调中尽量不要阻塞 任务可以阻塞等待或做复杂处理
开销 定时器服务任务调度 + 内存 简单直接,任务切换开销

选择建议

  • 若只是需要在未来某时触发一个"短小动作"(例如超时、重试、心跳触发)------用软件定时器
  • 若需要周期性且任务需要做大量工作------用独立任务 + vTaskDelayUntil
  • 若需要精确控制执行顺序和阻塞行为,把复杂工作放在任务中,用定时器作为触发器(回调只发通知)

4. 用软件定时器触发周期性处理

c 复制代码
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"

static TimerHandle_t xPeriodicTimer = NULL;
static TaskHandle_t xWorkerTask = NULL;

void vTimerCallback(TimerHandle_t xTimer)
{
    // 在定时器服务任务中运行:通知 worker 任务
    xTaskNotifyGive(xWorkerTask);
}

void vWorkerTask(void *pv)
{
    for (;;) {
        // 等待来自定时器的通知
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        // 收到通知,执行工作
        printf("[Worker] Timer fired at tick %lu\n", (unsigned long)xTaskGetTickCount());
        // 模拟短工作
        vTaskDelay(pdMS_TO_TICKS(50));
    }
}

int main(void)
{
    // 创建周期性软件定时器,周期 500ms
    xPeriodicTimer = xTimerCreate("Periodic", pdMS_TO_TICKS(500), pdTRUE, NULL, vTimerCallback);
    if (xPeriodicTimer == NULL) {
        printf("Failed to create timer\n");
        return -1;
    }

    xTaskCreate(vWorkerTask, "Worker", 256, NULL, tskIDLE_PRIORITY + 2, &xWorkerTask);

    // 启动定时器
    if (xTimerStart(xPeriodicTimer, 0) != pdPASS) {
        printf("Failed to start timer\n");
        return -1;
    }

    vTaskStartScheduler();
    for (;;);
}

对比代码

c 复制代码
void vWorkerPeriodic(void *pv)
{
    TickType_t last = xTaskGetTickCount();
    const TickType_t period = pdMS_TO_TICKS(500);

    for (;;) {
        // 做工作
        printf("[WorkerDelay] tick %lu\n", (unsigned long)xTaskGetTickCount());
        vTaskDelayUntil(&last, period);
    }
}

对比要点

  • 使用 vTaskDelayUntil 的任务在其自身上下文里运行,做更多复杂工作更方便
  • 使用软件定时器可以减少任务数量(定时器回调分发),更易于集中管理超时/重试逻辑
相关推荐
世人万千丶18 小时前
Flutter 框架跨平台鸿蒙开发 - 恐惧清单应用
学习·flutter·华为·开源·harmonyos·鸿蒙
yuzhuanhei18 小时前
Visual Studio 配置C++opencv
c++·学习·visual studio
Wenweno0o18 小时前
0基础Go语言Eino框架智能体实战-chatModel
开发语言·后端·golang
一轮弯弯的明月18 小时前
贝尔数求集合划分方案总数
java·笔记·蓝桥杯·学习心得
chenjingming66619 小时前
jmeter线程组设置以及串行和并行设置
java·开发语言·jmeter
cch891819 小时前
Python主流框架全解析
开发语言·python
不爱吃炸鸡柳19 小时前
C++ STL list 超详细解析:从接口使用到模拟实现
开发语言·c++·list
十五年专注C++开发19 小时前
RTTR: 一款MIT 协议开源的 C++ 运行时反射库
开发语言·c++·反射
Momentary_SixthSense19 小时前
设计模式之工厂模式
java·开发语言·设计模式
‎ദ്ദിᵔ.˛.ᵔ₎19 小时前
STL 栈 队列
开发语言·c++