FreeRtos内核源码分析(六)——事件

目录

一、事件简介

二、事件结构

三、 事件接口统计

四、事件组创建

五、事件操作(线程)

5.1 事件阻塞机制

5.1.1 事件控制字

5.1.2 阻塞机制        

5.2 事件设置

5.2 .1 代码分析

5.2.2 逻辑图分析

5.3 事件等待

5.3.1 代码分析

5.3.2  逻辑图分析

5.4 事件同步

5.5 事件清除

5.6 事件删除

5.7 应用实例

5.7.1与逻辑

5.7.2 或逻辑

5.7.3  多任务同步

六、事件操作(中断)

6.1 事件设置

6.2 事件清除


本章基于FreeRtos V9.0.0版本分析

一、事件简介

        事件是一种实现任务间通信的机制,主要用于多任务间的同步,可以实现一对多,多对多的同步,可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理;事件通信只能是事件类型的通信,无数据传输。

二、事件结构

        FreeRtos中的事件组是一个16位或32位的数据(EventBits_t ),数据的高8位表示阻塞任务对事件的处理属性,剩余的位置,每个bit位表示一个事件。

        如果configUSE_16_BIT_TICKS定义为 1,那么变量uxEventBits就是16位,低8位用来存储事件组;如果宏 configUSE_16_BIT_TICKS 定义为 0,那么变量uxEventBits就是32 位,低24位用来存储事件组。

        定义一个任务事件链表xTasksWaitingForBits,记录因等待事件而阻塞的任务。

// 事件结构体
typedef struct xEventGroupDefinition
{
	EventBits_t uxEventBits;          // 事件组(高8位表示操作过程标志,其他低比特位表示各个事件)
	List_t xTasksWaitingForBits;      // 等待事件阻塞任务链表
} EventGroup_t;

三、 事件接口统计

        事件接口可分为创建、设置、等待、同步、清除;在event_groups.h中声明。

/*-----------------------事件组创建------------------------------------*/
// 动态创建(返回事件组句柄)
EventGroupHandle_t xEventGroupCreate( void );
// 静态创建(返回事件组句柄)
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )
/*-----------------------设置事件(uxBitsToSet)-------------------------*/
// 线程模式,返回最新的事件组值(未清除)
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) 
// 中断模式(向定时器队列发送消息,由定时器任务进行事件设置)
#define xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken )
/*-----------------------等待事件组------------------------------------*/
// uxBitsToWaitFor  表示等待事件
// xClearOnExit     1:表示等待事件成功后,清除相关事件,0:不清除
// xWaitForAllBits  1:与运算,需满足所有等待事件。0:或运算,满足等待事件中任一个
// xTicksToWait     表示等待事件不满足时阻塞时间
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
/*-----------------------清除事件------------------------------------*/

// 根据uxBitsToClear 清除事件组事件(线程)
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
// 根据uxBitsToClear 清除事件组事件(中断)
#define xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear ) xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL )
/*-----------------------多任务同步事件------------------------------------------*/
// uxBitsToSet      表示发送事件
// uxBitsToWaitFor  表示等待事件
// xTicksToWait     表示等待事件不满足时阻塞时间
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) 
/*-----------------------删除事件组------------------------------------*/
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
/*-----------------------其他接口------------------------------------*/
// 事件组读取接口(线程)
#define xEventGroupGetBits(xEventGroup) xEventGroupClearBits( xEventGroup, 0 )
// 事件组读取接口(中断)
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) 
// 事件发送回调
void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ) 
// 事件清除回调
void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear )


四、事件组创建

         事件组创建有2种方式,静态创建和动态创建,与任务创建类似。静态创建即事件组和任务阻塞链表均通过全局变量存储,不能释放;动态创建为动态内存,可以生灭;本章我们仅以动态创建为例进行分析。

事件组创建过程:

(1)申请动态内存(大小为事件组结构体大小);

(2)初始化事件全为0;

(3)初始化等待事件任务阻塞链表。

(4)返回事件组句柄。

