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函数的比较
特性 | vTaskDelay | vTaskDelayUntil |
---|---|---|
延迟类型 | 相对时间 | 绝对时间 |
典型应用场景 | 不需要精确周期性的任务 | 需要精确周期性调度的任务 |
累计误差 | 存在可能 | 不存在 |
实现的复杂性 | 简单 | 较复杂 |
总结
vTaskDelay
更加简单,适用于不需要严格周期性的任务。vTaskDelayUntil
提供了更高的定时精度,是实现周期性任务的理想选择。