【ESP-IDF FreeRTOS】队列管理

news2024/11/12 13:04:35

先包含下头文件。

#include "freertos/queue.h"

队列大家应该不陌生,就是一个先进先出的容器。用在FreeRTOS里用途就多了。

首先是可以让任务与任务之间以及中断之间通信,任务A把数据塞进队列再让任务B取出,这样就可以传递数据了。第二个重要用途就是可以通过队列,来使得两个任务进行同步。

比如说我现在需要处理一批数据,要先预处理,再进一步处理,我派给了两个任务去做。

任务A预处理,任务B进一步处理。任务B需要等待任务A预处理完了才可以接着处理,所以任务B实际上是需要等待任务A的,这时候就可以用到队列了。

任务A预处理完一个数据之后就往队列里塞个东西,任务B从队列里取出一个东西之后就知道任务A已经预处理一份数据了,那么任务B就可以进行进一步处理了,这样就完成了同步。

在中断里也是一样的,因为中断函数中不能执行太多的内容,那么我可以让中断函数就执行一个操作,那就是往队列里塞东西,然后再其他地方不断地尝试从队列中取数据,若是从队列里取出东西了,那么表示中断触发过了,就可以在中断函数之外执行中断处理的逻辑了。

首先是创建队列。我们使用下面这个宏去创建获取队列。

xQueueCreate(uxQueueLength, uxItemSize)

不过本质上还是调用的函数。

我们不用在意这些细节,我们只需要知道创建队列需要传入两个参数,第一个是队列的大小,单位是数量;第二个是队列元素的大小,单位是字节。

如果创建成功,那么返回句柄,失败则返回0。

这个宏帮我创建的是动态分配内存的,我们一样可以自己给队列静态分配内存。

xQueueCreateStatic(uxQueueLength、uxItemSize、pucQueueStorage、pxQueueBuffer)

如果要删除队列,用下面这个函数。

void vQueueDelete(QueueHandle_t xQueue)

自动分配内存的队列它会帮我们释放内存,但如果是静态分配的,那么需要我们自己去释放了。

接下来我们往队列里塞点东西吧。

往队列后面塞东西有下面三个。

xQueueSend(xQueue, pvItemToQueue, xTicksToWait)

xQueueSendToBack(xQueue、pvItemToQueue、xTicksToWait)

这俩有什么区别呢?答案是没区别。

往上翻一层,不能说毫不相干吧,只能说是一模一样了。xQueueSend的存在就是为了向后兼容。所以说要是能用xQueueSendToBack的话就用xQueueSendToBack吧,毕竟还有往队列前面塞东西的函数,用这个比较见名知意。

三个参数分别是队列句柄,要传入数据的指针(注意是指针!!!),等待时间(单位为Tick,时钟周期)。因为队列是有容量的,如果队列满,那么数据是塞不进去的,因此有个等待时间,如果等待时间内队列被取了数据,也就是有位置空出来了,那还是可以成功塞东西进去的,如果超过了等待时间还是无法塞,那么调用失败,返回pdFALSE(等价于False,反之成功是pdTRUE)

等待时间可以传入portMAX_DELAY表示一直等待可以写入,一直写不了我就一直等下去。

但是实际上并不是永远,因为它这个宏本质上是一个很大的数而已,不过我们在使用的时候可以当它就是代表永远。

xQueueSendToBackFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken)

还有一个这个,它名字加了个后缀FromISR,意思就是它是用在中断函数里的。

另外还有俩往队列前面塞数据的宏。

xQueueSendToFront(xQueue, pvItemToQueue, xTicksToWait)
xQueueSendToFrontFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken)

用法是一样的就不介绍了,我们一般用的多的还是往后面塞数据。

接下来是从队列获取数据。

BaseType_t xQueueReceive(QueueHandle_t xQueue, void *const pvBuffer, TickType_t xTicksToWait)

BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue, void *const pvBuffer, BaseType_t *const pxHigherPriorityTaskWoken)

