新闻  |   论坛  |   博客  |   在线研讨会
FreeRTOS co-routine 分析
mayer | 2009-07-14 18:40:37    阅读:4188   发布文章

FreeRTOS co-routine 分析

 

FreeRTOS中联合程序(co-routine)与任务类似,但在实现上区别,可参考http://www.openrtos.cn/freertos/taskandcr.html获取详细内容。简单而言联合程序相当于不能抢占的任务,其优先级均低于任务。因为其非抢占性,因此其需要的系统资源更少,不需要独立的堆栈空间。下面将对联合程序进行简单分析。

 

联合程序控制块

typedef struct corCoRoutineControlBlock
{
         crCOROUTINE_CODE   pxCoRoutineFunction;    /*联合程序函数*/
         xListItem    xGenericListItem; /*< 用于把联合程序放到 就绪列表或挂起列表中*/
         xListItem    xEventListItem;  /*< 用于把联合程序放到事件列表中*/
         unsigned portBASE_TYPE  uxPriority;   /*< 联合程序的优先级,只是相对于其他联合程序而言*/
         unsigned portBASE_TYPE  uxIndex;   /*< 当多个联合程序使用同一个联合函数时作为区分的参数 */
         unsigned portSHORT   uxState;   /*<在联合程序实现的内部需要的一个参数 */
} corCRCB; /* Co-routine control block.  Note must be identical in size down to uxPriority with tskTCB. */
 

可以看出与任务控制块相比,联合程序的控制块没有保存堆栈相关的信息。其联合程序函数是以函数调用的方式运行,而不像任务那样把函数地址压入栈中。

联合程序全局变量


static xList pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< 就绪联合程序*/
static xList xDelayedCoRoutineList1;        /*两个延时队列*/
static xList xDelayedCoRoutineList2;       
static xList * pxDelayedCoRoutineList;         
static xList * pxOverflowDelayedCoRoutineList;    
static xList xPendingReadyCoRoutineList;          

corCRCB * pxCurrentCoRoutine = NULL;
static unsigned portBASE_TYPE uxTopCoRoutineReadyPriority = 0;
static portTickType xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0;

 

联合程序的全局变量使用与任务类似,均是有一个全局的就绪联合程序数组来保存全部就绪联合程序,定义了两个延时时使用的列表,及对应列表的指针,分别用来指向正在延时的联合程序以及已经超时的联合程序。

联合程序 co-routine 的创建

FreeRTOS中创建联合程序是需要传入三个参数,分别是:

/*

pxCoRoutineCode    联合程序函数的地址
uxPriority    联合程序的优先级,只相对于联合程序有效
uxIndex    用来区分使用同一个联合程序函数的不同联合程序的参数

*/

signed portBASE_TYPE xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxIndex )
{
    signed portBASE_TYPE xReturn;
    corCRCB *pxCoRoutine;

     /* Allocate the memory that will store the co-routine control block. */
    /*联合程序控制块使用的内存需要动态分配*/
     pxCoRoutine = ( corCRCB * ) pvPortMalloc( sizeof( corCRCB ) );   
     if( pxCoRoutine )
     {
          /* If pxCurrentCoRoutine is NULL then this is the first co-routine to
          be created and the co-routine data structures need initialising. */
          /*当创建的为第一个联合程序时需要进行全局的初始化*/
          if( pxCurrentCoRoutine == NULL )
           {
                pxCurrentCoRoutine = pxCoRoutine;
                prvInitialiseCoRoutineLists();
           }

          /* Check the priority is within limits. */
          if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES )
          {
               uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1;
          }

          /* Fill out the co-routine control block from the function parameters. */
          pxCoRoutine->uxState = corINITIAL_STATE;    /*此参数用于程序内部实现*/
          pxCoRoutine->uxPriority = uxPriority;
          pxCoRoutine->uxIndex = uxIndex;
          pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode;

          /* Initialise all the other co-routine control block parameters. */
          vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) );
          vListInitialiseItem( &( pxCoRoutine->xEventListItem ) );

          /* Set the co-routine control block as a link back from the xListItem.
          This is so we can get back to the containing CRCB from a generic item
          in a list. */
          listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine );
          listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine );
 
          /* Event lists are always in priority order. */
          listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) uxPriority );
  
          /* Now the co-routine has been initialised it can be added to the ready
          list at the correct priority. */
          /*添加到就绪队列*/
          prvAddCoRoutineToReadyQueue( pxCoRoutine );

          xReturn = pdPASS;
     }
     else    /*分配内存失败*/
     {  
          xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
     }
 
 return xReturn; 
}

