FreeRTOS任务管理与控制

来源:互联网 发布:小米note软件搬家 编辑:程序博客网 时间:2024/05/17 23:34

Task.c文件:

全局变量:

 static xList pxReadyTasksLists[configMAX_PRIORITIES ];

static xListxDelayedTaskList1;       
PRIVILEGED_DATA static xListxDelayedTaskList2;      

< Delayed tasks (two lists are used - one fordelays that have overflowed the current tick count.

PRIVILEGED_DATA static xListxPendingReadyList;      

 任务已经就绪,但是调度被禁止,暂时放到pending列表

 

 PRIVILEGED_DATA static xListxSuspendedTaskList;     

 

任务控制块结构:

typedef struct tskTaskControlBlock
{


 volatileportSTACK_TYPE *pxTopOfStack; 

 

 

 

 #if ( portUSING_MPU_WRAPPERS == 1 )
       xMPU_SETTINGSxMPUSettings;    


 #endif 
 
 xListItem   xGenericListItem; 
 xListItem    xEventListItem;  
 unsignedportBASE_TYPE uxPriority;
 portSTACK_TYPE   *pxStack;   


 signedchar    pcTaskName[configMAX_TASK_NAME_LEN ];

 

 #if ( portSTACK_GROWTH > 0)
  portSTACK_TYPE*pxEndOfStack;   
 #endif

 #if ( portCRITICAL_NESTING_IN_TCB == 1 )
  unsigned portBASE_TYPEuxCriticalNesting;
 #endif

 #if ( configUSE_TRACE_FACILITY == 1 )
  unsignedportBASE_TYPE uxTCBNumber; 


 #endif

 #if ( configUSE_MUTEXES == 1 )
  unsigned portBASE_TYPEuxBasePriority; 


 #endif

 #if ( configUSE_APPLICATION_TASK_TAG == 1)
  pdTASK_HOOK_CODEpxTaskTag;
 #endif

 #if ( configGENERATE_RUN_TIME_STATS == 1)
  unsigned longulRunTimeCounter;  


 #endif

} tskTCB;

 

 

任务函数API:

 

主要分为以下几个:

任务创建:xTaskCreate()

删除:vTaskDelete()

优先级设置:vTaskPrioritySet()

任务挂起:vTaskSuspend()

任务唤醒:vTaskResume()

从中断函数唤醒:xTaskResumeFromISR()

禁止调度:vTaskSuspendAll()

允许调度:xTaskResumeAll()

 

一:任务创建。

signed portBASE_TYPE xTaskGenericCreate( pdTASK_CODE pxTaskCode,const signed char * const pcName, unsigned short usStackDepth, void*pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle*pxCreatedTask, portSTACK_TYPE *puxStackBuffer, const xMemoryRegion* const xRegions )。

代码体概述:

1:分配TCB和任务堆栈  

 tskTCB * pxNewTCB; 

pxNewTCB = prvAllocateTCBAndStack( usStackDepth, puxStackBuffer);:

2:栈顶指针赋值: pxTopOfStack =pxNewTCB->pxStack + ( usStackDepth - 1 );

3:初始化变量:名称,优先级,那两个列表项:xGenericItem,xEventItem

 
  prvInitialiseTCBVariables(pxNewTCB, pcName, uxPriority, xRegions, usStackDepth );

       初始化列表项

       vListInitialiseItem( &(pxTCB->xGenericListItem ) );
       vListInitialiseItem( &(pxTCB->xEventListItem ) );

      设置所有者,以便通过xGenericListItem,xEventListItem找到盖TCB,及找到该任务。

      
     listSET_LIST_ITEM_OWNER( &(pxTCB->xGenericListItem ), pxTCB );

    
    listSET_LIST_ITEM_VALUE( &(pxTCB->xEventListItem ), configMAX_PRIORITIES -(               portTickType)    uxPriority );
      listSET_LIST_ITEM_OWNER( &(pxTCB->xEventListItem ), pxTCB );

4:判断是否是第一个任务

 if( uxCurrentNumberOfTasks == ( unsignedportBASE_TYPE ) 1 )
   {
    
    pxCurrentTCB=  pxNewTCB;

    

   如果是第一个任务,就初始化上面那些全局变量链表。
    prvInitialiseTaskLists();
   }
   else
   {
    
    if(xSchedulerRunning == pdFALSE )
    {
     if(pxCurrentTCB->uxPriority <=uxPriority )
     {
      pxCurrentTCB= pxNewTCB;
     }
    }
   }
 5:加入就绪链表

   uxTaskNumber++;

   prvAddTaskToReadyQueue(pxNewTCB );

下面列出函数体:

     while( (pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList) ) !=    NULL)      \
                            \
  if( xTickCount <listGET_LIST_ITEM_VALUE( &(pxTCB->xGenericListItem ) ))         \
                            
   break;                         

                            
  vListRemove( &(pxTCB->xGenericListItem ));                
                   
  if(pxTCB->xEventListItem.pvContainer)                  
                            
   vListRemove(&( pxTCB->xEventListItem ));                
           

   if(pxTCB->uxPriority >uxTopReadyPriority)                
                             
    uxTopReadyPriority=pxTCB->uxPriority;                 

                          
   vListInsertEnd( ( xList * )&( pxReadyTasksLists[pxTCB->uxPriority ] ), &(pxTCB-  >xGenericListItem ));                 
                  
     

 6:看看是否发生调度。

 if( xSchedulerRunning != pdFALSE )
  {
   

   如果系统已经在跑,而且这个新建立的任务优先级高,就发生调度。
   if(pxCurrentTCB->uxPriority < uxPriority)
   {
    portYIELD_WITHIN_API();//牵涉到任务调度,会在----FreeRTOS调度----详细介绍。
   }
  }

 

 

 

 

 

