FreeRTOS:事件标志组

news2024/9/20 14:15:03

目录

  • 一、事件标志组简介
    • 1.1事件位(事件标志)
    • 1.2事件组
    • 1.3事件标志组和事件位的数据类型
  • 二、创建事件标志组
    • 2.1函数 xEventGroupCreate()
    • 2.2函数xEventGroupCreateStatic()
  • 三、设置事件位
    • 3.1函数 xEventGroupClearBits()
    • 3.2函数xEventGroupClearBitsFromISR()
    • 3.3函数 xEventGroupSetBits()
    • 3.4函数xEventGroupSetBitsFromISR()
  • 四、获取事件标志组值
    • 4.1函数xEventGroupGetBits()
    • 4.2函数xEventGroupGetBitsFromISR()
  • 五、等待指定的事件位
  • 六、事件标志组实验
    • 6.1实验要求
    • 6.2实验代码

一、事件标志组简介

1.1事件位(事件标志)

事件位用于表明某个事件是否发生,事件位通常用作事件标志,比如下面的几个例子:
当收到一条消息并且把这条消息处理掉以后就可以将某个位(标志)置1,当队列中没有消息需要处理的时候就可以将这个位(标志)置0。
当把队列中的消息通过网络发送输出以后就可以将某个位(标志)置1,当没有数据需要从网络发送出去的话就将这个位(标志)置0。
现在需要向网络中发送一个心跳信息,将某个位(标志)置1。现在不需要项网络中发送心跳信息,这个位(标志)置0。

1.2事件组

一个事件组就是一组的事件位,事件组中的事件位通过位编号来访问,同样,以上面列出的三个例子为例:
事件标志组bit0 表示队列中的消息是否处理掉。
事件标志组bit1 表示是否有信息需要从网络中发送出去。
事件标志组bit2 表示现在是否需要向网路发送心跳信息。

1.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提供了两个用于创建事件标志组的函数:
在这里插入图片描述

2.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.2函数xEventGroupCreateStatic()

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

EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )

参数:

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

返回值:

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

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

三、设置事件位

FreeRTOS提供了4个函数用来设置事件标志组中事件位(标志),事件位的设置包括清零和置1两种操作:
在这里插入图片描述

3.1函数 xEventGroupClearBits()

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

EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, 
                                  const EventBits_t uxBitsToClear )

参数:

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

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

返回值:

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

3.2函数xEventGroupClearBitsFromISR()

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

BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, 
                                        const EventBits_t uxBitsToSet ) 

参数:

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

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

返回值:

pdPASS:事件位清零成功。

pdFALSE:事件位清零失败。

3.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.4函数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函数。
在这里插入图片描述

4.1函数xEventGroupGetBits()

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

EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup )

参数:

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

返回值:

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

4.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了。如果函数因为阻塞时间到而返回的话,那么这个返回值就不代表任何的含义。

六、事件标志组实验

6.1实验要求

创建事件标志组、将相应的事件位置1、等待相应事件位置1的操作。
实验设置三个任务:start_task、eventsetbit_task、eventgroup_task。
start_task:用来创建其他两个任务,创建时间标志组。
eventsetbit_task:通过不同按键值,将事件标志组中相应事件位置1。
eventgroup_task:等待事件标志组中的事件位,当这些事件位都置1,执行相应的处理。
EventGroupHandler:创建的事件标志组句柄。使用事件标志组的事件位:bit0 bit1 bit2

6.2实验代码

任务分配:

//任务优先级
#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!\r\n");
    }
    
    //创建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\r\n",EventBitsVal);
        }else{
            vTaskDelay(10);
        }
        
    }
}

参考视频:正点原子
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/660172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python模块MarkupPy 自定义html报告

简介 MarkupPy是Python模块用于生成HTML和XML格式的字符串。它的主要作用是提供了一种比原生HTML/XML更加易读和易写的编写方式&#xff0c;通过Python代码来生成HTML或XML代码。 使用MarkupPy&#xff0c;可以在Python中使用不同的对象类型和方法&#xff0c;来动态地生成HTML…

做自动化测试老是失败?你真的会做吗?资深测试的总结整理...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化正在不断普…

揭开生成式人工智能的力量:60+医疗保健应用场景

预计生成式AI在医疗保健领域的增长速度将超过任何其他行业。在医疗技术领域&#xff0c;AI可带来更高效流程、个性化客户互动、更大的创新和更高价值。为了帮助领导者理解这些机会&#xff0c;BCG最近研究了医疗技术中生成式AI的60多个应用场景&#xff1a;从产研和软件开发到业…

Android自动化测试,5个必备的测试框架

Appium Appium是一个开源的移动测试工具&#xff0c;支持iOS和Android&#xff0c;它可以用来测试任何类型的移动应用&#xff08;原生、网络和混合&#xff09;。作为一个跨平台的工具&#xff0c;你可以在不同的平台上运行相同的测试。为了实现跨平台的功能&#xff0c;Appi…

关于数据库索引的入门简述

一、简介 数据库索引是现代数据库中高效数据检索的一个重要工具。它在优化查询性能和加快数据检索操作方面发挥着重要作用。这里我们深入了解下数据库索引其内部工作原理、优点和局限性。 二、数据库 1、SQL 数据库 为了理解索引&#xff0c;先说一句数据库&#xff0c;数据库…

Jenkins小技巧汇总

设置变量 设置全局环境变量 全局变量除了系统内置的全局环境变量之外&#xff0c;用户也可以设置全局变量。设置路径&#xff1a;【Dashboard】–>【Manage Jenkins】–>【System Configuration 下的 System】–>【Global properties】从描述中我们可以看到&#xf…

Jetson nano 之 ROS入门 - - 深度学习环境配置

