FreeRTOS学习 --- 消息队列
队列简介
队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递)
全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量操作时,数据易受损
使用队列的情况如下:
读写队列做好了保护,防止多任务同时访问冲突;我们只需要直接调用API函数即可,简单易用!
FreeRTOS基于队列, 实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、
二值信号量、 递归互斥信号量,因此很有必要深入了解 FreeRTOS 的队列 。
在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度。
在创建队列时,就要指定队列长度以及队列项目的大小!
FreeRTOS队列特点:
1、数据入队出队方式
队列通常采用“先进先出”(FIFO)的数据存储缓冲机制,即先入队的数据会先从队列中被读取,FreeRTOS中也可以配置为“后进先出”LIFO方式;
2、数据传递方式
FreeRTOS中队列采用实际值传递,即将数据拷贝到队列中进行传递, FreeRTOS采用拷贝数据传递,也可以传递指针,所以在传递较大的数据的时候采用指针传递
3、多任务访问
队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息
4、出队、入队阻塞
当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队
入队阻塞:
队列满了,此时写不进去数据;
①将该任务的状态列表项挂载在pxDelayedTaskList;
②将该任务的事件列表项挂载在xTasksWaitingToSend;
出队阻塞:
队列为空,此时读取不了数据;
①将该任务的状态列表项挂载在pxDelayedTaskList;
②将该任务的事件列表项挂载在xTasksWaitingToReceive;
问题:当多个任务写入消息给一个“满队列”时,这些任务都会进入阻塞状态,也就是说有多个任务 在等待同一 个队列的空间。那当队列中有空间时,哪个任务会进入就绪态?
答:
1、优先级最高的任务
2、如果大家的优先级相同,那等待时间最久的任务会进入就绪态
队列结构体介绍
typedef struct QueueDefinition
{int8_t * pcHead /* 存储区域的起始地址 */int8_t * pcWriteTo; /* 下一个写入的位置 */union{QueuePointers_t xQueue; SemaphoreData_t xSemaphore; } u ;List_t xTasksWaitingToSend; /* 等待发送列表 */List_t xTasksWaitingToReceive; /* 等待接收列表 */volatile UBaseType_t uxMessagesWaiting; /* 非空闲队列项目的数量 */UBaseType_t uxLength; /* 队列长度 */UBaseType_t uxItemSize; /* 队列项目的大小 */volatile int8_t cRxLock; /* 读取上锁计数器 */volatile int8_t cTxLock; /* 写入上锁计数器 *//* 其他的一些条件编译 */
} xQUEUE;
当我们锁住队列的时候,你是可以正常读写队列的,只不过操作不了等待发送\接收列表。
当用于队列使用时:
typedef struct QueuePointers
{int8_t * pcTail; /* 存储区的结束地址 */int8_t * pcReadFrom; /* 最后一个读取队列的地址 */
} QueuePointers_t;
当用于互斥信号量和递归互斥信号量时 :
typedef struct SemaphoreData
{TaskHandle_t xMutexHolder; /* 互斥信号量持有者 */UBaseType_t uxRecursiveCallCount; /* 递归互斥信号量的获取计数器 */
} SemaphoreData_t;
队列结构体整体示意图:
队列相关API函数介绍
使用队列的主要流程:创建队列 ---> 写队列 ---> 读队列。
创建队列相关API函数介绍:
函数 | 描述 |
xQueueCreate() | 动态方式创建队列 |
xQueueCreateStatic() | 静态方式创建队列 |
动态和静态创建队列之间的区别:队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中分配,而静态创建需要用户自行分配内存。
创建队列函数入口参数解析:
#define xQueueCreate ( uxQueueLength, uxItemSize ) \ xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), (queueQUEUE_TYPE_BASE ))
此函数用于使用动态方式创建队列,队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中分配
前面说 FreeRTOS 基于队列实现了多种功能,每一种功能对应一种队列类型,队列类型的 queue.h 文件中有定义:
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) /* 队列 */
#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) /* 队列集 */
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) /* 互斥信号量 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) /* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) /* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) /* 递归互斥信号量 */
往队列写入消息API函数:
函数 | 描述 |
xQueueSend() | 往队列的尾部写入消息 |
xQueueSendToBack() | 同 xQueueSend() |
xQueueSendToFront() | 往队列的头部写入消息 |
xQueueOverwrite() | 覆写队列消息(只用于队列长度为 1 的情况) |
xQueueSendFromISR() | 在中断中往队列的尾部写入消息 |
xQueueSendToBackFromISR() | 同 xQueueSendFromISR() |
xQueueSendToFrontFromISR() | 在中断中往队列的头部写入消息 |
xQueueOverwriteFromISR() | 在中断中覆写队列消息(只用于队列长度为 1 的情况) |
队列写入消息:
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \ xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) \ xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) \ xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )
#define xQueueOverwrite( xQueue, pvItemToQueue ) \ xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )
可以看到这几个写入函数调用的是同一个函数xQueueGenericSend( ),只是指定了不同的写入位置!
队列一共有 3 种写入位置 :
#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) /* 写入队列尾部 */
#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) /* 写入队列头部 */
#define queueOVERWRITE ( ( BaseType_t ) 2 ) /* 覆写队列*/
注意:覆写方式写入队列,只有在队列的队列长度为 1 时,才能够使用
往队列写入消息函数入口参数解析:
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,const void * const pvItemToQueue,TickType_t xTicksToWait,const BaseType_t xCopyPosition );
从队列读取消息API函数:
函数 | 描述 |
xQueueReceive() | 从队列头部读取消息,并删除消息 |
xQueuePeek() | 从队列头部读取消息 |
xQueueReceiveFromISR() | 在中断中从队列头部读取消息,并删除消息 |
xQueuePeekFromISR() | 在中断中从队列头部读取消息 |
从队列读取消息函数入口参数解析:
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
此函数用于在任务中,从队列中读取消息,并且消息读取成功后,会将消息从队列中移除。
BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
此函数用于在任务中,从队列中读取消息, 但与函数 xQueueReceive()不同,此函数在成功读取消息后,并不会移除已读取的消息!
队列相关API函数工作流程具体解析:
创建队列:xQueueCreate( )
实际执行的是xQueueGenericCreate( )
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
1、计算队列需要多大内存 xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize )
2、为队列申请内存,申请大小:sizeof( Queue_t ) + xQueueSizeInBytes ,前面部分存放结构体成员,后面就是队列项大小
3、判断内存是否申请成功,成功即计算出队列项存储区的首地址
4、调用prvInitialiseNewQueue()初始化新队列pxNewQueue
(1)、初始化队列结构体成员变量
(2)、调用xQueueGenericReset()复位队列
1)、初始化其他队列结构体成员变量
2)、判断要复位的队列是否为新创建的队列
不是新创建队列,那就复位它,将列表xTasksWaitingToSend移除,是新创建的队列,那就初始化这两个列表xTasksWaitingToSend和xTasksWaitingToReceive
往队列写入数据(入队):xQueueSend( )
实际执行的是:xQueueGenericSend( )
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
1、进入临界区(关中断)
2、判断队列是否已满?
3、队列有空闲位置
(1)、只有在队列有空闲位置或为覆写的情况才能写入消息
(2)、当有空闲位置或覆写时:将待写入消息按指定写入方式复制到队列中
(3)、判断是否有因为读不到消息而阻塞的任务,有的话,将解除阻塞态,通过这个函数
判断调度器是否被挂起,没有挂起:将移除相应的事件列表项和状态列表项,并且将任务添加到就绪列表中。挂起:将移除事件列表项,将事件列表项添加到等待就绪列表:xPendingReadyList,当调用恢复调度器时xTaskResumeAll( ),xPendingReadyList中多任务就会被处理
(4)、退出临界区(开中断)
3、队列已满
(1)、此时不能写入消息,因此要将任务阻塞
(2)、如果阻塞时间为0 ,代表不阻塞,直接返回队列满错误
(3)、如果阻塞时间不为0,任务需要阻塞,记录下此时系统节拍计数器的值和溢出次数,用于下面对阻塞时间进行补偿(补偿就是计算剩余的阻塞时间还有多久)
(4)、判断阻塞时间补偿后,是否还需要阻塞
需要:将任务的事件列表项添加到等待发送列表中,将任务状态列表项添加到阻塞列表中进行阻塞,队列解锁,恢复调度器。不需要:队列解锁,恢复调度器,返回队列满错误。
不需要:队列解锁,恢复调度器,返回队列满错误
从队列读取数据(出队):xQueueReceive( )
1、进入临界区(关中断)
2、判断队列是否为空
3、有数据
(1)、使用函数prvCopyDataFromQueue( )拷贝数据
(2)、队列项目个数减一
(3)、因为前面已经减了一个队列项,所以队列已经有空位了,如果xTasksWaitingToSend等待发送列表中,有任务,则解除阻塞态,通过这个函数xTaskRemoveFromEventList( )
判断调度器是否被挂起,没有挂起:将移除相应的事件列表项和状态列表项,并且将任务添加到就绪列表中。挂起:将移除事件列表项,将事件列表项添加到等待就绪列表:xPendingReadyList,当调用恢复调度器时xTaskResumeAll( ),xPendingReadyList中多任务就会被处理
(4)、退出临界区(开中断)
4、为空
(1)、此时读取不到消息,因此要将任务阻塞
(2)、如果阻塞时间为0 ,代表不阻塞,直接返回队列空错误
(3)、如果阻塞时间不为0,任务需要阻塞,记录下此时系统节拍计数器的值和溢出次数,用于下面对阻塞时间进行补偿
(4)、判断阻塞时间补偿后,是否还需要阻塞
需要:将任务的事件列表项添加到等待接收列表中,将任务状态列表项添加到阻塞列表中进行阻塞,队列解锁,恢复调度器。
不需要:队列解锁,恢复调度器,返回队列空错误
相关文章:

