FreeRTOS队列(queue)

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

1、队列的特性

1、1常规操作

队列的简化操如下图所示,从此图可知:

  • 队列中可以包含若干数据:队列中有若干项,这被称为“长度”;
  • 每个数据大小固定;
  • 创建队列时就要指定长度、数据大小;
  • 数据的操作采用先进先出的方法:写数据时放入尾部,读数据时从头部读;
  • 也可以强制写队列头部:覆盖头部数据。

1、2 数据传输的两种方式

使用队列传输数据时有两种方法:

拷贝:把数据、变量的值复制进队列里;

引用:把数据、变量的地址复制进队列里。

FreeRTOS 使用拷贝值的方法,这更简单:
⚫ 局部变量的值可以发送到队列中,后续即使函数退出、局部变量被回收,也不会影响队列中的数据;
⚫ 无需分配 buffer 来保存数据,队列中有 buffer;
⚫ 局部变量可以马上再次使用;
⚫ 发送任务、接收任务解耦:接收任务不需要知道这数据是谁的、也不需要发送任务来释放数据;
⚫ 如果数据实在太大,你还是可以使用队列传输它的地址;
⚫ 队列的空间有 FreeRTOS 内核分配,无需任务操心;
⚫ 对于有内存保护功能的系统,如果队列使用引用方法,也就是使用地址,必须确保双方任务对这个地址都有访问权限。使用拷贝方法时,则无此限制:内核有足够的权限,把数据复制进队列、再把数据复制出队列。

1、3队列的阻塞访问

        只要知道队列的句柄,谁都可以读、写该队列。任务、 ISR 都可读、写队列。可以多个任务读写队列。
        任务读写队列时,简单地说:如果读写不成功,则阻塞;可以指定超时时间。口语化地说,就是可以定个闹钟:如果能读写了就马上进入就绪态,否则就阻塞直到超时。
        某个任务读队列时,如果队列没有数据,则该任务可以进入阻塞状态:还可以指定阻塞的时间。如果队列有数据了,则该阻塞的任务会变为就绪态。如果一直都没有数据,则时间到之后它也会进入就绪态。
        既然读取队列的任务个数没有限制,那么当多个任务读取空队列时,这些任务都会进入阻塞状态:有多个任务在等待同一个队列的数据。当队列中有数据时,哪个任务会进入就绪态?


⚫ 优先级最高的任务
⚫ 如果大家的优先级相同,那等待时间最久的任务会进入就绪态

        跟读队列类似,一个任务要写队列时,如果队列满了,该任务也可以进入阻塞状态:还可以指定阻塞的时间。如果队列有空间了,则该阻塞的任务会变为就绪态。如果一直都没有空间,则时间到之后它也会进入就绪态。既然写队列的任务个数没有限制,那么当多个任务写"满队列"时,这些任务都会进入阻塞状态:有多个任务在等待同一个队列的空间。当队列中有空间时,哪个任务会进入就绪态?


⚫ 优先级最高的任务
⚫ 如果大家的优先级相同,那等待时间最久的任务会进入就绪态

2、队列函数

使用队列的流程:创建队列、写队列、读队列、删除队列。

2.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
)

示例代码:

// 示例代码
#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_SIZExQueue1 = xQueueCreateStatic( QUEUE_LENGTH,ITEM_SIZE,ucQueueStorage,&xQueueBuffer );
}

2.2 复位

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

/* pxQueue : 复位哪个队列;
* 返回值: pdPASS(必定成功)
*/
BaseType_t xQueueReset( QueueHandle_t pxQueue);

2.3删除

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

void vQueueDelete( QueueHandle_t xQueue );

2.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
);

  

2.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
);

2.6查询

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

/*
* 返回队列中可用数据的个数
*/
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
/*
* 返回队列中可用空间的个数
*/
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

2.7覆盖 or偷看

当队列长度为 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
);

