首页 > 其他分享 >FreeRTOS 原理 --- 事件组

FreeRTOS 原理 --- 事件组

时间:2023-10-03 15:12:41浏览次数:43  
标签:set FreeRTOS pxEventBits --- uxEventBits task 事件 原理 bits

简介

信号量,队列。它们都具有一下特性:

  • 它们允许在阻塞态下等待单个事件的发送。
  • 它们在事件发送时可以取消阻塞的单个任务

事件组是FreeRTOS提供另一种事件传递的方法,它与队列和信号量的不同点:

  • 时间组允许任务在阻塞态下等待一个或多个事件。
  • 事件发生时,事件组将取消阻塞等待同一事件或事件组合的所有任务。

 

实际事件发生与否记录器:

事件组描述符的 uxEventBits 用于指示是否有事件发生。每一个事件占1位。如果某个位为1,则表示该位表示的事件已发生。如果某个位为0,则表示该位表示的事件未发生。

期望事件发生记录器:

任务描述符的成员变量 xEventListItem 作为一个链表项,此链表项的成员变量 xItemValue 记录了此任务等待哪些事件,等待事件的动作,比如是否等待所有事件。

事件组相关的API

xEventGroupCreate()

xEventGroupSetBits()

xEventGroupSetBitsFromISR()

xEventGroupWaitBits()

xEventGroupSync()

代码分析

xEventGroupCreate()

事件组的描述符如下:

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;
 

 主要就是初始化 uxEventBits 为0,表示还没事件发生;初始化 xTaskWaitingForBits 链表,此链表用于记录等待事件的任务的xEventListItem,此值记录任务等待哪几个事件,等待任一事件还是所有事件

xEventGroupSetBits()

首先宏 eventEVENT_BITS_CONTROL_BYTES 规定了一个事件组描述符支持的事件数,也就是 uxEventBits 哪几位用于表示事件发生与否,比如宏为0xFF000000表示uxEventBits的最低24位可用于表示事件,事件组描述符的高8位不用

每个等待事件任务的xEventListItem也有一个32bit的值,低24位表示等待哪些事件,高8位表示等待事件的动作,比如是否等待所有事件、唤醒任务后是否清事件组描述符的事件

    #define eventCLEAR_EVENTS_ON_EXIT_BIT    0x01000000UL
    #define eventUNBLOCKED_DUE_TO_BIT_SET    0x02000000UL
    #define eventWAIT_FOR_ALL_BITS           0x04000000UL
    #define eventEVENT_BITS_CONTROL_BYTES    0xff000000UL

 

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. */
                vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); // 唤醒等待事件的任务,把发生的事件记录到任务的 xEventListItem.xItemValue
            }

            /* 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()

#define xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken )

    BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken )
    {
    DaemonTaskMessage_t xMessage;
    BaseType_t xReturn;

        /* Complete the message with the function parameters and post it to the
        daemon task. */
        xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR;
        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;
    }

 

设置事件组描述符的事件,唤醒的等待事件任务可能不止一个,所以此函数指示向定时器任务相关的队列发送一条消息,让定时器任务去执行唤醒任务,从而保证中断函数不会允许太长事件。 既然中断不会直接设置事件组描述符,关于事件组的API都不需要关闭中断,只需要关闭调度器。

xEventGroupWaitBits()

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;
            xTimeoutOccurred = pdTRUE;
        }
        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();
                }
                xTimeoutOccurred = pdTRUE;
            }
            taskEXIT_CRITICAL();
        }
        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 );

    /* Prevent compiler warnings when trace macros are not used. */
    ( void ) xTimeoutOccurred;

    return uxReturn;
}

 

xEventGroupSync()

此函数允许几个任务等待相同的事件,事件发生,唤醒所有任务

 

标签:set,FreeRTOS,pxEventBits,---,uxEventBits,task,事件,原理,bits
From: https://www.cnblogs.com/god-of-death/p/17741145.html

