04 FreeRTOS 队列(queue)

1、队列的特性

        队列可以理解为一个传送带,一个流水线。

        队列可以包含若干个数据:队列中有若干项,这被称为"长度"(length)

        每个数据大小固定

        创建队列时就要指定长度、数据大小

        数据的操作采用先进先出的方法(FIFO,First In First Out):写数据时放到尾部,读数据时从头部读,把数据写在队列头部并不会覆盖原来头部的数据,因为队列中的数据使用环形缓冲区管理数据,把数据放到头部时,会先移动头部位置,并不会覆盖原来数据。

2、队列的函数

2.1 创建

//函数原型
    //队列长度,每一项的大小
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );

2.2 写队列

        可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在ISR中使用。

/* 等同于xQueueSendToBack
* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSend(QueueHandle_t xQueue,
                      const void *pvItemToQueue,
                      TickType_t xTicksToWait
);
/*
* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToBack(QueueHandle_t xQueue,
                            const void *pvItemToQueue,
                            TickType_t xTicksToWait
);
/*
* 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,
                                   const void *pvItemToQueue,
                                   BaseType_t *pxHigherPriorityTaskWoken
);
/*
* 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToFront(QueueHandle_t xQueue,
                             const void *pvItemToQueue,
                             TickType_t xTicksToWait
);
/*
* 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,
                                    const void *pvItemToQueue,
                                    BaseType_t *pxHigherPriorityTaskWoken
);
参数
说明
xQueue
队列句柄,要写哪个队列
pvItemToQueue
数据指针,这个数据的值会被复制进队列,
复制多大的数据?在创建队列时已经指定了数据大小
xTicksToWait
如果队列满则无法写入新数据,可以让任务进入阻塞状态,
xTicksToWait 表示阻塞的最大时间 (Tick Count)
如果被设为 0 ,无法写入数据时函数会立刻返回;
如果被设为 portMAX_DELAY ,则会一直阻塞直到有空间可写
返回值
pdPASS :数据成功写入了队列
errQUEUE_FULL :写入失败,因为队列满了。

 

2.3 读队列

        使用 xQueueReceive() 函数读队列,读到一个数据后,队列中该数据会被移除。这个函数有两个版 本:在任务中使用、在ISR中使用。

BaseType_t xQueueReceive( QueueHandle_t xQueue,
                            void * const pvBuffer,
                            TickType_t xTicksToWait 
);
BaseType_t xQueueReceiveFromISR(
                            QueueHandle_t xQueue,
                            void *pvBuffer,
                            BaseType_t *pxTaskWoken
);
参数
说明
xQueue
队列句柄,要写哪个队列
pvBuffer
bufer 指针,队列的数据会被复制到这个 buffer
复制多大的数据?在创建队列时已经指定了数据大小
xTicksToWait
如果队列空则无法读出数据,可以让任务进入阻塞状态,
xTicksToWait 表示阻塞的最大时间 (Tick Count)
如果被设为 0 ,无法读出数据时函数会立刻返回;
如果被设为 portMAX_DELAY ,则会一直阻塞直到有数据可写
返回值
pdPASS :从队列读出数据入
errQUEUE_EMPTY :读取失败,因为队列空了。

2.4 删除

        删除队列的函数为 vQueueDelete() ,只能删除使用动态方法创建的队列,它会释放内存。

void vQueueDelete( QueueHandle_t xQueue );

2.5 查询

        可以查询队列中有多少个数据、有多少空余空间。

/*
* 返回队列中可用数据的个数
*/
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
/*
* 返回队列中可用空间的个数
*/
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );

2.6 复位

        队列刚被创建时,里面没有数据,使用过程中可以调用 xQueueReset() 把队列恢复为初始状态。

/* pxQueue : 复位哪个队列;
* 返回值: pdPASS(必定成功)
*/
BaseType_t xQueueReset( QueueHandle_t pxQueue);