// 动态新建
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION;
#endif
// 静态新建
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
	EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
#endif
EventGroupHandle_t xEventGroupCreate( void )
{
	EventGroup_t *pxEventBits;
    // 【1.申请动态内存】 
	pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );
	if( pxEventBits != NULL )
	{
        // 【2.事件组初始化为0】
		pxEventBits->uxEventBits = 0;
        // 【3.初始化阻塞任务链表】
		vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );

		#if( configSUPPORT_STATIC_ALLOCATION == 1 )
		{
			pxEventBits->ucStaticallyAllocated = pdFALSE;
		}
		#endif /* configSUPPORT_STATIC_ALLOCATION */

		traceEVENT_GROUP_CREATE( pxEventBits );
	}
	else
	{
		traceEVENT_GROUP_CREATE_FAILED();
	}
     // 【4.返回事件组句柄】
	return ( EventGroupHandle_t ) pxEventBits;
}

五、事件操作(线程)

5.1 事件阻塞机制

5.1.1 事件控制字

        在任务因事件阻塞过程中,通过控制字(事件组高8位)记录处理方式及过程;控制字记录在阻塞任务事件条目值(xEventListItem->xItemValue)中,在事件组(uxEventBits)中不存储,事件组中高8位一直为0。

注:在信号、队列导致的阻塞中,xEventListItem->xItemValue存储阻塞任务的优先级,并根据优先级顺序对任务解锁;在事件导致的阻塞中,xEventListItem->xItemValue存储为“控制字+事件”模式根据时间顺序对任务统一进行事件判断和解锁。

        控制字定义如下:

// 16位事件阻塞属性位(xEventListItem->xItemValue)
#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  // 与逻辑位(1:与逻辑,所有等待事件都满足;0:或逻辑,任一等待事件满足  )
	#define taskEVENT_LIST_ITEM_VALUE_IN_USE  0x8000U  // 使能位(1:表示条目值为事件;0:表示条目值非事件)
	#define eventEVENT_BITS_CONTROL_BYTES	  0xff00U  // 事件位控制字节

// 32位事件阻塞属性位(xEventListItem->xItemValue)
#else
    #define eventCLEAR_EVENTS_ON_EXIT_BIT       0x01000000UL // 清除位(等待事件成功后,清除事件)
    #define eventUNBLOCKED_DUE_TO_BIT_SET       0x02000000UL // 解锁位(设置时间后会对阻塞任务的等待事件判别,判别成功,置解锁位)
    #define eventWAIT_FOR_ALL_BITS              0x04000000UL // 与逻辑位(1:与逻辑,所有等待事件都满足;0:或逻辑,任一等待事件满足  )
    #define taskEVENT_LIST_ITEM_VALUE_IN_USE	0x80000000UL //使能位(1:表示条目值为事件;0:表示条目值非事件)
    #define eventEVENT_BITS_CONTROL_BYTES       0xff000000UL // 事件位控制字节
   
#endif

【清除位】置1:当任务等待事件成功后,直接清除相应的等待事件;置0:当任务等待事件成功后,不清除。

【解锁位】设置事件后,会对所有事件阻塞任务进行等待事件判断,判断成功后,解锁任务,并对其事件条目值(xEventListItem->xItemValue)的【解锁位】置1,并更新当前事件组值;否则不置位;解锁后的任务,优先判断该标志,如果任务该标志为1,直接认为等待成功。

【与逻辑位】置1:与逻辑等待事件,即事件组满足所有的等待事件才算等待成功;置0:或逻辑等待事件,即事件组满足任一等待事件即可等待成功。

【使能位】置1:表示当前阻务塞任是事件阻塞,xEventListItem->xItemValue表示"控制字+事件";置0:xEventListItem->xItemValue表示任务优先级,任务非事件阻塞。

5.1.2 阻塞机制        