二:任务删除

freertos的任务删除分两步完成,

第一步在vTaskDelete中完成,FreeRTOS先把要删除的任务从就绪任务链表和事件等待链表中删除,然后把此任务添加到任务删除链表(即那个xTasksWaitingTermination), 若删除的任务是当前运行任务,系统就执行任务调度函数.

第2步 则是在idle任务中完成,idle任务运行时,检查xTasksWaitingTermination链表,如果有任务在这个表上,释放该任务占用的内存空间,并把该任务从任务删除链表中删除。

void vTaskDelete( xTaskHandle pxTaskToDelete )

{

    tskTCB*pxTCB;

 

       taskENTER_CRITICAL();

       {

           

           if( pxTaskToDelete == pxCurrentTCB )

           {

               pxTaskToDelete = NULL;

           }

 

           

           pxTCB = prvGetTCBFromHandle( pxTaskToDelete );

 

           traceTASK_DELETE( pxTCB );

 

           

           vListRemove( &(pxTCB->xGenericListItem ) );

 

                         

           if( pxTCB->xEventListItem.pvContainer )

           {//如果是,则把它从事件等待链表中删除

               vListRemove( &(pxTCB->xEventListItem ) );

           }

         //插入等待删除链表

           vListInsertEnd( ( xList * )&xTasksWaitingTermination, &(pxTCB->xGenericListItem ) );

          //增加uxTasksDeleted计 数

           ++uxTasksDeleted;

       }

       taskEXIT_CRITICAL();

 

       

       if( xSchedulerRunning != pdFALSE )

       {

           if( ( void * ) pxTaskToDelete == NULL )

           {

               taskYIELD();//调度会在FreeRTOS调度章节中介绍。

           }

       }

}

 

Idle任务。

static portTASK_FUNCTION( prvIdleTask, pvParameters)

