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同步。

你可能感兴趣的:(FreeRTOS原理剖析)