如果想让队列中的数据供多方读取,也就是说读取时不要移除数据,要留给后来人。那
么可以使用"窥视",也就是xQueuePeek()或xQueuePeekFromISR()。这些函数会从队列中
复制出数据,但是不移除数据。这也意味着,如果队列中没有数据,那么"偷看"时会导致阻
塞;一旦队列中有数据,以后每次"偷看"都会成功。
 

/* 偷看队列
* xQueue: 偷看哪个队列
* pvItemToQueue: 数据地址, 用来保存复制出来的数据
* xTicksToWait: 没有数据的话阻塞一会
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueuePeek(
QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait
);
BaseType_t xQueuePeekFromISR(
QueueHandle_t xQueue,
void *pvBuffer,
);

3、传输大数据

FreeRTOS的队列使用拷贝传输,也就是要传输uint32_t时,把4字节的数据拷贝进队列;
要传输一个8字节的结构体时,把8字节的数据拷贝进队列。
如果要传输1000字节的结构体呢?写队列时拷贝1000字节,读队列时再拷贝1000字节?
不建议这么做,影响效率!
这时候,我们要传输的是这个巨大结构体的地址:把它的地址写入队列,对方从队列得
到这个地址,使用地址去访问那1000字节的数据。
使用地址来间接传输数据时,这些数据放在RAM里,对于这块RAM,要保证这几点:
⚫ RAM 的所有者、操作者,必须清晰明了
这块内存,就被称为"共享内存"。要确保不能同时修改 RAM。比如,在写队列之
前只有由发送者修改这块 RAM,在读队列之后只能由接收者访问这块 RAM。
⚫ RAM 要保持可用
这块 RAM 应该是全局变量,或者是动态分配的内存。对于动然分配的内存,要
确保它不能提前释放:要等到接收者用完后再释放。 另外,不能是局部变量。
 

实例

程序会创建一个队列,然后创建1个发送任务、 1个接收任务:
⚫ 创建的队列:长度为 1,用来传输"char *"指针
⚫ 发送任务优先级为 1,在字符数组中写好数据后,把它的地址写入队列
⚫ 接收任务优先级为 2,读队列得到"char *"值,把它打印出来


        这个程序故意设置接收任务的优先级更高,在它访问数组的过程中,接收任务无法执行、无法写这个数组。

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

/* 定义一个字符数组 */
static char pcBuffer[100];
/* vSenderTask被用来创建2个任务,用于写队列
* vReceiverTask被用来创建1个任务,用于读队列
*/
static void vSenderTask( void *pvParameters );
static void vReceiverTask( void *pvParameters );
/*-----------------------------------------------------------*/
/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;
int main( void )
{
prvSetupHardware();
/* 创建队列: 长度为1,数据大小为4字节(存放一个char指针) */
xQueue = xQueueCreate( 1, sizeof(char *) );
if( xQueue != NULL )
{
/* 创建1个任务用于写队列
* 任务函数会连续执行,构造buffer数据,把buffer地址写入队列
* 优先级为1
*/
xTaskCreate( vSenderTask, "Sender", 1000, NULL, 1, NULL );
/* 创建1个任务用于读队列
* 优先级为2, 高于上面的两个任务
* 这意味着读队列得到buffer地址后,本任务使用buffer时不会被打断
*/
xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );
/* 启动调度器 */
vTaskStartScheduler();
}
else
{
/* 无法创建队列 */
}
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}

发送任务的函数中,现在全局大数组pcBuffer中构造数据,然后把它的地址写入队列,
代码如下
 