FreeRTOS学习 --- 消息队列
队列简介 队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递) 全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量操作时,数据易受损 使用队列的情况如下:…...

PHP If...Else 语句详解
PHP If...Else 语句详解 引言 在PHP编程中,if...else语句是流程控制的重要组成部分,它允许程序根据条件判断执行不同的代码块。本文将详细解析PHP中的if...else语句,包括其基本用法、高级技巧以及注意事项。 一、基本用法 if...else语句的…...

pytorch使用SVM实现文本分类
人工智能例子汇总:AI常见的算法和例子-CSDN博客 完整代码: import torch import torch.nn as nn import torch.optim as optim import jieba import numpy as np from sklearn.model_selection import train_test_split from sklearn.feature_extract…...

安卓(android)读取手机通讯录【Android移动开发基础案例教程(第2版)黑马程序员】
一、实验目的(如果代码有错漏,可在代码地址查看) 1.熟悉内容提供者(Content Provider)的概念和作用。 2.掌握内容提供者的创建和使用方法。 4.掌握内容URI的结构和用途。 二、实验条件 1.熟悉内容提供者的工作原理。 2.掌握内容提供者访问其…...

【Qt】常用的容器
Qt提供了多个基于模板的容器类,这些容器类可用于存储指定类型的数据项。例如常用的字符串列表类 QStringList 可用来操作一个 QList<QString>列表。 Qt的容器类比标准模板库(standard template library,STL)中的容器类更轻巧、使用更安全且更易于使…...

