FreeRTOS 23:事件组EventGroup创建、删除、置位操作
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; // 初始化事件位为0
vListInitialise( &( 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; // 初始化事件位为0
vListInitialise( &( 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; // 设置消息 ID
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 */
函数 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;
// 断言检查:确保等待的事件位不包含控制字节,并且等待的事件位不为0
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 )
{
// 所有需要的事件位都已设置,无需阻塞
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
防止编译器警告。
-
-
返回结果:
-
返回当前的事件位值。
-