天天看點

FreeRTOS系列---事件标志組前言事件組、事件位事件組和事件位的資料類型建立事件标志組設定事件位擷取事件标志組值等待指定的事件位

文章目錄

  • 前言
  • 事件組、事件位
  • 事件組和事件位的資料類型
  • 建立事件标志組
    • 事件組EventGroup_t 定義如下
    • 函數xEventGroupCreate()
    • 函數xEventGroupCreateStatic()
  • 設定事件位
    • 函數xEventGroupClearBits()
    • 函數 xEventGroupClearBitsFromISR()
    • 函數 xEventGroupSetBits()
    • 函數xEventGroupSetBitsFromISR()
  • 擷取事件标志組值
      • 函數xEventGroupGetBits()
      • 函數xEventGroupGetBitsFromISR()
  • 等待指定的事件位

前言

使用信号量可以完成任務之間的同步,但是使用信号量來同步的話任務隻能與單個的事件或任務進行同步。有時候某個任務可能會需要與多個事件或任務進行同步,此時信号量就無能為力了。FreeRTOS 為此提供了一個可選的解決方法,那就是事件标志組。

事件組、事件位

事件位用來表明某個事件是否發生,事件位通常用做事件标志。

一個事件組就是一組的事件位。

舉例:

  1. 事件組的bit0表示隊列中的消息是否被處理掉。
  2. 事件組的bit1表示是否有消息需要從網絡中發送出去。
FreeRTOS系列---事件标志組前言事件組、事件位事件組和事件位的資料類型建立事件标志組設定事件位擷取事件标志組值等待指定的事件位

事件組和事件位的資料類型

事件标志組中的所有事件位都存儲在一個無符号的EventBits_t 類型的變量中,EventBits_t在 event_groups.h 中有如下定義:

資料類型TickType_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
           

當 configUSE_16_BIT_TICKS 為 0 的時候 TickType_t 是個 32 位的資料類型,因 此 EventBits_t 也是個 32 位的資料類型。EventBits_t 類型的變量可以存儲 24 個事件位,另外的高 8 位有其他用。當 configUSE_16_BIT_TICKS 為 1 的時候 TickType_t 是個 16 位的資料類型,因 此 EventBits_t 也是個 16位的資料類型。EventBits_t 類型的變量可以存儲8 個事件位,另外的高 8 位有其他用。

建立事件标志組

函數 描述
xEventGroupCreate() 使用動态方法建立事件标志組。
xEventGroupCreateStatic() 使用靜态方法建立事件标志組

事件組EventGroup_t 定義如下

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;
           

函數xEventGroupCreate()

函數原型如下:

/*
* 傳回值: NULL: 事件标志組建立失敗
* 		  其他值:建立成功的事件标志組句柄
*
*/
#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;
	}
           

函數xEventGroupCreateStatic()

此函數用于建立一個事件标志組定時器,所需要的記憶體需要使用者自行配置設定,此函數原型如下:

/* 傳回值:
   NULL:事件标志組建立失敗。
   其他值:建立成功的事件标志組句柄
*/
#if( configSUPPORT_STATIC_ALLOCATION == 1 )

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

	    configASSERT( pxEventGroupBuffer );
		//pxEventGroupBuffer 用來儲存事件标志組結構體。
		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 */
           

設定事件位

FreeRTOS 提供了 4 個函數用來設定事件标志組中事件位(标志),事件位(标志)的設定包括清零和置 1 兩種操作,這 4 個函數如下表所示:

函數 描述
xEventGroupClearBits() 将指定的事件位清零,用在任務中。
xEventGroupClearBitsFromISR() 将指定的事件位清零,用在中斷服務函數中
xEventGroupSetBits() 将指定的事件位置 1,用在任務中。
xEventGroupSetBitsFromISR() 将指定的事件位置 1,用在中斷服務函數中。

函數xEventGroupClearBits()

// 該函數同時具有查詢事件位的功能,當uxBitsToClear 為0時,傳回的是事件組的所有事件位,
/*
參數:
xEventGroup: 要操作的事件标志組的句柄。
uxBitsToClear: 要清零的事件位,比如要清除 bit3 的話就設定為 0X08。可以同時清除多個
bit,如設定為 0X09 的話就是同時清除 bit3 和 bit0。
傳回值: 
任何值: 将指定事件位清零之前的事件組值。
*/
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;
}
           

函數 xEventGroupClearBitsFromISR()

