BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
traceTASK_INCREMENT_TICK( xTickCount );
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )---(1)
{
const TickType_t xConstTickCount = xTickCount + 1;---(2)
xTickCount = xConstTickCount;
if( xConstTickCount == ( TickType_t ) 0U )---(3)
{
taskSWITCH_DELAYED_LISTS();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xConstTickCount >= xNextTaskUnblockTime )---(4)
{
for( ;; )---(5)
{
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )---(6)
{
xNextTaskUnblockTime = portMAX_DELAY; ---(7)
break;
}
else
{
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );---(8)
xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );---(9)
if( xConstTickCount < xItemValue )---(10)
{
xNextTaskUnblockTime = xItemValue;
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
( void ) uxListRemove( &( pxTCB->xStateListItem ) );---(11)
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )---(12)
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
prvAddTaskToReadyList( pxTCB );---(13)
#if ( configUSE_PREEMPTION == 1 )---(14)
{
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
}
}
}
#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )---(15)
{
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
#if ( configUSE_TICK_HOOK == 1 )---(16)
{
if( uxPendedTicks == ( UBaseType_t ) 0U )
{
vApplicationTickHook();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TICK_HOOK */
}
else---(17)
{
++uxPendedTicks;
/* The tick hook gets called at regular intervals, even if the
scheduler is locked. */
#if ( configUSE_TICK_HOOK == 1 )---(18)
{
vApplicationTickHook();
}
#endif
}
#if ( configUSE_PREEMPTION == 1 )---(19)
{
if( xYieldPending != pdFALSE )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
return xSwitchRequired;
}
void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdFALSE;
/* A delay time of zero just forces a reschedule. */
if( xTicksToDelay > ( TickType_t ) 0U )---(1)
{
configASSERT( uxSchedulerSuspended == 0 );
vTaskSuspendAll();---(2)
{
traceTASK_DELAY();
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );---(3)
}
xAlreadyYielded = xTaskResumeAll();---(4)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xAlreadyYielded == pdFALSE )---(5)
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely )
{
TickType_t xTimeToWake;
const TickType_t xConstTickCount = xTickCount;---1
#if( INCLUDE_xTaskAbortDelay == 1 )
{
pxCurrentTCB->ucDelayAborted = pdFALSE;
}
#endif
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )---2
{
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );---3
}
else
{
mtCOVERAGE_TEST_MARKER();
}
#if ( INCLUDE_vTaskSuspend == 1 )---4
{
if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )---5
{
vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );---6
}
else
{
xTimeToWake = xConstTickCount + xTicksToWait;---7
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );---8
if( xTimeToWake < xConstTickCount )---9
{
vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
}
else
{
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );---10
if( xTimeToWake < xNextTaskUnblockTime )---11
{
xNextTaskUnblockTime = xTimeToWake;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
}
#else /* INCLUDE_vTaskSuspend */
{
···
}
#endif /* INCLUDE_vTaskSuspend */
}
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
{
TickType_t xTimeToWake;
BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;
vTaskSuspendAll();
{
const TickType_t xConstTickCount = xTickCount
xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;---1
if( xConstTickCount < *pxPreviousWakeTime )---2
{
if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )---3
{
xShouldDelay = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else---4
{
if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )---5
{
xShouldDelay = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
*pxPreviousWakeTime = xTimeToWake;---6
if( xShouldDelay != pdFALSE )---7
{
traceTASK_DELAY_UNTIL( xTimeToWake );
prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );---8
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
xAlreadyYielded = xTaskResumeAll();---9
if( xAlreadyYielded == pdFALSE )---10
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
计算唤醒时间,上一次唤醒时间+所要延时的时间
我们在前面已经说过,时钟节拍计数器是会溢出的,所以考虑计数器溢出,则会出现xConstTickCount <pxPreviousWakeTime的情况,那么既然xConstTickCount 都要小于pxPreviousWakeTime,xTimeToWake也会小于*pxPreviousWakeTime,并且肯定大于xConstTickCount 。关系图如下所示。
判断情况的有效性,排除不可能的情况,如果成立,则该任务应当延时
xConstTickCount 没有溢出
这种情况下也可能会有溢出,一种情况是只有xTimeToWake溢出,另外两个不溢出,则这时候三者关系为:xTimeToWake
另一种情况是,三者都不溢出,也就是pxPreviousWakeTime
所以该语句就是用于判断是否是以上两种情况,如果是,则标记为应当延时。
更新任务唤醒时间点
判断该任务是否满足延时条件,满足则添加到延时列表中
注意这里的延时时间不再是xTimeIncrement 而是 xTimeToWake - xConstTickCount,这样相当于减去了函数本体运行时间,所以能够确保较为精确地延时,因为相对延时函数是没有计算函数本体的时间的,所以其延时时间如果是1000ms,则其执行周期要大于1000ms
解除调度器挂起
判断是否要进行任务调度
void led_task(void *pvParameters)
{
long i;
TickType_t PreviousWakeTime;
const TickType_t TimeIncrement = pdMS_TO_TICKS(1000);//将ms转化为对应的时钟节拍数
PreviousWakeTime = xTaskGetTickCount();//获取当前时钟节拍
while(1)
{
LED0= ~LED0;
for(i=0; i<0x6ffff;i++);
//vTaskDelay(1000);
vTaskDelayUntil(&PreviousWakeTime,TimeIncrement);//执行绝对延时函数,周期为1000ms
}
}
到这里我们就将时钟节拍以及延时函数产生任务调度的原理讲述完了。