1 延时函数介绍
函数 | 描述 |
vTaskDelay() | 相对延时 |
xTaskDelayUntil() | 绝对延时 |
相对延时:指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束
绝对延时:指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务,如下图所示:
(1)为任务主体,也就是任务要真正要做的工作
(2)是任务函数中调用vTaskDealyUntil()对任务进行演示
(3)为其他任务在运行
xTimeIncrement即是绝对延时时间.
2 延时函数解析
void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdFALSE;
if( xTicksToDelay > ( TickType_t ) 0U )
/*只有在延时时间大于0的时候,才需要进行任务阻塞*/
{
configASSERT( uxSchedulerSuspended == 0 );
vTaskSuspendAll();
/*挂起任务调度器*/
{
traceTASK_DELAY();
/*用于调试*/
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
/*将当前正在执行的任务移到阻塞列表*/
}
xAlreadyYielded = xTaskResumeAll();
/*恢复任务调度器运行,调用此函数会返回是否需要进行任务切换*/
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xAlreadyYielded == pdFALSE )
/*根据返回值xAlreadyYielded判断需不需要进行任务切换*/
{
portYIELD_WITHIN_API();
/*触发PendSV*/
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,
const BaseType_t xCanBlockIndefinitely )
/*xTicksToWait为阻塞时间,xCanBlockIndefinitely是否无期限阻塞(阻塞时间为最大值) */
{
TickType_t xTimeToWake;
const TickType_t xConstTickCount = xTickCount;
#if ( INCLUDE_xTaskAbortDelay == 1 )
/*此宏用于开启任务延时中断功能*/
{
pxCurrentTCB->ucDelayAborted = pdFALSE;
/*如果开启了任务延时中断功能,那么将任务的延时中断标志复位设置为假,
*当任务延时被中断是,再将其设置为真*/
}
#endif
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
/*将当前任务从所在列表中移除,当前任务为调用了会触发阻塞的API函数的任务*/
{
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
/*如果将当前任务从所在就绪态任务列表中移除后,原本所在就绪态任务列表中每有其他任务
*那么就将任务优先级记录中该任务的优先级清除*/
}
else
{
mtCOVERAGE_TEST_MARKER();
}
#if ( INCLUDE_vTaskSuspend == 1 )
/*此宏用于启用任务挂起功能*/
{
if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
/*如果阻塞时间为最大值并且允许任务被无期限阻塞,入参xCanBlockIndefintely用于定义
*在入参xTicksToWait为最大值的情况下,是否将任务无期限阻塞,即任务挂起*/
{
listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
/*将任务添加到挂起任务列表中挂起,即将任务无期限阻塞*/
}
else
{
xTimeToWake = xConstTickCount + xTicksToWait;
/*计算任务在未来被取消阻塞时,系统时钟节拍计数器的值,
* 计算出来的值可能会出现溢出的情况。但系统会有相应的处理机制,
*即两个阻塞态任务列表*/
//记录时间,xConstTickCount为进入函数之前得到的时间,
//xTicksToWait为延时时间,xTimeToWake为真正的延时时间
listSET_LIST_ITEM_VALUE(&( pxCurrentTCB->xStateListItem ), xTimeToWake );
//将真正的延时时间写到列表项中,然后将这个值加载到阻塞列表中,
//通过该值进行升序排列
if( xTimeToWake < xConstTickCount )
/*判断计算的值是否溢出*/
{
vListInsert( pxOverflowDelayedTaskList,
&( pxCurrentTCB->xStateListItem ) );
/*如果计算出来的值溢出,将任务添加到阻塞超时时间溢出列表*/
}
else
{
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
/*将任务添加到阻塞态任务列表*/
if( xTimeToWake < xNextTaskUnblockTime )
/*全局变量xNextTaskUnblockTime用于保存系统中
*最近要发生超时的系统节拍计数器的值*/
{
xNextTaskUnblockTime = xTimeToWake;
/*有新的任务阻塞,因此要更新xNextTaskUnblockTime*/
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
}
#else
{
xTimeToWake = xConstTickCount + xTicksToWait;
/*计算任务在未来被取消阻塞时,系统时钟节拍计数器的值,
*计算出来的值可能会出现溢出的情况,但系统会有相应的处理机制,即两个阻塞态任务列表*/
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
/*设置任务的状态列表项的值为计算出来的值,那么在 SysTick 中断服务函数中处理阻塞态任务
*列表时,就可以通过这个值,判断任务时候阻塞超时*/
if( xTimeToWake < xConstTickCount )
/*如果计算出来的值溢出*/
{
vListInsert(pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem));
/*将任务添加到阻塞超时时间溢出列表*/
}
else
{
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
/* 将任务添加到阻塞态任务列表 */
if( xTimeToWake < xNextTaskUnblockTime )
/* 全局变量xNextTaskUnblockTime 用于保存系统中最近要发生超时的系统时
*钟节拍计数器的值 */
{
xNextTaskUnblockTime = xTimeToWake;
/*有新的任务阻塞,因此要更新*/
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
( void ) xCanBlockIndefinitely;
/*不使用任务挂起功能,就不使用这个入参*/
}
#endif
}
3 延时函数实验
1)实验设计:设计三个任务:start_task、task1、task2
2)三个任务功能如下:
start_task :用来创建task1和task2任务
task1 :用于展示相对延时函数vTaskDelay()的使用
task2: 用于展示绝对延时函数vTaskDelayUntil()的使用