FreeRTOS基础四:队列

2023-05-16

简介

FreeRTOS中的队列是一种用于实现【任务与任务】,【任务与中断】以及【中断与任务】之间的通信机制。

此外,任务从队列读数据或者写入数据到队列时,都可能被阻塞。这个特性使得任务可以被设计成基于事件驱动的运行模式,大大提高了CPU的执行效率。队列是实现FreeRTOS中其他特性的基础组件,像软件定时器,信号量,互斥锁都是基于队列而实现的。

队列的基本特性

队列是一种FIFO操作的数据结构,入队操作就是把一个新的元素放进队尾(tail),出队操作就是从队头(front)取出一个元素。FreeRTOS中也支持把一个元素放到队头的操作,这个操作会覆盖之前队头的元素。

备份入队策略

队列在设计的时候,主要有两种元素入队存储策略:Queue by copy 和 Queue by reference。FreeRTOS的队列使用的是Queue by copy存储策略,考虑到这种策略实现起来更加简单,灵活,且安全。

  • Queue by copy:数据入队的时候,队列中存储的是此数据的一份拷贝
  • Queue by reference:数据队列的时候,队列中存储的是数据的指针,而非数据本身

读队列时阻塞

一个任务在尝试从队列中读取数据时,可以指定一个阻塞时间,即任务在等待队列有元素可读前最长等待阻塞时间。当队列中有元素可读时,任务会自动从阻塞态转换为就绪态。如果在等待时间内队列一直没有数据可读,则等待时间到期后,任务也会自动从阻塞态转换为就绪态,但是它会返回一个读取失败的结果。

队列可能有多个reader Task,所以在等待队列有数据可读时,可能会有多个任务阻塞。当队列有数据可读时,调度器会从所有阻塞的任务中选取优先级最高的那个任务让其进入到就绪态。如果最高优先级的不止一个,则让等待最久的那个进入到就绪态。

写队列时阻塞

一个任务在尝试写入数据到队列时,可以指定一个阻塞时间,即任务在等待队列有空余空间(非满)可写前最长等待阻塞时间。当队列中有空间可写入时,任务会自动从阻塞态转换为就绪态。如果在等待时间内队列一直是满的,则等待时间到期后,任务也会自动从阻塞态转换为就绪态,但是它会返回一个写入失败的结果。

队列可能有多个writerTask,所以在等待队列有空闲空间时,可能会有多个任务阻塞。当队列有空闲空间可写时,调度器会从所有阻塞的任务中选取优先级最高的那个任务让其进入到就绪态。如果最高优先级的不止一个,则让等待最久的那个进入到就绪态。

队列的创建

在FreeRTOS中创建队列时要指定队列的长度和存储的元素的大小

使用xQueueCreate()内核函数来创建一个队列。队列的存储空间从FreeRTOS heap中分配。在使用xQueueCreate()创建队列时,如果FreeRTOS heap中没有足够的存储空间分配给当前队列,则函数返回NULL。如果创建成功,则返回队列的句柄(QueueHandle_t类型)。

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

参数 uxQueueLength:指定队列的长度,即最多可以存放的元素个数

参数 uxItemSize:队列中存储的元素的大小(占用的字节数)

返回值:返回NULL代表创建失败,没有足够的堆空间来创建当前队列;创建成功则返回队列的句柄。

元素入队操作

使用xQueueSendToBack()函数来向队尾存放一个元素,实现元素入队操作。xQueueSend()函数与xQueueSendToBack()等价,用法一样,只是名称不同。

BaseType_t xQueueSendToBack( QueueHandle_t xQueue,
                             const void * pvItemToQueue,
                             TickType_t xTicksToWait );


//xQueueSendToBack的定义
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) \
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )

//xQueueSend的定义
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )

参数xQueue:目标队列的句柄。

参数pvItemToQueue:入队元素的指针。队列将存储此指针指向的数据的备份。

参数xTicksToWait:指定等待队列有空间可以容纳新元素入队的最长等待(阻塞)时间,这种情况发生在队列已经满了的时候。如果需要等待,则任务会因为调用这个函数而进入阻塞状态,直到队列非满而能让这个任务写入数据或者指定的阻塞时间过期,才会转变为就绪态。如果此参数使用0,则当队列已经满了的时候,此函数立即返回而不阻塞。

使用portMAX_DELAY作为参数将使得等待时间为无限长,直到队列非满而能让这个任务写入数据,注意如果要使用portMAX_DELAY为参数,则必须在FreeRTOSConfig.h将INCLUDE_vTaskSuspend定义为1。