相关文章

  • 论文解读:HybridCR: weakly-supervised 3D point cloud semantic segmentation via hybr
    HybridCR:weakly-supervised3Dpointcloudsemanticsegmentationviahybridcontrastiveregularization基于混合对比学习正则化约束的增强方法,Li等人(2022a)使用极少标注(0.03%)在室内点云数据集上获得的分割精度为全监督方法的78.3%。是第一个利用点一致性并以端到端方式采用......
  • GSM7816工作原理分析(新手笔记)
    7816协议初学者刚上手7816,感觉有些吃力,但经过几天的死磕,终于也是大概了解整体的框架,所以打算记录一下理解过程。首先要明白几个大的IC卡功能,无非就是接口设备(读卡器)与卡片的通信过程所需要的几个重要功能。上电复位:在卡片接触读卡器后会自动通电,同时需要对卡片进行一次冷复位,防......
  • LangChain大模型应用开发指南-传统编程范式思维的应用
    LangChain大模型应用开发指南-传统编程范式思维的应用上节课,我带领小伙伴们完成了baichuan2量化模型的OpenAI标准接口封装,并完成LangChain对大模型的调用与测试。没有看过的小伙伴可以点击链接查看:AI课程合集今天我们将正式开始LangChain大模型应用开发课程。组件总览上图......
  • 题解 [CSP-S 2021] 括号序列
    题目链接对于括号题,基本是栈匹配没有匹配的左括号和区间\(dp\)两个方向。这道题括号序列并不确定,只能用区间\(dp\)搞。如果直接设\(f_{l,r}\)表示\(l\simr\)的合法括号序列,那么由区间\(dp\)的套路可知,需要枚举中间点进行合并,那么\(()()()\)的情况就会出问题,原因是......
  • Module build failed (from ./node_modules/css-loader/dist/cjs.js): CssSyntaxError
    问题描述在webpack的时候报错ERRORin./packages/theme-chalk/mixins/mixins.scss(./node_modules/css-loader/dist/cjs.js!./packages/theme-chalk/mixins/mixins.scss)Modulebuildfailed(from./node_modules/css-loader/dist/cjs.js):CssSyntaxError(14:8)......
  • Bash-条件判断(if)
    单分支if条件语句if[条件判断式];thenpassfi#或者if[条件判断式]thenpassfi 例子:检测磁盘(分支)使用百分比,超过10%,则echo 双分支if条件语句if[条件判断式]thenpasselsepassfi例子1:备份etc文件例子2:判......
  • 【231003CHEM-1】电解硫酸铜溶液 化学方程式虽简单 但稳定实验还需要多次尝试
    电解硫酸铜用以湿法炼铜或是制备稀硫酸,书本上的反应方程式倒是很简单,具体如下:阴极:2Cu2++4e-==2Cu阳极:2H2O-4e-==4H++O2总方程式:2Cu2++2H2O=通电=2Cu+O2+4H+或2CuSO4+2H2O=通电=2Cu+O2+2H2SO4(以上公式来自https://qb.zuoyebang.com/xfe-question/questi......
  • SpringBoot 配置多数据源 dynamic-datasource(多库)
    1.Maven包com.baomidoudynamic-datasource-spring-boot-starter3.1.12.配置文件###MySQLdruid多数据源配置(纯粹多库)####去除durid配置spring.autoconfigure.exclude=com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure#指定默认数据源(必须配置)spring.d......
  • spring boot 多数据源切换(dynamic-datasource-spring-boot-starter)
    官网 https://dynamic-datasource.com/guide/集成MybatisPlus https://dynamic-datasource.com/guide/integration/MybatisPlus.html#基础介绍自动读写分离 https://dynamic-datasource.com/guide/advance/Read-Write-Separation.html本地事物(不支持spring事务),使用@DSTransac......
  • [题解]CF1748C Zero-Sum Prefixes
    UPD23.10.3更新的对思路的描述,以及代码。思路对于每一个\(a_i=0\),如果我们将它变为\(x\),都可以直接将\(i\simn\)位置上的前缀和加\(x\)。设\(a_j\)是\(a_i\)后第一个\(0\),那么,在\(j\)时同样有上述规律。所以,我们只需在\(i\)时考虑,\(i\sim(j-1)\)的贡......