联合程序的创建主要的工作在于分配内存,初始化联合程序控制块的各个数据。与任务创建的区别在于没有临界段的操作,因此联合程序的创建可以被任务中断。

联合程序的调用相对比较简单,通过调用 vCoRoutineSchedule函数实现,函数首先通过调用prvCheckPendingReadyList()把已经就绪的联合程序从 xPendingReadyCoRoutineList 中移除,添加到就绪表中。prvCheckDelayedList()用来检查联合程序是否延时结束,并将延时结束的联合程序移到就绪表。最后把具有最高优先级的联合程序队列中的下一个联合程序置为当前,然后直接调用其联合程序函数pxCoRoutineFunction,传入参数 pxCurrentCoRoutine与 pxCurrentCoRoutine->uxIndex。联合程序函数完成返回后才会进行下一次的调度,因此联合程序间不会抢占。同时,由于联合程序函数是以函数调用的方式来运行,因此不会有独立的栈空间来保存局部变量,当因某些原因挂起时,联合程序函数会改变其 uxState 参数,同时返回,函数内部定义的局部变量会丢失,因此需要使用static关键字来修饰。联合程序调度函数如下:

void vCoRoutineSchedule( void )
{
     /* See if any co-routines readied by events need moving to the ready lists. */
     /*检查已经就绪的联合程序*/
     prvCheckPendingReadyList();

     /* See if any delayed co-routines have timed out. */
     prvCheckDelayedList();

     /* Find the highest priority queue that contains ready co-routines. 获取最高优先级的联合程序就绪表*/
     while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) )
     {
          if( uxTopCoRoutineReadyPriority == 0 )
          {
           /* No more co-routines to check. */
           return;
          }
          --uxTopCoRoutineReadyPriority;
     }

     /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines
      of the same priority get an equal share of the processor time. */
     /*用于实现同优先级的联合程序间的轮转调度*/
     listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) );

     /* Call the co-routine. 以调用函数的方式来调度联合程序*/
     ( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex );

     return;
}

co-routine 函数实现分析

联合程序(co-routine)函数负责实现联合程序的功能,其定义为
typedef void (*crCOROUTINE_CODE)( xCoRoutineHandle, unsigned portBASE_TYPE );
传入的两个参数分别为xCoRoutineHandle为联合程序控制块,portBASE_TYPE 用于在两个联合程序使用同一个联合程序函数时作为区分。
 

联合程序函数必须以crSTART( xHandle )开始,以crEND()结束,这两个宏的定义如下:
#define crSTART( pxCRCB ) switch( ( ( corCRCB * )pxCRCB )->uxState ) { case 0:
#define crEND() }
 

还有以下两个用于内部实现的宏:
#define crSET_STATE0( xHandle ) ( ( corCRCB * )xHandle)->uxState = (__LINE__ * 2); return; case (__LINE__ * 2):
#define crSET_STATE1( xHandle ) ( ( corCRCB * )xHandle)->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1):

分析下面这个简单的联合程序:

 void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
 {
     crSTART( xHandle );
     for( ;; )
     {
         vParTestToggleLED( 1 );
         crDELAY( xHandle, 1000);
         vParTestToggleLED( 2 );
     }
     crEND();
 }

用宏定义代人上面的联合程序函数得:
 void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
 {
     switch( ( ( corCRCB * )pxCRCB )->uxState ) { case 0:
     for( ;; )
     {
         vParTestToggleLED( 1 );
         //crDELAY( xHandle, 1000);
        vCoRoutineAddToDelayedList(1000, NULL );
        ( ( corCRCB * )xHandle)->uxState = (__LINE__ * 2); return; case (__LINE__ * 2):
        vParTestToggleLED(2 );
     }
     }
 }

其中__LINE__为编译器内部定义,表示代码的当前行号,假设此处的__LINE__数值为8,对上面函数定义整理后得:
 void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
 {
     switch( ( ( corCRCB * )pxCRCB )->uxState )
     {
        case 0:
             for( ;; )
             {
                 vParTestToggleLED( 1 );
                 //crDELAY( xHandle, 1000);
                 vCoRoutineAddToDelayedList(1000, NULL );
                ( ( corCRCB * )xHandle)->uxState = (8 * 2);
                return; 
        case (8 * 2):
            vParTestToggleLED(2 );
           }
     }
 }

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客