两个函数都是从队列中读取数据的,不一样的是一个是普通使用的,另一个后缀是FromISR的是在中断里使用的。二者从队列中读取了数据实际上可以算是从队列中取出了数据,成功取出后,数据则会在队列中被删除。

前两个参数都是一样的,参数一是队列句柄,参数二是存放读取数据的空间指针。

xQueueReceive的参数三是等待时间,也就是说读取队列的时候,队列为空也没关系,会等待直到等待时间耗尽或者队列被其他任务塞了数据可以读取了。

xQueueReceiveFromISR因为是用在中断里的,因此不能等待,所以参数三是个传出参数,如果有其他任务因为我们这次调用了xQueueReceiveFromISR而解除阻塞(当队列满了还要写入时就会阻塞),那么这个传出参数就会变成pdTRUE,如果没有,那么不改变这个传出参数原本的值(而不是变成pdFALSE,这个注意一下)

接下来来个小例子使用一下队列的发送和接收。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "freertos/queue.h"

QueueHandle_t q;    

void test(void*){
    int sendData = 0;
    while(1){
        if(pdTRUE == xQueueSendToBack(q,&sendData,pdMS_TO_TICKS(100))){
            printf("send data is %d\r\n",sendData);
        }
        sendData++;
        vTaskDelay(pdMS_TO_TICKS(300));
    }
}

void app_main(void){
    TaskHandle_t test1_handle;
    q = xQueueCreate(10,sizeof(int));
    xTaskCreate(test,"test1",1024*2,NULL,configMAX_PRIORITIES/2,&test1_handle);
    TickType_t curTime = xTaskGetTickCount();
    int getData = 0;
    while(1){
        if(pdTRUE == xQueueReceive(q,&getData,pdMS_TO_TICKS(500))){
            printf("get data is %d\r\n",getData);
        }
        xTaskDelayUntil(&curTime,pdMS_TO_TICKS(1000));
    }
}

创建了一个任务来给队列塞数据,每隔0.3s塞一次,最长等待时间是100ms。而主任务里是每隔1s读取一次队列,最长等待时间是500ms。

队列的大小是10,因为发送和接收有个时间差,发送的速度要大于接收,一下子没接收到的数据则会留在队列里,当队列被塞满之后就无法再塞,只能等待其他任务接收队列。

因此如下图所示,发送数据十来次之后就发生了数据遗失的现象。解决的方案有扩大队列;统一发送接受的频率;延长等待时间等。

总之这就是个小例子,大家结合着代码看看应该能对队列的操作有些许了解。

除了上面两种读取队列的方式,还有下面两种。

BaseType_t xQueuePeek(QueueHandle_t xQueue, void *const pvBuffer, TickType_t xTicksToWait)

BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue, void *const pvBuffer)

使用方法和上面两个读取队列的函数类似,前两个参数都是队列句柄和存放读取数据的空间指针。普通版本多了个等待时间,而中断版本则直接没有了第三个参数。

因为两个实际上是查看队列而不是读取队列,意思就是这俩函数可以得到队列头的数据,但是队列不会因此而删除被查看的数据,这就是Peek和Receive的区别,因为Peek不会删除队列的数据,所以自然不会有任务因此而解除阻塞状态,xQueuePeekFrom的第三个参数也就没有存在的必要了。

队列常用的操作也就是发送和接收了,除此之外再介绍几个队列相关的函数吧(用的不多)。

UBaseType_t uxQueueMessagesWaiting(const QueueHandle_t xQueue)

获取队列中数据的数量。

UBaseType_t uxQueueSpacesAvailable(const QueueHandle_t xQueue)

获取队列中的空位(队列大小 - 队列中数据的数量)

BaseType_t xQueueIsQueueEmptyFromISR(const QueueHandle_t xQueue)

BaseType_t xQueueIsQueueFullFromISR (const QueueHandle_t xQueue)

查询队列是否为空或为满,只能在中断中使用,没有普通任务中使用的版本。

xQueueReset(xQueue)

这是一个宏,传入队列句柄然后把句柄复位。

