FreeRTOS的时间管理
1. vTaskDelay
void vTaskDelay( const TickType_t xTicksToDelay )
{
TickType_t xTimeToWake;
BaseType_t xAlreadyYielded = pdFALSE;
/* A delay time of zero just forces a reschedule. */
/* 延时周期要大于0 */
if( xTicksToDelay > ( TickType_t ) 0U )
{
configASSERT( uxSchedulerSuspended == 0 );
vTaskSuspendAll(); /* 挂起调度器 */
{
traceTASK_DELAY();
/* A task that is removed from the event list while the
scheduler is suspended will not get placed in the ready
list or removed from the blocked list until the scheduler
is resumed.
This task cannot be in an event list as it is the currently
executing task. */
/* Calculate the time to wake - this may overflow but this is
not a problem. */
xTimeToWake = xTickCount + xTicksToDelay; /* 获取进入函数的时间点并保存在xConstTickCount中 */
/* We must remove ourselves from the ready list before adding
ourselves to the blocked list as the same list item is used for
both lists. */
/* 把当前任务从就绪状态链表中移除 */
if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
{
/* The current task must be in a ready list, so there is
no need to check, and the port reset macro can be called
directly. */
/* 取消任务在uxTopReadyPriority中的就绪标记 */
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 将要延时的任务添加到延时列表中 */
prvAddCurrentTaskToDelayedList( xTimeToWake );
}
xAlreadyYielded = xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Force a reschedule if xTaskResumeAll has not already done so, we may
have put ourselves to sleep. */
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
2.prvAddCurrentTaskToDelayedList
static void prvAddCurrentTaskToDelayedList( const TickType_t xTimeToWake )
{
/* The list item will be inserted in wake time order. */
/* 设置xItemValue的值为xTimeToWake */
listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake );
/* 计算得到的任务唤醒时间点小于xConstTickCount,说明发生了溢出 */
if( xTimeToWake < xTickCount )
{
/* Wake time has overflowed. Place this item in the overflow list. */
/* 若溢出,就把任务添加到延时溢出列表里 */
vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xGenericListItem ) );
}
else
{
/* The wake time has not overflowed, so the current block list is used. */
/* 没有溢出,把任务添加到延时列表中 */
vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xGenericListItem ) );
/* If the task entering the blocked state was placed at the head of the
list of blocked tasks then xNextTaskUnblockTime needs to be updated
too. */
/* 更新xNextTaskUnblockTime */
if( xTimeToWake < xNextTaskUnblockTime )
{
xNextTaskUnblockTime = xTimeToWake;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
3.xPortSysTickHandler
void xPortSysTickHandler( void )
{
/* The SysTick runs at the lowest interrupt priority, so when this interrupt
executes all interrupts must be unmasked. There is therefore no need to
save and then restore the interrupt mask value as its value is already
known. */
/*SysTick以最低的中断优先级运行,因此所有中断都必须取消屏蔽。因此,没有必要保存并恢复中断掩码值,因为其值已经存在已知*/
( void ) portSET_INTERRUPT_MASK_FROM_ISR();
{
/* Increment the RTOS tick. */
/* 若调度器返回true,触发pendSV异常 */
if( xTaskIncrementTick() != pdFALSE )
{
/* A context switch is required. Context switching is performed in
the PendSV interrupt. Pend the PendSV interrupt. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
/* 清除可屏蔽中断,即打开全部中断 */
portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 );
}
4.xTaskIncrementTick
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
/* Called by the portable layer each time a tick interrupt occurs.
Increments the tick then checks to see if the new tick value will cause any
tasks to be unblocked. */
traceTASK_INCREMENT_TICK( xTickCount );
/* uxSchedulerSuspended表示任务调度器是否挂起,,pdFALSE表示没有被挂起 */
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
/* Increment the RTOS tick, switching the delayed and overflowed
delayed lists if it wraps to 0. */
/* 时钟节拍计数器增加1 */
++xTickCount;
{
/* Minor optimisation. The tick count cannot change in this
block. */
const TickType_t xConstTickCount = xTickCount;
/* 判断tick是否溢出越界,为0说明发生了溢出 */
if( xConstTickCount == ( TickType_t ) 0U )
{
/* 若溢出,要更新延时列表 */
taskSWITCH_DELAYED_LISTS();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* See if this tick has made a timeout expire. Tasks are stored in
the queue in the order of their wake time - meaning once one task
has been found whose block time has not expired there is no need to
look any further down the list. */
/*查看此标记是否已使超时过期。任务存储在按唤醒时间顺序排列的队列-意味着一次完成一个任务已发现其阻止时间尚未到期,无需再往下*/
if( xConstTickCount >= xNextTaskUnblockTime )
{
/*会一直遍历整个任务延时列表,主要目的是,找到时间片最短的任务,进行切换 */
for( ;; )
{
/* 判断任务延时列表是否为空,即有没有任务在等待调度 */
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
{
/* The delayed list is empty. Set xNextTaskUnblockTime
to the maximum possible value so it is extremely
unlikely that the
if( xTickCount >= xNextTaskUnblockTime ) test will pass
next time through. */
/* 如果没有任务等待,把时间片赋值为最大值,不再调度 */
xNextTaskUnblockTime = portMAX_DELAY;
break;
}
else
{
/* The delayed list is not empty, get the value of the
item at the head of the delayed list. This is the time
at which the task at the head of the delayed list must
be removed from the Blocked state. */
/* 若有任务等待,获取延时列表第一个列表项对应的任务控制块*/
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
/* 获取上面任务控制块的状态列表项值 */
xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) );
/* 再次判断这个任务的时间片是否到达 */
if( xConstTickCount < xItemValue )
{
/* It is not time to unblock this item yet, but the
item value is the time at which the task at the head
of the blocked list must be removed from the Blocked
state - so record the item value in
xNextTaskUnblockTime. */
/* 记录xNextTaskUnblockTime */
xNextTaskUnblockTime = xItemValue;
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* It is time to remove the item from the Blocked state. */
/* 任务延时时间到,把任务从延时列表中移除 */
( void ) uxListRemove( &( pxTCB->xGenericListItem ) );
/* Is the task waiting on an event also? If so remove
it from the event list. */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
/* 再把任务从事件列表中移除 */
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Place the unblocked task into the appropriate ready
list. */
/* 把任务添加到就绪列表中 */
prvAddTaskToReadyList( pxTCB );
/* A task being unblocked cannot cause an immediate
context switch if preemption is turned off. */
/* 抢占式内核 */
#if ( configUSE_PREEMPTION == 1 )
{
/* Preemption is on, but a context switch should
only be performed if the unblocked task has a
priority that is equal to or higher than the
currently executing task. */
/* 判断解除阻塞的任务的优先级是否高于当前任务的优先级 */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
/* 如果是的话,就需要进行一次任务切换 */
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
}
}
}
}
/* Tasks of equal priority to the currently running task will share
processing time (time slice) if preemption is on, and the application
writer has not explicitly turned time slicing off. */
/* 若使能了时间片处理机制,还需要处理同优先级下任务之间的调度 */
#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
{
/* 任务调度器挂起 */
/* 挂起的tick+1 */
++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;
}