{

 

( void ) pvParameters;

 

for( ;; )

{

 

prvCheckTasksWaitingTermination();

…………………………….

这里prvCheckTasksWaitingTermination()就是干这第2步的工作:每次调用它删除一个任务

static void prvCheckTasksWaitingTermination( void)

{

#if ( INCLUDE_vTaskDelete == 1 )

{

portBASE_TYPE xListIsEmpty;

 

 

if( uxTasksDeleted > ( unsignedportBASE_TYPE ) 0 )

{//禁止调度

vTaskSuspendAll();

xListIsEmpty = listLIST_IS_EMPTY(&xTasksWaitingTermination);             //打开调度

xTaskResumeAll();

 

if( !xListIsEmpty )

{

tskTCB *pxTCB;

//关中断

portENTER_CRITICAL();

{

pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( (( xList * ) &xTasksWaitingTermination ) );

vListRemove( &(pxTCB->xGenericListItem ) );

--uxCurrentNumberOfTasks;

--uxTasksDeleted;

}

portEXIT_CRITICAL();

//释放内存,删除tcb

prvDeleteTCB( pxTCB );

}

}

}

#endif

}

 

三:禁止调度,打开调度

调度器的禁止和打开

这是一种同步机制,比关中断要温和点。禁止调度由vTaskSuspendAll实现,打开调度由xTaskResumeAll实现。

void vTaskSuspendAll( void )

{

portENTER_CRITICAL();

++uxSchedulerSuspended;

portEXIT_CRITICAL();

}

这个很简单,系统维护一个计数uxSchedulerSuspended,当它大于0时候表示禁止调度,等于0则打开调度(允许调度)。

signed portBASE_TYPE xTaskResumeAll( void )

{

register tskTCB *pxTCB;

signed portBASE_TYPE xAlreadyYielded = pdFALSE;

 

 

在禁止调度器件,如果ISR导致一个任务就绪,这个任务会放在xPendingReadyList中,一旦调度允许,必须把所有的xPendingzList中的任务移动到theappropriate ready list中。

portENTER_CRITICAL();

{//将计数减一

--uxSchedulerSuspended;

//如果等于0,则允许调度

if( uxSchedulerSuspended == ( unsignedportBASE_TYPE ) pdFALSE )

{

if( uxCurrentNumberOfTasks > (unsigned portBASE_TYPE ) 0 )

{

portBASE_TYPE xYieldRequired = pdFALSE;

 

 

while( ( pxTCB = ( tskTCB * )listGET_OWNER_OF_HEAD_ENTRY(  ( ( xList * )&xPendingReadyList ) ) ) != NULL )

{

vListRemove( &(pxTCB->xEventListItem ) );

vListRemove( &(pxTCB->xGenericListItem ) );

prvAddTaskToReadyQueue( pxTCB );

 

 

if( pxTCB->uxPriority>= pxCurrentTCB->uxPriority )

{

xYieldRequired = pdTRUE;

}

}

 

 

if( uxMissedTicks > ( unsignedportBASE_TYPE ) 0 )

{

while( uxMissedTicks > ( unsignedportBASE_TYPE ) 0 )

{

vTaskIncrementTick();

--uxMissedTicks;

}

 

 

#if configUSE_PREEMPTION == 1

{

xYieldRequired = pdTRUE;

}

#endif

}

 

if( ( xYieldRequired == pdTRUE ) || ( xMissedYield== pdTRUE ) )

{

xAlreadyYielded = pdTRUE;

xMissedYield = pdFALSE;

taskYIELD();  //又一次发生任务调度函数调用,任务调度章节会详细介绍。

}

}

}

}

portEXIT_CRITICAL();

 

return xAlreadyYielded;

}

 

 

四:任务的挂起与唤醒。

freertos的任务挂起与ucosii也不大一样。它把 所有挂起的任务加到xSuspendedTaskList中,而且一旦调用vTaskSuspend()函数挂起一个任务,该任务就将从所有它原先连入的链表中删除(包括就绪表,延时表和它等待的事件链表),也就是说,和ucosii不同,一旦一个任务被挂起,它将取消先前它的延 时和对事件的等待。ucosii中是不同的,在ucosii里 面一个任务被挂起仅仅是把任务的状态或上一个OS_STAT_SUSPEND并从就绪表中删除,如果先前这个任务正在等待某事件,则并不取消等待。

//如果传进来的pxTaskToSuspend==NULL,则表示挂起当前任务

void vTaskSuspend( xTaskHandle pxTaskToSuspend)

{

tskTCB *pxTCB;

 

taskENTER_CRITICAL();

{

 

if( pxTaskToSuspend == pxCurrentTCB )

{

pxTaskToSuspend = NULL;

}

 

 

pxTCB = prvGetTCBFromHandle( pxTaskToSuspend );

 

traceTASK_SUSPEND( pxTaskToSuspend );

 

 

vListRemove( &(pxTCB->xGenericListItem ) );

 

 

if(pxTCB->xEventListItem.pvContainer )

{

vListRemove( &(pxTCB->xEventListItem ) );

}

//插到xSuspendedTaskList

vListInsertEnd( ( xList * )&xSuspendedTaskList, &(pxTCB->xGenericListItem ) );

}

taskEXIT_CRITICAL();

 

 

if( ( void * ) pxTaskToSuspend == NULL )

{

taskYIELD();///又是调度。

}

}

 

相反的唤醒就是把任务从xSuspendedTaskList中删除,加到对应的就绪链表中(根据任务的优先级),然后如果唤醒的任务优先级高于当前任务优先级,则调度。

void vTaskResume( xTaskHandle pxTaskToResume )

{

tskTCB *pxTCB;

 

 

pxTCB = ( tskTCB * ) pxTaskToResume;

 

 

if( pxTCB != NULL )

{

taskENTER_CRITICAL();

{

if( prvIsTaskSuspended( pxTCB ) == pdTRUE )

{

traceTASK_RESUME( pxTCB );

 

 

vListRemove(  &(pxTCB->xGenericListItem ) );

prvAddTaskToReadyQueue( pxTCB );

 

 

if( pxTCB->uxPriority>= pxCurrentTCB->uxPriority )

{

 

taskYIELD();  调度。。。。。。。。

}

}

}

taskEXIT_CRITICAL();

}

}

 从中断函数唤醒:

 portBASE_TYPE xTaskResumeFromISR(xTaskHandle pxTaskToResume )
 {
 portBASE_TYPE xYieldRequired = pdFALSE;
 tskTCB *pxTCB;

  pxTCB = (tskTCB * ) pxTaskToResume;

  if(xTaskIsTaskSuspended( pxTCB ) == pdTRUE )
  {
   traceTASK_RESUME_FROM_ISR(pxTCB );

   if(uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE )
   {
    xYieldRequired= ( pxTCB->uxPriority >=pxCurrentTCB->uxPriority );
    vListRemove( &( pxTCB->xGenericListItem ));
    prvAddTaskToReadyQueue(pxTCB );
   }
   else
   {
    
    vListInsertEnd(( xList * ) &( xPendingReadyList ),&( pxTCB->xEventListItem ) );
   }
  }

  returnxYieldRequired;
 }

#endif

 

上面两种唤醒不大一样:

任务重唤醒:可以直接进行任务调度(如果唤醒的优先级比正在运行的优先级高)。

中断唤醒:最多可以把被唤醒的任务加入到就绪表或者pendinglist中,返回可以进行调度标志变量。

五:设置优先级

 unsigned portBASE_TYPEuxTaskPriorityGet( xTaskHandle pxTask )
 {
 tskTCB *pxTCB;
 unsigned portBASE_TYPE uxReturn;

  portENTER_CRITICAL();
  {
   
   pxTCB =prvGetTCBFromHandle( pxTask );
   uxReturn =pxTCB->uxPriority;
  }
  portEXIT_CRITICAL();

  returnuxReturn;
 }

#endif

#if ( INCLUDE_vTaskPrioritySet == 1 )

 void vTaskPrioritySet(xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority )
 {
 tskTCB *pxTCB;
 unsigned portBASE_TYPE uxCurrentPriority,xYieldRequired = pdFALSE;

  
  if( uxNewPriority>= configMAX_PRIORITIES )
  {
   uxNewPriority= configMAX_PRIORITIES - 1;
  }

  portENTER_CRITICAL();
  {
   if( pxTask ==pxCurrentTCB )
   {
    pxTask= NULL;
   }

   
   pxTCB =prvGetTCBFromHandle( pxTask );

   traceTASK_PRIORITY_SET(pxTask, uxNewPriority );

   #if (configUSE_MUTEXES == 1 )
   {
    uxCurrentPriority= pxTCB->uxBasePriority;
   }
   #else
   {
    uxCurrentPriority= pxTCB->uxPriority;
   }
   #endif

   if(uxCurrentPriority != uxNewPriority )
   {
    
    if(uxNewPriority > uxCurrentPriority )
    {
     if(pxTask != NULL )
     {
      
      xYieldRequired= pdTRUE;
     }
    }
    elseif( pxTask == NULL )
    {
     
     xYieldRequired= pdTRUE;
    }

 

    #if( configUSE_MUTEXES == 1 )
    {
     
     if(pxTCB->uxBasePriority ==pxTCB->uxPriority )
     {
      pxTCB->uxPriority= uxNewPriority;
     }

     
     pxTCB->uxBasePriority= uxNewPriority;
    }
    #else
    {
     pxTCB->uxPriority= uxNewPriority;
    }
    #endif

    listSET_LIST_ITEM_VALUE(&( pxTCB->xEventListItem ), (configMAX_PRIORITIES - ( portTickType ) uxNewPriority ) );

    
    if(listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[uxCurrentPriority ] ), &(pxTCB->xGenericListItem ) ) )
    {
     
     vListRemove(&( pxTCB->xGenericListItem ));
     prvAddTaskToReadyQueue(pxTCB );
    }

    if(xYieldRequired == pdTRUE )
    {
     portYIELD_WITHIN_API();
    }
   }
  }
  portEXIT_CRITICAL();
 }

#endif

原创粉丝点击