1、任务等待事件阻塞:当事件组不满足等待条件,任务会被阻塞;任务事件条目插入到事件链表xTasksWaitingForBits的尾部,而事项条目值(xEventListItem->xItemValue)设置为"控制字+事件"的模式: 

        (1)【使能位】设置为1;

        (2)【解锁位】设置为0 ;

        (3)【清除位】【与逻辑位】根据函数的形参设置;

        (4)【事件】为函数形参uxBitsToWaitFor (即任务需要等待的事件)。

2、任务设置事件: 等待任务在阻塞过程中可能会有其他任务设置事件,任务设置事件不会有阻塞现象,也不会受当前事件状态影响,直接在对应事件bit位进行覆盖;同时会对所有被事件阻塞的任务进行事件判断,将满足条件的任务进行解锁;判断方式如下:

         将任务按从头到尾的顺序从链表xTasksWaitingForBits中取出任务,根据xEventListItem->xItemValue解析出控制字需要等待的事件uxBitsToWaitFor。判断当前事件组是否满足任务等待事件;如满足,将任务解锁后,更新xEventListItem->xItemValue值为"控制字+事件"的模式: 

        (1)【使能位】设置为1;

        (2)【解锁位】设置为1, 

        (3)【清除位】【与逻辑位】设置为0;

        (4)【事件】当前事件组最新事件; 

        每解锁一个任务,统计需要清除的事件位,当所有任务处理完成后,根据清除位的统计,统一清除事件。

