FreeRTOS 提供了事件标志组的一些相关操作函数,如下表所示:
创建一个事件组
xEventGroupCreate() 动态方式创建事件标志组
xEventGroupCreate()用于创建一个事件组,并返回对应的句柄。 要想使用该函数必须
在头文件 FreeRTOSConfig.h 定义宏 configSUPPORT_DYNAMIC_ALLOCATION 为 1(在FreeRTOS.h 中默认定义为 1) 。
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
/*** @brief 创建一个事件组。** 此函数用于创建一个新的事件组,并初始化其成员变量。* 事件组可以用来同步多个任务之间的状态。** @return 返回新创建的事件组句柄,如果内存分配失败则返回NULL。*/
EventGroupHandle_t xEventGroupCreate( void )
{EventGroup_t * pxEventBits;/* 分配事件组内存。* 偏离MISRA规则的原因如下:* pvPortMalloc() 总是确保返回的内存块符合MCU堆栈的对齐要求。* 在这种情况下,pvPortMalloc() 必须返回一个指针,该指针保证满足 EventGroup_t 结构的对齐要求* (如果跟踪下去,就是 TickType_t 类型的对齐要求,因为 EventBits_t 本身是 TickType_t 类型)。* 因此,只要堆栈对齐要求大于或等于 TickType_t 的对齐要求,转换就是安全的。* 在其他情况下,如果架构的自然字大小小于 sizeof(TickType_t),TickType_t 变量将通过两次或多次读取操作访问,* 对齐要求只是每次单独读取的对齐要求。*/pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); /*lint !e9087 !e9079 见上述注释。*/if( pxEventBits != NULL ){pxEventBits->uxEventBits = 0; // 初始化事件位为0vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); // 初始化等待事件的任务列表#if ( configSUPPORT_STATIC_ALLOCATION == 1 ){/* 如果支持静态和动态分配,则记录此事件组是动态分配的。 */pxEventBits->ucStaticallyAllocated = pdFALSE;}#endif /* configSUPPORT_STATIC_ALLOCATION */traceEVENT_GROUP_CREATE( pxEventBits ); // 调用跟踪宏记录事件组创建}else{traceEVENT_GROUP_CREATE_FAILED(); /*lint !e9063 否则分支仅用于跟踪,如果未定义跟踪宏,则不会生成代码。*/}return pxEventBits;
}#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
函数 xEventGroupCreate
,用于创建一个新的事件组。具体功能如下:
-
内存分配:使用
pvPortMalloc
分配EventGroup_t
结构体所需的内存。 -
初始化:
-
将事件位
uxEventBits
初始化为 0。 -
初始化等待事件的任务列表
xTasksWaitingForBits
。 -
如果支持静态分配,记录此事件组是动态分配的。
-
-
跟踪:
-
如果内存分配成功,调用
traceEVENT_GROUP_CREATE
记录事件组创建。 -
如果内存分配失败,调用
traceEVENT_GROUP_CREATE_FAILED
记录创建失败。
-
事件创建函数,顾名思义, 就是创建一个事件,与其他内核对象一样,都是需要先创
建才能使用的资源, FreeRTOS 给我们提供了一个创建事件的函数xEventGroupCreate(),当创建一个事件时, 系统会首先给我们分配事件控制块的内存空间,然后对该事件控制块进行基本的初始化,创建成功返回事件句柄;创建失败返回 NULL。
xEventGroupCreateStatic () 静态方式创建事件标志组
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ){EventGroup_t * pxEventBits;/* 必须提供一个 StaticEventGroup_t 对象。 */configASSERT( pxEventGroupBuffer );#if ( configASSERT_DEFINED == 1 ){/* 检查声明 StaticEventGroup_t 变量的结构体大小是否等于实际事件组结构体的大小。 */volatile size_t xSize = sizeof( StaticEventGroup_t );configASSERT( xSize == sizeof( EventGroup_t ) );} /*lint !e529 如果定义了 configASSERT(),则 xSize 会被引用。 */#endif /* configASSERT_DEFINED *//* 用户提供了静态分配的事件组 - 使用它。 */pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 !e9087 EventGroup_t 和 StaticEventGroup_t 故意别名化,以隐藏数据,并保证具有相同大小和对齐要求 - 通过 configASSERT() 检查。 */if( pxEventBits != NULL ){pxEventBits->uxEventBits = 0; // 初始化事件位为0vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); // 初始化等待事件的任务列表#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ){/* 如果同时支持静态和动态分配,则记录此事件组是静态创建的,以防以后删除事件组。 */pxEventBits->ucStaticallyAllocated = pdTRUE;}#endif /* configSUPPORT_DYNAMIC_ALLOCATION */traceEVENT_GROUP_CREATE( pxEventBits ); // 调用跟踪宏记录事件组创建}else{/* xEventGroupCreateStatic 应该只在 pxEventGroupBuffer 指向预分配(编译时分配)的 StaticEventGroup_t 变量时被调用。 */traceEVENT_GROUP_CREATE_FAILED(); /*lint !e9063 否则分支仅用于跟踪,如果未定义跟踪宏,则不会生成代码。*/}return pxEventBits;}#endif /* configSUPPORT_STATIC_ALLOCATION */
函数 xEventGroupCreateStatic
,用于创建一个静态事件组。具体功能如下:
-
参数检查:
-
检查传入的
pxEventGroupBuffer
是否为NULL
,并进行断言。 -
检查
StaticEventGroup_t
和EventGroup_t
的大小是否一致,确保结构体的兼容性。
-
-
内存分配:
-
使用传入的
pxEventGroupBuffer
作为事件组的内存。
-
-
初始化:
-
将事件位
uxEventBits
初始化为 0。 -
初始化等待事件的任务列表
xTasksWaitingForBits
。 -
如果支持动态分配,记录此事件组是静态分配的。
-
-
跟踪:
-
如果内存分配成功,调用
traceEVENT_GROUP_CREATE
记录事件组创建。 -
如果内存分配失败,调用
traceEVENT_GROUP_CREATE_FAILED
记录创建失败。
-
事件删除函数vEventGroupDelete()
在很多场合,某些事件只用一次的,就好比在事件应用场景说的危险机器的启动,假
如各项指标都达到了,并且机器启动成功了,那这个事件之后可能就没用了,那就可以进行销毁了。想要删除事件怎么办? FreeRTOS 给我们提供了一个删除事件的函数——
vEventGroupDelete(),使用它就能将事件进行删除了。当系统不再使用事件对象时,可以通过删除事件对象控制块来释放系统资源 。
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
{EventGroup_t * pxEventBits = xEventGroup;const List_t * pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits );vTaskSuspendAll();{traceEVENT_GROUP_DELETE( xEventGroup ); // 调用跟踪宏记录事件组删除while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 ){/* 解阻塞任务,返回0,因为事件列表正在被删除,因此不可能有任何位被设置。 */configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) );vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET );}#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ){/* 事件组只能动态分配 - 释放内存。 */vPortFree( pxEventBits );}#elif ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ){/* 事件组可能是静态或动态分配的,因此在尝试释放内存之前进行检查。 */if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE ){vPortFree( pxEventBits );}else{mtCOVERAGE_TEST_MARKER(); // 覆盖测试标记}}#endif /* configSUPPORT_DYNAMIC_ALLOCATION */}( void ) xTaskResumeAll(); // 恢复所有任务
}
函数 vEventGroupDelete
,用于删除一个事件组。具体功能如下:
-
参数解析:
-
将
EventGroupHandle_t
类型的xEventGroup
转换为EventGroup_t
类型的指针pxEventBits
。 -
获取等待事件的任务列表
pxTasksWaitingForBits
。
-
-
任务调度暂停:
-
调用
vTaskSuspendAll
暂停任务调度,确保在删除事件组时不会有任务干扰。
-
-
跟踪:
-
调用
traceEVENT_GROUP_DELETE
记录事件组删除。
-
-
解阻塞任务:
-
遍历等待事件的任务列表,逐个解阻塞任务,并返回0,因为事件列表正在被删除,因此不可能有任何位被设置。
-
-
内存释放:
-
根据配置选项
configSUPPORT_DYNAMIC_ALLOCATION
和configSUPPORT_STATIC_ALLOCATION
,决定是否释放事件组的内存:-
如果只支持动态分配,直接释放内存。
-
如果同时支持静态和动态分配,检查事件组是否是动态分配的,如果是则释放内存,否则不做任何操作。
-
-
-
恢复任务调度:
-
调用
xTaskResumeAll
恢复任务调度。
-
vEventGroupDelete()用于删除由函数 xEventGroupCreate()创建的事件组, 只有被创建成功的事件才能被删除,但是需要注意的是该函数不允许在中断里面使用。 当事件组被删除之后,阻塞在该事件组上的任务都会被解锁,并向等待事件的任务返回事件组的值为 0。
事件组置位函数
xEventGroupSetBits()事件组置位函数
xEventGroupSetBits()用于置位事件组中指定的位, 当位被置位之后,阻塞在该位上的任务将会被解锁。 使用该函数接口时,通过参数指定的事件标志来设定事件的标志位,然后遍历等待在事件对象上的事件等待列表,判断是否有任务的事件激活要求与当前事件对象标志值匹配,如果有,则唤醒该任务。简单来说,就是设置我们自己定义的事件标志位为 1,并且看看有没有任务在等待这个事件,有的话就唤醒它。
注意的是该函数不允许在中断中使用。
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet )
{ListItem_t * pxListItem, * pxNext;ListItem_t const * pxListEnd;List_t const * pxList;EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;EventGroup_t * pxEventBits = xEventGroup;BaseType_t xMatchFound = pdFALSE;/* 检查用户是否试图设置内核自身使用的位。 */configASSERT( xEventGroup );configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );pxList = &( pxEventBits->xTasksWaitingForBits );pxListEnd = listGET_END_MARKER( pxList ); /* lint !e826 !e740 !e9087 使用 mini list 结构作为列表结束标志,以节省 RAM。这是有效的。 */vTaskSuspendAll();{traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); // 调用跟踪宏记录设置事件位pxListItem = listGET_HEAD_ENTRY( pxList );/* 设置位。 */pxEventBits->uxEventBits |= uxBitsToSet;/* 检查新的位值是否应解除阻塞任何任务。 */while( pxListItem != pxListEnd ){pxNext = listGET_NEXT( pxListItem );uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );xMatchFound = pdFALSE;/* 分离等待的位和控制位。 */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{/* 需要所有位都被设置,但并非所有位都被设置。 */}if( xMatchFound != pdFALSE ){/* 位匹配。退出时是否应清除这些位? */if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 ){uxBitsToClear |= uxBitsWaitedFor;}else{mtCOVERAGE_TEST_MARKER(); // 覆盖测试标记}/* 在从事件列表中移除任务之前,将实际的事件标志值存储在任务的事件列表项中。设置 eventUNBLOCKED_DUE_TO_BIT_SET 位,以便任务知道它是由于所需的位匹配而解除阻塞,而不是因为超时。 */vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );}/* 移动到下一个列表项。注意这里不使用 pxListItem->pxNext,因为列表项可能已被从事件列表中移除并插入到就绪/待读取列表中。 */pxListItem = pxNext;}/* 清除当控制字中的 eventCLEAR_EVENTS_ON_EXIT_BIT 位被设置时匹配的位。 */pxEventBits->uxEventBits &= ~uxBitsToClear;}( void ) xTaskResumeAll(); // 恢复所有任务return pxEventBits->uxEventBits; // 返回当前的事件位
}
函数 xEventGroupSetBits
,用于设置事件组中的位。具体功能如下:
-
参数解析:
-
将
EventGroupHandle_t
类型的xEventGroup
转换为EventGroup_t
类型的指针pxEventBits
。 -
获取等待事件的任务列表
pxList
和列表结束标志pxListEnd
。
-
-
2参数检查:
-
检查
xEventGroup
是否为NULL
。 -
检查
uxBitsToSet
是否包含内核自身使用的位。
-
-
任务调度暂停:
-
调用
vTaskSuspendAll
暂停任务调度,确保在设置事件位时不会有任务干扰。
-
-
跟踪:
-
调用
traceEVENT_GROUP_SET_BITS
记录设置事件位。
-
-
设置位:
-
将
uxBitsToSet
设置到事件组的uxEventBits
中。
-
-
检查任务:
-
遍历等待事件的任务列表,逐个检查任务等待的位是否与新设置的位匹配。
-
如果匹配,根据控制位决定是否清除这些位,并解除任务的阻塞。
-
-
清除位:
-
清除当控制字中的
eventCLEAR_EVENTS_ON_EXIT_BIT
位被设置时匹配的位。
-
-
恢复任务调度:
-
调用
xTaskResumeAll
恢复任务调度。
-
-
返回结果:
-
返回当前的事件位。
-
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 );/* 调用 xTimerPendFunctionCallFromISR 函数,将设置事件位的操作挂起到一个定时器回调函数中执行。参数包括回调函数指针、事件组句柄、要设置的位以及一个指向更高优先级任务唤醒标志的指针。注意:lint 工具警告可以忽略,因为强制转换为 void* 是安全的,回调函数会将其转换回原始类型。 */xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */return xReturn;}#endif /* if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */
函数 xEventGroupSetBitsFromISR
,用于从中断服务例程(ISR)中设置事件组中的位。具体功能如下:
-
条件编译:
-
只有在
configUSE_TRACE_FACILITY
、INCLUDE_xTimerPendFunctionCall
和configUSE_TIMERS
均为 1 的情况下,才会编译这段代码。
-
-
函数声明:
-
BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t * pxHigherPriorityTaskWoken)
:-
xEventGroup
:事件组句柄。 -
uxBitsToSet
:要设置的位。 -
pxHigherPriorityTaskWoken
:指向一个标志的指针,用于指示是否有更高优先级的任务被唤醒。
-
-
-
记录操作:
-
traceEVENT_GROUP_SET_BITS_FROM_ISR(xEventGroup, uxBitsToSet)
:调用跟踪宏记录从 ISR 设置事件位的操作。
-
-
调用定时器回调函数:
-
xTimerPendFunctionCallFromISR(vEventGroupSetBitsCallback, (void *) xEventGroup, (uint32_t) uxBitsToSet, pxHigherPriorityTaskWoken)
:-
vEventGroupSetBitsCallback
:回调函数指针,用于实际设置事件位。 -
(void *) xEventGroup
:事件组句柄,强制转换为void *
类型。 -
(uint32_t) uxBitsToSet
:要设置的位,强制转换为uint32_t
类型。 -
pxHigherPriorityTaskWoken
:指向一个标志的指针,用于指示是否有更高优先级的任务被唤醒。 -
注意:lint 工具警告可以忽略,因为强制转换为
void *
是安全的,回调函数会将其转换回原始类型。
-
-
-
返回结果:
-
返回
xTimerPendFunctionCallFromISR
的结果,表示操作是否成功。
-
#if ( INCLUDE_xTimerPendFunctionCall == 1 )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; // 设置消息 IDxMessage.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 */
函数 xTimerPendFunctionCallFromISR
,用于从中断服务例程(ISR)中挂起一个函数调用。具体功能如下:
-
条件编译:
-
只有在
INCLUDE_xTimerPendFunctionCall
为 1 的情况下,才会编译这段代码。
-
-
函数声明:
-
BaseType_t xTimerPendFunctionCallFromISR(PendedFunction_t xFunctionToPend, void * pvParameter1, uint32_t ulParameter2, BaseType_t * pxHigherPriorityTaskWoken)
:-
xFunctionToPend
:要挂起的回调函数指针。 -
pvParameter1
:传递给回调函数的第一个参数。 -
ulParameter2
:传递给回调函数的第二个参数。 -
pxHigherPriorityTaskWoken
:指向一个标志的指针,用于指示是否有更高优先级的任务被唤醒。
-
-
-
消息初始化:
-
创建一个
DaemonTaskMessage_t
类型的消息结构体xMessage
。 -
设置消息的
xMessageID
为tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR
,表示这是一个从中断服务例程执行回调函数的命令。 -
设置消息的回调函数、第一个参数和第二个参数。
-
-
发送消息:
-
调用
xQueueSendFromISR
函数,将消息发送到定时器队列xTimerQueue
。 -
xQueueSendFromISR
函数的第三个参数pxHigherPriorityTaskWoken
用于指示是否有更高优先级的任务被唤醒。
-
-
记录操作:
-
调用
tracePEND_FUNC_CALL_FROM_ISR
宏记录从中断服务例程挂起函数调用的操作。
-
-
返回结果:
-
返回
xQueueSendFromISR
的结果,表示操作是否成功。
-
xEventGroupSync()设置事件标志位,并等待事件标志位
此函数一般用于多任务同步,其中每个任务都必须等待其他任务达到同步点,然后才能继续执行。
/*** @brief 事件组同步函数* * 该函数用于在多个任务之间进行同步。它会设置指定的事件位,并等待指定的事件位被设置。* 如果在指定的超时时间内没有完成同步,则返回当前的事件位值。* * @param xEventGroup 事件组句柄* @param uxBitsToSet 要设置的事件位* @param uxBitsToWaitFor 要等待的事件位* @param xTicksToWait 等待的最长时间(ticks)* @return EventBits_t 返回当前的事件位值*/
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,const EventBits_t uxBitsToSet,const EventBits_t uxBitsToWaitFor,TickType_t xTicksToWait )
{EventBits_t uxOriginalBitValue, uxReturn;EventGroup_t * pxEventBits = xEventGroup;BaseType_t xAlreadyYielded;BaseType_t xTimeoutOccurred = pdFALSE;// 断言检查:确保等待的事件位不包含控制字节,并且等待的事件位不为0configASSERT( ( 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 ){// 所有需要的事件位都已设置,无需阻塞uxReturn = ( uxOriginalBitValue | uxBitsToSet );// 清除已经设置的事件位pxEventBits->uxEventBits &= ~uxBitsToWaitFor;// 设置等待时间为0,表示不需要阻塞xTicksToWait = 0;}else{// 如果设置了等待时间if( xTicksToWait != ( TickType_t ) 0 ){// 记录事件组同步阻塞traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor );// 将任务添加到等待事件位的列表中,并进入阻塞状态vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait );// 初始化返回值,避免编译器警告uxReturn = 0;}else{// 需要的事件位未设置,且没有指定等待时间,直接返回当前事件位值uxReturn = pxEventBits->uxEventBits;xTimeoutOccurred = pdTRUE;}}}// 恢复任务调度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( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor ){// 清除已经设置的事件位pxEventBits->uxEventBits &= ~uxBitsToWaitFor;}else{// 覆盖测试标记mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();xTimeoutOccurred = pdTRUE;}else{// 任务因事件位被设置而解除阻塞}// 清除控制字节uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;}// 记录事件组同步结束traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred );// 防止编译器警告( void ) xTimeoutOccurred;return uxReturn;
}
-
参数检查:
-
使用
configASSERT
宏检查uxBitsToWaitFor
是否包含控制字节,并且不为0。 -
如果配置了调度器状态检查或定时器支持,则进一步检查调度器是否挂起。
-
-
暂停任务调度:
-
调用
vTaskSuspendAll()
暂停所有任务调度,确保在修改事件组时不会发生竞态条件。
-
-
获取当前事件位值:
-
从
pxEventBits->uxEventBits
获取当前的事件位值。
-
-
设置指定的事件位:
-
调用
xEventGroupSetBits(xEventGroup, uxBitsToSet)
设置指定的事件位。
-
-
检查设置后的事件位是否满足等待条件:
-
如果
( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor
,则所有需要的事件位都已设置,无需阻塞。 -
清除已经设置的事件位。
-
设置等待时间为0,表示不需要阻塞。
-
返回当前的事件位值。
-
-
处理未满足条件的情况:
-
如果设置了等待时间 (
xTicksToWait != 0
):-
记录事件组同步阻塞。
-
将任务添加到等待事件位的列表中,并进入阻塞状态。
-
初始化返回值,避免编译器警告。
-
-
如果没有设置等待时间 (
xTicksToWait == 0
):-
直接返回当前事件位值,并设置超时标志。
-
-
-
恢复任务调度:
-
调用
xTaskResumeAll()
恢复任务调度。 -
检查是否发生了上下文切换,如果没有,手动进行一次上下文切换。
-
-
处理任务阻塞等待:
-
如果设置了等待时间:
-
任务阻塞等待所需的事件位被设置。
-
如果任务因事件位被设置而解除阻塞,返回当前事件位值。
-
如果任务超时,返回当前事件位值,并清除已经设置的事件位。
-
-
-
清除控制字节:
-
清除返回值中的控制字节。
-
-
记录事件组同步结束:
-
记录事件组同步结束。
-
-
防止编译器警告:
-
使用
(void) xTimeoutOccurred
防止编译器警告。
-
-
返回结果:
-
返回当前的事件位值。
-