除了队列之外,还有个队列集,可以理解为存放队列的队列。

用的不多,但我们还是来介绍一下。

QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength)

创建一个队列集,只需要传入队列集的大小。

BaseType_t xQueueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet)

将队列添加进队列集中,第一个参数是队列的句柄,第二个参数是队列集的句柄。至于为什么第一个参数的类型和上面队列句柄的类型不一样,这个后面再说。

BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet)

将队列集中的某个队列移除。

QueueSetMemberHandle_t xQueueSelectFromSet(QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait)

另外还有个中断版本的,就是名字加个后缀FromISR,就不另外拿出来介绍了。

参数一是队列集的句柄,参数二是等待时间(时钟周期数)。

如果在队列集中的队列收到数据了,那么通过这个函数,就会给我们返回那个收到数据的队列的句柄。

换言之,我们之前只能一个个去等待获取队列的数据,而通过队列集,我们可以一个函数同时监听多个队列的接收情况。

现在还剩最后一个问题,那就是返回值的类型QueueSetMemberHandle_t。这个我们再回顾一下添加队列进队列集的函数,参数一的类型也是这个。那么答案就显而易见了,这个类型就是队列句柄的类型。

它们本质上都是QueueDefinition这个类型。

那为什么要搞俩名字呢?个人认为是为了见名知意,并且我们虽然叫队列集,但是队列集可并不仅仅是装队列句柄的,还可以装信号量的(下一篇介绍信号量)。

而信号量句柄的类型就是队列句柄类型。

而队列句柄类型是QueueSetMemberHandle_t。所以本质上,它们这一堆都是同一个东西,换个名字是为了好区分各自的用途。

光介绍可能不理解队列集具体的用法,下面来个小例子,大家一看便知。

创建了两个任务,任务内容是分别在同一个队列集中的不同队列塞数据,然后在主循环里获取队列集中可以获取的队列,并且读取出数据。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

QueueHandle_t q1,q2;    