基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 3.核心程序 .…...

分布式事务组件Seata简介与使用,搭配Nacos统一管理服务端和客户端配置
文章目录 一. Seata简介二. 官方文档三. Seata分布式事务代码实现0. 环境简介1. 添加undo_log表2. 添加依赖3. 添加配置4. 开启Seata事务管理5. 启动演示 四. Seata Server配置Nacos1. 修改配置类型2. 创建Nacos配置 五. Seata Client配置Nacos1. 增加Seata关联Nacos的配置2. 在…...

JavaScript常用的内置构造函数
JavaScript作为一种广泛应用的编程语言,提供了丰富的内置构造函数,帮助开发者处理不同类型的数据和操作。这些内置构造函数在创建和操作对象时非常有用。本文将详细介绍JavaScript中常用的内置构造函数及其用途。 常用内置构造函数概述 1. Object Obj…...

25寒假算法刷题 | Day1 | LeetCode 240. 搜索二维矩阵 II,148. 排序链表
目录 240. 搜索二维矩阵 II题目描述题解 148. 排序链表题目描述题解 240. 搜索二维矩阵 II 点此跳转题目链接 题目描述 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。每列的元素从上到…...

MQTT知识
MQTT协议 MQTT 是一种基于发布/订阅模式的轻量级消息传输协议,专门针对低带宽和不稳定网络环境的物联网应用而设计,可以用极少的代码为联网设备提供实时可靠的消息服务。MQTT 协议广泛应用于物联网、移动互联网、智能硬件、车联网、智慧城市、远程医疗、…...

