freeRTOS之队列(queue)

一.概述

1.介绍

  队列(queue)可以用于"任务到任务"、“任务到中断”、"中断到任务"直接传输信息。

2.核心功能

线程安全:自动处理多任务访问时的互斥问题。

数据复制:入队时复制数据(而非引用),避免内存共享风险。

阻塞机制:当队列满 / 空时,任务可选择阻塞等待。

优先级支持:高优先级任务优先获取队列资源。

3.关键属性

固定长度:创建时需指定队列长度(元素个数)。

固定大小:每个元素的字节数固定(如sizeof(int))。

先进先出(FIFO):默认按入队顺序出队(也支持 LIFO)。

4.原理图

二.队列的基本操作

1.创建

队列的创建有两种方法:动态分配内存、静态分配内存,

(1)动态分配内存:xQueueCreate,队列的内存在函数内部动态分配

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

(2)静态分配内存:xQueueCreateStatic,队列的内存要事先分配好

QueueHandle_t xQueueCreateStatic(

                           UBaseType_t uxQueueLength,

                           UBaseType_t uxItemSize,

                           uint8_t *pucQueueStorageBuffer,

                           StaticQueue_t *pxQueueBuffer

                       );

(3)示例代码:

// 示例代码

 #define QUEUE_LENGTH 10

 #define ITEM_SIZE sizeof( uint32_t )

 // xQueueBuffer用来保存队列结构体

 StaticQueue_t xQueueBuffer;

 // ucQueueStorage 用来保存队列的数据

 // 大小为:队列长度 * 数据大小

 uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];

 void vATask( void *pvParameters )

 {

QueueHandle_t xQueue1;

// 创建队列: 可以容纳QUEUE_LENGTH个数据,每个数据大小是ITEM_SIZE

xQueue1 = xQueueCreateStatic( QUEUE_LENGTH,

  ITEM_SIZE,

  ucQueueStorage,

  &xQueueBuffer );

 }

2.复位

队列刚被创建时,里面没有数据;使用过程中可以调用xQueueReset()把队列恢复为初始状态,此函数原型为:

/* pxQueue : 复位哪个队列;

 * 返回值: pdPASS(必定成功)

 */

BaseType_t xQueueReset( QueueHandle_t pxQueue);

3.删除

删除队列的函数为vQueueDelete(),只能删除使用动态方法创建的队列,它会释放内存。原型如下:

void vQueueDelete( QueueHandle_t xQueue );

4.写队列

可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在ISR中使用。函数原型如下:

/* 等同于xQueueSendToBack

 * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait

 */

BaseType_t xQueueSend(

                                QueueHandle_t    xQueue,

                                const void       *pvItemToQueue,

                                TickType_t       xTicksToWait

                            );

/*

 * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait

 */

BaseType_t xQueueSendToBack(

                                QueueHandle_t    xQueue,

                                const void       *pvItemToQueue,

                                TickType_t       xTicksToWait

                            );

/*

 * 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞

 */

BaseType_t xQueueSendToBackFromISR(

                                      QueueHandle_t xQueue,

                                      const void *pvItemToQueue,

                                      BaseType_t *pxHigherPriorityTaskWoken

                                   );

/*

 * 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait

 */

BaseType_t xQueueSendToFront(

                                QueueHandle_t    xQueue,

                                const void       *pvItemToQueue,

                                TickType_t       xTicksToWait

                            );

/*

 * 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞

 */

BaseType_t xQueueSendToFrontFromISR(

                                      QueueHandle_t xQueue,

                                      const void *pvItemToQueue,

                                      BaseType_t *pxHigherPriorityTaskWoken

                                   );

5 读队列

使用xQueueReceive()函数读队列,读到一个数据后,队列中该数据会被移除。这个函数有两个版本:在任务中使用、在ISR中使用。函数原型如下:

BaseType_t xQueueReceive( QueueHandle_t xQueue,

                          void * const pvBuffer,

                          TickType_t xTicksToWait );

BaseType_t xQueueReceiveFromISR(

                                    QueueHandle_t    xQueue,

                                    void             *pvBuffer,

                                    BaseType_t       *pxTaskWoken

                                );