3、等待任务解锁: 事件等待任务解锁后,会再进行一次判断,同时将任务事件事项条目值(xEventListItem->xItemValue复位为任务优先级。判断方式如下:

        如果xEventListItem->xItemValue【解锁位】为1(即阻塞过程中被其他任务解锁),直接等待成功,返回xEventListItem->xItemValue中记录的事件值(即解锁时的最新事件组)。否则,根据最新事件和控制字再次判段和处理事件,并返回当前事件组值(未清除)。

注: 任务事件条目 xEventListItem插入到xTasksWaitingForBits仅在事件等待中进行,而任务解锁和xEventListItem移除在事件设置和删除中进行。

5.2 事件设置

5.2 .1 代码分析

        事件设置函数形参为事件组句柄xEventGroup和需要设置的事件uxBitsToSet;返回值为事件组xEventGroup的最新值。

        事件设置过程:

1、暂停任务调度器;

2、设置事件:使用uxBitsToSet直接覆盖旧事件;

3、解锁符合条件的阻塞任务(遍历所有的阻塞任务):

(1)通过任务事件条目值(xEventListItem->xItemValue),解析控制字需要等待的事件

(2)通过控制字【与逻辑位】判断当前事件组是否满足任务等待:

        【与逻辑位】如果为1,事件组中所有等待的事件均为1;

        【与逻辑位】如果为0,事件组中任一等待的事件为1。

(3)如果事件逻辑判断满足,判断【清除位】:

         【清除位】为1,累计该任务需要清除的事件。

        【清除位】为0,不处理。

(4)如果事件逻辑判断满足,进行任务解锁:

        ①将任务事件条目xEventListItem从事件链表xTasksWaitingForBits中移除;

        ②修改事件条目值xEventListItem->xItemValue【使能位】【解锁位】设置为1;        ​        【清除位】与逻辑位设置为0;【事件】事件组最新事件

        ③切换任务状态,从【阻塞态或挂起态】->【就绪态】 即将任务状态条目xStateListItemc

        从阻塞或挂起链表移除,插入到就绪链表尾部。

(5)当所有任务处理完成后,根据清除位的统计,统一清除事件。

4、重启任务调度器;

5、返回当前事件组xEventGroup->uxEventBits;

/*--------------------------发送事件---------------------------------*/
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 ); 
	// 【1】挂起调度器
	vTaskSuspendAll();
	{
		traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );
		pxListItem = listGET_HEAD_ENTRY( pxList );// 任务头部
         //
		pxEventBits->uxEventBits |= uxBitsToSet;	// 设置事件
		//【2】有被阻塞的任务
		while( pxListItem != pxListEnd )
		{
            // 从最新的阻塞任务开始
			pxNext = listGET_NEXT( pxListItem );
            //取该任务等待件值
			uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
			xMatchFound = pdFALSE;
			/* 分离等待位和控制位. */
			uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
			uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;
			//无控制位 或运算
			if((uxControlBits & eventWAIT_FOR_ALL_BITS) == ( EventBits_t ) 0 )
			{
				/*需找被设置的单个位.*/
				if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
				{
					xMatchFound = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			//控制位 与运算
			else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
			{
				
				xMatchFound = pdTRUE;
			}
			else
			{
				/* Need all bits to be set, but not all the bits were set. */
			}
			// 匹配成功
			if( xMatchFound != pdFALSE )
			{
				/*退出清除标志 */
				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.2 逻辑图分析

5.3 事件等待

5.3.1 代码分析

        等待函数形参为:事件组句柄xEventGroup、任务等待的事件uxBitsToWaitFor、等待成功后清除标志xClearOnExit和等待与逻辑方式xWaitForAllBits 。

        事件等待成功,函数返回等待成功时的事件组值(未清除)。

        事件等待失败,函数返回当前事件组值

        事件等待流程:

1、暂停任务调度器。

2、判断当前任务是否满足等待条件(判断条件见逻辑图)。

3、如果当前事件满足任务等待条件,进行清除判断及处理;然后重启调度器后返回事件组值(返回的事件组是清除之前的,方便对返回成功状态判断)。
4、如果事件组不满足任务等待条件,任务阻塞:

(1)修改任务事件条目值xEventListItem->xItemValue(【使能位】设置为1;【解锁位】设置为0;【清除位】与形参xClearOnExit一致;与逻辑位与形参xWaitForAllBits一致 【事件】任务等待的事件uxBitsToWaitFor )

(2)将任务事件条目xEventListItem插入链表xTasksWaitingForBits尾部。

(3)切换任务状态【运行态】->【就绪态或挂起态】,即将任务状态条目xStateListItemc从就绪链表移除,插入到阻塞或挂起链表。重启调度器后强制上下文切换。

5、任务解锁:任务阻塞时间到会解锁;或者,在任务阻塞过程中,有其他任务设置事件,从而解锁任务,并设置任务事件条目值xEventListItem->xItemValue解锁位】设置为1,【事件】事件组最新事件

6、任务解锁后【运行态】处理:

(1)解析出xEventListItem->xItemValue的【解锁位】【事件值】,并将xEventListItem->xItemValue的值恢复为任务优先级。

(2)如果【解锁位】为1,说明已被其他任务解锁,【事件值】便是解锁时的事件组值,函数直接返回【事件值】。

(3)如果【解锁位】为0,说明阻塞时间到,取当前最新事件重新判断等待条件,如果判断成功,进行事件清除处理;失败则不处理;最后返回当前最新事件组值(清除前);

/*-----------------------------------------------------------*/
// 等待事件(任务)
// uxBitsToWaitFor等待的事件
// xClearOnExit 等待成功后清除事件
// xWaitForAllBits 与运算、或运算
// 返回事件组原始值(非清除后的值)


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 );
	#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
		{
			// 置清除位   eventCLEAR_EVENTS_ON_EXIT_BIT
			if( xClearOnExit != pdFALSE )
			{
				uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
			// 置与或位  eventWAIT_FOR_ALL_BITS
			if( xWaitForAllBits != pdFALSE )
			{
				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();
	// 阻塞
	if( xTicksToWait != ( TickType_t ) 0 )
	{
		// 强制任务调度
		if( xAlreadyYielded == pdFALSE )
		{
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
		// 阻塞时间到(并取条目值)
		uxReturn = uxTaskResetEventItemValue();
		// 任务没有在事件设置函数里解锁
		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;
}

5.3.2  逻辑图分析

5.4 事件同步

        事件同步接口xEventGroupSync主要用于多任务同步,实际上是事件设置事件等待的组合,固定【与逻辑】等待事件,等待完成后固定【清除】事件;可实现多任务共同到达同步点。

        代码分析和逻辑图分析参照【5.2 事件设置】【5.3 事件等待】章节;参考实例【5.7.3 多任务同步】了解如何应用多任务同步。

/*-----------------------------------------------------------*/
// xEventGroup事件组句柄,uxBitsToSet发送事件,uxBitsToWaitFor等待事件,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 );
	// 调度器断言
	#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
	{
		configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
	}
	#endif
	// 【1】任务挂起
	vTaskSuspendAll();
	// 事件判断
	{	
		// 取事件原值
		uxOriginalBitValue = pxEventBits->uxEventBits;
		// 【2】设置事件
		( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet );
		// 【3】等待事件,事件值包含本任务发送的事件
		if(((uxOriginalBitValue|uxBitsToSet)&uxBitsToWaitFor)== uxBitsToWaitFor)// 事件原值检查(包含本任务发送的事件) 返回事件bit,并清除事件
		{
			// 返回值包含本任务发送的事件
			uxReturn = ( uxOriginalBitValue | uxBitsToSet );
			// 固定清除bit位
			pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
			xTicksToWait = 0;
		}
		// 【4】阻塞判断
		else
		{
			if( xTicksToWait != ( TickType_t ) 0 )
			{
				traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor );
				// 设置事件条目值(【清除】、【与逻辑】位固定为1)
				// 任务状态切换
				vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait );
				uxReturn = 0;
			}
			else
			{
				// 取最新状态返回
				uxReturn = pxEventBits->uxEventBits;
			}
		}
	}
	xAlreadyYielded = xTaskResumeAll();
	if( xTicksToWait != ( TickType_t ) 0 )
	{
		// 需要上下文切换
		if( xAlreadyYielded == pdFALSE )
		{
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
		//【5】恢复运行态后,(1) 获取控制位+事件、复位事件条目值
		uxReturn = uxTaskResetEventItemValue();
		// 未被接收
		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
		{
			/* The task unblocked because the bits were set. */
		}

		// 清除控制位
		uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
	}

	traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred );

	return uxReturn;
}

5.5 事件清除

        用于清除事件组指定的位, 如果在获取事件的时候没有将对应的标志位清除, 那么就需要用这个函数来进行清除;清除事件需要先进入临界区。

// 清除事件
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 );
    // 【1】进入临界区
	taskENTER_CRITICAL();
	{
		traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear );
		uxReturn = pxEventBits->uxEventBits;
		pxEventBits->uxEventBits &= ~uxBitsToClear;
	}
    // 【2】退出临界区
	taskEXIT_CRITICAL();

	return uxReturn;
}

5.6 事件删除

        很多场合,事件只用一次,用完就可以调用vEventGroupDelete()将事件组销毁。

        在事件组销毁前,会先释放被阻塞的任务,将这些任务强制移出阻塞链表,直接切换为就绪态,并且任务条目值强制为:使能位】设置为1、【解锁位】设置为1、其他均为0,这些等待该事件组的任务会直接判定等待完成,等待事件结果为0;

        事件组一旦删除,将不能再进行设置、等待的操作。

void vEventGroupDelete( EventGroupHandle_t xEventGroup )
{
	EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
	const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits );
	//【1】 暂停调度器
	vTaskSuspendAll();
	{
		traceEVENT_GROUP_DELETE( xEventGroup );
		//【2】有任务阻塞,释放阻塞任务
		while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 )
		{
			// 断言
			configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) );
			// 设置事件条目值(使能位+解锁+0),并将任务事件条目从xTasksWaitingForBits 移除
            // 解锁任务
			( 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 
	}
	( void ) xTaskResumeAll();
}

