FreeRTOS series - event flag group

Posted by tnylsej on Thu, 16 Dec 2021 23:26:55 +0100

preface

Using semaphores can complete the synchronization between tasks, but using semaphores to synchronize tasks can only synchronize with a single event or task. Sometimes a task may need to be synchronized with multiple events or tasks, and the semaphore is powerless. FreeRTOS provides an optional solution for this, which is the event flag group.

Event group, event bit

Event bits are used to indicate whether an event occurs. Event bits are usually used as event flags.
An event group is a group of event bits.
give an example:

  1. The bit0 of the event group indicates whether the messages in the queue are processed.
  2. bit1 of the event group indicates whether a message needs to be sent from the network.

Data types of event groups and event bits

All event bits in the event flag group are stored in an unsigned eventbits_ In a variable of type T, EventBits_t in event_groups.h has the following definitions:

typedef TickType_t EventBits_t;

Data type TickType_t is defined as follows

#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

When configure_ 16_ BIT_ Ticktype when tips is 0_ T is a 32-bit data type, so EventBits_t is also a 32-bit data type. EventBits_ A variable of type T can store 24 event bits, and the other upper 8 bits are used for other purposes. When configure_ 16_ BIT_ Ticktype when ticks is 1_ T is a 16 bit data type, so EventBits_t is also a 16 bit data type. EventBits_ A variable of type T can store 8 event bits, and the other upper 8 bits are used for other purposes.

Create event flag group

functiondescribe
xEventGroupCreate()Create event flag groups using dynamic methods.
xEventGroupCreateStatic()Create event flag groups using static methods

Event groupeventgroup_ T is defined as follows

typedef struct xEventGroupDefinition
{
	EventBits_t uxEventBits; // Event bit
	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;

Function xEventGroupCreate()

The function prototype is as follows:

/*
* Return value: NULL: event flag group creation failed
* 		  Other values: create successful event flag group handle
*
*/
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

	EventGroupHandle_t xEventGroupCreate( void )
	{
	EventGroup_t *pxEventBits;

		/* Allocate the event group. */
		pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); // The required memory is allocated through dynamic memory management

		if( pxEventBits != NULL )
		{
			pxEventBits->uxEventBits = 0;
			vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );// Initializes a list that is waiting for an event.

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

Function xEventGroupCreateStatic()

This function is used to create an event flag group timer. The required memory needs to be allocated by the user. The prototype of this function is as follows:

/* Return value:
   NULL:Event flag group creation failed.
   Other values: create successful event flag group handle
*/
#if( configSUPPORT_STATIC_ALLOCATION == 1 )

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

	    configASSERT( pxEventGroupBuffer );
		//pxEventGroupBuffer is used to save the event flag group structure.
		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 */

Set event bit

FreeRTOS provides four functions to set the event bit (flag) in the event flag group. The setting of event bit (flag) includes two operations: reset and set 1. These four functions are shown in the following table:

functiondescribe
xEventGroupClearBits()Clear the specified event bit and use it in the task.
xEventGroupClearBitsFromISR()Clears the specified event bit and uses it in the interrupt service function
xEventGroupSetBits()Use the specified event location 1 in the task.
xEventGroupSetBitsFromISR()Use the specified event location 1 in the interrupt service function.

Function xEventGroupClearBits()

// This function also has the function of querying event bits. When uxBitsToClear is 0, all event bits of the event group are returned,
/*
Parameters:
xEventGroup:  Handle to the event flag group to operate on.
uxBitsToClear:  The event bit to be cleared, such as bit3, is set to 0X08. Multiple can be cleared at the same time
bit,If it is set to 0X09, bit3 and bit0 are cleared at the same time.
Return value: 
Any value: the event group value before the specified event bit is cleared.
*/
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
{
EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup;
EventBits_t uxReturn;

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

	taskENTER_CRITICAL();
	{
		traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear );

		/* The value returned is the event group value prior to the bits being
		cleared. */
		uxReturn = pxEventBits->uxEventBits;

		/* Clear the bits. */
		pxEventBits->uxEventBits &= ~uxBitsToClear;
	}
	taskEXIT_CRITICAL();

	return uxReturn;
}

Function xEventGroupClearBitsFromISR()

