在嵌入式实时操作系统(RTOS)开发中,任务的时间控制是核心功能之一。FreeRTOS 提供了两个主要的任务延时 API:vTaskDelay 和 vTaskDelayUntil。虽然它们都能让任务进入阻塞状态以释放 CPU 资源给其他任务,但其底层机制、时间基准以及适用场景有着本质的区别。正确选择这两个函数,对于保证系统的实时性、稳定性和精度至关重要。
基本功能
无论vTaskDelay,还是vTaskDelayUntil函数两者的基本功能都是系统延时功能。两者延时的基础都是系统的tick数,即读取两次tick数值,当满足时限要求时则解除任务阻塞状态,转而进入就绪状态。在系统调试完成后,执行对应的任务业务逻辑。
vTaskDelay函数
vTaskDelay 实现的是相对延时。它的逻辑非常简单:从调用该函数的当前时刻开始,延时指定的 Tick(系统节拍)数后,任务解除阻塞。它的函数原型是
void vTaskDelay( const TickType_t xTicksToDelay );
工作机制:
当任务执行到 vTaskDelay(100) 时,它会记录当前的系统时间Tnow,并计算唤醒时间Twake=Tnow+100。任务随后进入阻塞状态,直到系统时钟到达Twake。
关键缺陷:
由于延时是从“调用时刻”开始计算的,如果任务在执行 vTaskDelay 之前的代码耗时发生变化(例如因中断干扰或逻辑分支不同),那么整个任务周期的长度就会随之波动。这会导致累积误差,无法保证固定的执行频率。
vTaskDelayUntil函数
vTaskDelayUntil 实现的是绝对延时。它旨在让任务以固定的频率周期性执行。它的函数原型是
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement );
参数说明:
pxPreviousWakeTime:指向一个变量,该变量保存了任务上一次被唤醒的时间点。首次使用时需初始化为当前系统时间。 FreeRTOS 会在每次调用后自动更新此值。
xTimeIncrement:任务执行的周期长度(以 Tick 为单位)。
工作机制:
该函数基于“上次唤醒时间”加上“固定周期”来计算下一次唤醒时间。即Tnext_wake=Tlast_wake+xTimeIncrement
无论任务代码执行了多久(只要小于周期时间),任务都会在固定的时间点被唤醒。
核心优势:
它能够自动补偿任务执行时间的波动,从而保持严格稳定的执行周期。
简单总结
vTaskDelay()主要应用于对周期性要求并不完全严格的应用上。比如5Hz闪烁LED灯。每次亮灭偏差几百个us,实际意义的影响实在不太太。
vTaskDelayUntil()的应用主要还是相对比较严格的周期触发应用。主要因其它没有太多的累计误差影响。
当然,如果对周期要求特别苛刻,还是要使用系统的硬件Timer来实现。
我要赚赏金
