没志青年
发布于 2025-06-16 / 28 阅读
0

FreeRTOS 软件定时器

使能软件定时器功能:

  1. 添加 timer.c 和 timer.h 文件

  2. 添加配置

#define configUSE_TIMERS 1

创建定时器

// 动态创建定时器
TimerHandle_t xTimerCreate(const char * const pcTimerName,          // 定时器名字
                            const TickType_t xTimerPeriodInTicks,   // 定时时间,单位为系统节拍
                            const UBaseType_t uxAutoReload,         // pdTRUE 自动重装载;pdFALSE 一次性
                            void * const pvTimerID,                 // 定时器ID,这个用于回调函数区分是哪个定时器
                            TimerCallbackFunction_t pxCallbackFunction);   // 回调函数

删除定时器

BaseType_t xTimerDelete( TimerHandle_t xTimer,       // 定时器句柄
                         TickType_t xTicksToWait );  // 超时时间

删除定时器并不是由 xTimerDelete 这个函数删除的,它只是向定时器任务发送了一个删除命令,定时器任务会真正删除掉该定时器。

超时原因:

  • 系统负载高,定时器任务优先级低,得不到调度。

  • 定时器任务的命令队列已满,规定时间内来不及删除。

开/关定时器

开启定时器

BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );

BaseType_t xTimerStartFromISR(TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken);

关闭定时器

BaseType_t xTimerStop(TimerHandle_t xTimer, TickType_t xTicksToWait);

BaseType_t xTimerStopFromISR(TimerHandle_t xTimer, BaseType_t *pxHigherPriorityTaskWoken);

修改定时周期

BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
                               TickType_t xNewPeriod,      // 定时时间
                               TickType_t xTicksToWait );  // 超时时间



BaseType_t xTimerChangePeriodFromISR(TimerHandle_t xTimer,
                                         TickType_t xNewPeriod,
                                         BaseType_t *pxHigherPriorityTaskWoken);

定时器复位

如果定时器已经在运行,会把它的倒计时重置为初始值;如果它是停止状态,会把它启动。

BaseType_t xTimerReset(TimerHandle_t xTimer,
                       TickType_t xTicksToWait);

返回结果:

  • pdFAIL:操作失败,已超时

  • pdPASS:操作成功

中断安全函数:

BaseType_t xTimerResetFromISR(TimerHandle_t xTimer,
                              BaseType_t *pxHigherPriorityTaskWoken);

示例:

void My_ISR_Handler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    // 重置定时器
    xTimerResetFromISR(myTimerHandle, &xHigherPriorityTaskWoken);

    // 通知内核是否要立即切换上下文
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

pxHigherPriorityTaskWoken这个参数,FreeRTOS告诉你,在执行定时器复位后,是否有高优先级的任务被唤醒。

疑惑:

既然 xTimerResetFromISR() 里都知道是否唤醒了高优先级任务,为啥不自己切换上下文,非得我去调 portYIELD_FROM_ISR()

答:

这是FreeRTOS的设计思想,故意不自动切换上下文,而是将切换上下文的控制权交给用户,可以使程序更加灵活。

很多FromISR 函数都可能会发生高优先级的任务被唤醒,如果每次都自动切换了上下文,会导致效率变低。

比如这样写代码,最后再切换任务:

BaseType_t xHigherPriorityTaskWoken = pdFALSE;

xQueueSendFromISR(queue1, &data1, &xHigherPriorityTaskWoken);
xQueueSendFromISR(queue2, &data2, &xHigherPriorityTaskWoken);
xTimerResetFromISR(timer, &xHigherPriorityTaskWoken);
...
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

FromISR 函数中对是否切换标志的处理逻辑如下,不会覆盖之前的 pdTRUE,可放心使用。

if (唤醒了更高优先级任务)
{
    *pxHigherPriorityTaskWoken |= pdTRUE; // 逻辑或,不会覆盖之前的 pdTRUE
}