This function is the interrupt level version of the function xEventGroupClearBits(), and also clears the specified event bits (flags). This function is used in the interrupt service function. The prototype of this function is as follows:

/*
Parameters:
xEventGroup:  Handle to the event flag group to operate on.
uxBitsToClear:  The event bit to be cleared, such as bit3, is set to 0X08. Multiple can be cleared at the same time
bit,If it is set to 0X09, bit3 and bit0 are cleared at the same time.
Return value: 
pdPASS:  Event bit cleared successfully.
pdFALSE: Event bit reset failed.
*/
	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;
	}

Function xEventGroupSetBits()

Set the specified event bit to 1. This function can only be used in tasks and cannot be used to interrupt service functions. The prototype of this function is as follows:

/*
Parameters:
xEventGroup:  Handle to the event flag group to operate on.
uxBitsToClear:  Specify the event bit to be set to 1. For example, if you want to set bit3 value to 1, it will be set to 0X08. Multiple bits can be set to 1 at the same time. If it is set to 0X09, bit3 and bit0 can be set to 1 at the same time.
Return value: 
Any value: the event group value after the event location 1 will be specified.
*/
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 ) // View is an event that is waiting for some time,
		{
			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;
}

Function xEventGroupSetBitsFromISR()

This function is also used to the specified event location 1. This function is the interrupt version of xEventGroupSetBits(). It is used in the interrupt service function. The function prototype is as follows:

/*
Parameters:
xEventGroup:  Handle to the event flag group to operate on.
uxBitsToSet:  Specify the event bit to be set to 1. For example, if you want to set bit3 value to 1, it will be set to 0X08. You can also
 Multiple bits are set to 1. If it is set to 0X09, bit3 and bit0 are set to 1 at the same time.
pxHigherPriorityTaskWoken: Mark whether to switch tasks after exiting this function. The value function of this variable will automatically
 For automatic setting, the user does not need to set it. The user only needs to provide a variable to save it
 Just this value. When this value is pdTRUE, it will exit the interrupt service function
 Be sure to switch tasks before.
Return value: 
pdPASS:  Event location 1 succeeded.
pdFALSE: Event location 1 failed.
*/
	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;
	}

Get event flag group value

functiondescribe
xEventGroupGetBits()Get the value of the current event flag group (the value of each event bit) and use it in the task
xEventGroupGetBitsFromISR()Gets the value of the current event flag group, which is used in the interrupt service function.

Function xEventGroupGetBits()

This function is essentially a macro, as follows:

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

The function xeventgroupclearbits (xeventgroup, 0) has been described above.

Function xEventGroupGetBitsFromISR()

The prototype of this function is as follows:

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

Wait for the specified event bit

A task may need to synchronize with multiple events, so the task needs to wait and judge multiple event bits (flags). This function can be completed by using the function xEventGroupWaitBits(). After calling the function, if the event bit the task is waiting for is not ready (set to 1 or clear), the task will enter the blocking state until the blocking time arrives or the waiting event bit is ready. The function prototype is as follows:

/*
Parameters:
xEventGroup:  Specifies the event flag group to wait for.
uxBitsToWaitFor: Specifies the event bits to wait for. For example, when you want to wait for bit0 and / or bit2, this parameter is 0X05,
If you want to wait for bit0 and / or bit1 and / or bit2, this parameter is 0X07, and so on.
xClearOnExit:  If this parameter is pdTRUE, it is determined by the parameter uxBitsToWaitFor before exiting this function
 The set event bits are cleared. If the bit pdFALSE is set, these event bits are
 It won't change.
xWaitForAllBits:  If this parameter is set to pdTRUE, when these events are set by uxBitsToWaitFor
 The function xEventGroupWaitBits() will not start until the bits are set to 1 or the specified blocking time expires
 return. When this function is pdFALSE, as long as these things are set by uxBitsToWaitFor
 If any one of the file bits is set to 1, or the specified blocking time expires, the function
xEventGroupWaitBits()Will return.
xTicksToWait:  Set the blocking time in beats.
Return value: 
Any value: returns the value of the event flag group after the waiting event position 1, or the blocking time has expired. root
 According to this value, we can know which event position 1. If the function returns because the blocking time is up, the return value does not mean anything
*/
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;
}

Topics: FreeRTOS RTOS