返回值:pdPASS,当元素成功入队时返回pdPASS。例如,在限定超时时间(xTicksToWait非0)过期前成功入队。

errQUEUE_FULL,因为队列满而无法入队时返回errQUEUE_FULL。例如,在限定超时时间(xTicksToWait非0)过期后,队列一直为满而无法入队新元素。

元素出队操作

使用xQueueReceive()内核函数来从队列中读取队头元素,读取成功后队列将删除这个元素,实现元素出队操作。

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

参数xQueue:目标队列的句柄。

参数pvBuffer:用于存放读取到的队列元素的缓冲区,队列将把出队的元素拷贝到此缓冲区中。

参数xTicksToWait:参见xQueueSendToBack()函数的解释。

返回值:pdPASS,当成功从队列读取到元素时返回pdPASS。例如,在限定超时时间(xTicksToWait非0)过期前,成功读取到元素。

errQUEUE_EMPTY,因为队列空而无法出队时返回errQUEUE_EMPTY。例如,在限定超时时间(xTicksToWait非0)过期后,队列一直为空而无法读取元素。

查询队列中元素个数

使用内核函数uxQueueMessagesWaiting()来获取队列中有多少个元素。

UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );

参数xQueue:队列的句柄

返回值:队列中的元素个数,返回0代表队列为空

处理来自多个数据源的队列数据

一个队列可以有多个向里写入数据的任务,也可以由多个从中读取数据的任务。 实际开发中,更常见的情况是一个或多个任务向队列中写入数据,另外一个任务从队列中读取数据。

有时候我们会遇到这样一种设计需求:只使用单个队列,一个任务作为队列的reader Task,多个任务作为队列的writer Task,那么reader Task如何区分数据的来源呢?毕竟来自不同数据源的数据有不同的意义,需要使用不同的解析手段。

这里介绍一种简单易行的办法。当 reader Task向队列中存放数据时,每个数据元素都附加一个数据来源ID字段,来标识数据是来自哪个任务,或者说具有何种意义的。如下图所示:

其中UART Task,AnOther Task和Keyboard Task都是writer Task,Process Task是reader Task。队列中来自UART Task的数据用数字1标识,来自AnOther Task的数据用数字2标识,来自Keyboard Task的数据用数字3标识。Process Task从队列中读取一个元素时,根据其数据源ID的不同,判断出数据的来源,并做出不同的处理动作。

使用队列存储大型数据元素

当需要存储在队列中的元素占用的字节数比较大时,最好在队列中存储指向这些元素所在空间的指针,而非数据元素本身,这样更加高效,也节省单片机SRAM,因为FreeRTOS的队列采用的是Queue by copy存储策略的。但是要注意一些细节。

  • 要避免两个或者多个任务同时通过队列中的指针来修改指针指向的大型数据,这会导致内存数据不一致,而发生不被期望的结果。正确的做法是,writer Task应该准备好这个大型数据,在他没把这个大块数据的指针放入到队列中前, 他可以任意读写。但当他将大型数据的地址入队,随后就不要通过指针访问这个大型数据了。而reader Task只有在从队列中取出指针后,才能通过这个指针来访问(读/写)它指向的大型数据。
  • 如果指针元素指向的大型数据内存是动态分配的,则必须有一个任务要负责释放这个内存,并且到内存被释放后,要保证没不会再访问这个无效的内存。
  • 位于任务的栈空间或者其他函数的栈空间的数据,不能通过这种方式入队,因为他们可能会随着栈帧的变化而失效。也就是说,指针指向的大型数据应该存放在FreeRTOS heap中,或者是用户自定义的全局变量。

处理来自多个数据源的不定类型数据

如果把指针和数据来源类型标识结合起来使用,就可以实现reader Task用单个队列接收来自多个writer Task的不定类型不定长度的数据了。FreeRTOS的TCP扩展库就是一个例子。

TCP Task通过队列接收来自多个其他任务的事件,事件被封装为一个队列元素,队列元素包含了事件类型和事件携带的数据,这个数据是通过void*类型指针传输的,所以数据可以是任意类型和任意长度的。TCP Task从队列接收到事件后,根据事件类型,做出不同的解析。

//定义事件类型枚举
typedef enum
{
    eNetworkDownEvent = 0, 
    eNetworkRxEvent, 
    eTCPAcceptEvent, 
} eIPEvent_t;

