首页 > 其他分享 >FreeRTOS--事件组

FreeRTOS--事件组

时间:2024-01-15 10:25:38浏览次数:29  
标签:task FreeRTOS -- EventBits pxEventBits 事件 bits event

示例源码基于FreeRTOS V9.0.0

事件组

1. 概述

FreeRTOS事件组,是任务间同步的一种方式。它基于bit map实现,所谓的事件组,即一个整数。整数中的每一位代表着一个事件,bit为1时表示事件发生,bit为0表示事件未发生;

事件触发可以由任务触发,也可以由中断服务触发,触发时将对应bit位置1;

当任务需要等待某个事件或某些事件发生,才执行后续动作时,可以使用事件组。任务可以等待一个事件也可以等待一组事件,还可以等待一组事件中的任意一个事件;

2. 事件组与信号量和队列的差异

  • 信号量和队列,在事件发生时,只会唤醒一个任务。而事件组,可以唤醒所有等待该事件的任务(运行态任务都是只有一个,但就绪态,事件组可以有多个),即事件组,支持“广播”功能;
  • 信号量和队列,是消耗性资源,当事件发生时,队列的数据被读走,信号量递减。而事件组,唤醒的任务可以选择保留资源,即事件发生时,可以选择不清除对应bit,保留事件;

3. 接口使用

用途 接口
创建 EventGroupHandle_t xEventGroupCreate( void );
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer );
删除 void vEventGroupDelete( EventGroupHandle_t xEventGroup );
设置事件发生 EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
等待事件发生 EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait );
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait );

4. 代码实现

4.1 事件组结构

// event_group.h
typedef TickType_t EventBits_t;

// event_group.c
typedef struct xEventGroupDefinition
{
	EventBits_t uxEventBits;
	List_t xTasksWaitingForBits;		/*< List of tasks waiting for a bit to be set. */

	#if( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxEventGroupNumber;
	#endif

	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
	#endif
} EventGroup_t;

主要是uxEventBits和xTasksWaitingForBits两项:

  • xTasksWaitingForBits 用于存储等待事件组上某些bit的任务;
  • uxEventBits 即事件组的bit map,每一位代表一个事件,1表示事件发生,0表示未发生。高8位为内核使用,用于记录控制信息;

uxEventBits 类型为TickType_t,根据宏configUSE_16_BIT_TICKS来决定数据类型是32位还是16位(决定了事件组可以表示的事件多少)

// protmacro.h
#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

	/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
	not need to be guarded with a critical section. */
	#define portTICK_TYPE_IS_ATOMIC 1
#endif

uxEventBits的高8位是内核使用的,用来记录控制信息(唤醒后是否清除事件位,是否等待所有事件等)

// event_group.c
#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
	#define eventWAIT_FOR_ALL_BITS			0x04000000UL
	#define eventEVENT_BITS_CONTROL_BYTES	0xff000000UL
#endif

4.2 事件组的创建和销毁

4.2.1 xEventGroupCreate

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

	EventGroupHandle_t xEventGroupCreate( void )
	{
	EventGroup_t *pxEventBits;

		/* Allocate the event group. */
		pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) );

		if( pxEventBits != NULL )
		{
			pxEventBits->uxEventBits = 0;
			vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );

			#if( configSUPPORT_STATIC_ALLOCATION == 1 )
			{
				/* Both static and dynamic allocation can be used, so note this
				event group was allocated statically in case the event group is
				later deleted. */
				pxEventBits->ucStaticallyAllocated = pdFALSE;
			}
			#endif /* configSUPPORT_STATIC_ALLOCATION */

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

		return ( EventGroupHandle_t ) pxEventBits;
	}

#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

xEventGroupCreate用于动态创建事件组,事件组的结构EventGroup_t通过pvPortMalloc接口动态创建。并初始化uxEventBits和xTasksWaitingForBits。实现较简单。

4.2.2 xEventGroupCreateStatic

