当前位置: 首页 > news >正文

【ESP-IDF FreeRTOS】事件组

事件组,叫做组,但其实它就是一个数,我们使用的是这个数的位。

我们简单回顾一下信号量,我们执行take和give的时候,是不是一次性只能take一个或是give一个。如果有个资源比较复杂,我需要使用三个信号量来控制,那么就意味着我需要三个信号量同时可以take,可是我们信号量take的话要么失败要么成功,我们没法控制到等三个信号量都能take的时候我们再take。

这时候就需要用到事件组了。事件组是一个数,一个数有很多位,我们可以在位上面做文章。每个位我们都当它们是一个事件,事件发生了,我们就把位置1,否则置0,我们可以等待事件组的某位或者是某几位都置1以此来控制任务间的同步。

那么事件组这个数有多少位呢?

我们可以查看这个宏的值configUSE_16_BIT_TICKS如果是1,那么事件组是16bit,如果是0,那么事件组是32bit。

但无论是16bit还是32bit,我们都要扣掉高八位8bit,因为那是给内核用的。

比如说我这边这个宏是0,那么我的事件组就是32bit,扣掉高八位,能用的是24bit。

听起来事件组好像也就是一个数而已,我们拿一个全局变量按照上面的说法操作是不是也能代替事件组?问题还是在于我们的FreeRTOS是多任务的,很有可能操作到一半的时候就切换任务导致出错,使用事件组可以保证我们的操作属于原子操作。

接下来我们看看ESP-IDF FreeRTOS如何使用事件组。

首先包含头文件。

#include "freertos/event_groups.h"

然后创建一个事件组。

EventGroupHandle_t xEventGroupCreate(void)

创建完之后我们的操作基本就俩,设置事件组的位,等待获取事件组的位。

下面这个函数是设置事件组的位。

EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet)

参数俩,一个是事件组句柄,另一个是我们要给事件组置1的位,比如说我要给位一置1,那么传的参数是0x01(0000 0001b),位二就是0x02(0000 0010b),并且可以同时给多个位置1,可以直接传0xFF(1111 1111),把低八位全部置1。

接下来是等待获取事件组的位。

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

第一个参数传入句柄。

第二个参数就是我们要等待的位了,和可以设置多个位一样,我们也可以等待多个位。

第三个参数指定获取之后是否将那些位置0回去,传入pdTRUE或是pdFALSE。

第四个参数指定是否等待全部的位,如果是pdTRUE,那么函数会阻塞到参数二的全部位置1,如果是pdFALSE,那么参数二中只要有一个位被置一了,函数就会解除阻塞。

第五个参数就是传入时钟周期数,也就是函数阻塞的最长时间。

那么我们应该如何判断事件组是获取到了指定位而解除了函数阻塞还是因为超时而解除阻塞的呢?

编程指南里是说这个函数会返回事件组的值,我们通过查看返回值来判断是什么情况,因为如果我们设置了获取到就将位置0,那么因超时而解除阻塞是不会改变原来的值的。

编程指南的解释有点绕(反正一开始我误解了,也可能是翻译的问题),我以为返回值是解除阻塞之后的事件组的值,因此只需要判断返回值中我们等待的位是否被置0即可(这是错误的!!!)。

后来多实验了几次才悟了,返回值返回的是解除阻塞时的事件组的值。如果像我一开始那样理解,那么会出问题的,如果我一直一个位也没置,那么函数就会因为超时而解除阻塞,这时候判断事件组的值,就会误判为等待的位被置0(其实一开始就没有置1一直都是0)。

所以正确的做法是解除阻塞之后再获取一次事件组的值,和这个函数返回值对比一下,相比之下等待的位被置为0了,那才是等待成功。

我们使用下面这个函数来获取当前事件组的值。

xEventGroupGetBits(xEventGroup)

void vEventGroupDelete(EventGroupHandle_t xEventGroup)

删除事件组。

EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear)

清除指定位,也就是主动置0。用法和置1一样。

有上面几个函数基本就够用了,接下来来个小例子。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"EventGroupHandle_t egHandle;void test(void* arg){uint32_t data,waittime;if(*(int*)arg == 1){data = 0x01;waittime = 2500;}else if(*(int*)arg == 2){data = 0x02;waittime = 3500;}else{data = 0x04;waittime = 5000;}while(1){uint32_t val = xEventGroupGetBits(egHandle);printf("test%d before val is %lx\r\n",*(int*)arg,val);xEventGroupSetBits(egHandle,data);val = xEventGroupGetBits(egHandle);printf("test%d after val is %lx\r\n",*(int*)arg,val);vTaskDelay(waittime / portTICK_PERIOD_MS);}
}void app_main(void) {egHandle = xEventGroupCreate();int t1 = 1, t2 = 2, t3 = 3;xTaskCreate(test,"test1",1024*2,(void*)&t1,configMAX_PRIORITIES/2,NULL);xTaskCreate(test,"test2",1024*2,(void*)&t2,configMAX_PRIORITIES/2,NULL);xTaskCreate(test,"test3",1024*2,(void*)&t3,configMAX_PRIORITIES/2,NULL);while (1) {xEventGroupWaitBits(egHandle,0x07,pdTRUE,pdTRUE,pdMS_TO_TICKS(10000));if(((val & 0x07) == 0x07) && (xEventGroupGetBits(egHandle) & 0x07) == 0x00){printf("get event group\r\n");}}
}

创建一个事件组,在主循环里等待获取事件组的低3位。创建了三个任务分别以不同的时间间隔去将三个不同的位设置为1,可以看到等待获取事件组的函数是在间隔时间最久的那个任务执行过后接触阻塞的。


http://www.mrgr.cn/news/25914.html

相关文章:

  • 基于Web的门店管理系统的设计与实现---附源码76269
  • Jetpack Compose Side Effects in Details 副作用的详细信息
  • 通过 Sniper Links 提高您的电子邮件确认率
  • 【SSRF漏洞】——http协议常见绕过
  • K1计划100%收购 MariaDB; TDSQL成为腾讯云核心战略产品; Oracle@AWS/Google/Azure发布
  • java 可变参数
  • java.io.IOException: Too many open files 分析与解决
  • [DCVRP] 基于复杂网络的k-opt算法解空间表示(五)
  • ElementPlus自定义更换主题色
  • Excel图片批量插入单元格排版处理插件【图片大师】
  • 出海公司如何快速搭建海外团队指南
  • MongoDB与Pymongo深度实践:从基础概念到无限级评论应用示例
  • 项目实战应用Redis分布式锁
  • 828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署CodeX Docs文档工具
  • python 实现euler modified变形欧拉法算法
  • 学懂C++(六十):C++ 11、C++ 14、C++ 17、C++ 20新特性大总结(万字详解大全)
  • ArcGIS Pro SDK (十四)地图探索 3 弹出窗口
  • 基于Spark 的零售交易数据挖掘分析与可视化
  • 亚马逊测评自建团队与工作室的五大优势亮点,打造高权重评价系统
  • 腾讯发布大模型安全与伦理报告:以负责任AI引领大模型创新