当我们开发嵌入式系统时,经常需要在任务执行过程中添加延时。FreeRTOS提供了两个常用的任务延时函数:vTaskDelay()和vTaskDelayUntil()。它们在实现延时功能上有一些区别,本文将详细介绍这两个函数的特点和用法。
vTaskDelay()是FreeRTOS中的相对延时函数,它可以让当前任务进入阻塞状态,在指定的时间后重新激活任务。vTaskDelay()的原型如下:
void vTaskDelay(TickType_t xTicksToDelay);
参数xTicksToDelay表示延时的时钟周期数,一般以tick为单位。tick是FreeRTOS内核的时间单位,默认情况下为系统时钟的一部分。我们可以通过configTICK_RATE_HZ配置内核时钟周期。例如,如果内核时钟为1kHz,那么1 tick对应的时间为1ms。
下面是一个简单的示例代码,演示了如何使用vTaskDelay()延时500ms:
void vTaskDelayExampleTask(void *pvParameters)
{
while(1)
{
// 任务逻辑
vTaskDelay(pdMS_TO_TICKS(500)); // 延时500ms
}
}
在此示例中,任务将执行自己的逻辑,并在每次循环结束时调用vTaskDelay()以实现500ms的延时。vTaskDelay()的调用会将任务置于阻塞状态,在延时结束后会重新激活任务,使其可以继续执行下一次循环。
值得注意的是,vTaskDelay()的延时时间是相对的,它不会考虑实际时间的流逝,而是仅仅通过tick数来计算延时持续的时间。
vTaskDelayUntil()是FreeRTOS中的绝对延时函数,它允许任务在指定的绝对时间再次激活。vTaskDelayUntil()的原型如下:
void vTaskDelayUntil(TickType_t *pxPreviousWakeTime, TickType_t xTimeIncrement);
参数pxPreviousWakeTime指向一个变量,用于存储上次任务唤醒的时刻。xTimeIncrement表示每次任务唤醒后,需要延时的时间。通常,我们使用TickType_t类型来表示时刻,这是一个32位或16位的无符号整数类型。
下面是一个示例代码,展示了如何使用vTaskDelayUntil()实现周期性任务:
void vPeriodicTask(void *pvParameters)
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = pdMS_TO_TICKS(1000); // 周期1秒
// 初始化上次唤醒时刻
xLastWakeTime = xTaskGetTickCount();
while(1)
{
// 任务逻辑
vTaskDelayUntil(&xLastWakeTime, xFrequency); // 周期性唤醒
}
}
在这个示例中,任务会按照1秒的周期运行。在任务的每次循环中,我们使用vTaskDelayUntil()函数来实现周期性唤醒。该函数会将任务置于阻塞状态,直到下一个周期开始。
与vTaskDelay()不同的是,vTaskDelayUntil()是基于实际时间的延时机制。它会考虑任务的唤醒时刻,以及每次唤醒后需要延时的时间。
综上所述,vTaskDelay()和vTaskDelayUntil()是FreeRTOS中常用的任务延时函数。它们分别提供了相对延时和绝对延时的能力,可以根据实际需求选择适当的函数来实现任务的延时操作。在使用这两个函数时,我们需要在使用这两个函数时,需要考虑一些注意事项:
1. 任务优先级和调度策略:vTaskDelay()和vTaskDelayUntil()都会阻塞当前任务的执行,让其他任务有机会执行。因此,在使用这两个函数时,需要考虑任务的优先级和调度策略。确保高优先级的任务能及时得到执行。
2. 延时精度:使用vTaskDelay()和vTaskDelayUntil()进行延时操作时,应该注意系统时钟的精度。系统时钟的精度越高,延时的精确度越好。如果需要更精确的延时,可以使用更高精度的时钟源或者其他定时器。
3. 系统中断处理:在进行任务延时时,要确保系统的中断处理不会影响延时功能的正常运行。一些中断可能会打断任务的延时操作,需要根据实际情况进行适当的处理和配置,以确保延时操作的准确性。
总结起来,vTaskDelay()和vTaskDelayUntil()是FreeRTOS中用于任务延时的两个函数。vTaskDelay()是相对延时函数,适合需要固定时间间隔的延时操作;vTaskDelayUntil()是绝对延时函数,适合实现周期性的延时操作。在使用这两个函数时,需要根据实际需求和系统特性选择适当的函数,并注意任务的优先级、调度策略、延时精度以及系统中断处理等因素。这样才能确保系统的正常运行和延时功能的准确性。
1. 就绪状态(Ready):任务已经创建,并且已经准备好执行,但是还没有被调度器选中执行。
2. 运行状态(Running):任务正在执行。
3. 阻塞状态(Blocked):任务由于某种原因无法继续执行,例如等待某个事件发生、等待某个资源释放等。
4. 挂起状态(Suspended):任务被显式地挂起,不再参与调度,直到被恢复为就绪状态。
四种状态之间的转化关系是:
1. 就绪状态(Ready)可以转化为运行状态(Running),当调度器选中该任务并开始执行时。
2. 运行状态(Running)可以转化为阻塞状态(Blocked),当任务需要等待某个事件发生或等待某个资源释放时,任务会主动放弃CPU的使用权,进入阻塞状态。
3. 阻塞状态(Blocked)可以转化为就绪状态(Ready),当任务等待的事件发生或资源释放时,任务会被移出阻塞状态,重新进入就绪状态,等待调度器选中执行。
4. 就绪状态(Ready)可以转化为挂起状态(Suspended),当任务被显式地挂起时,任务不再参与调度,进入挂起状态。
5. 挂起状态(Suspended)可以转化为就绪状态(Ready),当任务被恢复为就绪状态时,任务重新参与调度,等待调度器选中执行。