#if( configSUPPORT_STATIC_ALLOCATION == 1 )

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

		/* A StaticEventGroup_t object must be provided. */
		configASSERT( pxEventGroupBuffer );

		/* The user has provided a statically allocated event group - use it. */
		pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 EventGroup_t and StaticEventGroup_t are guaranteed to have the same size and alignment requirement - checked by configASSERT(). */

		if( pxEventBits != NULL )
		{
			pxEventBits->uxEventBits = 0;
			vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );

			#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
			{
				/* Both static and dynamic allocation can be used, so note that
				this event group was created statically in case the event group
				is later deleted. */
				pxEventBits->ucStaticallyAllocated = pdTRUE;
			}
			#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

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

		return ( EventGroupHandle_t ) pxEventBits;
	}

#endif /* configSUPPORT_STATIC_ALLOCATION */

xEventGroupCreateStatic用于静态创建事件组。事件组的结构StaticEventGroup_t需要函数外部自行定义,通过参数给入。函数内部主要是初始化uxEventBits和xTasksWaitingForBits。

4.2.3 vEventGroupDelete

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 )
		{
			/* Unblock the task, returning 0 as the event list is being deleted
			and	cannot therefore have any bits set. */
			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 ) )
		{
			/* The event group can only have been allocated dynamically - free
			it again. */
			vPortFree( pxEventBits );
		}
		#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
		{
			/* The event group could have been allocated statically or
			dynamically, so check before attempting to free the memory. */
			if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
			{
				vPortFree( pxEventBits );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
	}
	( void ) xTaskResumeAll();
}

vEventGroupDelete用于删除事件组,主要有两个步骤:

  • 循环调用xTaskRemoveFromUnorderedEventList,移除等待事件的任务列表;
  • 调用vPortFree释放事件组的内存;

以上两个步骤需要先挂起调度器,执行后再恢复调度器。

xTaskRemoveFromUnorderedEventList定义与task.c,用于移除无序的任务列表(等待某些事件的任务)。任务从阻塞态或暂停态恢复,进入就绪态,等待调度执行。

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

	/* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED.  It is used by
	the event flags implementation. */
	configASSERT( uxSchedulerSuspended != pdFALSE );

	/* Store the new item value in the event list. */
	listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE );

	/* Remove the event list form the event flag.  Interrupts do not access
	event flags. */
	pxUnblockedTCB = ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxEventListItem );
	configASSERT( pxUnblockedTCB );
	( void ) uxListRemove( pxEventListItem );

	/* Remove the task from the delayed list and add it to the ready list.  The
	scheduler is suspended so interrupts will not be accessing the ready
	lists. */
	( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) );
	prvAddTaskToReadyList( pxUnblockedTCB );

	if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority )
	{
		/* Return true if the task removed from the event list has
		a higher priority than the calling task.  This allows
		the calling task to know if it should force a context
		switch now. */
		xReturn = pdTRUE;

		/* Mark that a yield is pending in case the user is not using the
		"xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */
		xYieldPending = pdTRUE;
	}
	else
	{
		xReturn = pdFALSE;
	}

	return xReturn;
}

xTaskRemoveFromUnorderedEventList函数必须在挂起调度器后执行,主要执行如下动作:

  • 设置链表的item value,将原值最高位置1(或运算taskEVENT_LIST_ITEM_VALUE_IN_USE);
  • 将链表项(任务)从链表移除;
  • 任务就绪(从原状态链表移除,加入就绪链表);
  • 根据优先级决策是否让步切换,设置xYieldPending,并通过返回值返回。

4.3 设置和等待事件

