由于FreeRTOS是抢占式的,任务需主动让出CPU,否则会卡死。
vTaskDelay
void vTaskDelay(TickType_t xTicksToDelay);延时的是系统节拍,需使用 pdMS_TO_TICKS() 宏将毫秒ms转换为系统节拍。时基通常为1ms,所以也不用这个函数转换了,直接写,单位是ms。
适用于非周期性的任务。
为什么这个是相对延时?
另一个任务执行复杂代码的话耗时,等这个任务需要唤醒的时候没唤醒,就比预期唤醒的时间推迟了。
这个是没有精度的,大致的。
vTaskDelayUntil
这个用于一个任务的周期性执行,不会因为某次延时执行,不会累计误差。
vTaskDelayUntil() 的周期是严格准确的(绝对时间准确) ,若时间到了但是被其它任务抢占了,任务会“稍微滞后”执行,但下一个周期仍然会按原计划时间运行。
假设任务周期10ms,如果在某次醒来时被别的高优先级任务抢占:
你可能实际在【第 12ms】才执行
但下一次 vTaskDelayUntil 仍然会使用:
LastWakeTime + 10ms
固定的节拍:
10ms, 20ms, 30ms, 40ms …
不会因为某次延时而变成:
12ms, 22ms, 32ms, 42ms …
void vTaskDelayUntil(TickType_t *pxPreviousWakeTime, TickType_t xTimeIncrement);相比于 vTaskDelay,vTaskDelayUntil 可防止由于任务执行时间不同步而造成周期漂移。
比如:
for(;;)
{
执行逻辑(); // 比如执行花了 200ms
vTaskDelay(pdMS_TO_TICKS(1000)); // 每次都延时 1000ms
}
这个实际上延时了1200ms,而且每次执行所占用的时间会发生抖动变化,造成周期混乱,并不是周期执行了。
另一种写法:vTaskDelay(100 / portTICK_PERIOD_MS); 表示延时100ms
使用 vTaskDelayUntil 改进:
void vTaskFunction(void *pvParameters)
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = pdMS_TO_TICKS(1000); // 1秒周期
xLastWakeTime = xTaskGetTickCount(); // 初始化唤醒时间
for(;;)
{
// 执行任务操作
执行逻辑();
// 精准周期性延时
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}