文章目录 前言一、Anaconda安装二、Pytorch 与 TensorFlow 环境配置三、TensorRT 推理引擎配置总结 前言 Jetson Nano是一款由NVIDIA推出的小型计算机&#xff0c;其性能优异、功耗低、体积小巧&#xff0c;非常适合用于嵌入式系统和边缘设备的深度学习应用。Jetson Nano搭载了…

【Git原理与使用】-- 基本操作

目录 添加文件 查看objects中的文件 小结 修改文件 版本回退 回退的回退 小结 撤销修改 情况一&#xff1a;对于工作区的代码&#xff0c;还没有 add 情况二&#xff1a;已经 add &#xff0c;但没有 commit 情况三&#xff1a;已经 add &#xff0c;并且也 commit …

Cordic IP核使用说明以及避坑记录

Cordic IP核使用说明以及避坑记录 参考文章&#xff1a;(140条消息) Vivado cordic IP核rotate和translate使用详解(附有代码)_cordic ip核 rotate_迎风打盹儿的博客-CSDN博客 (140条消息) VIVADO cordic IP核_卡布奇诺加勺糖的博客-CSDN博客 文章目录 Cordic IP核使用说明以及…

面试题:推排序是一种稳定排序吗?

面试题&#xff1a;推排序是一种稳定排序吗&#xff1f; 在回答该问题前&#xff0c;首先需要了解什么是稳定排序。 稳定性就是指对于两个关键字相等的记录&#xff0c;它们在序列中的相对位置&#xff0c;在排序之前和排序之后没有发生改变。通俗地讲就是有两个关键字相等的…

Node.js---菜鸟教程

文章目录 创建第一个应用创建 Node.js 应用 NPM 使用介绍使用 npm 命令安装模块本地安装使用 package.json模块的操作 回调函数阻塞代码实例非阻塞代码 事件循环事件驱动程序 EventEmitterEventEmitter 类方法实例error 事件继承 EventEmitter Buffer&#xff08;缓冲区&#x…

Redis-缓存

新增或者更新数据时,创建以后顺便存到redis中去【维护缓存】 获取的时候先从redis缓存中拿数据 如果拿数据的时候为空,则到数据库中拿数据,后再存到redis缓存中去 大量的商品【包括冷门商品】都进行上面的缓存,那么就很耗内存 针对每个数据进行缓存的时候 维护一个过期时间…

MQTT(一)

MQTT&#xff08;一&#xff09; 1.背景 学习目标&#xff1a;经过了解&#xff0c;Netty占用服务器资源内存大、远距离传感器在极端条件下数据处理兼容较差&#xff08;网络条件差&#xff0c;需要反复重连等&#xff09;。从同行业了解到&#xff0c;现在主流工业传输使用M…

【博学谷学习记录】超强总结,用心分享 | 架构师 zabbix学习总结

文章目录 一、介绍zabbix zabbix专有词汇 二、zabbix zabbix实践修改zabbix zabbix语⾔服务器可视化指标解决zabbix zabbix乱码问题查看监控内容可视化监控agent agent的cpu cpu动态查看模板--监控项⾃定义监控项语法 一、介绍 Zabbix 是由 Alexei Vladishev 开发的⼀种⽹络监…

达梦数据库介绍

文章目录 前言一、达梦数据库的定位二、达梦有哪些工具1、达梦管理工具2、达梦数据迁移工具3、 达梦数据库配置助手4、其它工具 三、Linux下的工具1、数据库初始化工具2、数据库迁移工具3、其它工具 四、其它连接工具总结 前言 近几年由于各种原因&#xff0c;国内开启了一波国…

【Android】-- 如何对APP版本控制/更新?

目录 一、 前提准备 1、获取服务器 2、使用工具操作云服务器 二、Json格式网页 三、创建file_paths.xml及修改AndroidManifest.xml 四、在java代码加入更新检测代码 效果如图&#xff1a; 可以强制更新和非强制更新&#xff0c;和浏览器下载安装包。 一、 前提准备 1、获取…

0001-TIPS-2020-hxp-kernel-rop : ret2user

目的 理解系统调用的过程&#xff1a;从用户态进入内核态&#xff0c;再从内核态返回用户态。细节见文末的参考了解一般性提权方法commit_creds(prepare_kernel_cred (0)); 环境搭建 下载 pwn 2020-kernel-rop wget https://2020.ctf.link/assets/files/kernel-rop-bf9c106…

说精神力量的词,愿力很神奇

说精神力量的词&#xff0c;愿力最神奇&#xff01; ​愿力&#xff0c;心力&#xff0c;精神&#xff0c;精 气 神&#xff0c;气 &#xff0c;能量 【能量】是个外来词 趣讲大白话&#xff1a;200天了&#xff0c;布道的愿力推动我 【趣讲信息科技200期】 ******************…

【换根DP】生活在树上

换根DP板子题 D-生活在树上_牛客小白月赛46 (nowcoder.com) 题意&#xff1a; 思路&#xff1a; 看数据范围是1e6且是统计问题&#xff0c;求的是对于每一个点的统计问题&#xff0c;那就逃不出是换根DP了 首先dfs1一次把树形DP求出来&#xff0c;然后再考虑换根 设dp[u]…

Wireshark抓包分析(ARP TCP DNS HTTP)

目录 一、ARP 二、DNS 三、TCP TCP的总过程&#xff1a; ​TCP三次握手&#xff1a; TCP四次挥手&#xff1a; 四、HTTP 一、ARP 1.ARP&#xff08;Address Resolution Protocol&#xff09;&#xff0c;是根据IP地址获取物理地址的一个TCP/IP协议。 我们要抓ARP 同网段内…