4.3.1 xEventGroupSetBits

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;

	/* Check the user is not attempting to set the bits used by the kernel
	itself. */
	configASSERT( xEventGroup );
	configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );

	pxList = &( pxEventBits->xTasksWaitingForBits );
	pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */
	vTaskSuspendAll();
	{
		traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );

		pxListItem = listGET_HEAD_ENTRY( pxList );

		/* Set the bits. */
		pxEventBits->uxEventBits |= uxBitsToSet;

		/* See if the new bit value should unblock any tasks. */
		while( pxListItem != pxListEnd )
		{
			pxNext = listGET_NEXT( pxListItem );
			uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
			xMatchFound = pdFALSE;

			/* Split the bits waited for from the control bits. */
			uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
			uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;

			if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
			{
				/* Just looking for single bit being set. */
				if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
				{
					xMatchFound = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
			{
				/* All bits are set. */
				xMatchFound = pdTRUE;
			}
			else
			{
				/* Need all bits to be set, but not all the bits were set. */
			}

			if( xMatchFound != pdFALSE )
			{
				/* The bits match.  Should the bits be cleared on exit? */
				if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )
				{
					uxBitsToClear |= uxBitsWaitedFor;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				/* Store the actual event flag value in the task's event list
				item before removing the task from the event list.  The
				eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows
				that is was unblocked due to its required bits matching, rather
				than because it timed out. */
				( void ) xTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
			}

			/* Move onto the next list item.  Note pxListItem->pxNext is not
			used here as the list item may have been removed from the event list
			and inserted into the ready/pending reading list. */
			pxListItem = pxNext;
		}

		/* Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT
		bit was set in the control word. */
		pxEventBits->uxEventBits &= ~uxBitsToClear;
	}
	( void ) xTaskResumeAll();

	return pxEventBits->uxEventBits;
}

xEventGroupSetBits用于设置事件组某些位,标记事件发生,同时唤醒等待事件的任务;

参数说明

  • xEventGroup:代表要设置的事件组;
  • uxBitsToSet:代表要设置的事件位;

返回值说明
返回设置后的事件组的值,该值可能被唤醒后的任务修改(任务唤醒后可能清除了对应的事件位);

流程如下

  • 挂起调度器;
  • 设置事件组,根据参数uxBitsToSet,将事件组的对应位置1(比如uxBitsToSet=0x15 就表示设置bit4, bit2, bit0),标识事件发生;
  • 遍历事件组的任务链表xTasksWaitingForBits,判断任务等待的事件是否发生(根据高8位的控制字段,判断任务是等待某个事件还是等待一组事件)。xMatchFound变量为匹配结果;
  • 如果任务等待的事件发生了(xMatchFound为pdTRUE),根据控制字段决定是否要清除对应事件的bit位。uxBitsToClear记录了循环中事件组需要清除的bit位;
  • 调用xTaskRemoveFromUnorderedEventList将任务从事件组链表移除,并唤醒任务(就绪态);
  • 循环结束,根据uxBitsToClear清除事件组相应bit位;
  • 恢复调度器;

说明

  • uxControlBits为事件组高8位,作为控制字段:
    • ( uxControlBits & eventWAIT_FOR_ALL_BITS ) != ( EventBits_t ) 0 判断是否等待所有事件位;
    • ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 判断是否清除事件位;
  • uxBitsWaitedFor为任务等待的事件:
    • ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 表示任务等待的事件中有至少一个发生;
    • ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor 表示任务等待的所有事件都发生;

任务等待的事件,记录在任务控制块TCB中的xEventListItem.xItemValue。该值在创建任务初始化TCB时将其初始为任务优先级configMAX_PRIORITIES - uxPriority,在使用事件组时,复用此字段将其作为任务等待的事件掩码

任务等待的事件,在调用xEventGroupWaitBits函数时设置。

4.3.2 xEventGroupSetBitsFromISR

#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )

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

#endif

#if( INCLUDE_xTimerPendFunctionCall == 1 )

	BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken )
	{
	DaemonTaskMessage_t xMessage;
	BaseType_t xReturn;

		/* Complete the message with the function parameters and post it to the
		daemon task. */
		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;
	}

#endif /* INCLUDE_xTimerPendFunctionCall */

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

xEventGroupSetBitsFromISR用于在中断ISR内设置事件组某些位,标记事件发生,同时唤醒等待事件的任务。

xEventGroupSetBitsFromISR实现较特殊,它并不直接设置事件组和唤醒任务,它是借助消息队列,通知timer任务,由timer任务设置事件组和唤醒任务。因为,设置事件组,可能会唤醒多个任务,这会带来不确定性,不应由中断实现;

xEventGroupSetBitsFromISR将vEventGroupSetBitsCallback函数指针和参数通过消息队列发给了timer任务,timer任务根据此进行回调。vEventGroupSetBitsCallback本质也是调用xEventGroupSetBits。

