延时函数介绍
函数 | 描述 |
vTaskDelay() | 相对延时 |
xTaskDelayUntil() | 绝对延时 |
相对延时:指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束
绝对延时:指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务
- 为任务主体,也就是任务真正要做的工作
- 是任务函数中调用vTaskDelayUntil()对任务进行延时
- 为其他任务在运行
vTaskDelay()
函数 vTaskDelay() 用于对任务进行延时,延时的时间单位为系统时钟节拍,使用函数,需要的 FreeRTOSConfig.h 文件中将配置项 INCLUDE_vTaskDelay 配置为 1。
void vTaskDelay(const TickType_t xTicksToDelay);
函数用于使任务进入阻塞状态,以实现延时功能。延时的时间以 FreeRTOS 的时钟节拍为单位。 指每次延时都是从执行函数 vTaskDelay() 开始,直到延时指定的时间(参数:滴答值)结束。
参数 xTicksToDelay 用于设置延迟的时钟节拍个数,范围 1- 0xFFFFFFFF。
在使用此函数进行任务延时时,如果传入的参数为 0,那表明不进行任务延时,而是强制进行一次任务切换。
void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdFALSE;
/* 只有在延时时间大于 0 的时候,才需要进行任务阻塞,
* 否则相当于强制进行任务切换,而不阻塞任务*/
if( xTicksToDelay > ( TickType_t ) 0U )
{
configASSERT( uxSchedulerSuspended == 0 );
/* 挂起任务调度器 */
vTaskSuspendAll();
{
/* 用于调试,不用理会 */
traceTASK_DELAY();
/* 将任务添加到阻塞态任务列表中 */
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
}
/* 恢复任务调度器运行,调用此函数会返回是否需要进行任务切换*/
xAlreadyYielded = xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 根据标志进行任务切换 */
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
xTaskDelayUntil()
函数 xTaskDelayUntil() 用于对任务进行绝对延时,延时的时间单位为系统时钟节拍,使用需要在 FreeRTOSConfig.h 文件中将配置项 INCLUDE_vTaskDelayUntil 配置为 1。
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement );
函数描述:函数 vTaskDelayUntil 用于周期性延迟。 属于绝对延时,指间隔指定的时间(参数:滴答值),执行一次调用 vTaskDelayUntil() 函数的任务。
参数 | 说明 |
pxPreviousWakeTime | 存储任务最后一次解除阻塞的时间 |
xTimeIncrement | 周期性延迟时间 |
返回值 | BaseType_t |
pdTRUE | 操作成功 |
pdFALSE | 操作失败 |
#if ( INCLUDE_xTaskDelayUntil == 1 )
BaseType_t xTaskDelayUntil(TickType_t * const pxPreviousWakeTime, /*上一次阻塞超时时间*/
const TickType_t xTimeIncrement ) /*延时的时间*/
{
TickType_t xTimeToWake;
BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;
configASSERT( pxPreviousWakeTime );
configASSERT( ( xTimeIncrement > 0U ) );
configASSERT( uxSchedulerSuspended == 0 );
/* 挂起任务调度器 */
vTaskSuspendAll();
{
const TickType_t xConstTickCount = xTickCount;
/* 计算任务下一次阻塞超时的时间,
* 这个阻塞超时时间是相对于上一次阻塞超时的时间的*/
xTimeToWake = *pxPreviousWakeTime + xTimeIncrement
/* 如果在上一次阻塞超时后,
* 系统时钟节拍计数器溢出过*/
if( xConstTickCount < *pxPreviousWakeTime )
{
/* 只有在下一次阻塞超时时间也溢出,
* 并且下一次阻塞超时时间大于系统时钟节拍计数器的值时,
* 需要做相应的溢出处理,否则就好像没有溢出*/
if( ( xTimeToWake < *pxPreviousWakeTime ) &&( xTimeToWake > xConstTickCount ) )
{
xShouldDelay = pdTRUE;/* 标记因为溢出,需要做相应的处理 */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
/* 系统时钟节拍计数器没有溢出,
* 但是下一次阻塞超时时间溢出了,
* 并且下一次阻塞超时时间大于系统时钟节拍计数器的值时,
* 需要做相应的溢出处理*/
if( ( xTimeToWake < *pxPreviousWakeTime ) ||( xTimeToWake > xConstTickCount ) )
{
xShouldDelay = pdTRUE; /* 标记因为溢出,需要做相应的溢出处理 */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 更新上一次阻塞超时时间为下一次阻塞超时时间 */
*pxPreviousWakeTime = xTimeToWake;
/* 根据标记,做相应的溢出处理 */
if( xShouldDelay != pdFALSE )
{
/* 用于调试,不用理会 */
traceTASK_DELAY_UNTIL( xTimeToWake );
/* 将任务添加到阻塞态任务列表中 */
prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount,pdFALSE );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 恢复任务调度器运行,
* 调用此函数会返回是否需要进行任务切换*/
xAlreadyYielded = xTaskResumeAll();
/* 根据标志进行任务切换 */
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
return xShouldDelay;
}
#endif /* INCLUDE_xTaskDelayUntil */
不知道你们有没有想过为什么是 xTaskDelayUntil() 而不是 vTaskDelayUntil(),我在tasks.c文件中找到
延时函数演示实验
学习 FreeRTOS 相对延时和绝对延时API 函数的使用,并了解其区别
实验设计:
将设计三个任务:start_task、task1、task2
三个任务的功能如下:
- start_task:用来创建其他的2个任务
- task1:用于展示相对延时函数vTaskDelay ( )的使用
- task2:用于展示绝对延时函数vTaskDelayUntil( )的使用
其他部分代码如有需要可参考上一篇博文
//用于展示相对延时函数vTaskDelay ( )的使用
void task1(void * pvParameters)
{
while(1)
{
LED0_TOGGLE();
delay_ms(10);
vTaskDelay(500);
}
}
//用于展示绝对延时函数vTaskDelayUntil( )的使用
void task2(void * pvParameters)
{
TickType_t xLastWakeTime = xTaskGetTickCount();
while(1)
{
LED1_TOGGLE();
delay_ms(10);
xTaskDelayUntil(&xLastWakeTime,500);
}
}
现象
注意:
可能会出现任务一在延时时,任务2在运行,但是在任务1解除延时时候任务2刚好在死延时,因为优先级原因所以任务1运行不了代表任务1不止延时了510ms。