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;
联合程序的全局变量使用与任务类似,均是有一个全局的就绪联合程序数组来保存全部就绪联合程序,定义了两个延时时使用的列表,及对应列表的指针,分别用来指向正在延时的联合程序以及已经超时的联合程序。
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值,从而实现函数的正确运行。
walnutcy_696810119 2009-6-30 00:31
用户394669 2009-6-24 16:58
用户1361860 2009-6-24 13:19