4.3.3 xEventGroupWaitBits

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;

	/* Check the user is not attempting to wait on the bits used by the kernel
	itself, and that at least one bit is being requested. */
	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;

		/* Check to see if the wait condition is already met or not. */
		xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );

		if( xWaitConditionMet != pdFALSE )
		{
			/* The wait condition has already been met so there is no need to
			block. */
			uxReturn = uxCurrentEventBits;
			xTicksToWait = ( TickType_t ) 0;

			/* Clear the wait bits if requested to do so. */
			if( xClearOnExit != pdFALSE )
			{
				pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else if( xTicksToWait == ( TickType_t ) 0 )
		{
			/* The wait condition has not been met, but no block time was
			specified, so just return the current value. */
			uxReturn = uxCurrentEventBits;
		}
		else
		{
			/* The task is going to block to wait for its required bits to be
			set.  uxControlBits are used to remember the specified behaviour of
			this call to xEventGroupWaitBits() - for use when the event bits
			unblock the task. */
			if( xClearOnExit != pdFALSE )
			{
				uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			if( xWaitForAllBits != pdFALSE )
			{
				uxControlBits |= eventWAIT_FOR_ALL_BITS;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			/* Store the bits that the calling task is waiting for in the
			task's event list item so the kernel knows when a match is
			found.  Then enter the blocked state. */
			vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );

			/* This is obsolete as it will get set after the task unblocks, but
			some compilers mistakenly generate a warning about the variable
			being returned without being set if it is not done. */
			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();
		}

		/* The task blocked to wait for its required bits to be set - at this
		point either the required bits were set or the block time expired.  If
		the required bits were set they will have been stored in the task's
		event list item, and they should now be retrieved then cleared. */
		uxReturn = uxTaskResetEventItemValue();

		if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
		{
			taskENTER_CRITICAL();
			{
				/* The task timed out, just return the current event bit value. */
				uxReturn = pxEventBits->uxEventBits;

				/* It is possible that the event bits were updated between this
				task leaving the Blocked state and running again. */
				if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
				{
					if( xClearOnExit != pdFALSE )
					{
						pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			taskEXIT_CRITICAL();

			/* Prevent compiler warnings when trace macros are not used. */
			xTimeoutOccurred = pdFALSE;
		}
		else
		{
			/* The task unblocked because the bits were set. */
		}

		/* The task blocked so control bits may have been set. */
		uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
	}
	traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );

	return uxReturn;
}

xEventGroupWaitBits用于任务等待事件发生。

参数说明

  • xEventGroup:代表等待哪个事件组;
  • uxBitsToWaitFor:代表等待哪些事件(哪些位);
  • xClearOnExit:表示事件发生后,函数退出前,是否清除对应的事件位;
  • xWaitForAllBits:表示是否等待uxBitsToWaitFor的所有事件(所有位)。pdTRUE表示要等待所有,pdFALSE表示只要有一个发生就唤醒返回;
  • xTicksToWait:表示阻塞等待的tick时长;

返回值说明
如果等待的事件发生了,返回事件发生后的事件组的值(清除事件位前);如果超时退出,返回超时时刻的事件组的值;

流程如下

  • 挂起调度器;
  • 调用prvTestWaitCondition,检查任务等待的事件是否发生;
    • 如果已发生,设置返回值,设置xTicksToWait = 0(仅为了不执行后续分支流程),判断是否需要清除事件位;
    • 如果未发生,且xTicksToWait = 0(不阻塞等待),设置返回值;
    • 如果未发生,且xTicksToWait > 0(阻塞等待),根据参数设置高8位控制位,调用vTaskPlaceOnUnorderedEventList将任务阻塞,并设置任务等待的事件值(含控制位);
  • 恢复调度器;
  • 如果xTicksToWait > 0(事件发生任务唤醒,或超时唤醒)
    • 根据xTaskResumeAll返回值判断是否需求让步切换;
    • 如果是超时唤醒(检查控制位( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
      • 设置返回值,临界区内再次调用prvTestWaitCondition判断是否等待的事件发生,是的话根据参数决定是否清除事件位;
  • 返回uxReturn;

4.3.4 xEventGroupSync

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

	vTaskSuspendAll();
	{
		uxOriginalBitValue = pxEventBits->uxEventBits;

		( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet );

		if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor )
		{
			/* All the rendezvous bits are now set - no need to block. */
			uxReturn = ( uxOriginalBitValue | uxBitsToSet );

			/* Rendezvous always clear the bits.  They will have been cleared
			already unless this is the only task in the rendezvous. */
			pxEventBits->uxEventBits &= ~uxBitsToWaitFor;

			xTicksToWait = 0;
		}
		else
		{
			if( xTicksToWait != ( TickType_t ) 0 )
			{
				traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor );

				/* Store the bits that the calling task is waiting for in the
				task's event list item so the kernel knows when a match is
				found.  Then enter the blocked state. */
				vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait );

				/* This assignment is obsolete as uxReturn will get set after
				the task unblocks, but some compilers mistakenly generate a
				warning about uxReturn being returned without being set if the
				assignment is omitted. */
				uxReturn = 0;
			}
			else
			{
				/* The rendezvous bits were not set, but no block time was
				specified - just return the current event bit value. */
				uxReturn = pxEventBits->uxEventBits;
			}
		}
	}
	xAlreadyYielded = xTaskResumeAll();

	if( xTicksToWait != ( TickType_t ) 0 )
	{
		if( xAlreadyYielded == pdFALSE )
		{
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		/* The task blocked to wait for its required bits to be set - at this
		point either the required bits were set or the block time expired.  If
		the required bits were set they will have been stored in the task's
		event list item, and they should now be retrieved then cleared. */
		uxReturn = uxTaskResetEventItemValue();

		if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
		{
			/* The task timed out, just return the current event bit value. */
			taskENTER_CRITICAL();
			{
				uxReturn = pxEventBits->uxEventBits;

				/* Although the task got here because it timed out before the
				bits it was waiting for were set, it is possible that since it
				unblocked another task has set the bits.  If this is the case
				then it needs to clear the bits before exiting. */
				if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor )
				{
					pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			taskEXIT_CRITICAL();

			xTimeoutOccurred = pdTRUE;
		}
		else
		{
			/* The task unblocked because the bits were set. */
		}

		/* Control bits might be set as the task had blocked should not be
		returned. */
		uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
	}

	traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred );

	return uxReturn;
}

xEventGroupSync 用于任务同步等待事件发生,类同xEventGroupWaitBits,只是xEventGroupSync 必须所有在任务等待的所有事件发生才会唤醒;

参数说明

  • xEventGroup:要等待的事件组;
  • uxBitsToSet:要设置哪些事件(任务完成了哪些事件);
  • uxBitsToWaitFor:要等待哪些事件;
  • xTicksToWait:阻塞等待的tick时长;

返回值说明
如果等待的事件发生了,返回事件发生后的事件组的值(清除事件位前);如果超时退出,返回超时时刻的事件组的值;

流程如下

  • 挂起调度器;
  • 设置当前任务完成的事件(xEventGroupSetBits);
  • 判断任务等待的事件是不是都发生了:
    • 是,设置返回值为事件发生后的事件值,然后清除事件组相应位(等待的事件);
    • 否,如果xTicksToWait!=0, 阻塞等待,调用vTaskPlaceOnUnorderedEventList将任务阻塞,并设置事件组控制位,标识等待所有事件,事件发生后清除对应的事件位。如果xTicksToWait==0,不阻塞等待,设置返回值为当前时刻事件组的值;
  • 恢复调度器;
  • 根据xTaskResumeAll返回值判断是否需要让步切换任务;
  • 调用uxTaskResetEventItemValue重置TCB中的xEventListItem.xItemValue,返回重置前的xItemValue,即任务等待的事件值;
  • 根据控制位判断是事件都发生了还是超时退出了。如果是超时退出,在临界区内再次检查等待事件是否都发生,是的话清除事件位;
  • 返回事件值;

注:xEventGroupSync默认是等待uxBitsToWaitFor指定的所有事件发生,同时事件发生后清除事件位;

参考链接

百问网-FreeRTOS入门与工程实践-基于STM32F103-第14章事件组

标签:task,FreeRTOS,--,EventBits,pxEventBits,事件,bits,event
From: https://www.cnblogs.com/hjx168/p/17964809

相关文章

  • 一种基于偏移流和纯字符串流来存储和读取字符串列表的方法【C#】
    字符串的存储长度是可变的,在C#中,BinaryWriter和BinaryReader在Write,ReadStirng的时候,都在单个流中字符串的二进制数组前面加了一个二进制数组的长度信息,方便读取的时候,造成了记录字符串的流并不纯粹是字符串的内容。但是,有时候,我们可以,也可能必须记录纯粹的字符串的二进制内容,然后......
  • Spring/Spring Boot 常用注解总结
    1、我们可以把 @SpringBootApplication看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合 2、@RestControllervs@Controller(qq.com)@RestController注解是@Controller和@ResponseBody的合集,表示这是个控制器bean,并且是将函数的返回值直......
  • 金融行业CRM选型必看:哪款金融CRM好用?
    市场形式波诡云谲,金融行业也面临着资源体系分散、竞争力后继不足、未知风险无法规避等问题。金融企业该如何解决这些问题,或许可以了解一下Zoho CRM管理系统,和其提供的金融行业CRM解决方案。金融CRM系统可以智能化客户筛选、整合各个资源体系、提升企业核心竞争力、规避安全风险......
  • Python打包exe文件方法汇总【4种】
    Python作为解释型语言,发布即公开源码,虽然是提倡开源但是有些时候就是忍不住想打包成exe,不仅仅是为了对代码进行加密,而是为了跨平台。防止有些没有安装py环境的电脑无法运行软件。目录对python代码打包成exe的方式有4种,py2exe,pyinstaller,cx_Freeze,nuitka,分别对以上4种方法介绍,欢迎......
  • mysql备份方案
    1.为什么要备份运维是干什么的?保护服务器数据安全维护公司运维资产7*24小时运转企业真实案件:https://www.leiphone.com/category/sponsor/Isb7Smi17CHBTxVF.html企业丢了数据,就等于失去了商机、客户、产品、甚至倒闭。在各式各样的数据中,数据库的数据更是核心之核心,当......
  • IIS实现负载均衡,通过ARR和URL重写
    目录一、实现整体方式介绍 二、配置负载均衡服务 三、把请求转发到负载均衡器 回到顶部一、实现整体方式介绍项目中部署在windows服务器上的项目,需要部署负载均衡,本来想用nginx来配置的,奈何iis上有几个项目,把80端口和443端口占用了,nginx就用不了了(因为通过域名访......
  • SpringBoot enter door基础_配springBoot项目,读取配置,配置全局异常捕获
    1、Spring是重量级企业开发框架 EnterpriseJavaBean(EJB) 的替代品,Spring为企业级Java开发提供了一种相对简单的方法,通过 依赖注入 和 面向切面编程 ,用简单的 Java对象(PlainOldJavaObject,POJO) 实现了EJB的功能虽然Spring的组件代码是轻量级的,但它的配置却是重......
  • mysql备份实战
    1.备份恢复演练(mysqldump+binlog)知识储备如下内容。。全量备份全量数据,指的是某一整个数据库(如kings)中所有的表、以及表数据,进行备份。例如备份所有数据库、以及所有数据,上面也讲了mysqldump的全量备份操作。备份所有库mysqldump-uroot-pwww.yuchaoit.cn-S/data/330......
  • 神经网络优化篇:理解指数加权平均数(Understanding exponentially weighted averages)
    理解指数加权平均数回忆一下这个计算指数加权平均数的关键方程。\({{v}_{t}}=\beta{{v}_{t-1}}+(1-\beta){{\theta}_{t}}\)\(\beta=0.9\)的时候,得到的结果是红线,如果它更接近于1,比如0.98,结果就是绿线,如果\(\beta\)小一点,如果是0.5,结果就是黄线。进一步地分析,来理解如何计......
  • xtrabackup备份工具
    为什么要学这个工具背景一个合格的运维工程师或者dba工程师,如果有从事数据库方面的话,首先需要做的就是备份,如果没有备份,出现问题的话,你的业务就会出问题,你的工作甚至会。。。所以备份是重要的,但光有备份还不行,备份后如果出现问题,你还得使用备份数据来恢复,但恢复数据的时间一......