此函數為函數 xEventGroupClearBits()的中斷級版本,也是将指定的事件位(标志)清零。此函數用在中斷服務函數中,此函數原型如下:

/*
參數:
xEventGroup: 要操作的事件标志組的句柄。
uxBitsToClear: 要清零的事件位,比如要清除 bit3 的話就設定為 0X08。可以同時清除多個
bit,如設定為 0X09 的話就是同時清除 bit3 和 bit0。
傳回值: 
pdPASS: 事件位清零成功。
pdFALSE: 事件位清零失敗。
*/
	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;
	}
           

函數 xEventGroupSetBits()

設定指定的事件位為 1,此函數隻能用在任務中,不能用于中斷服務函數,此函數原型如下:

/*
參數:
xEventGroup: 要操作的事件标志組的句柄。
uxBitsToClear: 指定要置 1 的事件位,比如要将 bit3 值 1 的話就設定為 0X08。可以同時将多個 bit 置 1,如設定為 0X09 的話就是同時将 bit3 和 bit0 置 1。
傳回值: 
任何值: 在将指定事件位置 1 後的事件組值。
*/
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;
}
           

函數xEventGroupSetBitsFromISR()

此函數也用于将指定的事件位置 1,此函數是 xEventGroupSetBits()的中斷版本,用在中斷服務函數中,函數原型如下:

/*
參數:
xEventGroup: 要操作的事件标志組的句柄。
uxBitsToSet: 指定要置 1 的事件位,比如要将 bit3 值 1 的話就設定為 0X08。可以同時将
多個 bit 置 1,如設定為 0X09 的話就是同時将 bit3 和 bit0 置 1。
pxHigherPriorityTaskWoken:标記退出此函數以後是否進行任務切換,這個變量的值函數會自
動設定的,使用者不用進行設定,使用者隻需要提供一個變量來儲存
這個值就行了。當此值為 pdTRUE 的時候在退出中斷服務函數之
前一定要進行一次任務切換。
傳回值: 
pdPASS: 事件位置 1 成功。
pdFALSE: 事件位置 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;
	}
           

擷取事件标志組值

函數 描述
xEventGroupGetBits() 擷取目前事件标志組的值(各個事件位的值),用在任務中
xEventGroupGetBitsFromISR() 擷取目前事件标志組的值,用在中斷服務函數中。

函數xEventGroupGetBits()

該函數本質是一個宏,如下所示:

函數xEventGroupClearBits( xEventGroup, 0 )在上邊已經介紹過了。

函數xEventGroupGetBitsFromISR()

該函數原型如下:

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

等待指定的事件位

某個任務可能需要與多個事件進行同步,那麼這個任務就需要等待并判斷多個事件位(标 志),使用函數xEventGroupWaitBits()可以完成這個功能。調用函數以後如果任務要等待的事件位還沒有準備好(置 1 或清零)的話任務就會進入阻塞态,直到阻塞時間到達或者所等待的事件位準備好。函數原型如下:

/*
參數:
xEventGroup: 指定要等待的事件标志組。
uxBitsToWaitFor:指定要等待的事件位,比如要等待bit0和(或)bit2的時候此參數就是0X05,
如果要等待 bit0 和(或)bit1 和(或)bit2 的時候此參數就是 0X07,以此類推。
xClearOnExit: 此參數要是為 pdTRUE 的話,那麼在退出此函數之前由參數 uxBitsToWaitFor
所設定的這些事件位就會清零。如果設定位 pdFALSE 的話這些事件位就
不會改變。
xWaitForAllBits: 此參數如果設定為 pdTRUE 的話,當 uxBitsToWaitFor 所設定的這些事件
位都置 1,或者指定的阻塞時間到的時候函數 xEventGroupWaitBits()才會
傳回。當此函數為 pdFALSE 的話,隻要 uxBitsToWaitFor 所設定的這些事
件位其中的任意一個置 1 ,或者指定的阻塞時間到的話函數
xEventGroupWaitBits()就會傳回。
xTicksToWait: 設定阻塞時間,機關為節拍數。
傳回值: 
任何值: 傳回當所等待的事件位置 1 以後的事件标志組的值,或者阻塞時間到。根
據這個值我們就知道哪些事件位置 1 了。如果函數因為阻塞時間到而傳回的話那麼這個傳回值就不代表任何的含義
*/
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;
}
           

繼續閱讀