• FreeRTOS_事件标志组


    FreeRTOS事件标志组

    事件标志组简介

    1. 事件位(事件标志)

    事件位用于表明某个事件是否发生,事件位通常用作事件标志,比如下面的几个例子:

      当收到一条消息并且把这条消息处理掉以后就可以将某个位(标志)置1,当队列中没有消息需要处理的时候就可以将这个位(标志)置0。

      当把队列中的消息通过网络发送输出以后就可以将某个位(标志)置1,当没有数据需要从网络发送出去的话就将这个位(标志)置0。

      现在需要向网络中发送一个心跳信息,将某个位(标志)置1。现在不需要项网络中发送心跳信息,这个位(标志)置0。

    2. 事件组

    一个事件组就是一组的事件位,事件组中的事件位通过位编号来访问,同样,以上面列出的三个例子为例:

      事件标志组bit0 表示队列中的消息是否处理掉。

      事件标志组bit1 表示是否有纤细需要从网络中发送出去。

      事件标志组bit2 表示现在是否需要向网路发送心跳信息。

    3. 事件标志组和事件位的数据类型

      事件标志组的数据类型为 EventBits_t,当configUSE_16_BIT_TICKS为1的时候,事件标志组可以存储8个事件位,当configUSE_16_BIT_TICKS为0的时候,事件标志组存储24个事件位。

      事件标志组中所有事件位都存储在一个无符号的EventBits_t类型的变量中,EventBits_t在event_groups.h中有如下定义:

    typedef TickType_t EventBits_t;

      数据类型TickType_t在文件portmacro.h中有如下定义:

    #if( configUSE_16_BIT_TICKS == 1 )
        typedef uint16_t TickType_t;
        #define portMAX_DELAY ( TickType_t ) 0xffff
    #else
        typedef uint32_t TickType_t;
        #define portMAX_DELAY ( TickType_t ) 0xffffffffUL
    
        /* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
        not need to be guarded with a critical section. */
        #define portTICK_TYPE_IS_ATOMIC 1
    #endif

      可以看出当 configUSE_16_BIT_TICKS 为0的时候,TickType_t是个32位的数据类型,因此EventBits_t也是个32位的数据类型。EventBits_t类型的变量可以存储24个事件位,另外的那高8位有其他用。事件位0存放在这个变量的bit0上,变量的bit1就是事件位1,以此类推。对于STM32来说,一个事件标志组最多可以存储24个事件位,如下图:

    创建事件标志组

       FreeRTOS提供了两个用于创建事件标志组的函数:

    函数 描述
    xEventGroupCreate() 使用动态方法创建事件标志组
    xEventGroupCreateStatic() 使用静态方法创建事件标志组

    1. 函数 xEventGroupCreate()

      此函数用于创建一个时间标志组,锁需要的内存通过动态内存管理方法分配。由于内部处理的原因,事件标志组可用的bit数取决于configUSE_16_BIT_TICKS,当configUSE_16_BIT_TICKS为1的时候,事件标志组有8个可用的位(bit0~bit7),当configUSE_16_BIT_TICKS为0的时候,时间标志组有24个可用的位(bit0~bit23)。EventBits_t类型的变量用来存储事件标志组中的各个事件位,函数原型如下:

    EventGroupHandle_t xEventGroupCreate( void )

    参数:

      无。

    返回值:

      NULL:事件标志组创建失败。

      其他值:创建成功的事件标志组句柄。

    2. 函数xEventGroupCreateStatic()

    此函数用于创建一个事件标志组,所需要的内存需要用于自行分配,此函数原型如下:

    EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )

    参数:

      pxEventGroupBuffer:参数指向一个StaticEventGroup_t类型的变量,用来保存时间组结构体。

    返回值:

      NULL:事件标志组创建失败。

      其他值:创建成功的事件标志组句柄。

    设置事件位

      FreeRTOS提供了4个函数用来设置事件标志组中事件位(标志),事件位的设置包括清零和置1两种操作:

    函数 描述
    xEventGroupClearBits() 将指定的事件位清零,用在任务中。
    xEventGroupClearBitsFromISR() 将指定的事件位清零,用在中断服务函数中。
    xEventGroupSetBits() 将指定的事件位置1,用在任务中。
    xEventGroupSetBitsFromISR() 将指定的事件位置1,用在中断服务函数中。

    1. 函数 xEventGroupClearBits()

      将事件标志组中指定事件位清零,此函数只能用在任务中,不能用在中断服务函数中,中断服务函数中有其他的API函数。函数原型如下:

    EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, 
    const EventBits_t uxBitsToClear )

    参数:

      xEventGroup:要操作的事件标志组的句柄。

      uxBitsToClear:要清零的事件位,比如要清除bit3的话就设置为0x08。可以同时清除多个bit,如设置0x09的话就是同时清除bit3和bit0。

    返回值:

      任何值:将指定事件位清零之前的事件组值。

    2. 函数xEventGroupClearBitsFromISR()

      此函数为xEventGroupClearBits()的中断级版本,也是将指定的事件位(标志)清零。此函数用在中断服务函数中,函数原型如下:

    BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, 
    const EventBits_t uxBitsToSet )

    参数:

      xEventGroup:要操作的事件标志组的句柄。

      uxBitsToClear:要清零的事件位,比如要清除bit3的话就设置为0x08。可以同时清除多个bit,如设置0x09的话就是同时清除bit3和bit0。

    返回值:

      pdPASS:事件位清零成功。

      pdFALSE:事件位清零失败。

    3. 函数 xEventGroupSetBits()

      设置指定的事件位为1,此函数只能用在任务中,不能用于中断服务函数。此函数原型如下:

    EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, 
    const EventBits_t uxBitsToSet )

    参数:

      xEventGroup:要操作的事件标志组句柄。

      uxBitsToSet:指定要置1的事件位,比如要将bit3 置1的话就设置为0x08。可以同时将多个bit置1,如设置为0x09的话就是同时将bit3和bit0置1。

    返回值:

      任何值:在将指定事件位置1后的事件组值。

    3. 函数xEventGroupSetBitsFromISR()

      此函数也用于将指定事件位置1,此函数是xEventGroupSetBits()的中断版本,用在中断服务函数中,函数原型如下:

    BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, 
    const EventBits_t uxBitsToSet,
    BaseType_t *pxHigherPriorityTaskWoken )

    参数:

      xEventGroup:要操作的事件标志组的句柄。

      uxBitsToClear:指定要置1的事件位,比如要将bit3置1的话就设置0x08。可以同时将多个bit置1,如设置为0x09的话就是同时将bit3和bit0置1。

      pxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换。这个变量的值,函数会自动设置,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换。

    返回值:

      pdPASS:事件位置1成功。

      pdFALSE:事件位置1失败。

    获取事件标志组值

      我们可以通过FreeRTOS提供的API函数来查询事件标志组值。FreeRTOS一共提供了两个这样的API函数。

    函数 描述
    xEventGroupGetBits() 获取当前事件标志组的值(各个事件位的值),用在任务中
    xEventGroupGetBitsFromISR() 获取当前事件标志组的值,用在中断服务函数中。

    1. 函数xEventGroupGetBits()

      此函数用于获取当前事件标志组的值,也就是各个事件位的值。此函数用在任务中,不能用在中断服务函数中。此函数是个宏,真正执行的事函数xEventGroupClearBits(),函数原型如下:

    EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup )

    参数:

      xEventGroup:要获取的事件标志组的句柄。

    返回值:

      任何值:当前时间标志组的值。

    2. 函数xEventGroupGetBitsFromISR()

      获取当前事件标志组的值,此函数是xEventGroupGetBits()的中断版本,函数原型如下:

    EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )

    参数:

      xEventGroup:要获取的事件标志组的句柄。

    返回值:

      任何值:当前事件标志组的值。

    等待指定的事件位

      某个任务可能需要与多个事件进行同步,那么这个任务就需要等待并判断多个事件位(标志),使用函数 xEventGroupWaitBits()可以完成这个功能。调用函数以后如果任务要等待的事件位还没有准备好(置1或清零)的话任务就会进入阻塞态,直到阻塞时间达到或者所等待的事件位准备好。函数原型如下:

    EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, 
    const EventBits_t uxBitsToWaitFor,
    const BaseType_t xClearOnExit,
    const BaseType_t xWaitForAllBits,
    TickType_t xTicksToWait )

    参数:

      xEventGroup:指定要等待的事件标志组。

      uxBitsToWaitFor:指定要等待的事件位,比如要等待bit0和(或)bit2的时候,此参数就是0x05,如果要等待bit0和(或)bit1和(或)bit2的时候此函数就是0x07,以此类推。

      xClearOnExit:此参数要是pdTRUE的话,那么在退出此函数之前有由参数uxBitsToWaitFor所设置的这些事件位就会清零。如果设置为pdFALSE的话这些事件位就不会改变。

      xWaitForAllBits:此参数如果设置为pdTRUE的话,当uxBitsToWaitFor所设置的这些事件位都置1,或者指定的阻塞时间到的时候函数xEventGroupWaitBits()才会返回。当此参数为pdFALSE的话,只要uxBitsToWaitFor所设置的这些事件位其中的任意一个置1,或者指定的阻塞时间到的话函数xEventGroupWaitBits()就会返回。

      xTicksToWait:设置阻塞时间,单位为节拍数。

    返回值:

      任何值:返回当所等待的事件位置1以后的事件标志组的值,或者阻塞时间到。根据这个值我们就知道哪些事件位置1了。如果函数因为阻塞时间到而返回的话,那么这个返回值就不代表任何的含义。

    事件标志组实验

    创建事件标志组、将相应的事件位置1、等待相应事件位置1的操作。

    实验设置三个任务:start_task、eventsetbit_task、eventgroup_task。

    start_task:用来创建其他两个任务,创建时间标志组。

    eventsetbit_task:通过不同按键值,将事件标志组中相应事件位置1。

    eventgroup_task:等待事件标志组中的事件位,当这些事件位都置1,执行相应的处理。

    EventGroupHandler:创建的事件标志组句柄。使用事件标志组的事件位:bit0 bit1 bit2

    任务分配:

    //任务优先级
    #define START_TASK_PRIO        1
    //任务堆栈大小    
    #define START_STK_SIZE         128  
    //任务句柄
    TaskHandle_t StartTask_Handler;
    //任务函数
    void start_task(void *pvParameters);
    
    //任务优先级
    #define EVENTSETBIT_TASK_PRIO        2
    //任务堆栈大小    
    #define EVENTSETBIT_STK_SIZE         50  
    //任务句柄
    TaskHandle_t EventSetbitTask_Handler;
    //任务函数
    void eventsetbit_task(void *pvParameters);
    
    //任务优先级
    #define EVENTGROUP_TASK_PRIO        3
    //任务堆栈大小    
    #define EVENTGROUP_STK_SIZE         50  
    //任务句柄
    TaskHandle_t EventGroupTask_Handler;
    //任务函数
    void eventgroup_task(void *pvParameters);
    
    EventGroupHandle_t EventGroupHandle;    // 事件标志组句柄
    
    // 定义事件位
    #define BIT_0    (1<<0)
    #define BIT_1    (1<<1)
    #define BIT_2    (1<<2)

    main() 函数:

    int main(void)
    {
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4     
        delay_init();                        //延时函数初始化      
        uart_init(115200);                    //初始化串口
        LED_Init();                              //初始化LED
        KEY_Init();                            // 初始化按键
         
        //创建开始任务
        xTaskCreate((TaskFunction_t )start_task,            //任务函数
                    (const char*    )"start_task",          //任务名称
                    (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                    (void*          )NULL,                  //传递给任务函数的参数
                    (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                    (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
        vTaskStartScheduler();          //开启任务调度
    }

    任务函数:

    //开始任务任务函数
    void start_task(void *pvParameters)
    {
        taskENTER_CRITICAL();           //进入临界区
        
        EventGroupHandle =  xEventGroupCreate();            // 创建事件标志组
        if(EventGroupHandle == NULL)
        {
            printf("EventGroup Create Failed!
    ");
        }
        
        //创建eventsetbit任务
        xTaskCreate((TaskFunction_t )eventsetbit_task,         
                    (const char*    )"eventsetbit_task",       
                    (uint16_t       )EVENTSETBIT_STK_SIZE, 
                    (void*          )NULL,                
                    (UBaseType_t    )EVENTSETBIT_TASK_PRIO,    
                    (TaskHandle_t*  )&EventSetbitTask_Handler);   
        //创建eventgroup任务
        xTaskCreate((TaskFunction_t )eventgroup_task,     
                    (const char*    )"eventgroup_task",   
                    (uint16_t       )EVENTGROUP_STK_SIZE, 
                    (void*          )NULL,
                    (UBaseType_t    )EVENTGROUP_TASK_PRIO,
                    (TaskHandle_t*  )&EventGroupTask_Handler);         
        vTaskDelete(StartTask_Handler); //删除开始任务
        taskEXIT_CRITICAL();            //退出临界区
    }
    
    //eventsetbit_task任务函数 
    void eventsetbit_task(void *pvParameters)
    {
        u8 key = 0;
        
        while(1)
        {
            key = KEY_Scan(0);
            switch(key)
            {
                case KEY1_PRES:
                    if(EventGroupHandle != NULL)
                    {
                        xEventGroupSetBits(EventGroupHandle,BIT_0);    // 设置事件标志组 BIT_0 置1
                    }
                    break;
                case KEY2_PRES:
                    if(EventGroupHandle != NULL)
                    {
                        xEventGroupSetBits(EventGroupHandle,BIT_1);    // 设置事件标志组 BIT_0 置1
                    }
                    break;
            }
            vTaskDelay(10);
        }
    }   
    
    //eventgroup_task任务函数
    void eventgroup_task(void *pvParameters)
    {
        EventBits_t EventBitsVal = 0;
        
        while(1)
        {
            if(EventGroupHandle != NULL)
            {
                EventBitsVal = xEventGroupWaitBits(
                                                    EventGroupHandle,        // 要等待的事件标志组
                                                    (BIT_0|BIT_1),            // 等待的事件位
                                                    pdTRUE,                    // 清零
                                                    pdFALSE,                // 只要事件位其一得到就退出
                                                    portMAX_DELAY );        // 死等
                
                printf("EventBitsVal = %#x
    ",EventBitsVal);
            }else{
                vTaskDelay(10);
            }
            
        }
    }

    运行结果:

    按下KEY1,输出 EventBitsVal = 0x1

    按下KEY2,输出  EventBitsVal = 0x2

  • 相关阅读:
    [Functional Programming] liftA2 and converge
    [Javascript] Convert a forEach method to generator
    [React Native] Up & Running with React Native & TypeScript
    [React] Create a Query Parameter Modal Route with React Router
    [ES2019] Represent Collision-free String Constants as Symbols in JavaScript
    形形色色的软件生命周期模型(4)——MSF、实用型
    整型数组处理算法(九)给定任意一个正整数,求比这个数大且最小的“不重复数”(性能优化)[2014百度笔试题]
    Easyui获取数据库date数据的显示
    [置顶] 如何更改CSDN博客高亮代码皮肤的样式,使博客看起来更有范(推荐)
    try-catch-finally 引发的奇怪问题
  • 原文地址:https://www.cnblogs.com/doitjust/p/11058293.html
Copyright © 2020-2023  润新知