前言
其实是接上一篇task这篇文章的,上一篇写的有点多。
代码分析
调度器开启、关闭
一般在调度器没有开启之前需要创建一个start_task来创建一系列任务task,然后就是调用vTaskStartScheduler
来启动调度器。下面分析具体代码
void vTaskStartScheduler( void )
{
BaseType_t xReturn;
/* 创建idle task,使其运行在最低优先级 */
xReturn = xTaskCreate( prvIdleTask,
"IDLE", configMINIMAL_STACK_SIZE,
( void * ) NULL,
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
&xIdleTaskHandle );
/*如果使能了定时器功能,那么不论你之后会使用定时器功能,
都会先创建一个定时器函数*/
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask();
}
}
if( xReturn == pdPASS )
{/*只有上两步操作都ok的情况下,才能继续执行*/
/*中断在这里被关闭,以确保在调用xPortStartScheduler()之前或期间不会发生tick。
已经创建的任务的栈中包含了开启中断的状态字,当第一个任务执行时,
中断将自动重新启用开始运行。*/
portDISABLE_INTERRUPTS();
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE;
xTickCount = ( TickType_t ) 0U;
/* 计时器刻度的设置是决定于硬件的,因此在移植的接口设置。*/
if( xPortStartScheduler() != pdFALSE ){对于cm3平台来说就是systick定时器
}
}
}
void vTaskEndScheduler( void )
{
/* 停止中断,并调用可移植调度器停止函数,以便在必要时恢复原始ISRs。
可移植层必须确保中断启用位保持在正确的状态。*/
portDISABLE_INTERRUPTS();
xSchedulerRunning = pdFALSE;
vPortEndScheduler();
}
阻塞调度器
在系统运行的时候如果不想让系统继续调度task运行,可以直接阻塞调度器,这样本来的任务就可以独占cpu了,不会被抢占打断。在做完工作之后在解挂就ok了
void vTaskSuspendAll( void )
{
/* 由于该变量是BaseType_t类型的,所以不需要临界区*/
++uxSchedulerSuspended;
}
BaseType_t xTaskResumeAll( void )
{
TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;
/*ISR可能导致从事件中删除任务在调度程序暂停时列出。
如果是这样,那么删除的任务将被添加到xPendingReadyList。
一旦已恢复调度程序,它是安全的移动所有挂起准备任务从这个列表进入相应的就绪列表。*/
taskENTER_CRITICAL();{
--uxSchedulerSuspended;
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ){
if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ){
/* 将 xPendingReadyList 中所有的的task移入 ReadyTasksLists中,所以用了个循环*/
while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ){
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
prvAddTaskToReadyList( pxTCB );
/* If the moved task has a priority higher than the current
task then a yield must be performed. */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ){
xYieldPending = pdTRUE;
}
}
if( pxTCB != NULL ){
/*当调度器被挂起的时候,可能导致了一个任务被阻塞了,而如果这个
任务恰好使得 xNextTaskUnblockTime 没有被重新计算,
那就在此重新计算。主要是对于低功耗无tickless实现
非常重要,这可以防止不必要的低功耗出口状态。*/
prvResetNextTaskUnblockTime();
}
/*如果在调度器暂停时发生了tick,那么现在应该进行处理了。
这确保了tick计数不错乱,任何延迟的任务都在正确的时间恢复。*/
{
UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */
if( uxPendedCounts > ( UBaseType_t ) 0U ){
do{
if( xTaskIncrementTick() != pdFALSE ){
xYieldPending = pdTRUE;
}
--uxPendedCounts;
} while( uxPendedCounts > ( UBaseType_t ) 0U );
uxPendedTicks = 0;
}
}
if( xYieldPending != pdFALSE ){
#if( configUSE_PREEMPTION != 0 )
{//这个返回值会导致内核抢占调度发生
xAlreadyYielded = pdTRUE;
}
#endif
taskYIELD_IF_USING_PREEMPTION();
}
}
}
}
taskEXIT_CRITICAL();
return xAlreadyYielded;
}