原创 FreeRTOS co-routine 分析

2009-6-24 10:35 3712 5 8 分类: MCU/ 嵌入式





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 );

           }

     }

 }


 


整个联合程序函数的实现以uxState为基础,用于记录函数运行的状态,当函数调用crDELAY时,把联合函数控制块添加到延时队列,同时把uxState以当前行号作为状态记录,当延时结束联合程序函数继续运行时,通过uxState标记来决定从调用延时函数后开始继续运行。通过内嵌的__LINE__定义,可以为每一次的阻塞调用提供唯一的uxState值,从而实现函数的正确运行。



PARTNER CONTENT

文章评论3条评论)

登录后参与讨论

walnutcy_696810119 2009-6-30 00:31

bluehacker在找那本书? 我的博客里提供了啊

用户394669 2009-6-24 16:58

http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3329453&bbs_page_no=1&search_mode=1&search_text=freertos&bbs_id=9999 不知道指的是不是这个

用户1361860 2009-6-24 13:19

不错,支持下。不知道大家有没有那本freertos的书,如果有的话分享下啊
相关推荐阅读
用户394669 2010-01-17 08:55
(笔记)无光驱使用U盘安装XP与Vista双系统
原系统为Vista,由于受不了其蜗牛般的速度,决定换为XP,又因为XP下面驱动不完善,因此还需要保留vista备用。又由于笔记本光驱用不了几次就坏了,因此需要使用U盘安装。准备工作:将xp oem安装...
用户394669 2009-12-31 11:29
跨平台同步的文档管理
不知道什么时候开始养成了上网记笔记的坏习惯,见到实用的内容总想摘录下来。由于有时需要同时在linux与windows下工作,所以需要一个跨平台的解决方法。开始的时候使用google doc,但是由于众...
用户394669 2009-12-11 10:36
VPN配置笔记
因为众所周知的原因,没办法只好给自己配置了一个私有的VPN来访问国外技术网站,忙了大半天终于完成,特总结为笔记方便以后使用。我使用的为DiaHosting的VPS主机,CentOs5 i386系统,2...
用户394669 2009-10-23 10:57
linux 中文乱码转换方法
使用 enconv 命令, enconv 文件名 。自动检测编码,并转换为utf-8...
用户394669 2009-09-16 13:52
ARM正式加盟Linux基金会
新闻来源:网易科技9月16日消息,英国微处理器和微控制器制造商ARM公司宣布正式加盟Linux基金会。Linux负责销售和开发者计划的副总裁AmandaMcPherson在Liunx基金会上表示,“通...
用户394669 2009-08-20 17:59
ubuntu 不重启更新PATH的方法
编辑 /etc/bash.bashrc添加 PATH=$PATH:(添加的位置)键入 source /etc/profile 命令更新完成因记性不好, 写下来,免得以后又去搜 :)如有更好方法欢迎交流...
EE直播间
更多
我要评论
3
5
关闭 站长推荐上一条 /3 下一条