【FreeRTOS学习04】小白都能懂的 Queue Management 消息队列使用详解
消息队列作为任务间同步扮演着必不可少的角色;
相关文章
【FreeRTOS实战汇总】小白博主的RTOS学习实战快速进阶之路(持续更新)
文章目录
1 前言
任务之间的同步(同步就是任务之间做数据交互,或为两个任务之间的通讯),任务和中断之间的同步都可以依靠消息队列,从而实现异步处理,FreeRTOS的队列采用FIFO(先进先出)缓冲区,具体如下图所示;

2 xQUEUE
FreeRTOS消息队列的实现主要是queue.c,需要包含头文件queue.h,下面先看一下queue.c中的数据类型xQUEUE,源码如下所示;
typedef struct QueueDefinition
{
int8_t *pcHead; /*< Points to the beginning of the queue storage area. */
int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */
int8_t *pcWriteTo; /*< Points to the free next place in the storage area. */
union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */
{
int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */
UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */
} u;
List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */
List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */
volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. */
UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */
UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. */
volatile int8_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
volatile int8_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
本文暂时不需要深入源码,在queue.h的接口已经封装得相当好,无需对细节太过于关注,下面会是对常用接口的使用的总结,如果英文好,直接看源码中的函数注释也是很好的选择。
3 相关概念
3.1 数据结构
队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称为队列的“深度”。在队列创建时需要设定其深度和每个单元的大小。通常情况下,队列被作为 FIFO(先进先出)使用,即数据由队列尾写入,从队列首读出。当然,由队列首写入也是可能的。往队列写入数据是通过字节拷贝把数据复制存储到队列中;从队列读出数据使得把
队列中的数据拷贝删除。1 如下图所示;

注意
上面提到的数据单元可以是一个char或int类型的数,但是相对比较合理的设计是,封装成一个合理的类,或者称之为结构体,可以明确当前数据单元的数据类型,数据来源(来自哪个任务)等等,因为一个队列可以被多个任务进行读取和发送函数,这样就避免了传输数据出现混淆的情况。通常设计是一个队列被多个任务写入数据,然后有一个任务读取,暂时称之为多写一读,反之,多读一写则较少遇到。
3.2 收发数据堵塞
当某个任务试图读或者写一个队列时,其可以指定一个阻塞超时时间,
读取:
任务读取数据时,在设置堵塞超时时间内,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务例程
往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。写入:如果队列被多个任务写入,那么将导致多个任务堵塞以等待队列有效,当队列有效的时候,这些任务中的优先级最高的任务优先进入就绪态。
4 常用函数
FreeRTOS的消息队列常用接口都封装在queue.h中,通过宏定义统一将接口函数的命名风格整理得十分统一;具体如下图所示;

这里有两种需要注意;
- 任务与任务之间同步:例如图中①处的API适用于任务间同步;
xQueueSendToFrontxQueueSendToFrontxQueueSendxQueueOverwrite
- 任务与中断之间同步:图中②处的API适用于任务于中断间同步,
xxxISR()后缀的函数都是FreeRTOS中保证了线程安全的;xQueueSendToFrontFromISRxQueueSendToBackFromISRxQueueOverwriteFromISRxQueueSendFromISR
4.1 创建队列
- QueueHandle_t
QueueHandle_t是一个void类型的指针变量,定义在queue.h中,具体如下;
/**
* Type by which queues are referenced. For example, a call to xQueueCreate()
* returns an QueueHandle_t variable that can then be used as a parameter to
* xQueueSend(), xQueueReceive(), etc.
*/
typedef void * QueueHandle_t;
基本上每个队列函数都会使用这个变量,这里我们统一称为队列的句柄;
- xQueueCreate
这个函数可以创建一个队列,创建成功则会返回一个队列句柄,如果创建失败则返回NULL,其函数原型是xQueueGenericCreate,具体如下所示;
#define xQueueCreate( uxQueueLength, uxItemSize ) \
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
xQueueCreate如下所示;
QueueHandle_t xQueueCreate(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize
);
| 参数 | 描述 |
|---|---|
uxQueueLength |
队列能够存储的最大单元数目,即队列深度 |
uxItemSize |
队列中数据单元的长度,以字节为单位 |
retval |
NULL:创建失败;否则为创建成功 |
4.2 发送数据
下面都是从任务发送数据到队列,
- xQueueSendToFront
将数据发送至队首,函数声明如下;
BaseType_t xQueueSendToBack(
QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait
);
- xQueueSendToBack
将数据发送至队尾,函数声明如下;
BaseType_t xQueueSendToFront(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait
);
- xQueueSend
与xQueueSendToBack入队顺序相同,函数声明如下所示;
BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait
);
具体的参数描述如下:
| 参数 | 描述 |
|---|---|
xQueue |
创建队列时返回的句柄 |
pvItemToQueue |
需要从任务发送到队列的数据 |
xTicksToWait |
阻塞超时时间。如果在发送时队列已满,这个时间即是任务处于阻塞态等待队列空间有效的最长等待时间。 |
retval |
pdPass:发送数据成功 errQUEUE_FULL:无法写入数据 |
关于xTicksToWait
xTicksToWait设为0 ,且队列已满,则xQueueSendToFront()与xQueueSendToBack()均会立即返回。阻塞时间是以系统心跳周期为单位的,所以绝对时间取决于系统心跳频率。常量 portTICK_RATE_MS 可以用来把心跳时间单位转换为毫秒时间单位。xTicksToWait设置为portMAX_DELAY, 并且在FreeRTOSConig.h 中设定INCLUDE_vTaskSuspend为 1,那么阻塞等待将没有超时限制。
4.3 接收数据
- xQueueReceive
xQueueReceive()用于从队列中接收(读取)数据单元。接收到的单元同时会从队列
中删除。函数声明如下;
BaseType_t xQueueReceive(
QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait
);
- xQueuePeek
xQueuePeek()也是从从队列中接收数据单元,不同的是并不从队列中删出接收到的单元。 xQueuePeek()从队列首接收到数据后,不会修改队列中的数据,也不会改变数据在队列中的存储序顺。函数声明如下;
BaseType_t xQueuePeek(
QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait
);
具体的参数描述如下:
| 参数 | 描述 |
|---|---|
| xQueue | 队列的句柄 |
| pvBuffer | 接收缓存指针。其指向一段内存区域,用于接收从队列中拷贝来的数据 |
| xTicksToWait | 阻塞超时时间 |
| retavl | pdPASS:接收成功 errQUEUE_FULL:接收失败 |
- uxQueueSpacesAvailable
uxQueueSpacesAvailable()用于查询队列中可用的空闲空间数量;函数声明如下;
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
| 参数 | 描述 |
|---|---|
| xQueue | 队列的句柄 |
| retval | 当前队列中空余的数据单元个数,0表示队列已满 |
- uxQueueMessagesWaiting
uxQueueMessagesWaiting()用于查询队列中当前有效数据单元个数;函数声明如下;
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
| 参数 | 描述 |
|---|---|
| xQueue | 队列的句柄 |
| retval | 当前队列中空余的数据单元个数,0表示队列为空 |
4.4 删除队列
- vQueueDelete
用来删除一个队列,直接传入已创建的队列句柄即可,函数声明如下;
void vQueueDelete( QueueHandle_t xQueue );
5 举例
多个任务写入一个任务读取的时候应该怎么做呢?如下图所示2;

