FreeRTOS原理剖析:事件标志组

1. 事件标志组相关API函数

函数描述
xEventGroupCreate()使用动态方式创建事件标志组
xEventGroupCreateStatic()使用静态方式创建事件标志组
vEventGroupDelete()删除事件标志组
xEventGroupSetBits()将指定的事件位置1, 用于任务中
xEventGroupSetBitsFromISR()将指定的事件位置1,用于中断服务函数
xEventGroupClearBits()将指定的事件位清零, 用于任务
xEventGroupClearBitsFromISR()将指定的事件位清零,用于中断服务函数
xEventGroupGetBits获取当前事件标志组的值(各个事件位的值),用在任务中
xEventGroupGetBitsFromISR()获取当前事件标志组的值, 用在中断服务中
xEventGroupWaitBits()事件组等待位
xEventGroupSync()事件组同步函数

事件标志组中其它重要的API函数:

函数描述
xTaskRemoveFromUnorderedEventList()将阻塞的任务从事件列表中移除,并添加到就绪列表中
vTaskPlaceOnUnorderedEventList()将任务添加到事件列表中
prvTestWaitCondition()判断当前事件位是否匹配当前等待的事件

2. 事件标志组的基本概念

事件标志组是实现多任务同步的有效机制之一。

事件标志组数据类型:

typedef struct xEventGroupDefinition
{
	/* 储存事件标志组中的事件位 */
	EventBits_t uxEventBits;	

	/* 事件标志组列表 */			
	List_t xTasksWaitingForBits;					

	/* 启用可视化跟踪调试 */
	#if( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxEventGroupNumber;
	#endif

	/* 用于标记事件标志组申请内存的方式 */
	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated; 
	#endif
} EventGroup_t;

时间标志组中的事件位储存在EventBits_t类型变量中,其中:

typedef TickType_t EventBits_t;

又:

#if( configUSE_16_BIT_TICKS == 1 )
	typedef uint16_t TickType_t;
	#define portMAX_DELAY ( TickType_t ) 0xffff
#else
	typedef uint32_t TickType_t;
	#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
	#define portTICK_TYPE_IS_ATOMIC 1
#endif

可以看出,当configUSE_16_BIT_TICKS为1时,EventBits_t是16位的数据类型,为0时,EventBits_t是32位的数据类型。
当EventBits_t是16位的数据类型,只储存8个事件位,当EventBits_t是32位的数据类型,只储存24个事件位,其高8位用于其他用途。

高8位用途:

#if configUSE_16_BIT_TICKS == 1
	#define eventCLEAR_EVENTS_ON_EXIT_BIT	0x0100U
	#define eventUNBLOCKED_DUE_TO_BIT_SET	0x0200U
	#define eventWAIT_FOR_ALL_BITS			0x0400U
	#define eventEVENT_BITS_CONTROL_BYTES	0xff00U
#else
	/* 表示事件退出时,是否需要删除事件位 */
	#define eventCLEAR_EVENTS_ON_EXIT_BIT	0x01000000UL

	/* 标记该任务事件位设置了,已解除阻塞 */
	#define eventUNBLOCKED_DUE_TO_BIT_SET	0x02000000UL
	
	/* 表示设置的所有事件位都为1才有效 */
	#define eventWAIT_FOR_ALL_BITS			0x04000000UL	

	#define eventEVENT_BITS_CONTROL_BYTES	0xff000000UL
#endif
#define taskEVENT_LIST_ITEM_VALUE_IN_USE	0x80000000UL

3. 事件标志组的创建

函数描述
xEventGroupCreate()动态方式创建事件标志组
xEventGroupCreateStatic()静态方式创建事件标志组
3.1 动态方式创建

使用动态方式创建事件标志组,需要将configSUPPORT_DYNAMIC_ALLOCATION设置为1。

函数原型如下:

/********************************************************
参数:无
返回:事件标志组的句柄
*********************************************************/
EventGroupHandle_t xEventGroupCreate( void )

EventGroupHandle_t是新定义的类型:

typedef void * EventGroupHandle_t;

函数源代码如下:

EventGroupHandle_t xEventGroupCreate( void )
{
	EventGroup_t *pxEventBits;

	/* 为事件标志组申请内存 */
	pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );

	if( pxEventBits != NULL )
	{
		pxEventBits->uxEventBits = 0;								/* 初始化事件位都为0 */
		vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );	/* 初始化事件标志组的列表 */

		/* 如果使能了静态申请方式 */
		#if( configSUPPORT_STATIC_ALLOCATION == 1 )
		{
			/* 标记不是通过静态申请方式 */
			pxEventBits->ucStaticallyAllocated = pdFALSE;
		}
		#endif /* configSUPPORT_STATIC_ALLOCATION */

		traceEVENT_GROUP_CREATE( pxEventBits );
	}
	else
	{
		traceEVENT_GROUP_CREATE_FAILED();
	}

	return ( EventGroupHandle_t ) pxEventBits;	/* 返回事件标志组的句柄 */
}
3.1 静态方式创建

使用静态方式创建事件标志组,需要将configSUPPORT_STATIC_ALLOCATION设置为1。

函数原型如下:

/********************************************************
参数:pxEventGroupBuffer:指向一个StaticEventGroup_t类型变量的指针
                         用来保存事件标志组结构体
返回:事件标志组的句柄
*********************************************************/
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )

函数源代码如下:

EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )
{
	EventGroup_t *pxEventBits;

	configASSERT( pxEventGroupBuffer );

	/* 获取事件标志组的句柄 */
	pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer;

	if( pxEventBits != NULL )
	{
		pxEventBits->uxEventBits = 0;								/* 初始化事件位 */
		vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );	/* 初始化事件标志列表 */

		/* 如果使能动态创建方式 */
		#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
		{
			/* 标记是使用静态方式创建 */
			pxEventBits->ucStaticallyAllocated = pdTRUE;
		}
		#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

		traceEVENT_GROUP_CREATE( pxEventBits );
	}
	else
	{
		traceEVENT_GROUP_CREATE_FAILED();
	}

	return ( EventGroupHandle_t ) pxEventBits;
}

4. 事件标志组的删除

4.1 函数vEventGroupDelete()

事件标志组删除使用函数vEventGroupDelete()。

函数原型如下:

/********************************************************
参数:xEventGroup :事件标志组句柄
返回:无
*********************************************************/
void vEventGroupDelete( EventGroupHandle_t xEventGroup )

函数源代码如下:

void vEventGroupDelete( EventGroupHandle_t xEventGroup )
{
	EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
	const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits );

	vTaskSuspendAll();	/* 挂起调度器 */
	{
		traceEVENT_GROUP_DELETE( xEventGroup );

		/* 当事件列表中存在列表项,即存在阻塞的任务 */
		while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 )
		{
			configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) );
	
			/* 将任务从事件列表中移除,添加到就绪列表中 */		
			( void ) xTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET );
		}

		/* 如果使能动态申请方式,没有使能静态申请方式 */
		#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
		{
			vPortFree( pxEventBits );
		}
		/* 如果使能了动态申请方式和静态申请方式 */
		#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
		{
			/* 如果不是使用静态方式创建时间标志组 */
			if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
			{
				vPortFree( pxEventBits );
			}
			/* 如果使用静态方式创建事件标志组,则需要用户手动释放内存 */
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
	}
	( void ) xTaskResumeAll();	/* 恢复调度器 */
}
4.2 函数xTaskRemoveFromUnorderedEventList()

该函数将阻塞的任务从事件列表中移除,并添加到就绪列表中,如果恢复的任务优先级比当前运行任务的优先级高,则标记需要进行任务切换。

函数原型如下:

/********************************************************
参数:pxEventListItem:某任务的事件列表项
          xItemValue:时间列表项的项值
返回: pdTRUE:可进行任务切换
     pdFALSE:不需要进行任务切换
*********************************************************/
BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue )

函数源代码如下:

BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue )
{
	TCB_t *pxUnblockedTCB;
	BaseType_t xReturn;

	configASSERT( uxSchedulerSuspended != pdFALSE );

	/* 
	 * taskEVENT_LIST_ITEM_VALUE_IN_USE为0x80000000UL 
	 * 	xItemValue等于eventUNBLOCKED_DUE_TO_BIT_SET,即为0x02000000UL
	 */
	listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE );

	/* 获取因该事件而阻塞任务的TCB */
	pxUnblockedTCB = ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxEventListItem );
	
	configASSERT( pxUnblockedTCB );
	
	( void ) uxListRemove( pxEventListItem );	/* 将该事件从事件列表中删除 */
	
	( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) );	/* 删除任务的状态信息,如阻塞态 */
	prvAddTaskToReadyList( pxUnblockedTCB );	/* 将任务添加到就绪列表中 */

	/* 当前阻塞的任务优先级大于当前运行的任务 */
	if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority )
	{
		xReturn = pdTRUE;
		xYieldPending = pdTRUE;	/* 标记调度器挂起 */
	}
	else
	{
		xReturn = pdFALSE;
	}

	return xReturn;
}

5. 事件标志位的设置

函数描述
xEventGroupSetBits()将指定的事件位置1, 用在任务中
xEventGroupSetBitsFromISR()将指定的事件位置1,用在中断服务函数中
5.1 函数xEventGroupSetBits()

函数原型如下:

/********************************************************
参数:xEventGroup:事件标志组的句柄
     uxBitsToSet:需要设置的事件位
返回:EventBits_t:事件位
*********************************************************/
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, 
  								const EventBits_t  uxBitsToSet )

函数源代码如下:

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
{
	ListItem_t *pxListItem, *pxNext;
	ListItem_t const *pxListEnd;
	List_t *pxList;
	EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;
	EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
	BaseType_t xMatchFound = pdFALSE;

	configASSERT( xEventGroup );
	configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );

	pxList = &( pxEventBits->xTasksWaitingForBits );	/* 获取事件标志组对应的事件列表 */
	pxListEnd = listGET_END_MARKER( pxList ); 			/* 获取列表中最后一个列表项,即Mini列表项 */
	
	vTaskSuspendAll();	/* 挂起任务调度器 */
	{
		traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );

		pxListItem = listGET_HEAD_ENTRY( pxList );	/* 得到第一个列表项,即第一个因事件而阻塞的任务 */
		pxEventBits->uxEventBits |= uxBitsToSet;	/* 设置事件位 */

		/* 如果事件列表中有列表项,即有阻塞任务,则进入循环中 */
		while( pxListItem != pxListEnd )
		{
			pxNext = listGET_NEXT( pxListItem );	/* 获取下一个任务 */
			uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );	/* 获取对应任务的项值 */
			xMatchFound = pdFALSE;

			/* 
			 * eventEVENT_BITS_CONTROL_BYTES为0xff000000UL 
			 * 将高8位和事件位分别存在uxControlBits和uxBitsWaitedFor 
			 */
			uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
			uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;

			/* 如果不要等待所有的事件位都为1才有效 */
			if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
			{
				/* 
				 * 每个任务的事件位和控制位(高8位)存在任务的项值中,即uxBitsWaitedFor
				 * pxEventBits->uxEventBits表示用户设置了某事件位,存在事件标志组中
				 */
				if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
				{
					xMatchFound = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			/* 需要等待所有的事件位,通过eventWAIT_FOR_ALL_BITS决定 */
			else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
			{
				/* 所有的事件位都设置了 */
				xMatchFound = pdTRUE;
			}
			else
			{
				/* 运行到这里,表示需要等待所有的事件位,但其中有事件位不存在 */
			}

			/* 如果匹配成功 */
			if( xMatchFound != pdFALSE )
			{
				/* 
				 * eventCLEAR_EVENTS_ON_EXIT_BIT为0x01000000UL
				 * 表示事件退出时,是否需要删除事件位
				 */
				if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )
				{
					uxBitsToClear |= uxBitsWaitedFor;	/* 获取要删除的位 */
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				/* 将任务从事件列表中移除,添加到就绪列表中 */	
				( void ) xTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
			}
			pxListItem = pxNext;	/* 指向下一个阻塞的任务 */
		}
		pxEventBits->uxEventBits &= ~uxBitsToClear;	/* 删除事件位 */
	}
	( void ) xTaskResumeAll();	/* 恢复调度器 */

	return pxEventBits->uxEventBits;	/* 返回事件位 */
}
5.2 函数xEventGroupSetBitsFromISR()

该函数是中断版的事件位设置,用于中断函数。

函数原型如下:

/********************************************************
参数:xEventGroup:事件标志组的句柄
返回:EventBits_t:事件位
*********************************************************/
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )

函数源代码如下:

EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
{
	UBaseType_t uxSavedInterruptStatus;
	EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
	EventBits_t uxReturn;

	/* 关闭中断,进入临界区 */
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();	
	{
		uxReturn = pxEventBits->uxEventBits;
	}
	/* 开启中断,退出临界区 */
	portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

	return uxReturn;
}

5. 事件标志位的清除

函数描述
xEventGroupClearBits()将指定的事件位清零, 用在任务中
xEventGroupClearBitsFromISR()将指定的事件位清零,用在中断服务函数中
5.1 函数xEventGroupClearBits()

函数原型如下:

/********************************************************
参数:xEventGroup:事件标志组的句柄
   uxBitsToClear:需要清除的事件位
返回:EventBits_t:事件位
*********************************************************/
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )

函数源代码如下:

EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
{
	EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
	EventBits_t uxReturn;

	configASSERT( xEventGroup );
	configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 );

	taskENTER_CRITICAL();	/* 进入临界区 */
	{
		traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear );

		/* 获取事件标志位 */
		uxReturn = pxEventBits->uxEventBits;

		/* 清除事件标志位 */
		pxEventBits->uxEventBits &= ~uxBitsToClear;
	}
	taskEXIT_CRITICAL();	/* 退出临界区 */

	return uxReturn;
}
5.2 函数xEventGroupClearBitsFromISR()

清除事件标志组中事件位的中断版本,用于中断函数中。

函数原型如下:

/********************************************************
参数:xEventGroup:事件标志组的句柄
   uxBitsToClear:需要清除的事件位
返回:BaseType_t:事件位
*********************************************************/
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )

函数源代码如下:

BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
{
	BaseType_t xReturn;

	traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear );
	
	xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL );

	return xReturn;
}

6. 事件标志位的获取

函数描述
xEventGroupGetBits获取当前事件标志组的值(各个事件位的值),用在任务中
xEventGroupGetBitsFromISR()获取当前事件标志组的值, 用在中断服务中
6.1 函数xEventGroupGetBits()

函数通过宏定义,其调用函数xEventGroupClearBits(),如下:

#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 )

通过函数xEventGroupClearBits(),可以返回当前事件位,同时设置清除的事件位为0,即不清除事件位。
通过函数xEventGroupGetBits(),并不会阻塞函数,而是通过其返回值判断来执行相应的程序。

6.2 函数xEventGroupGetBitsFromISR()

该函数获取事件位,用于中断服务函数中。

函数原型如下:

/********************************************************
参数:xEventGroup:事件标志组的句柄
返回:EventBits_t:事件位
*********************************************************/
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )

函数源代码如下:

EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
{
	UBaseType_t uxSavedInterruptStatus;
	EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
	EventBits_t uxReturn;

	/* 进入临界区 */
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		uxReturn = pxEventBits->uxEventBits;
	}
	/* 退出临界区 */
	portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

	return uxReturn;
}

7. 事件标志位等待

7.1 函数xEventGroupWaitBits()

函数xEventGroupWaitBits()可以在阻塞状态下等待事件组中一个或者多个事件。

函数原型如下:

/********************************************************
参数:
     xEventGroup:事件标志组的句柄
 uxBitsToWaitFor:需要等待的事件位
    xClearOnExit:若为pdTURE,退出时,所有事件位删除
                  若为pdFALSE,退出时,事件位不删除
 xWaitForAllBits:若为pdTURE,所有事件位都为1或阻塞时间到,则有效
                  若为pdFALSE,任意其中一个为1或阻塞时间到,则有效
    xTicksToWait:设置阻塞事件,单位为节拍数
返回:EventBits_t:事件位
*********************************************************/
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup, 
								const EventBits_t  uxBitsToWaitFor, 
								const BaseType_t   xClearOnExit, 
								const BaseType_t   xWaitForAllBits, 
								TickType_t         xTicksToWait )

函数源代码如下:

EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
{
	EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
	EventBits_t uxReturn, uxControlBits = 0;
	BaseType_t xWaitConditionMet, xAlreadyYielded;
	BaseType_t xTimeoutOccurred = pdFALSE;

	configASSERT( xEventGroup );
	configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
	configASSERT( uxBitsToWaitFor != 0 );
	
	/* 如果使能函数xTaskGetSchedulerState()或启动软件定时器功能 */
	#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
	{
		configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
	}
	#endif

	vTaskSuspendAll();	/* 挂起任务调度器 */
	{
		const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;

		/* 判断当前事件位是否匹配 */
		xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );

		/* 如果当前事件位符合要求 */
		if( xWaitConditionMet != pdFALSE )
		{
			uxReturn = uxCurrentEventBits;
			xTicksToWait = ( TickType_t ) 0;

			/* 如果退出时,需要删除事件位,则进入 */
			if( xClearOnExit != pdFALSE )
			{
				pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		/* 如果设置阻塞时间为0 */
		else if( xTicksToWait == ( TickType_t ) 0 )
		{
			uxReturn = uxCurrentEventBits;
		}
		else
		{
			/* 如果退出时,需要删除事件位,则进入 */
			if( xClearOnExit != pdFALSE )
			{
				/* 设置相应的控制位,高8位中 */
				uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
			/* 如果需要等待所有的事件位为1,则有效 */
			if( xWaitForAllBits != pdFALSE )
			{
				/* 设置相应的控制位,在高8位中 */
				uxControlBits |= eventWAIT_FOR_ALL_BITS;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			/* 将任务添加到相应的事件列表中 */
			vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );

			uxReturn = 0;

			traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
		}
	}
	xAlreadyYielded = xTaskResumeAll();	/* 恢复任务调度器,可能会执行任务切换 */

	/* 如果设置的阻塞时钟节拍数不为0 */
	if( xTicksToWait != ( TickType_t ) 0 )
	{
		/* 恢复调度器时没有进行任务切换 */
		if( xAlreadyYielded == pdFALSE )
		{
			portYIELD_WITHIN_API();	/* 执行任务切换 */
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		uxReturn = uxTaskResetEventItemValue();	/* 获取事件列表项的项值 */

		/* eventUNBLOCKED_DUE_TO_BIT_SET用来表示任务已经解挂 */
		if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
		{
			taskENTER_CRITICAL();	/* 进入临界区 */
			{

				uxReturn = pxEventBits->uxEventBits;

				/* 如果事件位符合匹配 */
				if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
				{
					/* 如果设置事件退出时清除事件位 */
					if( xClearOnExit != pdFALSE )
					{
						pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			taskEXIT_CRITICAL();	/* 退出临界区 */

			xTimeoutOccurred = pdFALSE;
		}
		else
		{
			/* 执行到这里,表示任务已经解除阻塞态 */
		}
		
		uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;	/* 清除控制位 */
	}
	traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );

	return uxReturn;
}
7.2 函数prvTestWaitCondition()

函数原型如下:

/********************************************************
参数:
     xEventGroup:事件标志组的句柄
 uxBitsToWaitFor:需要等待的事件位
    xClearOnExit:若为pdTURE,退出时,所有事件位删除
                  若为pdFALSE,退出时,事件位不删除
 xWaitForAllBits:若为pdTURE,所有事件位都为1或阻塞时间到,则有效
                  若为pdFALSE,任意其中一个为1或阻塞时间到,则有效
    xTicksToWait:设置阻塞事件,单位为节拍数
返回:EventBits_t:事件位
*********************************************************/
static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits )

函数源代码如下:

static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits )
{
	BaseType_t xWaitConditionMet = pdFALSE;

	/* 如果某个事件位为1,则有效 */
	if( xWaitForAllBits == pdFALSE )
	{
		/* 如果匹配到某个事件位 */
		if( ( uxCurrentEventBits & uxBitsToWaitFor ) != ( EventBits_t ) 0 )
		{
			xWaitConditionMet = pdTRUE;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else	/* 要全部设置的事件位为1,则有效 */
	{
		/* 如果所有的事件位都为1 */
		if( ( uxCurrentEventBits & uxBitsToWaitFor ) == uxBitsToWaitFor )
		{
			xWaitConditionMet = pdTRUE;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}

	return xWaitConditionMet;
}
7.3 函数vTaskPlaceOnUnorderedEventList()
void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait )
{
	configASSERT( pxEventList );

	configASSERT( uxSchedulerSuspended != 0 );

	/* 设置相应的列表项中的项值 */
	listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE );

	/* 将任务插入到事件列表中 */
	vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) );

	/* 添加当前任务到延时列表中 */
	prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
}

8. 事件的同步

函数xEventGroupSync()能使多个任务彼此之间同步。

函数原型如下:

/********************************************************
参数:
     xEventGroup:事件标志组的句柄
     uxBitsToSet:设置事件位
 uxBitsToWaitFor:需要等待的事件位
    xTicksToWait:设置阻塞事件,单位为节拍数
返回:EventBits_t:事件位
*********************************************************/
EventBits_t xEventGroupSync(EventGroupHandle_t xEventGroup, 
							const EventBits_t  uxBitsToSet, 
							const EventBits_t  uxBitsToWaitFor, 
							TickType_t         xTicksToWait )

函数源代码如下:

EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait )
{
	EventBits_t uxOriginalBitValue, uxReturn;
	EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
	BaseType_t xAlreadyYielded;
	BaseType_t xTimeoutOccurred = pdFALSE;

	configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
	configASSERT( uxBitsToWaitFor != 0 );
	
	/* 如果使能函数xTaskGetSchedulerState()或启动软件定时器功能 */
	#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
	{
		configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
	}
	#endif

	vTaskSuspendAll();	/* 挂起调度器 */
	{
		uxOriginalBitValue = pxEventBits->uxEventBits;	/* 记录原有事件位 */

		( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet );

		/* 如果所有事件位都为1 */
		if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor )
		{
			uxReturn = ( uxOriginalBitValue | uxBitsToSet );

			pxEventBits->uxEventBits &= ~uxBitsToWaitFor;	/* 清除事件位 */

			xTicksToWait = 0;
		}
		else
		{
			/* 如果设置阻塞时间xTicksToWait不为0 */
			if( xTicksToWait != ( TickType_t ) 0 )
			{
				traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor );

				/* 将任务添加到相应的事件列表中 */
				vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait );

				uxReturn = 0;
			}
			else
			{
				uxReturn = pxEventBits->uxEventBits;
			}
		}
	}
	xAlreadyYielded = xTaskResumeAll();	/* 恢复调度器,有可能进行任务切换 */

	/* 如果设置阻塞时间xTicksToWait不为0 */
	if( xTicksToWait != ( TickType_t ) 0 )
	{
		/* 如果没有进行任务切换 */
		if( xAlreadyYielded == pdFALSE )
		{
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		/* 获取事件列表项的项值 */
		uxReturn = uxTaskResetEventItemValue();

		/* eventUNBLOCKED_DUE_TO_BIT_SET用来任务已经解挂 */
		if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
		{
			/* 进入临界区 */
			taskENTER_CRITICAL();
			{
				uxReturn = pxEventBits->uxEventBits;

				/* 如果设置事件退出时清除事件位 */
				if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor )
				{
					pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			taskEXIT_CRITICAL();	/* 退出临界区 */

			xTimeoutOccurred = pdTRUE;
		}
		else
		{
			/* 执行到这里,表示任务已经解除阻塞态 */
		}

		uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;	/*  清除高8位的控制位 */
	}

	traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred );

	return uxReturn;
}

当使用事件组进行任务同步时,有:

  • 参与同步的任务分配唯一的事件位。
  • 每个任务达到同步点设置自己的事件位。
  • 当事件设置事件位后,任务会进入阻塞态以等待其他任务的同步。

假设有任务A、任务B、任务C三个任务。任务A的执行是需要任务B、任务C同步后才进行下一步。我们不能使用函数xEventGroupSetBits()和函数xEventGroupWaitBits()。

  • 每个任务任务获取事件位都是单独操作的,假如任务B和任务C此时获得了事件位,即这两个任务达到同步,此时他们会处于阻塞态,等待任务C的同步。
  • 任务A达到同步点,并调用函数xEventGroupWaitBits()设置事件位,一旦设置任务A的事件位,任务B和任务C事件位被删除,由于此时任务A没有进入阻塞态,不会清除事件位,任务A和任务B离开各自同步点,同步失败。

而对于函数xEventGroupSync()即使任务B和任务C事件位被删除,但记录了原有的事件位uxOriginalBitValue,通过变量uxOriginalBitValue判断达到任务A同步。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值