FreeRTOS 24:事件组EventGroup等待、清零、获取操作

等待事件标志位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 函数用于等待事件组中的特定事件位被设置。具体功能和步骤如下:

  1. 参数检查

    • 检查传入的事件组是否有效。

    • 检查等待的位是否包含内核自身使用的位。

    • 检查至少请求了一个位。

    • 如果配置了 INCLUDE_xTaskGetSchedulerState 或 configUSE_TIMERS,则检查调度器是否暂停并且等待时间不为零。

  2. 任务调度暂停

    • 暂停任务调度,防止其他任务干扰。

  3. 检查当前事件位

    • 获取当前事件位。

    • 检查等待条件是否已经满足。

      • 如果满足,直接返回当前事件位,并根据需要清除这些位。

      • 如果不满足且未指定阻塞时间,直接返回当前事件位并设置超时标志。

      • 如果不满足且指定了阻塞时间,将任务加入等待队列。

  4. 任务阻塞

    • 根据需要设置控制位,记录任务的行为。

    • 将任务加入等待队列,等待所需事件位被设置。

    • 恢复任务调度。

  5. 任务唤醒

    • 如果任务因事件位被设置而唤醒,返回当前事件位。

    • 如果任务因超时而唤醒,返回当前事件位并设置超时标志。

  6. 清除控制位

    • 返回前清除控制位,防止影响后续操作。

清零事件标志位

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,用于清除事件组中的指定位。

  1. 参数检查

    • configASSERT( xEventGroup );:检查事件组句柄是否有效。
    • configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 );:确保要清除的位不包含内核保留的位。
  2. 进入临界区

    • taskENTER_CRITICAL();:进入临界区,防止多任务环境下的并发问题。
  3. 记录当前事件组值

    • uxReturn = pxEventBits->uxEventBits;:在清除位之前,记录当前事件组的值。
  4. 清除指定位

    • pxEventBits->uxEventBits &= ~uxBitsToClear;:通过按位与操作清除指定的位。
  5. 退出临界区

    • taskEXIT_CRITICAL();:退出临界区。
  6. 返回结果

    • 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)中清除事件组中的指定位。具体功能如下:

  1. 参数检查:无显式的参数检查,但通过调用 xTimerPendFunctionCallFromISR 来确保安全。

  2. 记录事件:记录从中断服务例程清除事件组位的操作。

  3. 挂起函数调用:使用 xTimerPendFunctionCallFromISR 将清除位的操作挂起到调度器,以便在合适的时机执行。

  4. 返回结果:返回 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)中获取事件组的当前位。具体功能如下:

  1. 保存中断状态:使用 portSET_INTERRUPT_MASK_FROM_ISR 保存当前的中断状态,防止中断嵌套。

  2. 获取事件组位:从事件组中读取当前的位值。

  3. 恢复中断状态:使用 portCLEAR_INTERRUPT_MASK_FROM_ISR 恢复中断状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦梨甜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值