2.7 覆盖

        当队列长度为1时,可以使用 xQueueOverwrite() xQueueOverwriteFromISR() 来覆盖数据。 注意,队列长度必须为1。当队列满时,这些函数会覆盖里面的数据,这也意味着这些函数不会被阻塞。

/* 覆盖队列
* xQueue: 写哪个队列
* pvItemToQueue: 数据地址
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueueOverwrite(QueueHandle_t xQueue,
                            const void * pvItemToQueue
);
BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue,
                                    const void * pvItemToQueue,
                                    BaseType_t *pxHigherPriorityTaskWoken
);

2.8 偷看

        如果想让队列中的数据供多方读取,也就是说读取时不要移除数据,要留给后来人。那么可以使用"窥视",也就是 xQueuePeek() xQueuePeekFromISR() 。这些函数会从队列中复制出数据,但是不移除数据。这也意味着,如果队列中没有数据,那么"偷看"时会导致阻塞;一旦队列中有数据,以后每次"偷看"都会成功。

/* 偷看队列
* xQueue: 偷看哪个队列
* pvItemToQueue: 数据地址, 用来保存复制出来的数据
* xTicksToWait: 没有数据的话阻塞一会
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueuePeek(QueueHandle_t xQueue,
                        void * const pvBuffer,
                        TickType_t xTicksToWait
);
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,
                                void *pvBuffer,
);

3、示例代码

3.1 同步

static int sum = 0;
static volatile int flagCalcEnd = 0;
static QueueHandle_t xQueueCalcHandle;

void Task1Function( void * param)
{
	volatile int i = 0;	//使用volatile修饰,让系统不要去优化这个变量
	while(1){
		for(i = 0; i < 10000000; i++){
			sum++;
		}
		xQueueSend(xQueueCalcHandle, &sum, portMAX_DELAY);
		sum = 1;
	}
}

void Task2Function( void * param)
{
	int val;
	while(1){
		flagCalcEnd = 0;
		xQueueReceive(xQueueCalcHandle, &val, portMAX_DELAY);
		flagCalcEnd = 1;
		printf("sum = %d\r\n", val);
	}
}


//main函数中
printf("hello go\r\n");
	
xQueueCalcHandle = xQueueCreate(2, sizeof(int));
if(xQueueCalcHandle == NULL){
	printf("can not create queue\r\n");
}
	
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);

        使用队列的方法实现了同步,flagCalcEnd在被拉高的时候,用时2s

3.2 互斥

static QueueHandle_t xQueueUARTHandle;

int InitUARTLock(void)
{
	int val;
	xQueueUARTHandle = xQueueCreate(1, sizeof(int));
	if(xQueueUARTHandle == NULL){
		printf("can not create queue\r\n");
		return -1;
	}
	xQueueSend(xQueueUARTHandle, &val, portMAX_DELAY);
	return 0;
}

void GetUARTLock(void)
{
	int val;
	xQueueReceive(xQueueUARTHandle, &val, portMAX_DELAY);
}

void PutUARTLock(void)
{
	int val;
	xQueueSend(xQueueUARTHandle, &val, portMAX_DELAY);
}

void TaskGenericFunction( void * param)
{
	while(1){
		GetUARTLock();
		printf("%s\r\n", (char *)param);
		//在释放锁之前,任务三在等待
		PutUARTLock();	//在释放锁时,任务三进入就绪状态,但是此时任务四是运行状态,马上又要上锁,轮不到任务三去执行
		vTaskDelay(1);	//主动让一下
		/*
			除了通过vTaskDelay让出CPU资源,还有更合理的函数:
				使用taskYIELD(),主动发起—次任务切换
				vTaskDelay会让任务阻塞、暂停若干tick,taskYIELD()更合理
				可以设置不同的优先级来实现抢占
		*/
	}
}

//main函数中
InitUARTLock();
	
xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);
xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);

         先初始化一个锁,这个锁用队列来实现,创建完队列,往里面随便写一个数据,队列里面有数据就表示别人可以来读取这个数据。假设某个任务要使用串口,先用GetUARTLock获得串口的锁,去读队列,得到这个队列的数据就表示得到这个串口的使用权,用完串口之后就往队列里随便写一个数据,表示使用完串口了,把这个使用权释放掉。

3.3 队列集

        创建两个队列Queue1和Queue2,Task1和Task2分别往这两个队列里写入数据,Task3使用Queue Set来监测这两个队列。队列集的长度是包含的队列长度之和。

static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;
static QueueHandle_t xQueueHandle1;
static QueueHandle_t xQueueHandle2;
static QueueSetHandle_t xQueueSet;


void Task1Function(void * param)
{
	int i = 0;
	while (1)
	{
		xQueueSend(xQueueHandle1, &i, portMAX_DELAY);
		i++;
		vTaskDelay(10);
	}
}

void Task2Function(void * param)
{
	int i = -1;
	while (1)
	{
		xQueueSend(xQueueHandle2, &i, portMAX_DELAY);
		i--;
		vTaskDelay(20);
	}
}

void Task3Function(void * param)
{
	QueueSetMemberHandle_t handle;
	int i;
	while (1)
	{
		/* 1. read queue set: which queue has data */
		handle = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);

		/* 2. read queue */
		xQueueReceive(handle, &i, 0);

		/* 3. print */
		printf("get data : %d\r\n", i);
	}
}

//main函数中
TaskHandle_t xHandleTask1;

/* 1. 创建2个queue */

xQueueHandle1 = xQueueCreate(2, sizeof(int));
if (xQueueHandle1 == NULL)
{
	printf("can not create queue\r\n");
}

xQueueHandle2 = xQueueCreate(2, sizeof(int));
if (xQueueHandle2 == NULL)
{
	printf("can not create queue\r\n");
}

/* 2. 创建queue set */
xQueueSet = xQueueCreateSet(3);

/* 3. 把2个queue添加进queue set */
xQueueAddToSet(xQueueHandle1, xQueueSet);
xQueueAddToSet(xQueueHandle2, xQueueSet);

/* 4. 创建3个任务 */
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
xTaskCreate(Task3Function, "Task3", 100, NULL, 1, NULL);

3.4 邮箱(mailboc)

        FreeRTOS的邮箱概念跟别的RTOS不一样,这里的邮箱称为"橱窗"也许更恰当:

                它是一个队列,队列长度只有1

                写邮箱:新数据覆盖旧数据,在任务中使用 xQueueOverwrite() ,在中断中使用 xQueueOverwriteFromISR() 。 既然是覆盖,那么无论邮箱中是否有数据,这些函数总能成功写入数据。

                读邮箱:读数据时,数据不会被移除;在任务中使用 xQueuePeek() ,在中断中使用 xQueuePeekFromISR() 。 这意味着,第一次调用时会因为无数据而阻塞,一旦曾经写入数据,以后读邮箱时总能成功。

        main函数中创建了队列(队列长度为1)、创建了发送任务、接收任务:

                发送任务的优先级为2,它先执行

                接收任务的优先级为1

/* 队列句柄, 创建队列时会设置这个变量 */
QueueHandle_t xQueue;
int main( void )
{
    prvSetupHardware();
    /* 创建队列: 长度为1,数据大小为4字节(存放一个char指针) */
    xQueue = xQueueCreate( 1, sizeof(uint32_t) );
    if( xQueue != NULL )
    {
        /* 创建1个任务用于写队列
        * 任务函数会连续执行,构造buffer数据,把buffer地址写入队列
        * 优先级为2
        */
        xTaskCreate( vSenderTask, "Sender", 1000, NULL, 2, NULL );
        /* 创建1个任务用于读队列
        * 优先级为1
        */
        xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL );
        /* 启动调度器 */
        vTaskStartScheduler();
    }
    else
    {
        /* 无法创建队列 */
    }
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Spring-01】BeanFactory和ApplicationContext

