【学习FreeRTOS】第13章——FreeRTOS队列

1.队列简介

队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递)
FreeRTOS基于队列, 实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、二值信号量、 递归互斥信号量,因此很有必要深入了解 FreeRTOS 的队列 。

1.1.队列与全局变量的区别

在这里插入图片描述
在这里插入图片描述

  • 全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量操作时,数据易受损
  • 队列的优点:读写队列做好了保护,防止多任务同时访问冲突,我们只需要直接调用API函数即可,简单易用

1.2.队列的特点

  • 数据入队出队方式:队列通常采用**“先进先出”(FIFO)的数据存储缓冲机制,即先入队的数据会先从队列中被读取,FreeRTOS中也可以配置为“后进先出”LIFO**方式;
  • 数据传递方式:FreeRTOS中队列采用实际值传递,即将数据拷贝到队列中进行传递, FreeRTOS采用拷贝数据传递,也可以传递指针,所以在传递较大的数据的时候采用指针传递
  • 多任务访问:队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息
  • 出队、入队阻塞:当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队
    若阻塞时间为0 :直接返回不会等待;
    若阻塞时间为0~port_MAX_DELAY :等待设定的阻塞时间,若在该时间内还无法入队,超时后直接返回不再等待;
    若阻塞时间为port_MAX_DELAY :死等,一直等到可以入队为止。出队阻塞与入队阻塞类似;

1.3.队列的属性

在队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度 。在创建队列时,就要指定队列长度以及队列项目的大小。

在这里插入图片描述
在这里插入图片描述

当多个任务写入消息给一个“满队列”时,这些任务都会进入阻塞状态,也就是说有多个任务 在等待同一 个队列的空间。当队列中有空间时,哪个任务会进入就绪态?
1、优先级最高的任务
2、如果大家的优先级相同,那等待时间最久的任务会进入就绪态

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;

在这里插入图片描述

3.队列相关API函数介绍

使用队列的主要流程:创建队列——写队列——读队列。

3.1.创建队列

  • 动态方式创建队列:xQueueCreate()【本文重点讲解】

  • 静态方式创建队列:xQueueCreateStatic()

区别:队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中分配,而静态创建需要用户自行分配内存。

#define xQueueCreate(uxQueueLength, uxItemSize)	xQueueGenericCreate((uxQueueLength), (uxItemSize),(queueQUEUE_TYPE_BASE ))
#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 )	/* 递归互斥信号量 */	
  • 形参uxQueueLength:队列长度
  • 形参uxItemSize:队列项目的大小
  • queueQUEUE_TYPE_BASE:宏定义,创建队列的类型,本文是队列
  • 返回值:NULL ,队列创建失败;其他值, 队列创建成功,返回队列句柄【句柄也即是队列的首地址】

3.2.写队列

  • xQueueSend() :往队列的尾部写入消息
  • xQueueSendToBack(): 同 xQueueSend()
  • xQueueSendToFront(): 往队列的头部写入消息
  • xQueueOverwrite(): 覆写队列消息(只用于队列长度为 1 的情况)
  • xQueueSendFromISR(): 在中断中往队列的尾部写入消息
  • xQueueSendToBackFromISR(): 同 xQueueSendFromISR()
  • xQueueSendToFrontFromISR(): 在中断中往队列的头部写入消息
  • xQueueOverwriteFromISR(): 在中断中覆写队列消息(只用于队列长度为 1 的情况)

头部写入与尾部写入的区别:头部写入从队列项1开始往后写(可循环),尾部写入从队列n开始写(可循环)

本文重点讲前四个,这几个写入函数调用的是同一个函数xQueueGenericSend( ),只是指定了不同的写入位置

#define queueSEND_TO_BACK                     ( ( BaseType_t ) 0 )		/* 写入队列尾部 */
#define queueSEND_TO_FRONT                    ( ( BaseType_t ) 1 )		/* 写入队列头部 */
#define queueOVERWRITE                        ( ( BaseType_t ) 2 )		/* 覆写队列*/#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 )
BaseType_t	xQueueGenericSend(	QueueHandle_t 			xQueue, const void * const 		pvItemToQueue, TickType_t 				xTicksToWait,const BaseType_t 		xCopyPosition);
  • 参数xQueue:待写入的队列
  • 参数pvItemToQueue:待写入消息
  • 参数xTicksToWait:阻塞超时时间
  • 参数xCopyPosition:写入的位置
  • 返回值:pdTRUE,队列写入成功;errQUEUE_FULL ,队列写入失败

