目录
2.1 prvAddCurrentTaskToDelayedList()-解析
2.2 滴答定时器中断函数中的 xTaskIncrementTick() - 解析
2.3 taskSWITCH_DELAYED_LISTS() - 解析
一.延时函数的介绍
函数 | 描述 |
---|---|
vTaskDelay() | 相对延时 |
vTaskDelayUntil() | 绝对延时 |
相对延时:
指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束(任务被阻塞的时间,到调用此函数开始的时间);
绝对延时:指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务(整个任务执行的时间,从头到尾的时间)。
二.相对延时的源码解读
vTaskDelay()函数的源码 + 中文注释:
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();
}
/*判断xAlreadyYielded 是否需要进行任务切换。*/
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
此函数是将任务挂载到阻塞列表,解除是在滴答定时器的中断服务函数xPortSysTickHandler()
2.1 prvAddCurrentTaskToDelayedList()-解析
/*
函数有两个入口参数一个是延时时间xTicksToWait,
另一个是xCanBlockIndefinitely 等于pdFALSE。
*/
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,
const BaseType_t xCanBlockIndefinitely )
prvAddCurrentTaskToDelayedList()函数的源码 + 中文注释:
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely )
{
TickType_t xTimeToWake;
/*xConstTickCount存储时钟节拍,滴答定时器中断一次,变量xTickCount加1*/
const TickType_t xConstTickCount = xTickCount;
/*宏INCLUDE_xTaskAbortDelay判断是否是中断延时,这里并没有使用,所以不用管。*/
#if( INCLUDE_xTaskAbortDelay == 1 )
{
pxCurrentTCB->ucDelayAborted = pdFALSE;
}
#endif
/*使用函数uxListRemove()将正在执行的任务的状态列表项从就绪列表中移除*/
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/*
移除完判断是否有同等优先级的任务,没有就代表只有这一个任务,被移除掉后就绪列表中剩余任务为0,
那么将此优先级的任务优先级复位。
*/
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/*宏INCLUDE_vTaskSuspend 判断是否使能挂起*/
#if ( INCLUDE_vTaskSuspend == 1 )
{
/*判断延时时间xTicksToWait等于最大延时时间并且xCanBlockIndefinitely 不等于pdFALSE*/
if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
{
/*此时将任务挂载到挂起列表中*/
vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
/*
首先记录时间,xConstTickCount 为进入函数prvAddCurrentTaskToDelayedList()时记录的时间,
加上延时时间xTicksToWait,就是任务到截止阻塞时间该被恢复的时间
*/
xTimeToWake = xConstTickCount + xTicksToWait;
/*
通过函数listSET_LIST_ITEM_VALUE将延时时间写入到列表项值里,
此值将用作挂载到阻塞列表时根据此值进行升序排列;
*/
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
/*
判断需要等待截止的时间是否小于进入函数prvAddCurrentTaskToDelayedList()时记录的时间,
这里判断是否数值溢出,如果溢出就将任务挂载到溢出阻塞列表中,否则挂载到阻塞列表中。
*/
if( xTimeToWake < xConstTickCount )
{
vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
/*判断下一个阻塞超时时间如果大于新的阻塞时间,那么将新的阻塞时间更新为下一个阻塞超时时间。*/
if( xTimeToWake < xNextTaskUnblockTime )
{
xNextTaskUnblockTime = xTimeToWake;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
}
.........
以下代码省略
}
2.2 滴答定时器中断函数中的 xTaskIncrementTick() - 解析
xTaskIncrementTick()函数的源码 + 中文注释:
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
traceTASK_INCREMENT_TICK( xTickCount );
/*首先判断任务调度器是否被挂起,如果等于pdFALSE 则没有被挂起*/
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
/*
将系统时钟节拍xTickCount加1,然后再将值赋给自己,
每进来一次时钟节拍将自加1
*/
const TickType_t xConstTickCount = xTickCount + 1;
xTickCount = xConstTickCount;
/*判断xConstTickCount 是否为0,为0则值溢出*/
if( xConstTickCount == ( TickType_t ) 0U )
{
taskSWITCH_DELAYED_LISTS();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/*判断当前时钟节拍ConstTickCount 是否大于等于下一个阻塞超时时间*/
if( xConstTickCount >= xNextTaskUnblockTime )
{
for( ;; )
{
/*判断阻塞列表中是否有任务,如果没有任务则没有需要被解除的任务。*/
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
{
/*则将下一个阻塞超时时间xNextTaskUnblockTime设置为最大值。*/
xNextTaskUnblockTime = portMAX_DELAY;
break;
}
/*else则阻塞列表中有任务*/
else
{
/*通过函数listGET_OWNER_OF_HEAD_ENTRY()获取阻塞列表的第一个成员的任务控制块*/
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
/*
通过函数listGET_LIST_ITEM_VALUE()获取列表项的数值,列表项中一般存放的是阻塞时间,
则xItemValue被赋值阻塞时间
*/
xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
/*判断系统时间节拍的数值是否小于阻塞时间,代表此时发生异常*/
if( xConstTickCount < xItemValue )
{
/*
因为在首先进入if时判断了当前的系统时钟节拍比下一个阻塞超时时间大。
此时将列表项的值赋值给下一个阻塞超时时间,退出。
*/
xNextTaskUnblockTime = xItemValue;
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/*
使用函数listREMOVE_ITEM()将任务从阻塞列表中移除,
同时也从使用函数listREMOVE_ITEM从事件列表中移除。
*/
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 将任务添加到就绪列表中 */
prvAddTaskToReadyList( pxTCB );
/* 判断宏configUSE_PREEMPTION是否使能抢占式任务调度 */
#if ( configUSE_PREEMPTION == 1 )
{
/*判断恢复的任务的任务优先级是否比当前正在执行的任务优先级高*/
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
/*将任务切换xSwitchRequired变量赋值pdTRUE。*/
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
}
}
}
/* 以下程序是时间片调度 */
#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
{
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */
#if ( configUSE_TICK_HOOK == 1 )
{
/* Guard against the tick hook being called when the pended tick
count is being unwound (when the scheduler is being unlocked). */
if( uxPendedTicks == ( UBaseType_t ) 0U )
{
vApplicationTickHook();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TICK_HOOK */
}
else
{
++uxPendedTicks;
/* The tick hook gets called at regular intervals, even if the
scheduler is locked. */
#if ( configUSE_TICK_HOOK == 1 )
{
vApplicationTickHook();
}
#endif
}
#if ( configUSE_PREEMPTION == 1 )
{
if( xYieldPending != pdFALSE )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
return xSwitchRequired;
}
2.3 taskSWITCH_DELAYED_LISTS() - 解析
只溢出之后,将就绪列表pxDelayedTaskList和溢出就绪列表pxOverflowDelayedTaskList 进行互换。
#define taskSWITCH_DELAYED_LISTS() \
{ \
List_t *pxTemp; \
\
/* The delayed tasks list should be empty when the lists are switched. */ \
configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); \
\
pxTemp = pxDelayedTaskList; \
pxDelayedTaskList = pxOverflowDelayedTaskList; \
pxOverflowDelayedTaskList = pxTemp; \
xNumOfOverflows++; \
prvResetNextTaskUnblockTime(); \
}