6.查询

可以查询队列中有多少个数据、有多少空余空间。函数原型如下:

/*

 * 返回队列中可用数据的个数

 */

UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );

/*

 * 返回队列中可用空间的个数

 */

UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

7.覆盖/偷看

当队列长度为1时,可以使用xQueueOverwrite()或xQueueOverwriteFromISR()来覆盖数据。

注意,队列长度必须为1。当队列满时,这些函数会覆盖里面的数据,这也以为着这些函数不会被阻塞。

函数原型如下:

/* 覆盖队列

 * xQueue: 写哪个队列

 * pvItemToQueue: 数据地址

 * 返回值: pdTRUE表示成功, pdFALSE表示失败

 */

BaseType_t xQueueOverwrite(

                           QueueHandle_t xQueue,

                           const void * pvItemToQueue

                      );

BaseType_t xQueueOverwriteFromISR(

                           QueueHandle_t xQueue,

                           const void * pvItemToQueue,

                           BaseType_t *pxHigherPriorityTaskWoken

                      );

8.队列的基本使用

本程序会创建一个队列,然后创建2个发送任务、1个接收任务:

发送任务优先级为1,分别往队列中写入100、200

接收任务优先级为2,读队列、打印数值

(1)main函数中创建的队列、创建了发送任务、接收任务,代码如下:

/* 队列句柄, 创建队列时会设置这个变量 */

QueueHandle_t xQueue;

int main( void )

{

prvSetupHardware();

    /* 创建队列: 长度为5,数据大小为4字节(存放一个整数) */

    xQueue = xQueueCreate( 5, sizeof( int32_t ) );

if( xQueue != NULL )

{

/* 创建2个任务用于写队列, 传入的参数分别是100、200

 * 任务函数会连续执行,向队列发送数值100、200

 * 优先级为1

 */

xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );

xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );

/* 创建1个任务用于读队列

 * 优先级为2, 高于上面的两个任务

 * 这意味着队列一有数据就会被读走

 */

xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );

/* 启动调度器 */

vTaskStartScheduler();

}

else

{

/* 无法创建队列 */

}

/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */

return 0;

}

(2)发送任务的函数中,不断往队列中写入数值,代码如下:

static void vSenderTask( void *pvParameters )

{

int32_t lValueToSend;

BaseType_t xStatus;

/* 我们会使用这个函数创建2个任务

 * 这些任务的pvParameters不一样

   */

lValueToSend = ( int32_t ) pvParameters;

/* 无限循环 */

for( ;; )

{

/* 写队列

 * xQueue: 写哪个队列

 * &lValueToSend: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列

 * 0: 不阻塞, 如果队列满的话, 写入失败, 立刻返回

 */

xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );

if( xStatus != pdPASS )

{

printf( "Could not send to the queue.\r\n" );

}

}

}

(3)接收任务的函数中,读取队列、判断返回值、打印,代码如下:

static void vReceiverTask( void *pvParameters )

{

/* 读取队列时, 用这个变量来存放数据 */

int32_t lReceivedValue;

BaseType_t xStatus;

const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );

/* 无限循环 */

for( ;; )

{

/* 读队列

 * xQueue: 读哪个队列

 * &lReceivedValue: 读到的数据复制到这个地址

 * xTicksToWait: 如果队列为空, 阻塞一会

 */

xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );

if( xStatus == pdPASS )

{

/* 读到了数据 */

printf( "Received = %d\r\n", lReceivedValue );

}

else

{

/* 没读到数据 */

printf( "Could not receive from the queue.\r\n" );

}

}

}

三.邮箱

1.FreeRTOS的邮箱概念跟别的RTOS不一样,这里的邮箱称为"橱窗"也许更恰当:

它是一个队列,队列长度只有1;

写邮箱:新数据覆盖旧数据,在任务中使用xQueueOverwrite(),在中断中使用xQueueOverwriteFromISR()。

既然是覆盖,那么无论邮箱中是否有数据,这些函数总能成功写入数据。

读邮箱:读数据时,数据不会被移除;在任务中使用xQueuePeek(),在中断中使用xQueuePeekFromISR()。