5.7 应用实例

5.7.1与逻辑

        任务A等待任务B、任务C数据都采集完成后进行数据处理。任务B、任务C等待任务A处理完数据后进行数据采集。

#define  CN_EVENT_B_STR  (0x0001)  // 任务A数据采集完成事件
#define  CN_EVENT_C_STR  (0x0002)  // 任务B数据采集完成事件
#define  CN_EVENT_STR    (CN_EVENT_B_STR+CN_EVENT_C_STR)  // 任务数据采集完成事件

#define  CN_EVENT_B_END  (0x0010)  // 任务B数据处理结束事件
#define  CN_EVENT_C_END  (0x0020)  // 任务C数据处理结束事件
#define  CN_EVENT_END    (CN_EVENT_B_END  +CN_EVENT_C_END  )  // 任务数据处理结束事件

int dataB[];                   // 任务B数据
int dataC[];                   // 任务C数据


EventGroupHandle_t synEventHandle;

// 任务A处理数据
void taskA()
{
	for(;;)
	{
       
        // 【1】等待数据采集完成事件,与逻辑,等待完成后清除
           xEventGroupWaitBits(synEventHandle,CN_EVENT_STR,1,1,portMAX_DELAY);
        // 【2】数据ddataB[]、dataC[]处理
            ...............
        // 【3】设置处理结束事件
           xEventGroupSetBits(synEventHandle,CN_EVENT_END);
	}
}
// 任务B数据采集
void taskB()
{
  for(;;)
	{
       
        // 【1】数据dataB[]采集
            ...............
        // 【2】设置采集结束事件
            xEventGroupSetBits(synEventHandle,CN_EVENT_B_STR);
        // 【3】等待数据处理完成事件,与逻辑,等待完成后清除
            xEventGroupWaitBits(synEventHandle,CN_EVENT_B_END,1,1,portMAX_DELAY);
	}

}
// 任务C数据采集
void taskC()
{
	 for(;;)
	{
       
        // 【1】数据dataC[]采集
            ...............
        // 【2】设置采集结束事件
            xEventGroupSetBits(synEventHandle,CN_EVENT_C_STR);
        // 【3】等待数据处理完成事件,与逻辑,等待完成后清除
            xEventGroupWaitBits(synEventHandle,CN_EVENT_C_END,1,1,portMAX_DELAY);
	}
}
void main()
{
	synEventHandle=xEventGroupCreate();
	// 新建任务A
	.....
	// 新建任务B
    .....
    // 新建任务C
    .....
	//启动调度器
    .....
}