//定义队列元素类型
typedef struct IP_TASK_COMMANDS
{
    eIPEvent_t eEventType;   //事件类型
    void *pvData;            //事件携带的数据
} IPStackEvent_t;


//事件发送者,queue writer
void vSendAcceptRequestToTheTCPTask( Socket_t xSocket )
{
    IPStackEvent_t xEventStruct;
    xEventStruct.eEventType = eTCPAcceptEvent;  //指定事件类型
    xEventStruct.pvData = ( void * ) xSocket;   //事件携带的数据
    xSendEventStructToIPTask( &xEventStruct );  //将事件发送到队列
}


//TCP Task,queue reader Task
xQueueReceive( xNetworkEventQueue, &xReceivedEvent, xNextIPSleep );  //从队列取元素
switch( xReceivedEvent.eEventType )  //根据元素的事件类型,做出相应的数据的解析
{
    case eTCPAcceptEvent:
    xSocket = ( FreeRTOS_Socket_t * ) ( xReceivedEvent.pvData );
    xTCPCheckNewClient( pxSocket );
    break;
}

接收来自多个队列的数据

忠告:一个任务接收来着多个队列的数据,这种设计模式在FreeRTOS中是不优雅的,不高效的,是不被推荐的,应该尽量让一个任务只接收来自一个队列的数据。如果应用程序迫不得已,必须让某一个任务接收来自多个队列的消息,那么可以使用队列集合来实现这种需求。

队列集合可以实现接收来自多个队列的数据,同时不需要开发者编写代码挨个查询这些目标队列是否有元素可读。此内容详见FreeRTOS官方手册。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS基础四:队列 的相关文章

  • 简记ReNet

    ResNet 论文给出了Resnet有3中类型 xff0c ResNet50 res3 res4 res6 res3 xff0c ResNet101 res3 res4 res23 res3 和ResNet152 res3 res4 res
  • triplet loss的原理及caffe代码

    1 xff1a triplet loss的原理及梯度推到 http blog csdn net tangwei2014 article details 46788025 2 xff1a triplet loss如何增加到caffe中 xff
  • object detection资料汇总

    转自 xff1a https handong1587 github io deep learning 2015 10 09 object detection html Jump to LeaderboardPapers R CNNMulti
  • 离职小记~~~

    这个月作出的最重要的决定 离职 对于已工作的人来说 xff0c 离职就是跳槽 xff0c 特别是对于我们计算机专业 xff0c 简简单单的一封辞职信 xff0c 等个半月 xff0c 办完手续 xff0c 一切OK xff0c 而且貌似越跳
  • UC Berkeley 马毅:深度学习的第一性原理

    专栏 xff1a 研究思路 我们认为 xff0c 人工智能进入了新的拐点 在一个后深度学习时代 xff0c 不同的学者对未来智能发展道路的理解逐渐清晰 xff0c 当然也逐渐开始分化 xff0c 由此导致了开展布局完全不同的研究工作 智源社
  • 播放器插件实现系列 —— vlc

    vlc 是通过模块来扩展它的功能 xff0c 插件一般就是实现一个模块 vlc的模块有很多类型 xff1a AccessDemuxAccess DemuxDecoderInterfaceVideo filterAudio filterAud
  • 逻辑回归算法——经典的二分类算法

    一 概述 1 逻辑回归 xff08 Logistic Regression xff09 算法是分类算法 xff0c 而不是回归算法 2 决策边界 xff1a 可以是非线性的 xff08 高阶 xff09 二 sigmoid函数 1 定义 x
  • docker 部署redis 并设置密码

    安装docker docker pull redis 获取redis conf 从官网获取redis conf 修改默认配置文件 bind 127 0 0 1 注释掉这部分 xff0c 这是限制redis只能本地访问 protected m
  • 获取全文的13种方法

    注 xff1a 由于大部分院校未能购卖国内外商业医学数据库 xff0c 如 PUBMED ElseVier等 xff0c 因而检索国外全文文献很复杂 这往往成为少数学校的专利 北大医学院网站上有大量文献题录 xff0c 但仅供自已学生使用
  • PID调节(理论)

    AQ的PID相关资料 xff0c 我添加了些 机械控制工程基础 中的内容 什么是PID PID包括比例P xff0c 积分I xff0c 微分D控制器 使用PID控制器 xff0c 你需要以下6个基本元素 xff1a Error xff08
  • Mavlink协议

    Mavlink协议简介 Mavlink协议简介 一Mavlink协议二数据结构三协议支持的数据类型四性能五航点协议 读取MAV的航点列表写MAV的航点列表清除MAV的航点列表设置当前MAV航点 六参数的读写 读取参数列表读取单个参数写参数
  • 计算机图形学方向投稿国外期刊

    图形学领域的杂志 xff08 主要发表三维重构与可视化的一般理论方面的论文 xff09 对图形学领域的杂志 xff0c 国外一个专家有一个很好的评价 xff0c 我基本上按照其的意思翻译 xff0c 加上自己的一点理解 xff1a 1 顶级
  • 使用电脑摄像头计算aruco marker位姿(Python)

    一 效果图 刚做了一些尝试 xff0c 算两个aruco之间的距离 先算x方向 xff0c 用ID 61 12减去ID 61 13 xff0c tvec的三个坐标依次是Z Y X 所以 xff0c ID 61 12和ID 61 13的x距离
  • vnc登录时connection refused(10061)解决方法

    问题 xff1a 当你在windows上连接linux上vnc服务器时 xff0c 点击connect后弹出connect connect refused 10061 警告框时连接被拒绝 首先 xff0c 如果你的VNC配置没有问题并且在l
  • 2014年计算机求职总结--准备篇

    版权所有 xff0c 转载请注明出处 xff0c 谢谢 xff01 http blog csdn net walkinginthewind article details 13000431 找工作是一个长期准备的过程 xff0c 突击是没什
  • 2022智源大会议程公开 | 人工智能新基建论坛

    2022年北京智源大会召开在即 xff0c 5月31日至6月2日 xff0c 持续三天 xff0c 26场由各领域领军学者主导的专题论坛 大会将紧紧围绕这些当前学术领域迫切需要解决的问题 xff0c 以及产业落地过程中存在的诸多挑战 xff
  • 因特尔神经计算棒(NCS)+树莓派+yolo-v3实现object-detection

    因特尔神经计算棒 NCS 43 树莓派 43 yolo v3实现object detection 踩了好多坑啊 一言难尽 Step1 在树莓派上安装环境 首先安装openVINO工具包 xff08 最好不要在树莓派上装NCSDK 太坑了 x
  • prometheus监控k8s集群

    prometheus监控k8s集群 1 监控方式 node expoter部署在k8s节点收集当前节点资源 xff0c Prometheus用来分析处理收集汇总的数据 xff0c grafana用来进行图形展示 2 部署Prometheus
  • 耦合、解耦是什么?怎么做到解耦呢?

    一 耦合 耦合指的是两个类之间的联系的紧密程度 xff1b 强耦合 类之间存在着直接关系 弱耦合 xff1a 在两个类的中间加入一层 xff0c 将原来的直接关系变成间接关系 xff0c 使得两个类对中间层是强耦合 xff0c 两类之间变为
  • 圈复杂度检测

    1 下载链接 xff1a https github com terryyin lizard 2 使用 xff1a python lizard py 文件名 文件路径 3 显示 NLOC xff1a 代码行数 CCN xff1a 复杂度 to