这意味着,第一次调用时会因为无数据而阻塞,一旦曾经写入数据,以后读邮箱时总能成功。

2.代码示例

main函数中创建了队列(队列长度为1)、创建了发送任务、接收任务:

发送任务的优先级为2,它先执行

接收任务的优先级为1

代码如下:

/* 队列句柄, 创建队列时会设置这个变量 */

QueueHandle_t xQueue;

int main( void )

{

prvSetupHardware();

    /* 创建队列: 长度为1,数据大小为4字节(存放一个char指针) */

    xQueue = xQueueCreate( 1, sizeof(uint32_t) );

if( xQueue != NULL )

{

/* 创建1个任务用于写队列

 * 任务函数会连续执行,构造buffer数据,把buffer地址写入队列

 * 优先级为2

 */

xTaskCreate( vSenderTask, "Sender", 1000, NULL, 2, NULL );

/* 创建1个任务用于读队列

 * 优先级为1

 */

xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );

/* 启动调度器 */

vTaskStartScheduler();

}

else

{

/* 无法创建队列 */

}

/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */

return 0;

}

发送任务、接收任务的代码和执行流程如下:

运行结果如下图所示:

四.队列与其他 FreeRTOS 对象的对比

五.队列的性能考虑

1.内存开销:

队列本身的控制结构(约 40 字节)。

数据缓冲区(长度 × 元素大小)。

3.复制开销:

入队 / 出队时复制数据,大元素(如结构体)会影响性能。

优化方案:传递指针而非完整数据(需确保内存安全)。

3.阻塞唤醒开销:

任务从阻塞到就绪的上下文切换成本。

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

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

相关文章

【python】typing用法

一、基础类型提示 1. 基本类型注解 # 变量类型注解 age: int 30 name: str "Alice" is_student: bool False height: float 1.752. 函数注解 def greet(name: str, age: int) -> str:return f"Hello {name}, you are {age} years old!"二、组合类…

web前端开发核心基础:Html结构分析,head,body,不同标签的作用

前端技术协同关系 协作流程:HTML构建页面框架—>css美化样式(选择器属性)—>JavaScript实现交互(类似于python的脚本语言)扩展基础:在上面三项基础上学习Vue\React、构建工具WePack和浏览器工作原理…

精益数据分析(105/126):移动应用核心指标解析与用户分层营收策略

精益数据分析(105/126):移动应用核心指标解析与用户分层营收策略 在移动应用市场竞争白热化的今天,单纯追求下载量已无法保证商业成功,精细化运营核心指标成为盈利关键。本文将深入解析每日活跃用户平均营收&#xff…

被CC攻击了,对服务器有什么影响?

博客正文: 最近,不少网站管理员和运维人员反映遭遇了CC攻击,导致服务器性能异常甚至瘫痪。那么,CC攻击究竟会对服务器造成哪些影响?本文将为你简要解析CC攻击的原理及其带来的危害,帮助你更好地理解并应对…

Tensorflow安装出现dependency conflict错误

Python版本: 3.11.4 pip版本已升到最新 电脑上有mac的原装Python2.x,我装的3.11.4,还有个什么依赖的3.9 运行 pip3 install tensorflow 出现类似以下错误 (我报错的是另一个不是tensorflow—estimator,但基本就是…

2025年HTTP半开与错误攻击防御指南:原理拆解与实战防护

你以为限流就能防住HTTP攻击?黑客用协议畸形包AI调度正在撕裂传统防线! 一、HTTP半开攻击:慢速绞杀服务器资源 ▶ 攻击原理剖析 HTTP半开攻击(如Slowloris)是一种应用层DoS攻击,通过建立大量半开连接耗尽…

Mybatis(XML映射文件、动态SQL)

目录 基础操作 准备: 删除: 新增: 更新: 查询: 条件查询: XML映射文件 动态SQL if foreach sql&include 基础操作 准备: 准备数据库表 创建一个新的springboot工程&#xff0…

python校园拼团系统

目录 技术栈介绍具体实现截图系统设计研究方法:设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理,难度适中&#xf…

多模态大语言模型arxiv论文略读(127)

When SAM2 Meets Video Camouflaged Object Segmentation: A Comprehensive Evaluation and Adaptation ➡️ 论文标题:When SAM2 Meets Video Camouflaged Object Segmentation: A Comprehensive Evaluation and Adaptation ➡️ 论文作者:Yuli Zhou, …

剑指offer32_二叉搜索树的后序遍历序列

二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。 如果是则返回true,否则返回false。 假设输入的数组的任意两个数字都互不相同。 数据范围 数组长度 [ 0 , 1000 ] [0,1000] [0,1000]。 样例 输入&…

《仿盒马》app开发技术分享-- 订单结合优惠券结算(端云一体)

技术栈 Appgallery connect 开发准备 上一节我们已经实现了优惠券的选择,并且成功的把券后的价格也展示给用户,不能使用的优惠券我们也用友好的方式告知用户,这一节我们来实现优惠券内容的下一步,优惠券内容结合订单进行结算提…

Python+Selenium+Pytest+POM自动化测试框架封装

🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 1、测试框架简介 1)测试框架的优点 代码复用率高,如果不使用框架的话,代码会显得很冗余。可以组装日志、报告、邮件等一些…

