Bootstrap

FreeRTOS中的两个Delay函数

        FreeRTOS 提供了两个主要的延时函数:vTaskDelay 和 vTaskDelayUntil。虽然它们都用于引入任务延迟,但它们的使用场景和实现机制有所不同。

vTaskDelay

概述

vTaskDelay 是基于相对时间的延时函数。它根据当前时间进行延时,使任务进入阻塞状态,并在指定的Tick数后重新进入就绪态。

函数原型
void vTaskDelay( const TickType_t xTicksToDelay );
参数
  • xTicksToDelay:延迟的 Tick 数数量。
示例

任务每隔100个Tick周期执行一次:

void vTaskFunction( void * pvParameters )
{
    for( ;; )
    {
        // 执行任务
        PerformTask();
        
        // 延迟100个Tick周期
        vTaskDelay(100);
    }
}
特点
  • 简单易用:只需指定要延迟的 Tick 数量。
  • 无法保证精确的周期时间:由于任务的开始时间不固定,无法保证精确的周期时间。
  • 适用于不可预知的延迟:适合那些不需要精确周期的场景。

vTaskDelayUntil

概述

vTaskDelayUntil 是基于绝对时间的延时函数。它用于实现精确的周期性任务调度,通过设置绝对的唤醒时间来达到精准的控制。

函数原型
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement );
参数
  • pxPreviousWakeTime:指向任务上次被唤醒时间的指针。
  • xTimeIncrement:周期性唤醒时间间隔。
示例

任务每隔100个Tick周期执行一次,使用 vTaskDelayUntil 保证周期的精确度:

void vTaskFunction( void * pvParameters )
{
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = 100;

    // 初始化上次唤醒时间
    xLastWakeTime = xTaskGetTickCount();

    for( ;; )
    {
        // 执行任务
        PerformTask();

        // 相对于上次唤醒时间进行延迟,确保固定周期
        vTaskDelayUntil( &xLastWakeTime, xFrequency );
    }
}
特点
  • 精确的周期性调度:基于绝对时间,能够确保任务在精确的周期时间被唤醒。
  • 避免累计误差:通过相对于上次唤醒时间进行延迟,避免了累计误差。
  • 适用于周期性任务:特别适合那些需要严格周期执行的任务。

疑问:vTaskDelayUntil 是基于绝对时间的延时函数,在上述例子中如果任务执行的时间大于100tick 这种情况会怎么处理?

答:这种情况通常叫做“时间噪音”或者“任务过载”,意味着任务的执行时间超过了预定的周期时间。如果任务的执行时间超过了周期时间 xTimeIncrement,那么下次调用 vTaskDelayUntil 时,会发现唤醒时间已经过了,函数会立即返回。

两个Delay函数的比较

特性vTaskDelayvTaskDelayUntil
延迟类型相对时间绝对时间
典型应用场景不需要精确周期性的任务需要精确周期性调度的任务
累计误差存在可能不存在
实现的复杂性简单较复杂

总结

  • vTaskDelay 更加简单,适用于不需要严格周期性的任务。
  • vTaskDelayUntil 提供了更高的定时精度,是实现周期性任务的理想选择。

;