这里有三个任务,所以为了搞清楚数据来自哪个任务,因此将数据单元封装起来,使用
iMeaning表示数据单元的源头,当然这里还是比较简单的应用。
6 总结
本文介绍了FreeRTOS的消息队列比价常用的方法,当然是相对简单的,侧重在了解概念上,需要实际的应用从而加深理解,更加详细已经灵活的应用可以参考FreeRTOS作者撰写的Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide。
【FreeRTOS学习04】小白都能懂的 Queue Management 消息队列使用详解的更多相关文章
- 【FreeRTOS学习03】小白都能懂的Task Management 任务管理基本概念介绍
在FreeRTOS中,线程的术语又可以被称之为任务,或许这样更加合适,本文将介绍任务的创建/删除,任务参数的使用,以及任务优先级: 1 软实时和硬实时 硬实时系统的任务运行正确性与响应时限是紧密相关的 ...
- Shell学习(七)——sort、uniq、cut、wc命令详解
Shell学习(七)--sort.uniq.cut.wc命令详解 转自:[1]linux sort,uniq,cut,wc命令详解 https://www.cnblogs.com/ggjucheng/ ...
- Ubuntu14.04下Mongodb数据库可视化工具安装部署步骤(图文详解)(博主推荐)
不多说,直接上干货! 前期博客 Ubuntu14.04下Mongodb(离线安装方式|非apt-get)安装部署步骤(图文详解)(博主推荐) Ubuntu14.04下Mongodb官网安装部署步骤(图 ...
- 大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解
引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单 ...
- Kubernetes学习之路(二十)之K8S组件运行原理详解总结
目录 一.看图说K8S 二.K8S的概念和术语 三.K8S集群组件 1.Master组件 2.Node组件 3.核心附件 四.K8S的网络模型 五.Kubernetes的核心对象详解 1.Pod资源对 ...
- TortoiseGit学习系列之Windows上本地代码如何通过TortoiserGit提交到GitHub详解(图文)
不多说,直接上干货! 前面博客 TortoiseGit学习系列之Windows上TortoiseGit的安装详解(图文) 上面博文给大家讲解了一下如何本地安装TortoiseGit. 这篇为大家讲一下 ...
- ActiveMQ学习总结(5)——Java消息服务JMS详解
JMS: Java消息服务(Java Message Service) JMS是用于访问企业消息系统的开发商中立的API.企业消息系统可以协助应用软件通过网络进行消息交互. JMS的编程过程很简单,概 ...
- Ubuntu14.04下沙盒数据导入到 Neo4j 数据库(图文详解)
不多说,直接上干货! 参考博客 http://blog.csdn.net/u012318074/article/details/72793914 (表示感谢) 前期博客 Neo4j沙盒实验申请过程 ...
- Ubuntu16.04下沙盒数据导入到 Neo4j 数据库(图文详解)
不多说,直接上干货! 参考博客 http://blog.csdn.net/u012318074/article/details/72793914 (表示感谢) 前期博客 Neo4j沙盒实验申请过 ...
随机推荐
- 百度api实现人脸对比
第一步(注册账号): 点这里注册百度云账号 如图: 创建应用得到 APP_ID API_KEY SECRET_KEY 第二步(代码): import requests import base64 ...
- 如果这篇文章说不清epoll的本质,那就过来掐死我吧!
转载自:https://www.toutiao.com/i6683264188661367309/ 目录 一.从网卡接收数据说起 二.如何知道接收了数据? 三.进程阻塞为什么不占用cpu资源? 四.内 ...
- Spring Data REST不完全指南(二)
上一篇文章介绍了Spring Data REST的功能及特征,以及演示了如何在项目中引入Spring Data REST并简单地启动演示了Spring Data REST项目.在本文中,我们将深入了解 ...
- 新增记录txt文档
StringBuilder s = new StringBuilder(); s.Append("[" + dt.Rows[i]["Store"].Conver ...
- tf.keras的模块
- Linux必须会的命令---也是以前记录的,ctrl+z fg 啥的 jobs 比较实用
fg.bg.jobs.&.ctrl + z都是跟系统任务有关的,虽然现在基本上不怎么需要用到这些命令,但学会了也是很实用的 一.& 最经常被用到 这个用在一个命令的最后,可以把这个命令 ...
- 数据结构(C语言版)---排序
1.排序:重排表中元素. 2.根据数据元素是否完全在内存中,将排序算法分为内部排序和外部排序两类. 3.插入排序:将一个待排序记录按关键字大小插入到前面已排好的子序列中,直到全部记录插入完成. 1)直 ...
- Linux 日常操作
Linux 日常操作 */--> Linux 日常操作 Table of Contents 1. 查看硬件信息 1.1. 服务器型号序列号 1.2. 主板型号 1.3. 查看BIOS信息 1.4 ...
- 2019-2020-1 20199310《Linux内核原理与分析》第八周作业
1.问题描述 在前面的文章中,学习了在Linux系统之中如何创建一个新进程进行追踪,本文将围绕编译链接的过程和ELF可执行文件格式,对Linux内核装载和启动一个可执行程序. 2.解决过程 2.1 E ...
- Zabbix磁盘性能监控
iostat统计磁盘信息的时候,使用的是/proc/diskstats ,cat /proc/diskstats显示如下 ram0 ram1 ram2 ram3 ram4 ram5 ram6 ram7 ...