宋代大模型:智能重构下的文明再发现

引言:当汴京城遇见生成式AI 一幅动态的《清明上河图》正通过全息投影技术演绎汴京城的市井百态。这个虚实交融的场景,恰似宋代大模型技术的隐喻——以人工智能为纽带,连接起东京梦华的繁盛图景与数字时代的文明重构。作为人工智能与历史学交…

K-means++:让K-means“聪明”地选择初始中心点

大家好!欢迎来到我的技术分享博客~ 👋 在前两篇博客中,我们深入探讨了经典的 K-means 算法 以及它的优化方案 Canopy K-means。如果你还没有看过,强烈建议先回顾一下,因为今天的主题 K-means 和它们有着千丝万缕的联系…

Langchain学习笔记(1)——如何调用Huggingface的模型并实现实时返回生成结果

Langchain支持很方便的OpenAI模型的调用,可以做到快速开发大模型应用。但是要使用Huggingface上的开源模型就没有那么方便了,本文就详细阐述如何用Langchain开发基于Huggingface上的模型,并实时返回生成结果。 实时返回生成结果是LLM很关键的…

Java安全-常规漏洞问题(SQL注入,XXE,SSRF,RCE)

靶场搭建 靶场下载 : https://github.com/whgojp/JavaSecLab这个靶场是使用Springboot搭建的所以不要下载 jar 文件运行,要使用IDEA运行他的文件夹 先打开pom 然后进行maven一下 改一下端口 配置完成之后修改一下 运行的模式 使用phpstudy搞一个sql数…

基于视频的 AI 内存库,极速语义检索

简介 在大模型应用里,将文本数据分块嵌入存储在向量数据库已经是标准做法。然而,传统向量数据库虽然功能强大,但其高昂的RAM和存储需求,以及复杂的部署运维,常常让开发者望而却步。今天,介绍一个名为 Memv…

接口适配器模式实现令牌桶算法和漏桶算法

以下是令牌桶算法、漏桶算法和雪花算法的清晰对比解析。它们属于完全不同的技术领域,前两者用于流量控制,后者用于分布式ID生成: 1. 令牌桶算法(Token Bucket) 领域:流量整形 / 速率限制核心目标&#xff…

618背后的电商逻辑重构:从价格血战到价值共生

“今年终于没做数学题。” 618进行到一半,行云已经买了很多,大件的有iPad、iWatch,小件的有运动鞋、面膜、纸巾。往年她要凑凑减减,经常要找个店铺凑单,下完单再马上退掉,今年她没废太多脑细胞&#xff0c…

解决 PyTorch 与 Python 3.12 的兼容性问题:`operator torchvision::nms does not exist` 深度解析

解决 PyTorch 与 Python 3.12 的兼容性问题 问题现象错误根源分析终极解决方案🚀 推荐方案:创建 Python 3.11 虚拟环境⚡ 备选方案:使用 PyTorch 夜间构建版(Python 3.12)验证修复技术深度解析最佳实践建议问题现象 当在 Python 3.12 环境中运行以下代码时: from tran…