Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1908504
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类: 嵌入式

2014-09-27 15:25:24

Task.c文件:

全局变量:

 static xList pxReadyTasksLists[ configMAX_PRIORITIES ];

static xList xDelayedTaskList1;       
PRIVILEGED_DATA static xList xDelayedTaskList2;      

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

PRIVILEGED_DATA static xList xPendingReadyList;      

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

 

 PRIVILEGED_DATA static xList xSuspendedTaskList;     

 

任务控制块结构:

typedef struct tskTaskControlBlock
{


 volatile portSTACK_TYPE *pxTopOfStack; 

 

 

 

 #if ( portUSING_MPU_WRAPPERS == 1 )
        xMPU_SETTINGS xMPUSettings;    


 #endif 
 
 xListItem   xGenericListItem; 
 xListItem    xEventListItem;  
 unsigned portBASE_TYPE uxPriority;
 portSTACK_TYPE   *pxStack;   


 signed char    pcTaskName[ configMAX_TASK_NAME_LEN ];

 

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

 #if ( portCRITICAL_NESTING_IN_TCB == 1 )
  unsigned portBASE_TYPE uxCriticalNesting;
 #endif

 #if ( configUSE_TRACE_FACILITY == 1 )
  unsigned portBASE_TYPE uxTCBNumber; 


 #endif

 #if ( configUSE_MUTEXES == 1 )
  unsigned portBASE_TYPE uxBasePriority; 


 #endif

 #if ( configUSE_APPLICATION_TASK_TAG == 1 )
  pdTASK_HOOK_CODE pxTaskTag;
 #endif

 #if ( configGENERATE_RUN_TIME_STATS == 1 )
  unsigned long ulRunTimeCounter;  


 #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 == ( unsigned portBASE_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 > ( unsigned portBASE_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中的任务移动到the appropriate ready list中。

portENTER_CRITICAL();

{//将计数减一

--uxSchedulerSuspended;

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

if( uxSchedulerSuspended == ( unsigned portBASE_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 > ( unsigned portBASE_TYPE ) 0 )

{

while( uxMissedTicks > ( unsigned portBASE_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 ) );
   }
  }

  return xYieldRequired;
 }

#endif

 

上面两种唤醒不大一样:

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

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

五:设置优先级

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

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

  return uxReturn;
 }

#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;
     }
    }
    else if( 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

阅读(5691) | 评论(0) | 转发(0) |
0

上一篇:FreeRTOS概述

下一篇:FreeRTOS任务切换解析

给主人留下些什么吧!~~