随机推荐

  • 服务限流降级

    https segmentfault com a 1190000015967922 高并发的线上服务 xff0c 有时候面对突发流量 xff0c 比如流量突然激增 xff0c 服务扛不住了 xff0c 类似这种场景 xff0c 需要有一个防
  • gdb调试

    c 43 43 编译生成可执行文件 g 43 43 main cpp 1 进入gdb gdb main 2 运行 r 查看 l 断点 b line num 查看 p 参数 展示display 参数 继续 c 3 比如展示vector首地址
  • git 修改commit的名字与撤回提交

    当你不小心 xff0c 写错了提交的注视 信息 xff0c 该如何处理呢 理论上 xff0c SCM是不应该修改历史的信息的 xff0c 提交的注释也是 不过在git中 xff0c 其commit提供了一个 amend参数 xff0c 可以
  • SQLAlchemy 的 scoped_session

    SQLAlchemy 的 scoped session 是啥玩意 通常我们用 SQLAlchemy 写数据的时候要创建 Session 对象来维护数据库会话 xff0c 用完了再关掉 但是听说还有个叫scoped session的玩意 xf
  • python 的StringIO

    1 IO的含义 在计算机中 xff0c IO是Input Output的简写 xff0c 也就是输入和输出 由于程序和运行时数据是在内存中驻留 xff0c 由CPU这个超快的计算核心来执行 xff0c 涉及到数据交换的地方 xff0c 通常
  • python 的fcntl模块

    python 中给文件加锁 fcntl模块 import fcntl 打开一个文件 f 61 open 39 test 39 当前目录下test文件要先存在 xff0c 如果不存在会报错 对该文件加密 xff1a fcntl flock f
  • Jmeter添加MD5方法插件

    1 xff1a 下载 https jmeter plugins org install Install 2 xff1a jmeter plugins manager 1 3 jar放到 apache jmeter 5 0 lib ext目录
  • sql 语句 将查询结果中数字等标示转成汉字

    使用case 语句 将sex 字段进行转换 0 1 2 男 xff0c 女 xff0c 未知 SELECT u userid u username CASE u sex WHEN 0 THEN 39 女 39 WHEN 1 THEN 39
  • linux的more命令

    more命令 xff0c 功能类似 cat xff0c cat命令是整个文件的内容从上到下显示在屏幕上 more会以一页一页的显 示方便使用者逐页阅读 xff0c 而最基本的指令就是按空白键 xff08 space xff09 就往下一页
  • docker入门实例

    1 image文件 Docker 把应用程序及其依赖 xff0c 打包在 image 文件里面 只有通过这个文件 xff0c 才能生成 Docker 容器 image 文件可以看作是容器的模板 Docker 根据 image 文件生成容器的
  • vim批量替换&行首尾增加&删除列

    删除列 删除列的方法 xff1a 1 ctrl 43 v 这样会启动可视模式 xff0c 按 j k 可以发现它能够在一列上面选中字符 2 按下 G 这样可以从文本的第一行选中到最后一行 3 按下 x 就会把这一列删掉 行首尾替换 xff1
  • vim 支持 python

    https www jianshu com p 3e606e31da5f 前段时间刚玩linux时为了图个简单打算直接用vim来写python代码省得再下个软件 xff08 好吧 xff0c 其实是自己下了好多次都失败了 xff0c 一气之
  • 用户态和核心态的概念以及为什么要区别?以及两者之间的切换

    一 用户态和核心态的概念 用户态 xff1a 内核态与用户态是操作系统的两种运行级别 当程序运行在3级特权级上时 xff0c 就可以称之为运行在用户态 xff0c 因为这是最低特权级 xff0c 是普通的用户进程运行的特权级 xff0c 大
  • strstr(str1,str2)函数使用时注意事项

    可能有的人还没听过strstr函数 xff0c 个人认为这个一个很实用的函数 xff0c strstr str1 str2 函数是字符串处理函数之一 xff0c 位于头文件 string h 中 对于处理字符串的一些问题有很大的帮助 定义
  • Java This 的用法

    JAVA This的用法 先写一个要调用的类 都放在名为test的包下面 在这里插入代码片package test span class token keyword public span span class token keyword
  • 关于松耦合和紧耦合的理解

    松耦合系统通常是基于消息的系统 xff0c 此时客户端和远程服务并不知道对方是如何实现的 客户端和服务之间的通讯由消息的架构支配 只要消息符合协商的架构 xff0c 则客户端或服务的实现就可以根据需要进行更改 xff0c 而不必担心会破坏对
  • 第六篇 键盘中断与应用程序读取键盘缓冲区

    这篇博文主要介绍在X86下键盘的中断过程 xff0c 以及应用程序如何利用中断读取键盘缓冲区内容 一 撰写该篇博文的背景介绍 在我们全屏看视频时 xff0c 按下Esc键 xff0c 播放器还原或者最小化 xff1b 在利用其他软件的时候
  • 发现一个aruco在线生成器,可以在线生成aruco CharucoBoard GridBoard AprilTag 图片, 真香

    最近在研究 opencv 检测 aruco标记项目 xff0c 想弄点aruco标记码来测试 xff0c 发现网上很少在线生成aruco标记码的工具 xff0c 导致在做测试时候浪费了很多时间去搞这个码 xff0c 基本上大家都用 xff0
  • FreeRTOS基础六:中断管理1

    嵌入式实时系统需要对外界的某个事件做出及时的响应动作 例如串口外设收到了一帧数据后 xff0c 需要通知数据解析任务 xff0c 同时还要将数据帧传递给解析任务 xff0c 完成数据的处理 设计出一种好的策略来完成这个过程时需要考虑以下几个
  • FreeRTOS基础四:队列

    简介 FreeRTOS中的队列是一种用于实现 任务与任务 xff0c 任务与中断 以及 中断与任务 之间的通信 机制 此外 xff0c 任务从队列读数据或者写入数据到队列时 xff0c 都可能被阻塞 这个特性使得任务可以被设计成基于事件驱动