static void vSenderTask( void *pvParameters )
{BaseType_t xStatus;static int cnt = 0;char *buffer;/* 无限循环 */for( ;; ){sprintf(pcBuffer, "www.100ask.net Msg %d\r\n", cnt++);buffer = pcBuffer; // buffer变量等于数组的地址, 下面要把这个地址写入队列/* 写队列* xQueue: 写哪个队列* pvParameters: 写什么数据? 传入数据的地址, 会从这个地址把数据复制进队列* 0: 如果队列满的话, 即刻返回*/xStatus = xQueueSendToBack( xQueue, &buffer, 0 ); /* 只需要写入4字节, 无需写入整个buffer */if( xStatus != pdPASS ){printf( "Could not send to the queue.\r\n" );}}
}

接收任务的函数中,读取队列、得到buffer的地址、打印,代码如下
 

static void vReceiverTask( void *pvParameters )
{/* 读取队列时, 用这个变量来存放数据 */char *buffer;const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );BaseType_t xStatus;/* 无限循环 */for( ;; ){/* 读队列* xQueue: 读哪个队列* &xReceivedStructure: 读到的数据复制到这个地址* xTicksToWait: 没有数据就阻塞一会*/xStatus = xQueueReceive( xQueue, &buffer, xTicksToWait); /* 得到buffer地址,只是4字节 */if( xStatus == pdPASS ){/* 读到了数据 */printf("Get: %s", buffer);}else{/* 没读到数据 */printf( "Could not receive from the queue.\r\n" );}}
}

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

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

相关文章

给客户做WMS培训时,客户说这是什么垃圾系统……

导语 大家好,我是社长,老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》 在上周的一次WMS系统培训现场,我们遭遇了意想不到的“小插曲”。 当时,培训师小李正热情洋溢地介绍WMS系统的种种优点&am…

使用 PNPM 从零搭建 Monorepo,测试组件并发布

1 目标 通过 PNPM 创建一个 monorepo(多个项目在一个代码仓库)项目,形成一个通用的仓库模板。 这里以在该 monorepo 项目中搭建 web components 类型的组件库为例,介绍从仓库搭建、组件测试到组件发布的整个流程。 这个仓库既可…

ffmpeg封装和解封装介绍-(10)综合完成视频重编码为h265,解封装解码编码再封装