5.7.2 或逻辑

         任务A等待任务B或任务C数据任一个采集完成后进行数据处理;任务B、任务C等待任务A处理完数据后进行数据采集。

#define  CN_EVENT_B_STR  (0x0001)  // 任务A数据采集完成事件
#define  CN_EVENT_C_STR  (0x0002)  // 任务B数据采集完成事件
#define  CN_EVENT_STR    (CN_EVENT_B_STR+CN_EVENT_C_STR)  // 任务数据采集完成事件

#define  CN_EVENT_B_END  (0x0010)  // 任务B数据处理结束事件
#define  CN_EVENT_C_END  (0x0020)  // 任务C数据处理结束事件
#define  CN_EVENT_END    (CN_EVENT_B_END  +CN_EVENT_C_END  )  // 任务数据处理结束事件

int dataB[];                   // 任务B数据
int dataC[];                   // 任务C数据

EventGroupHandle_t synEventHandle;

// 任务A处理数据
void taskA()
{
	for(;;)
	{
       
      // 【1】等待数据采集完成事件,或逻辑,等待成功后清除                                       
        EventBits_t retn=xEventGroupWaitBits(synEventHandle,CN_EVENT_STR,1,0,portMAX_DELAY);
       if(retn&CN_EVENT_B_STR  ) 
        {
            // 【2.1】数据dataB处理
            ...............
            // 【2.2】设置dataB处理结束事件
           xEventGroupSetBits(synEventHandle,CN_EVENT_B_END);
        }
        if(retn&CN_EVENT_C_STR  ) 
        {

            // 【3.1】数据dataC处理
            ...............
            // 【3.2】设置dataC处理结束事件
            xEventGroupSetBits(synEventHandle,CN_EVENT_C_END)
        }
	}
}
// 任务B数据采集
void taskB()
{
  for(;;)
	{
       
        // 【1】数据dataB[]采集
            ...............
        // 【2】设置采集结束事件
            xEventGroupSetBits(synEventHandle,CN_EVENT_B_STR);
        // 【3】等待数据处理完成事件,与逻辑,等待完成后清除
            xEventGroupWaitBits(synEventHandle,CN_EVENT_B_END,1,1,portMAX_DELAY);
	}

}
// 任务C数据采集
void taskC()
{
	 for(;;)
	{
       
        // 【1】数据dataC[]采集
            ...............
        // 【2】设置采集结束事件
            xEventGroupSetBits(synEventHandle,CN_EVENT_C_STR);
        // 【3】等待数据处理完成事件,与逻辑,等待完成后清除
            xEventGroupWaitBits(synEventHandle,CN_EVENT_C_END,1,1,portMAX_DELAY);
	}
}
void main()
{
	synEventHandle=xEventGroupCreate();
    // 预先发送处理结束事件
    xEventGroupSetBits(synEventHandle,CN_EVENT_END);
	// 新建任务A
	.....
	// 新建任务B
    .....
    // 新建任务C
    .....
	//启动调度器
    .....
}