const void * const 表示一个不可修改的指针,它指向不可修改的 void 类型数据。void 指针通常用于表示未知类型的数据,而 const 修饰符确保无论是指针本身还是指向的数据都不会被修改。
无论是写入数字还是地址,都只需要把变量的地址传入即可,函数会自动赋值。

3.3.读队列

  • xQueueReceive() :从队列头部读取消息,并删除消息
  • xQueuePeek():从队列头部读取消息
  • xQueueReceiveFromISR() :在中断中从队列头部读取消息,并删除消息
  • xQueuePeekFromISR() :在中断中从队列头部读取消息

头部读取删除的底层原理:队列结构体成员uxMessagesWaiting的变化,删除则变化,不删除则不变化

BaseType_t	xQueueReceive(	QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
BaseType_t  xQueuePeek( 	QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
  • 形参xQueue:待读取的队列
  • 形参pvBuffer:信息读取缓冲区
  • 形参xTicksToWait:阻塞超时时间
  • 返回值:pdTRUE,读取成功;pdFALSE,读取失败

void * const 表示一个指向不可修改的数据的指针,但它可以指向任何类型的数据。这种类型的指针通常用于需要保护指针不被意外修改,但允许修改指向的数据的情况。
无论是读出数字还是地址,都只需要把变量的地址传入即可,函数会自动赋值。

4.队列操作实验

  • 实验目的:学习 FreeRTOS 的队列相关API函数的使用 ,实现队列的入队和出队操作。
  • 实验设计:将设计四个任务:start_task、task1、task2、task3
    start_task:用来创建task1和task2以及task3任务
    task1:当按键key0或key1按下,将键值拷贝到队列key_queue(入队);当按键key_up按下,将传输大数据,这里拷贝大数据的地址到队列big_date_queue中
    task2:读取队列key_queue中的消息(出队),打印出接收到的键值
    task3:从队列big_date_queue读取大数据地址,通过地址访问大数据

5.队列相关API函数解析

5.1.队列的创建API函数(动态创建)xQueueCreate

在这里插入图片描述

5.2.写队列(尾部写入)xQueueSend

在这里插入图片描述

5.3.读队列(读取并出队) xQueueReceive

在这里插入图片描述

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

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

相关文章

Linux CentOS7系统,抓取http协议的数据包

使用 tcpdump 命令 1.首先确认是否安装 [rootlocalhost ~]# which tcpdump /usr/bin/which: no tcpdump in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin) [rootlocalhost ~]#我这里没有安装 1.1 安装 tcpdump yum install tcpdump 安装成功如下&#xf…

【C++ 学习 ⑯】- 继承(上)

目录 一、继承的概念和定义 1.1 - 概念 1.2 - 定义 二、继承时的对象内存模型 三、向上转型和向下转型 四、继承时的名字遮蔽问题 4.1 - 有成员变量遮蔽时的内存分布 4.2 - 重名的基类成员函数和派生类成员函数不构成重载 一、继承的概念和定义 1.1 - 概念 C 中的继承…

【AWS】创建IAM用户;无法登录IAM用户怎么办?错误提示:您的身份验证信息错误,请重试(已解决)

目录 0.背景问题分析 1.解决步骤 0.背景问题分析 windows 11 ,64位 我的问题情景: 首先我创建了aws的账户,并且可以用ROOT用户登录,但是在登录时选择IAM用户,输入ROOT的名字和密码,就会提示【您的身份验证…

【分布式技术专题】「OSS中间件系列」从0到1的介绍一下开源对象存储MinIO技术架构

MinIO背景介绍 MinIO创始者是Anand Babu Periasamy, Harshavardhana(戒日王)等人, Anand是GlusterFS的初始开发者、Gluster公司的创始人与CTO,Harshavardhana曾经是GlusterFS的开发人员,直到2011年红帽收购了Gluster公…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十:实体配置功能实现

一、本章内容 本章实现实体配置功能,包括识别实体属性、设置各属性的展示方式、相关类型、要和展示、编辑的内容等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 3.1 B站视频地址:

MISRA 2012学习笔记(3)-Rules 8.4-8.7

文章目录 Rules8.4 字符集和词汇约定(Character sets and lexical conventions)Rule 4.1 八进制和十六进制转译序列应有明确的终止识别标识Rule 4.2 禁止使用三字母词(trigraphs) 8.5 标识符(Identifiers)Rule 5.1 外部标识符不得重名Rule 5.2 同范围和命名空间内的标识符不得重…

673. 最长递增子序列的个数

673. 最长递增子序列的个数 原题链接:完成情况:解题思路:方法一:动态规划方法二:贪心 前缀和 二分查找 参考代码:__673最长递增子序列的个数__动态规划__673最长递增子序列的个数__贪心_前缀和_二分查找…

书单背景图片哪里找?如何制作成视频?

有没有小伙伴们发现,如今很多热门的短视频平台有很多使用书单文案制作的视频,很多情感博主会配上一些精致的图片,唯美的背景承载着一些美好的文案内容。这种类型的视频让不少的小伙伴都想制作专属于自己的视频来投稿,那么小伙伴们…

linux系统(centos、ubuntu、银河麒麟服务、uos、deepin)判断程序是否已安装,通用判断方法:使用所有应用和命令的判断

前言 项目中需要判断linux服务器中是否已经安装了某个服务 方法有很多种,但是很多都不通用, 脚本代码就不容易做成统一的 解决方案 用下面的脚本代码去进行判断 用jdk测试 脚本意思如下: 输入java -version命令,将返回的字…

【数据结构练习】单链表OJ题(一)

目录 一、移除链表元素思路1:思路2: 二、反转链表三、链表的中间节点四、链表中倒数第k个节点五、回文结构六、合并两个有序链表 一、移除链表元素 题目: 思路1: 在原来的链表上进行修改,节点的数据是val的删除&am…

Cesium 使用 Entity 绘制点线面

文章目录 一、绘制点1. 第一种2. 第二种 二、绘制面三、绘制线四、移除 Entity <!--* Author: HuKang* Date: 2023-08-18 11:06:43* LastEditTime: 2023-08-25 09:16:59* LastEditors: HuKang* Description: program-c* FilePath: \global-data-display\src\views\program-c…

第一讲使用IDEA创建Java工程——HelloWorld

一、前言导读 为了能够让初学者更快上手Java,不会像其他书籍或者视频一样,介绍一大堆历史背景,默认大家已经知道Java这么编程语言了。本专栏只会讲解干货,直接从HelloWord入手,慢慢由浅入深,讲个各个知识点,这些知识点也是目前工作中项目使用的,而不是讲一些老的知识点…

16、Flink 的table api与sql之连接外部系统: 读写外部系统的连接器和格式以及FileSystem示例(1)

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

nvm安装使用教程

文章目录 下载配置安装最新稳定版 node安装指定版本查看版本切换版本删除版本 常见问题安装node后 显示拒绝访问的问题使用cnpm会报错的问题降低cnpm版本npm镜像 下载 NVM for Windows 下载地址&#xff1a;https://link.juejin.cn/?targethttps%3A%2F%2Fgithub.com%2Fcoreyb…

《深度学习计算机视觉 》书籍分享(包邮送书三本)

深度学习计算机视觉介绍 随着计算机技术的发展和进步&#xff0c;计算机视觉领域得到了广泛的关注和研究。而深度学习作为一种强大的机器学习方法&#xff0c;已经成为计算机视觉领域的重要工具之一。本文将介绍深度学习在计算机视觉中的应用和取得的成果。 深度学习是一种模…

Sim/circuit10

通过观察可知&#xff0c;在a、b同时为0或1时&#xff0c;state的值改变 state的值可以改变q的输出&#xff0c;1为ab的同或&#xff0c;0为异或 利用assign q进行输出 module top_module (input clk,input a,input b,output q,output state );always(posedge clk)if(a&…

基于JAVA SpringBoot和UniAPP的宠物服务预约小程序

随着社会的发展和人们生活水平的提高&#xff0c;特别是近年来&#xff0c;宠物快速进入人们的家中&#xff0c;成为人们生活中重要的娱乐内容之一&#xff0c;过去宠物只是贵族的娱乐&#xff0c;至今宠物在中国作为一种生活方式得到了广泛的认可&#xff0c;随着人们精神文明…

Docker拉取并配置Grafana

Linux下安装Docker请参考&#xff1a;Linux安装Docker 安装准备 新建挂载目录 /opt/grafana/data目录&#xff0c;准备用来挂载放置grafana的数据 /opt/grafana/plugins目录&#xff0c;准备用来放置grafana的插件 /opt/grafana/config目录&#xff0c;准备用来挂载放置graf…

正则表达式一小时学完

闯关式学习Regex 正则表达式&#xff0c;我感觉挺不错的&#xff0c;记录一下。 遇到不会的题&#xff0c;可以评论交流。 真的很不错 链接 Regex Learn - Step by step, from zero to advanced.

HTTP原理与实现

一、基本概念 一、基本原理* 1、全称&#xff1a; HyperText Transfer Protocol (超文本传输协议) 2、底层实现协议&#xff1a;建立在 TCP/IP 上的无状态连接。 3、基本作用&#xff1a;用于客户端与服务器之间的通信&#xff0c;规定客户端和服务器之间的通信格式。包括请…