void test1(void*){
    int sendData = 0;
    while(1){
        if(pdTRUE == xQueueSendToBack(q1,&sendData,pdMS_TO_TICKS(100))){
            printf("send data to q1 is %d\r\n",sendData);
        }
        sendData++;
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

void test2(void*){
    int sendData = 100;
    while(1){
        if(pdTRUE == xQueueSendToBack(q2,&sendData,pdMS_TO_TICKS(100))){
            printf("send data to q2 is %d\r\n",sendData);
        }
        sendData++;
        vTaskDelay(pdMS_TO_TICKS(300));
    }
}

void app_main(void){
    TaskHandle_t test1_handle,test2_handle;
    QueueSetHandle_t qs = xQueueCreateSet(10);
    q1 = xQueueCreate(10,sizeof(int));
    q2 = xQueueCreate(10,sizeof(int));
    xQueueAddToSet(q1,qs);
    xQueueAddToSet(q2,qs);
    xTaskCreate(test1,"test1",1024*2,NULL,configMAX_PRIORITIES/2,&test1_handle);
    xTaskCreate(test2,"test2",1024*2,NULL,configMAX_PRIORITIES/2,&test2_handle);
    int getData = 0;
    while(1){
        QueueSetMemberHandle_t getq = xQueueSelectFromSet(qs,pdMS_TO_TICKS(1000));
        if(getq == q1){
            xQueueReceive(q1,&getData,pdMS_TO_TICKS(100));
            printf("receive from q1, data is %d\r\n",getData);
        }else if(getq == q2){
            xQueueReceive(q2,&getData,pdMS_TO_TICKS(100));
            printf("receive from q2, data is %d\r\n",getData);
        }else if(getq == NULL){
            printf("not receive\r\n");
        }
    }
}

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

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

相关文章

BMC lighttpd kvm数据分析(websocket)

1.说明 lighttpd源码: https://github.com/lighttpd/lighttpd1.4.gitlighttpd wiki: https://redmine.lighttpd.net/projects/lighttpd/wiki/libfcgi: https://github.com/toshic/libfcgi/tree/master 注意: 本章的代码仓库: https://gitee.com/wit_yuan/lighttpd_kvm 2.编…

3127.构造相同颜色的正方形

1.题目描述 给你一个二维 3 x 3 的矩阵 grid &#xff0c;每个格子都是一个字符&#xff0c;要么是 B &#xff0c;要么是 W 。字符 W 表示白色&#xff0c;字符 B 表示黑色。 你的任务是改变 至多一个 格子的颜色&#xff0c;使得矩阵中存在一个 2 x 2 颜色完全相同的正方形。…

无敌美少男和无敌美少女构建企业级私有仓库(harbor)

一&#xff1a;harbor简介 Harbor 是由 vmware 公司开源的企业级 Docker Registry 项目。 它提供了以下主要功能和特点&#xff1a; 基于角色的访问控制&#xff08;RBAC&#xff09;&#xff1a;可以为不同的用户和用户组分配不同的权限&#xff0c;增强了安全性和管理的灵…

Linux下的MySQL8.0报错:[Err]1055

Linux下的MySQL8.0报错&#xff1a;[Err]1055 报错信息解决办法 报错信息 在Linux环境下的MySQL里执行SQL语句报如下错误&#xff1a;[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column information_schema.PROFIL…

maven 父子工程创建详解

maven 父子工程创建详解 一、Maven工程继承关系 继承概念 maven继承是指的Maven的项目中&#xff0c;让一个项目从另外项目中继承配置信息的机制。继承可以让我们在多个项目中共享同一个配置信息&#xff0c;简化项目的管理和维护工作 继承作用&#xff1a;在父工程中统一管理…

Datawhale AI夏令营 第五期 CV方向 Task3笔记

Task3&#xff1a;上分思路——数据集增强与模型预测 Part1&#xff1a;数据增强 数据增强是机器学习和深度学习中的一种技术&#xff0c;通过在原始数据集上应用一系列变换来人工地增加数据样本的数量和多样性&#xff0c;从而提高模型的泛化能力&#xff0c;减少过拟合&…

简单的 nginx 学习

简单的 nginx 学习 1. nginx的安装 1.1 下载安装包 去官网下载对应的nginx包&#xff0c;推荐使用稳定版本&#xff0c;上传nginx到linux系统 1.2 安装依赖环境 安装gcc环境 yum install gcc-c安装PCRE库&#xff0c;用于解析正则表达式 yum install -y pcre pcre-develzlib压…

【立体匹配】双目相机外参自标定方法介绍

双目相机外参自标定方法 原理实践 双目相机外参自标定方法是一种无需固定标定板&#xff0c;在拍摄实际场景的两张图像时&#xff0c;通过计算两幅图像之间的匹配特征点对&#xff0c;结合相机的内参矩阵&#xff0c;来实时求解两个相机之间相对位置&#xff08;即外参&#xf…

ThermoParser 介绍

ThermoParser是一个工具包&#xff0c;用于简化专业材料科学代码产生的数据分析&#xff0c;以热电学为中心&#xff0c;但也适用于任何与电子和/或声子传输有关的内容。ThermoParser是一个Python库&#xff0c;它包含数据检索、操作和绘图的函数&#xff0c;只需几行代码就可以…

HashMap 链表转红黑树的阈值为何为 8

与一个重要的统计学原理——泊松分布密切相关&#xff1a;该原理阐明了在单位时间&#xff08;或面积、体积&#xff09;内&#xff0c;随机事件的平均发生次数遵循泊松分布 为什么这因子设定为0.5呢&#xff1f; 在忽略方差的情况下&#xff0c;哈希表容量占比的期望值约为 0.…

揭秘扩散模型:DDPM的数学基础与代码实现全攻略!

(DDPM) denoising diffusion probabilistic models 理论学习 本文价值 本文是 Diffusion 这一类模型的开山之作&#xff0c;首次证明 diffusion 模型能够生成高质量的图片&#xff0c;且奠定了所有后续模型的基本原理&#xff1a;加噪 --> 去噪。DDPM 模型的效果如下&#x…

springboot+vue+mybatis计算机毕业设计飞机订票系统+PPT+论文+讲解+售后

快速发展的社会中&#xff0c;人们的生活水平都在提高&#xff0c;生活节奏也在逐渐加快。为了节省时间和提高工作效率&#xff0c;越来越多的人选择利用互联网进行线上打理各种事务&#xff0c;然后线上管理系统也就相继涌现。与此同时&#xff0c;人们开始接受方便的生活方式…

IDEA向mysql写入中文字符时出现乱码问题

可参考该博客&#xff1a;https://www.cnblogs.com/bb1008/p/7704458.html 第一步是将IDEA软件中的编码方式全部改为utf8 File -> Settings -> Editor -> File Encodings 第二步是在数据库链接中加入 ?characterEncodingUTF-8

备战2024年全国大学生数学建模竞赛:蔬菜类商品的自动定价与补货决策

目录 一、引言 二、问题分析 三、解题思路 问题1&#xff1a;销售量分布规律及相互关系 问题2&#xff1a;品类级别的补货计划与定价策略 问题3&#xff1a;单品级别的补货计划与定价策略 问题4&#xff1a;补充数据的建议与分析 四、知识点解析 五、模型建立与求解 1…

没有永远免费的加速器,但是永远有免费的加速器【20240831更新】

没有永远免费的加速器&#xff0c;但是永远有免费的加速器【每日更新】 一、迅雷加速器&#xff08;免费时长最高38天&#xff09; 可免费时长&#xff1a;8天 如果是迅雷会员&#xff0c;则免费时长为38天 官网下载链接&#xff1a;迅雷加速器—迅雷官方出品&#xff0c;为快…

关于数字存储和byte[]数组的一些心得

前言 最近做项目&#xff0c;发现一些规律&#xff0c;比如数字的存储和字符串的存储&#xff0c;先说数字吧&#xff0c;最常见的整数&#xff0c;就涉及反码和补码&#xff0c;根据这些规则&#xff0c;甚至我们自己也能造一种数据存储结构&#xff0c;比如1个字节8bit&…

bbr 和 inflight 守恒的收敛原理

先看 bbr&#xff0c;以 2 条流 bw 收敛为例&#xff0c;微分方程组如下&#xff1a; { d x d t C ⋅ g ⋅ x g ⋅ x y − x d y d t C ⋅ g ⋅ y g ⋅ y x − y \begin{cases} \dfrac{dx}{dt}C\cdot\dfrac{g\cdot x}{g\cdot xy}-x\\\ \dfrac{dy}{dt}C\cdot\dfrac{g\cdot y…

秋风送爽,夏意未央|VELO Prevail Revo坐垫,一骑绿动起来吧~

夏末秋初&#xff0c;当第一片落叶缓缓飘落&#xff0c;是时候骑上你的自行车&#xff0c;迎接新的季节啦。带上维乐Prevail Revo坐垫&#xff0c;因为它独树一帜地采用EVA与回收咖啡渣精制而成的轻量发泡提升了减震性能&#xff0c;可以让你的每一次骑行都充满意义。    “…

基于DS18B20的温度检测

前言 DS18B20是DALLAS半导体公司生产的单总线数字温度传感器&#xff0c;其输出的是数字信号&#xff0c;具有体积小&#xff0c;功耗低&#xff0c;抗干扰能力强&#xff0c;精度高的特点。 温度范围-55摄氏度至125摄氏度&#xff0c;在-10摄氏度至85摄氏度可以达到不超过 0.5…

Redis进阶(五):集群

1.概念 集群&#xff0c;从广义来讲&#xff1a;只要是多个机器&#xff0c;构成了分布式系统&#xff0c;都可以称为一个集群 狭义的集群&#xff1a;redis提供的集群模式&#xff0c;这个集群模式之下&#xff0c;主要是要解决存储空间不足的问题&#xff08;拓展存储空间&…