5.7.3  多任务同步

         任务A、任务B、任务C数据均采集完成后同步处理。

#define  CN_EVENT_A_STR  (0x0001)  // 任务A数据处理开始事件
#define  CN_EVENT_B_STR  (0x0002)  // 任务B数据处理开始事件
#define  CN_EVENT_C_STR  (0x0004)  // 任务C数据处理开始事件
#define  CN_EVENT_STR    (CN_EVENT_A_STR+CN_EVENT_B_STR+CN_EVENT_C_STR)  // 任务数据处理开始事件组

#define  CN_EVENT_A_END  (0x0010)  // 任务A数据处理结束事件
#define  CN_EVENT_B_END  (0x0020)  // 任务B数据处理结束事件
#define  CN_EVENT_C_END  (0x0040)  // 任务C数据处理结束事件
#define  CN_EVENT_END    (CN_EVENT_A_END+CN_EVENT_B_END+CN_EVENT_C_END)  // 任务数据处理结束事件组

int dataA[];                   // 任务A数据
int dataB[];                   // 任务B数据
int dataC[];                   // 任务C数据


EventGroupHandle_t synEventHandle;

// 任务A处理数据
void taskA()
{
	for(;;)
	{
       
        // 【1】数据dataA[]采集
            ...............
        // 【2】同步任务数据处理开始事件
           xEventGroupSync(synEventHandle,CN_EVENT_A_STR,CN_EVENT_STR ,portMAX_DELAY);
        // 【3】数据dataA[]、dataB[]、dataC[]处理
            ...............
        // 【4】同步任务数据处理结束事件
           xEventGroupSync(synEventHandle,CN_EVENT_A_END,CN_EVENT_END ,portMAX_DELAY);
	}
}
// 任务B数据采集
void taskB()
{
  for(;;)
	{
       
       // 【1】数据dataB[]采集
            ...............
        // 【2】同步任务数据处理开始事件
            xEventGroupSync(synEventHandle,CN_EVENT_B_STR,CN_EVENT_STR ,portMAX_DELAY);
        // 【3】数据dataA[]、dataB[]、dataC[]处理
            ...............
        // 【4】同步任务数据处理结束事件
            xEventGroupSync(synEventHandle,CN_EVENT_B_END,CN_EVENT_END ,portMAX_DELAY);
	}

}
// 任务C数据采集
void taskC()
{
	for(;;)
	{
       
        // 【1】数据dataC[]采集
            ...............
        // 【2】同步任务数据处理开始事件
            xEventGroupSync(synEventHandle,CN_EVENT_C_STR,CN_EVENT_STR ,portMAX_DELAY);
        // 【3】数据dataA[]、dataB[]、dataC[]处理
            ...............
        // 【4】同步任务数据处理结束事件
            xEventGroupSync(synEventHandle,CN_EVENT_C_END,CN_EVENT_END ,portMAX_DELAY);
	}
}
void main()
{
	synEventHandle=xEventGroupCreate();
	// 新建任务A
	.....
	// 新建任务B
    .....
    // 新建任务C
    .....
	//启动调度器
    .....
}