主函数逐句解析: 由于代码太多我们只解析主函数,(其他封装函数见前面文章,同时用到了解码编码封装代码)。 初始化和参数处理 int main(int argc, char* argv[]) {/// 输入参数处理string useage "124_test_x…

教育小程序开发:技术实现与实践案例

随着信息技术的不断进步,教育小程序在教育领域的应用越来越广泛。教育小程序开发不仅可以提高教学效率,还能够提供个性化的学习体验。本文将以技术代码为例,详细介绍教育小程序开发的关键技术和实践案例,帮助开发者更好地理解和实…

鸿蒙应用开发

学习视频: 00.课程介绍_哔哩哔哩_bilibili 官网:开发者文档中心 | 华为开发者联盟 (huawei.com) 开发工具 :DevEcoStudio , 类似Jetbrains 全家桶 ArkTS开发语言 :(基于TS,集成了前端语言&#xf…

奥特曼谈AI的机遇、挑战与人类自我反思:中国将拥有独特的大语言模型

奥特曼在对话中特别提到,中国将在这个领域扮演重要角色,孕育出具有本土特色的大语言模型。这一预见不仅彰显了中国在全球人工智能领域中日益增长的影响力,也预示着未来技术发展的多元化趋势。 ①奥特曼认为AI在提升生产力方面已显现积极作用&…

【JS重点17】原型链(面试重点)

一:原型链底层原理 以下面一段代码为例,基于原型对象(Star构造函数的原型对象)的继承使得不同构造函数的原型对象关联在一起(此处是最大的构造函数Object原型对象),并且这种关联的关系是一种链…

Project ERROR: Unknown module(s) in QT: xlsx

Qt5下Qxlsx模块安装及使用_qt5xlsx-CSDN博客 主要参考上面这篇文章! Perl的安装与配置_perl安装-CSDN博客 1.1 windows环境安装Perl_windows perl-CSDN博客 首先,需要安装Perl,我安装的是Windows版本的。 Download & Install Perl - ActiveStat…

绿色版DirectoryOpus功能强大且高度可定制的Windows文件管理器

Directory Opus(通常简称为DOpus)是一款功能强大且高度可定制的Windows文件管理器。它提供了许多超越Windows默认文件资源管理器(Explorer)的功能,使得文件和文件夹的管理变得更加高效和直观。以下是对Directory Opus的…

如何用Java程序实现一个简单的消息队列?

在Java程序中,可以使用内置的java.util.concurrent.BlockingQueue作为消息队列存放的容器,来实现一个简单的消息队列。 具体实现如下,在这个例子中,我们创建了一个生产者线程和一个消费者线程,他们共享同一个阻塞队列…

Nginx配置详细解释:(4)高级配置

目录 1.网页的状态页 2.Nginx第三方模块(echo) 3.变量 4.自定义访问日志 5.Nginx压缩功能 6.https功能 7.自定义图标 Nginx除了一些基本配置外,还有一些高级配置,如网页的状态,第三方模块需要另外安装,支持变量&#xff0c…

使用了代理IP怎么还会被封?代理IP到底有没有效果

代理IP作为一种网络工具,被广泛应用于各种场景,例如网络爬虫、海外购物、规避地区限制等。然而,很多用户在使用代理IP的过程中却发现自己的账号被封禁,这让他们不禁产生疑问:使用了代理IP怎么还会被封?代理…

Unity接入PS5手柄和Xbox手柄以及Android平台的(以及不同平台分析)

Unity接入PS5手柄和Xbox手柄以及Android平台的(以及不同平台分析) 介绍Unity手柄小知识PC端和编辑器上的摇杆事件和滑动事件PS5手柄Xbox手柄北通手柄 安卓环境下(安卓手机或者安卓模拟器)PS5手柄Xbox手柄北通手柄 总结 介绍 最近…

Scala网络编程:代理设置与Curl库应用实例

在网络编程的世界里,Scala以其强大的并发模型和函数式编程特性,成为了开发者的得力助手。然而,网络请求往往需要通过代理服务器进行,以满足企业安全策略或访问控制的需求。本文将深入探讨如何在Scala中使用Curl库进行网络编程&…

消息队列-RabbitMQ-延时队列实现

死信队列 DLX,全称为Dead-Letter-Exchange,死信交换机,死信邮箱。当消息在一个队列中变成死信之后,它能重新发送到另外一个交换器中,这个交换器就是DLX,绑定DLX的队列就称为死信队列。 导致死信的几种原因: ● 消息…

数据交换平台_10_activatemq 中间件容错性测试

目录概要 3. 容错测试: - 模拟ActiveMQ在异常情况下的表现,如网络中断、节点故障等。 - 观察ActiveMQ的容错机制是否能够正确处理异常情况,保证消息的可靠传输。 - 根据容错测试结果,优化ActiveMQ的容错机制,确保系统在面对异常情况时能够正确处理并恢复。 设计: 容错测…

ffmpeg解封装rtsp并录制视频-(2)使用VLC模拟一个rtsp服务器并用ffmpeg解封装该rtsp流

VCL模拟服务器并打开播放该视频文件: - 准备好一个mp4文件,打开vlc软件 - 选择“媒体”》“流” - 添加一个mp4文件 - 点击下方按钮选择“串流” - 下一步目标选择rtsp 点击“添加” - 端口默认8554 - 路径设置 /test - 用…

Shell脚本从入门到实战

一、概述 shell 是一个命令行解释器,它接受应用程序、用户命令,然后调用操作系统内核。 shell 还是一个功能强大编程语言,易调试,易编写,灵活性强。 二、mac 怎么重启docker 1.如何重启 Docker on Mac 在 macOS 上…

DockerCompose+Jenkins+Pipeline流水线打包Vue项目(解压安装配置Node)入门

场景 DockerComposeJenkinsPipeline流水线打包SpringBoot项目(解压安装配置JDK、Maven等)入门: DockerComposeJenkinsPipeline流水线打包SpringBoot项目(解压安装配置JDK、Maven等)入门-CSDN博客 以上使用流水线配置和打包springboot后台项目,如果要使…

机器学习(V)--无监督学习(二)主成分分析

当数据的维度很高时,很多机器学习问题变得相当困难,这种现象被称为维度灾难(curse of dimensionality)。 在很多实际的问题中,虽然训练数据是高维的,但是与学习任务相关也许仅仅是其中的一个低维子空间&am…