【Spring-01】BeanFactory和ApplicationContext 1. 容器接口1.1 什么是 BeanFactory1.2 BeanFactory 能做什么&#xff1f; 1. 容器接口 以 SpringBoot 的启动类为例&#xff1a; /*** BeanFactory 与 ApplicationContext的区别*/ SpringBootApplication public class Spring…

27快28了,想转行JAVA或者大数据,还来得及吗?

转行到JAVA或者大数据领域&#xff0c;27岁快28岁的年龄完全来得及。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我在后台发给你。…

通义千问图像识别功能的23个实用案例

●给出穿搭建议 这位女士佩戴的是一款精致的长款耳坠&#xff0c;设计上融合了复古和现代元素。为了更好地搭配这款耳环&#xff0c;以下是一些建议&#xff1a; 服装风格&#xff1a;由于耳环本身具有一定的华丽感&#xff0c;建议选择简约而优雅的服装来平衡整体造型。可以选…

二叉树习题精讲-单值二叉树

单值二叉树 965. 单值二叉树 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/univalued-binary-tree/description/ 判断这里面的所有数值是不是一样 方案1&#xff1a;遍历 方案2&#xff1a;拆分子问题 /*** Definition for a binary tree node.* struc…

意外发现openGauss兼容Oracle的几个条件表达式

意外发现openGauss兼容Oracle的几个条件表达式 最近工作中发现openGauss在兼容oracle模式下&#xff0c;可以兼容常用的两个表达式&#xff0c;因此就随手测试了一下。 查看数据库版本 [ommopenGauss ~]$ gsql -r gsql ((openGauss 6.0.0-RC1 build ed7f8e37) compiled at 2…

嵌入式进阶——RTC时钟

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 RTC时钟原理图PCF8563寄存器控制与状态寄存器 设备地址I2C环境初始化RTC寄存器数据读取RTC寄存器数据写入RTC闹钟设置RTC定时器设置…

HNU-人工智能-作业3

人工智能-作业3 计科210X 甘晴void 202108010XXX 1.贝叶斯网络 根据图所给出的贝叶斯网络&#xff0c;其中&#xff1a;P(A)0.5&#xff0c;P(B|A)1&#xff0c; P(B|A)0.5&#xff0c; P(C|A)1&#xff0c; P(C|A)0.5&#xff0c;P(D|BC)1&#xff0c;P(D|B, C)0.5&#xff…

基于51单片机的直流电机调速设计

一.硬件方案 本系统采用STC89C51控制输出数据&#xff0c;由单片机IO口产生PWM信号&#xff0c;送到直流电机&#xff0c;直流电机通过测速电路将实时转速送回单片机&#xff0c;进行转速显示&#xff0c;从而实现对电机速度和转向的控制&#xff0c;达到直流电机调速的目的。…

C 基础环境配置(vscode || vs)

目录 一.发展 二. 环境设置 1.vs2022 2.vscode (1.)首先下载VsCode (2)安装vsCode插件 (3)下载MinGW-W64 (4)配置文件 (5)注意把里面配置的:mingw64路径改为自己的路径 (6)示例代码 三.总结 一.发展 编程语言的发展 机器语言(打孔纸带编程),汇编语言,高级语言,一步步…

超详细的前后端实战项目(Spring系列加上vue3)前端篇+后端篇(三)(一步步实现+源码)

好了&#xff0c;兄弟们&#xff0c;继昨天的项目之后&#xff0c;开始继续敲前端代码&#xff0c;完成前端部分&#xff08;今天应该能把前端大概完成开启后端部分了&#xff09; 昨天补充了一下登录界面加上了文章管理界面和用户个人中心界面 完善用户个人中心界面 修改一…

【对算法期中卷子的解析和反思】