六、事件操作(中断)

        事件的发送和等待不支持在中断中直接调用(事件设置与等待均通过暂停调度实现临界区保护,对中断无防护);FreeRtos通过定时器任务和定时器队列实现中断对事件的操作,其本质还是任务操作,并且只能实现无阻塞的操作:事件设置、事件清除。

        中断向定时器消息队列xTimerQueue发送回调函数消息,并附带函数指针和参数,定时器任务接收到消息后,调用回调函数,实现事件设置和清除。

        注:定时器任务是系统自带任务,关于定时器的解析将在后面章节进行。

6.1 事件设置

        中断:向定时器消息队列xTimerQueue发送函数回调消息,消息内容如下

消息参数事件设置中断发送的参数
消息类型中断回调:tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR
回调函数指针设置事件回调函数:vEventGroupSetBitsCallback
回调函数参数1事件组:xEventGroup,取自中断接口形参
回调函数参数2设置的事件:uxBitsToSet,取自中断接口形参
/*--------------------消息结构---------------------------------*/
typedef struct tmrTimerQueueMessage
{
    BaseType_t            xMessageID;           /* << 发送给定时器服务任务的命令. */
    typedef struct tmrCallbackParameters
    {
	    PendedFunction_t	pxCallbackFunction;	/* << 回调函数. */
	    void *pvParameter1;						/* << 参数1. */
	    uint32_t ulParameter2;					/* << 参数2. */
    } CallbackParameters_t;
} DaemonTaskMessage_t;
/*--------------------设置事件(中断)-------------------------------*/
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken )
{
	BaseType_t xReturn;
	traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet );
	xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken );
		return xReturn;
}
// 发送回调消息
BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken )
{
	DaemonTaskMessage_t xMessage;
	BaseType_t xReturn;
	xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR;
	xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend;
	xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1;
	xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2;

	xReturn = xQueueSendFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken );
	tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, xReturn );
	return xReturn;
}

        定时器任务:接收到消息后,获取到回调函数指针vEventGroupSetBitsCallback,事件组:xEventGroup,设置事件值:uxBitsToSet;执行vEventGroupSetBitsCallback(xEventGroup,uxBitsToSet);实现事件设置。


void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet )
{
	( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet );
}

6.2 事件清除

中断:向定时器消息队列xTimerQueue发送函数回调消息,消息内容如下

消息参数事件清除中断发送的参数
消息类型中断回调:tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR
回调函数指针清除事件回调函数:vEventGroupClearBitsCallback
回调函数参数1事件组:xEventGroup,取自中断接口形参
回调函数参数2清除的事件:uxBitsToClear ,取自中断接口形参

/*--------------------消息结构---------------------------------*/
typedef struct tmrTimerQueueMessage
{
    BaseType_t            xMessageID;           /* << 发送给定时器服务任务的命令. */
    typedef struct tmrCallbackParameters
    {
	    PendedFunction_t	pxCallbackFunction;	/* << 回调函数. */
	    void *pvParameter1;						/* << 参数1. */
	    uint32_t ulParameter2;					/* << 参数2. */
    } CallbackParameters_t;
} DaemonTaskMessage_t;
/*--------------------清除事件(中断)-------------------------------*/
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;
}
// 发送回调消息(任务)
BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait )
{
	DaemonTaskMessage_t xMessage;
	BaseType_t xReturn;
	configASSERT( xTimerQueue );

	// 发送至任务
	xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK;
	xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend;// 函数回调
	xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1;         // 参数1
	xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2;         // 参数2
		
	xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); // 发送
	tracePEND_FUNC_CALL( xFunctionToPend, pvParameter1, ulParameter2, xReturn );
	return xReturn;
}

     定时器任务:接收到消息后,获取到回调函数指针vEventGroupClearBitsCallback,事件组:xEventGroup,清除事件:uxBitsToClear ;执行vEventGroupClearBitsCallback(xEventGroup,uxBitsToClear )实现事件清除。

void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear )
{
	( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear );
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿来不是梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值