等待事件标志位xEventGroupWaitBits()
既然标记了事件的发生,那么我怎么知道他到底有没有发生,这也是需要一个函数来获 取 事 件 是 否 已 经 发 生 , FreeRTOS 提 供 了 一 个 等 待 指 定 事 件 的 函 数 — —
xEventGroupWaitBits(),通过这个函数, 任务可以知道事件标志组中的哪些位,有什么事件发生了, 然后通过 “逻辑与”、“逻辑或”等操作对感兴趣的事件进行获取,并且这个函数实现了等待超时机制, 当且仅当任务等待的事件发生时,任务才能获取到事件信息。在这段时间中,如果事件一直没发生,该任务将保持阻塞状态以等待事件发生。当其它任务或中断服务程序往其等待的事件设置对应的标志位,该任务将自动由阻塞态转为就绪态。当任务等待的时间超过了指定的阻塞时间,即使事件还未发生,任务也会自动从阻塞态转移为就绪态。这样子很有效的体现了操作系统的实时性,如果事件正确获取(等待到) 则返回对应的事件标志位,由用户判断再做处理, 因为在事件超时的时候也会返回一个不能确定的事件值,所以需要判断任务所等待的事件是否真的发生。
EventGroupWaitBits()用于获取事件组中的一个或多个事件发生标志, 当要读取的事件标 志 位 没 有 被 置 位 时 任 务 将 进 入 阻 塞 等 待 状 态 。
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait )
{
EventGroup_t * pxEventBits = xEventGroup;
EventBits_t uxReturn, uxControlBits = 0;
BaseType_t xWaitConditionMet, xAlreadyYielded;
BaseType_t xTimeoutOccurred = pdFALSE;
// 检查用户是否尝试等待内核自身使用的位,并且至少请求了一个位
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;
// 检查等待条件是否已经满足
xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );
if( xWaitConditionMet != pdFALSE )
{
// 等待条件已经满足,无需阻塞
uxReturn = uxCurrentEventBits;
xTicksToWait = ( TickType_t ) 0;
// 如果需要,清除等待的位
if( xClearOnExit != pdFALSE )
{
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xTicksToWait == ( TickType_t ) 0 )
{
// 等待条件未满足,但未指定阻塞时间,直接返回当前值
uxReturn = uxCurrentEventBits;
xTimeoutOccurred = pdTRUE;
}
else
{
// 任务将阻塞等待所需位被设置
// 使用控制位记录此调用的行为
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();
}
// 将调用任务等待的位存储在任务的事件列表项中,以便内核知道何时找到匹配项
vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
// 这是过时的,但在某些编译器中如果不这样做会生成警告
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();
}
// 任务阻塞等待所需位被设置,此时要么所需位已被设置,要么阻塞时间已到期
// 如果所需位已被设置,它们将存储在任务的事件列表项中,现在应检索并清除
uxReturn = uxTaskResetEventItemValue();
if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
{
taskENTER_CRITICAL();
{
// 任务超时,返回当前事件位值
uxReturn = pxEventBits->uxEventBits;
// 可能在任务离开阻塞状态和再次运行之间更新了事件位
if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
{
if( xClearOnExit != pdFALSE )
{
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
xTimeoutOccurred = pdTRUE;
}
taskEXIT_CRITICAL();
}
else
{
// 任务因位被设置而解除阻塞
}
// 任务阻塞,因此可能设置了控制位
uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
}
traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );
// 防止编译器警告
( void ) xTimeoutOccurred;
return uxReturn;
}
xEventGroupWaitBits
函数用于等待事件组中的特定事件位被设置。具体功能和步骤如下:
-
参数检查:
-
检查传入的事件组是否有效。
-
检查等待的位是否包含内核自身使用的位。
-
检查至少请求了一个位。
-
如果配置了
INCLUDE_xTaskGetSchedulerState
或configUSE_TIMERS
,则检查调度器是否暂停并且等待时间不为零。
-
-
任务调度暂停:
-
暂停任务调度,防止其他任务干扰。
-
-
检查当前事件位:
-
获取当前事件位。
-
检查等待条件是否已经满足。
-
如果满足,直接返回当前事件位,并根据需要清除这些位。
-
如果不满足且未指定阻塞时间,直接返回当前事件位并设置超时标志。
-
如果不满足且指定了阻塞时间,将任务加入等待队列。
-
-
-
任务阻塞:
-
根据需要设置控制位,记录任务的行为。
-
将任务加入等待队列,等待所需事件位被设置。
-
恢复任务调度。
-
-
任务唤醒:
-
如果任务因事件位被设置而唤醒,返回当前事件位。
-
如果任务因超时而唤醒,返回当前事件位并设置超时标志。
-
-
清除控制位:
-
返回前清除控制位,防止影响后续操作。
-
清零事件标志位
xEventGroupClearBits()与 xEventGroupClearBitsFromISR()都是用于清除事件组指定的位, 如果在获取事件的时候没有将对应的标志位清除, 那么就需要用这个函数来进行显式清除, xEventGroupClearBits()函数不能在中断中使用,而是由具有中断保护功能 的xEventGroupClearBitsFromISR() 来代替,中断清除事件标志位的操作在守护任务(也叫定时 器 服 务 任 务 ) 里 面 完 成 。 守 护 进 程 的 优 先 级 由 FreeRTOSConfig.h 中 的 宏configTIMER_TASK_PRIORITY 来 定 义 。
xEventGroupClearBits()清零事件标志位
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear )
{
EventGroup_t * pxEventBits = xEventGroup;
EventBits_t uxReturn;
/* 检查用户是否尝试清除内核自身使用的位 */
configASSERT( xEventGroup );
configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
taskENTER_CRITICAL();
{
traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear );
/* 返回值是清除位之前的事件组值 */
uxReturn = pxEventBits->uxEventBits;
/* 清除指定的位 */
pxEventBits->uxEventBits &= ~uxBitsToClear;
}
taskEXIT_CRITICAL();
return uxReturn;
}
函数 xEventGroupClearBits
,用于清除事件组中的指定位。
-
参数检查:
configASSERT( xEventGroup );
:检查事件组句柄是否有效。configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
:确保要清除的位不包含内核保留的位。
-
进入临界区:
taskENTER_CRITICAL();
:进入临界区,防止多任务环境下的并发问题。
-
记录当前事件组值:
uxReturn = pxEventBits->uxEventBits;
:在清除位之前,记录当前事件组的值。
-
清除指定位:
pxEventBits->uxEventBits &= ~uxBitsToClear;
:通过按位与操作清除指定的位。
-
退出临界区:
taskEXIT_CRITICAL();
:退出临界区。
-
返回结果:
return uxReturn;
:返回清除位之前的事件组值。
xEventGroupClearBitsFromISR()在中断中清零事件标志位
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
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 ); /*lint !e9087 无法避免将指针强制转换为 void*,因为这是一个通用的回调函数,不是特定于此用例的。回调会将指针重新转换为原始类型,因此是安全的。 */
// 返回挂起结果
return xReturn;
}
#endif /* if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */
函数 xEventGroupClearBitsFromISR
,用于在中断服务例程(ISR)中清除事件组中的指定位。具体功能如下:
-
参数检查:无显式的参数检查,但通过调用
xTimerPendFunctionCallFromISR
来确保安全。 -
记录事件:记录从中断服务例程清除事件组位的操作。
-
挂起函数调用:使用
xTimerPendFunctionCallFromISR
将清除位的操作挂起到调度器,以便在合适的时机执行。 -
返回结果:返回
xTimerPendFunctionCallFromISR
的结果,指示是否成功挂起函数调用。
获取事件组中各事件标志位的值xEventGroupGetBits()
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
{
UBaseType_t uxSavedInterruptStatus;
EventGroup_t const * const pxEventBits = xEventGroup;
EventBits_t uxReturn;
// 保存当前的中断状态,防止中断嵌套
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
// 从事件组中读取当前的位值
uxReturn = pxEventBits->uxEventBits;
}
// 恢复中断状态
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
// 返回事件组的当前位值
return uxReturn;
} /*lint !e818 EventGroupHandle_t 是一个在其他函数中使用的 typedef,因此不能是指向常量的指针。*//*lint !e818 EventGroupHandle_t is a typedef used in other functions to so can't be pointer to const. */
函数 xEventGroupGetBitsFromISR
,用于在中断服务例程(ISR)中获取事件组的当前位。具体功能如下:
-
保存中断状态:使用
portSET_INTERRUPT_MASK_FROM_ISR
保存当前的中断状态,防止中断嵌套。 -
获取事件组位:从事件组中读取当前的位值。
-
恢复中断状态:使用
portCLEAR_INTERRUPT_MASK_FROM_ISR
恢复中断状态。