一、程序阅读并回答问题&#xff08;共30分&#xff09; #include<cstdio>#include<cstring>#include<iostream>using namespace std;char chess[10][10];int sign[10];int n, k, ans;void dfs(int x, int k) { if (k 0){ans;return; } if (xk-1 >…

IDEA升级web项目为maven项目乱码

今天将一个java web项目改造为maven项目。 首先&#xff0c;创建一个新的maven项目&#xff0c;将文件拷贝到新项目中。 其次&#xff0c;将旧项目的jar包&#xff0c;在maven的pom.xml做成依赖 接着&#xff0c;把没有maven坐标的jar包在编译的时候也包含进来 <build>…

Python | Leetcode Python题解之第117题填充每个节点的下一个右侧节点指针II

题目&#xff1a; 题解&#xff1a; class Solution:def connect(self, root: Node) -> Node:if not root:return Nonestart rootwhile start:self.last Noneself.nextStart Nonep startwhile p:if p.left:self.handle(p.left)if p.right:self.handle(p.right)p p.nex…

NV-LIO:一种基于法向量的激光雷达-惯性系统(LIO)

论文&#xff1a;NV-LIO: LiDAR-Inertial Odometry using Normal Vectors Towards Robust SLAM in Multifloor Environments 作者&#xff1a;Dongha Chung, Jinwhan Kim NV-LIO&#xff1a;一种基于法向量的激光雷达-惯性系统&#xff08;LIO&#xff09;NV-LIO利用从激光雷…

ChatGPT魔法,定制个性化提示词!

扮演Prompt创作者的角色 我想让你成为我的Prompt创作者。你的目标是帮助我创建最佳的Prompt&#xff0c;这个Prompt将由 你ChatGPT使用。 你将遵循以下过程&#xff1a; 1.首先&#xff0c;你会问我Prompt是关于什么的。我会告诉你&#xff0c;但我们需要通过不断的重复来改进…

【动态规划】速解简单多状态类问题

目录 17.16 按摩师 题⽬描述&#xff1a; 解法&#xff08;动态规划&#xff09;&#xff1a; 1. 状态表⽰&#xff1a; 2. 状态转移⽅程&#xff1a; 3. 初始化&#xff1a; 4. 填表顺序 5. 返回值 代码 总结&#xff1a; 213.打家劫舍II&#xff08;medium&#x…

mysql内存和磁盘的关系

mysql内存和磁盘的关系 1.MySQL的内存和磁盘之间的关系是密切的。MySQL的数据存储在磁盘上&#xff0c;但为了高效地执行查询操作&#xff0c;它也会将数据页&#xff08;每个页通常为16KB&#xff09;读入内存。MySQL的缓冲池&#xff08;buffer pool&#xff09;是在内存中的…

网络安全防御之下一代防火墙部署思路分享

随着企业在数字化转型过程中不断深化&#xff0c;为了促进业务快速且安全地推出和更新&#xff0c;企业所采用的应用架构和部署方式经历了显著的演进&#xff1a;它们从单一应用转变为分层架构&#xff0c;进而发展为微服务架构&#xff1b;同时部署方式也由传统的本地部署进化…

Java面试八股之Thread类中的yeild方法有什么作用

Thread类中的yeild方法有什么作用 谦让机制&#xff1a;Thread.yield()方法主要用于实现线程间的礼让或谦让机制。当某个线程执行到yield()方法时&#xff0c;它会主动放弃当前已获得的CPU执行权&#xff0c;从运行状态&#xff08;Running&#xff09;转变为可运行状态&#…

详解make file中的notdir

在 Makefile 中&#xff0c;$(notdir names…) 是一个函数&#xff0c;用于获取一组文件名或路径中的文件名部分&#xff0c;并将其返回。 这个函数通常用于从给定的路径中提取文件名部分&#xff0c;非常适合在 Makefile 中进行文件处理操作。 语法&#xff1a; makefile C…