【机器学习与数据挖掘实战】案例11:基于灰色预测和SVR的企业所得税预测分析
【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈机器学习与数据挖掘实战 ⌋ ⌋ ⌋ 机器学习是人工智能的一个分支,专注于让计算机系统通过数据学习和改进。它利用统计和计算方法,使模型能够从数据中自动提取特征并做出预测或决策。数据挖掘则是从大型数据集中发现模式、关联…...

新一代搜索引擎,是 ES 的15倍?
Manticore Search介绍 Manticore Search 是一个使用 C 开发的高性能搜索引擎,创建于 2017 年,其前身是 Sphinx Search 。Manticore Search 充分利用了 Sphinx,显着改进了它的功能,修复了数百个错误,几乎完全重写了代码…...

使用 Context API 管理临时状态,避免 Redux/Zustand 的持久化陷阱
在开发 React Native 应用时,我们经常需要管理全局状态,比如用户信息、主题设置、网络状态等。而对于某些临时状态,例如 数据同步进行中的状态 (isSyncing),我们应该选择什么方式来管理它? 在项目开发过程中ÿ…...

PyTorch框架——基于深度学习YOLOv8神经网络学生课堂行为检测识别系统
基于YOLOv8深度学习的学生课堂行为检测识别系统,其能识别三种学生课堂行为:names: [举手, 读书, 写字] 具体图片见如下: 第一步:YOLOv8介绍 YOLOv8 是 ultralytics 公司在 2023 年 1月 10 号开源的 YOLOv5 的下一个重大更新版本…...

word2vec 实战应用介绍
Word2Vec 是一种由 Google 在 2013 年推出的重要词嵌入模型,通过将单词映射为低维向量,实现了对自然语言处理任务的高效支持。其核心思想是利用深度学习技术,通过训练大量文本数据,将单词表示为稠密的向量形式,从而捕捉单词之间的语义和语法关系。以下是关于 Word2Vec 实战…...

C# 操作符重载对象详解
.NET学习资料 .NET学习资料 .NET学习资料 一、操作符重载的概念 在 C# 中,操作符重载允许我们为自定义的类或结构体定义操作符的行为。通常,我们熟悉的操作符,如加法()、减法(-)、乘法&#…...

python学opencv|读取图像(五十四)使用cv2.blur()函数实现图像像素均值处理
【1】引言 前序学习进程中,对图像的操作均基于各个像素点上的BGR值不同而展开。 对于彩色图像,每个像素点上的BGR值为三个整数,因为是三通道图像;对于灰度图像,各个像素上的BGR值是一个整数,因为这是单通…...

