第13章——FreeRTOS队列

news2024/11/18 23:24:59

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.coloradmin.cn/o/898659.html

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

相关文章

软考A计划-系统集成项目管理工程师-标准规范

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 👉关于作者 专注于Android/Unity和各种游…

python多线程真是让人受够了

一、有8种不同的方法 三、参考文献 https://superfastpython.com/multiprocessing-pool-issue-tasks

linux-进程

1.先谈硬件 冯诺依曼体系结构 一个计算机能够正常运行,就必须遵守冯诺依曼体系 数据流向 为什么不把Cpu直接怼到输入设备和输出设备中间,非要加个内存呢? 答:因为根据木桶原理,如果这样设计,导致最终效…

赴印设厂获得的份额减少,富士康后悔莫及,中国制造获苹果认可

随着iPhone的量产在推进,产业链人士指出iPhone15的分配份额已基本确定,富士康获得了58%的份额,中国大陆的纬创获得28%的份额,而纬创只获得了1%的份额,显示出富士康和纬创这两家企业听从苹果的要求赴印设厂反而被抛弃。…

如何优雅地处理Java多线程编程中的共享资源问题,以确保线程安全和高性能?

文章目录 🎉欢迎来到Java面试技巧专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页:IT陈寒的博客🎈该系列文章专栏:Java面试技巧文章作者技术和水平有限,如果文中出现错误…

2017年3月全国计算机等级考试真题(C语言二级)

2017年3月全国计算机等级考试真题(C语言二级) 第1题 每个学校有一名校长,且不同学校的校长可以是同一人,则实体学校和实体校长间的联系是 A. 多对一 B. 多对多 C. 一对一 D. 一对多 正确答案:A 第2题 若有以下定义…

听GPT 讲Prometheus源代码--promtool

promtool是Prometheus的一个命令行工具,它提供了一些功能来帮助用户进行Prometheus配置文件(如prometheus.yml)的检查、规则检查和调试,还可以用于查询Prometheus服务器以获取度量值等。 以下是一些主要的promtool命令&#xff1a…

Azure不可变Blob存储

文章目录 Azure不可变Blob存储介绍Azure不可变性策略实战演练 Azure不可变Blob存储介绍 不可变的存储是一种用于存储业务关键型 Blob 数据的存储方式。与可变存储相反,不可变存储的特点是一旦数据被写入后,便无法再对其进行修改或删除。这种存储方式提供…

一百六十一、Kettle——Linux上安装的kettle9.2开启carte服务(亲测、附流程截图)

一、目的 在Linux上安装好kettle9.2并且连接好各个数据库后,下面开启carte服务 二、实施步骤 (一)carte服务文件路径 kettle的Linux运行的carte服务文件是carte.sh (二)修改kettle安装路径下的pwd文件夹里的服务器…

『C语言』数据在内存中的存储规则

前言 小羊近期已经将C语言初阶学习内容与铁汁们分享完成,接下来小羊会继续追更C语言进阶相关知识,小伙伴们坐好板凳,拿起笔开始上课啦~ 一、数据类型的介绍 我们目前已经学了基本的内置类型: char //字符数据类型 short …

会声会影2023旗舰版电脑端视频剪辑软件

随着短视频、vlog等媒体形式的兴起,视频剪辑已经成为了热门技能。甚至有人说,不会修图可以,但不能不会剪视频。实际上,随着各种智能软件的发展,视频剪辑已经变得越来越简单。功能最全的2023新版,全新视差转…

【Vue】Mixin 混入

Vue Mixin 混入 1.简介 混入(mixin)提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项(如data、methods、mounted等等)。当组件使用混入对象时,所有混入对象的…

微信小程序卡片横向滚动竖图

滚动并不是使用swiper&#xff0c;该方式使用的是scroll-view实现 Swiper局限性太多了&#xff0c;对竖图并不合适 从左往右滚动图片示例 wxml代码&#xff1a; <view class"img-x" style"margin-top: 10px;"><view style"margin: 20rpx;…

XOR Subsequence 2023“钉耙编程”中国大学生算法设计超级联赛(10)hdu7390

Problem - 7390 题目大意&#xff1a;有一个n个数的数组a&#xff0c;对他们的所有非空子序列求异或和得到长度为的数组b&#xff0c;给出b&#xff0c;求a 1<n<18 思路&#xff1a;可以发现&#xff0c;a数组其实是b数组的线性基 &#xff08;线性基详解_Hypoc_的博客…

el-tabs的上方目录栏增加自定义按钮

需求如图&#xff1a;需要在el-tabs的最右侧加一个自定义按钮&#xff0c;它不属于el-tab-pane&#xff0c;而且要在最右侧。这时候就要使用定位来完成这个按钮的显示。 代码结构如下&#xff1a;自定义按钮要与el-tabs的层级并列&#xff0c;然后通过css设置custom-btn的定位…

15、分布式锁简介

一 分布式锁简介 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程进行&#xff0c;让程序串行执行&#xf…

2023.8-java-基本语法

基本语法 编写 Java 程序时&#xff0c;应注意以下几点&#xff1a; 大小写敏感&#xff1a;Java 是大小写敏感的&#xff0c;这就意味着标识符 Hello 与 hello 是不同的。类名&#xff1a;对于所有的类来说&#xff0c;类名的首字母应该大写。如果类名由若干单词组成&#x…

nginx(七十七)nginx与包体的探究

一 nginx与body体 说明&#xff1a;本文不具有生产意义,只是为了nginx知识的闭环,可以跳过即可 --> "数据脱敏"题外话&#xff1a; 对body的CURD,nginx和openresty处理方式不同强调&#xff1a; 本文是基于http演示的,如果是https加密我们是看不到的 ① core模…

博客系统之功能测试

博客系统共有&#xff1a;用户登录功能、发布博客功能、查看文章详情功能、查看文章列表功能、删除文章功能、退出功能 1.登录功能&#xff1a; 1.1测试对象&#xff1a;用户登录 1.2测试用例 方法&#xff1a;判定表 用例 编号 操作步骤预期结果实际结果截图1 1.用户名正确…

安全学习DAY18_信息打点-APP资产搜集

信息打点-APP资产&静态提取&动态抓包&动态调试 文章目录 信息打点-APP资产&静态提取&动态抓包&动态调试本节知识&思维导图本节使用到的链接&工具 如何获取目标APP从名称中获取APP从URL获取APP APP搜集资产信息APP提取信息分类信息提取方式信息…