CNN的各种知识点(四): 非极大值抑制(Non-Maximum Suppression, NMS)
非极大值抑制(Non-Maximum Suppression, NMS) 1. 非极大值抑制(Non-Maximum Suppression, NMS)概念:算法步骤:具体例子:PyTorch实现: 总结: 1. 非极大值抑制(…...

虚幻基础16:locomotion direction
locomotion locomotion:角色运动系统的总称:包含移动、奔跑、跳跃、转向等。 locomotion direction 玩家输入 玩家输入:通常代表玩家想要的移动方向。 direction 可以计算当前朝向与移动方向的Δ。从而实现朝向与移动(玩家输入)方向的分…...

C++游戏开发实战:从引擎架构到物理碰撞
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 1. 引言 C 是游戏开发中最受欢迎的编程语言之一,因其高性能、低延迟和强大的底层控制能力,被广泛用于游戏…...

代理模式——C++实现
目录 1. 代理模式简介 2. 代码示例 1. 代理模式简介 代理模式是一种行为型模式。 代理模式的定义:由于某些原因需要给某对象提供一个代理以控制该对象的访问。这时,访问对象不适合或者不能直接访问引用目标对象,代理对象作为访问对象和目标…...

什么情况下,C#需要手动进行资源分配和释放?什么又是非托管资源?
扩展:如何使用C#的using语句释放资源?什么是IDisposable接口?与垃圾回收有什么关系?-CSDN博客 托管资源的回收有GC自动触发,而非托管资源需要手动释放。 在 C# 中,非托管资源是指那些不由 CLR(…...

LeetCode 2909. 元素和最小的山形三元组 II
**### LeetCode 2909. 元素和最小的山形三元组 II 问题描述 给定一个下标从 0 开始的整数数组 nums,我们需要找到一个“山形三元组”(i, j, k)满足以下条件: i < j < knums[i] < nums[j] 且 nums[k] < nums[j] 并…...

搬迁至bilibili声明
我将搬迁到bilibili ,用户名:北苏清风 在这个用户名上的文章部分将出自csdn的这个账号,均属于本人原创...

【周易哲学】生辰八字入门讲解(八)
😊你好,我是小航,一个正在变秃、变强的文艺倾年。 🔔本文讲解【周易哲学】生辰八字入门讲解,期待与你一同探索、学习、进步,一起卷起来叭! 目录 一、六亲女命六亲星六亲宫位相互关系 男命六亲星…...

复制粘贴小工具——Ditto
在日常工作中,复制粘贴是常见的操作,但Windows系统自带的剪贴板功能较为有限,只能保存最近一次的复制记录,这对于需要频繁复制粘贴的用户来说不太方便。今天,我们介绍一款开源、免费且功能强大的剪贴板增强工具——Dit…...

3、从langchain到rag
文章目录 本文介绍向量和向量数据库向量向量数据库 索引开始动手实现rag加载文档数据并建立索引将向量存放到向量数据库中检索生成构成一条链 本文介绍 从本节开始,有了上一节的langchain基础学习,接下来使用langchain实现一个rag应用,并稍微…...

稀疏进化训练:机器学习优化算法中的高效解决方案
稀疏进化训练:机器学习优化算法中的高效解决方案 稀疏进化训练:机器学习优化算法中的高效解决方案引言第一部分:背景与动机1.1 传统优化算法的局限性1.2 进化策略的优势1.3 稀疏性的重要性 第二部分:稀疏进化训练的核心思想2.1 稀…...

10 Flink CDC
10 Flink CDC 1. CDC是什么2. CDC 的种类3. 传统CDC与Flink CDC对比4. Flink-CDC 案例5. Flink SQL 方式的案例 1. CDC是什么 CDC 是 Change Data Capture(变更数据获取)的简称。核心思想是,监测并捕获数据库的变动(包括数据或数…...

【LeetCode 刷题】回溯算法-子集问题
此博客为《代码随想录》二叉树章节的学习笔记,主要内容为回溯算法子集问题相关的题目解析。 文章目录 78.子集90.子集II 78.子集 题目链接 class Solution:def subsets(self, nums: List[int]) -> List[List[int]]:res, path [], []def dfs(start: int) ->…...