2、FreeRTOS之队列管理

news2024/9/18 14:00:13

xQueueReceive() 用于从队列中接收 ( 读取)数据单元。接收到的单元同时会从队列
中删除。
xQueuePeek() 也是从从队列中接收数据单元,不同的是并不从队列中删出接收到
的单元。

 uxQueueMessagesWaiting()用于查询队列中当前有效数据单元个数。

写队列任务的代码实现:
这个任务被创建了两个实例,一个不
停地往队列中写数值 100 ,而另一个实例不停地往队列中写入数值 200 。任务的入口参
数被用来为每个实例传递各自的写入值。
static void vSenderTask( void *pvParameters ) 
{ 
	long lValueToSend; 
	portBASE_TYPE xStatus; 
	/* 该任务会被创建两个实例,所以写入队列的值通过任务入口参数传递 – 这种方式使得每个实例使用不同的
	值。队列创建时指定其数据单元为long型,所以把入口参数强制转换为数据单元要求的类型 */ 
	lValueToSend = ( long ) pvParameters; 
	/* 和大多数任务一样,本任务也处于一个死循环中 */ 
	for( ;; ) 
	{ 
		/* 往队列发送数据
		第一个参数是要写入的队列。队列在调度器启动之前就被创建了,所以先于此任务执行。
		第二个参数是被发送数据的地址,本例中即变量lValueToSend的地址。
		第三个参数是阻塞超时时间 – 当队列满时,任务转入阻塞状态以等待队列空间有效。本例中没有设定超
		时时间,因为此队列决不会保持有超过一个数据单元的机会,所以也决不会满。
		 */ 
		xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 ); 
		if( xStatus != pdPASS ) 
		{ 
		/* 发送操作由于队列满而无法完成 – 这必然存在错误,因为本例中的队列不可能满。 */ 
		vPrintString( "Could not send to the queue.\r\n" ); 
		} 
		/* 允许其它发送任务执行。 taskYIELD()通知调度器现在就切换到其它任务,而不必等到本任务的时
		间片耗尽 */ 
		taskYIELD(); 
	} 
}
读队列任务的代码实现:
读队列任务设定了 100 毫秒的阻塞超时时
间,所以会进入阻塞态以等待队列数据有效。一旦队列中数据单元有效,或者即使队列
数据无效但等待时间超过 100 毫秒,此任务将会解除阻塞。在本例中,将永远不会出
100 毫秒超时,因为有两个任务在不停地往队列中写数据。
static void vReceiverTask( void *pvParameters ) 
{ 
	/* 声明变量,用于保存从队列中接收到的数据。 */ 
	long lReceivedValue; 
	portBASE_TYPE xStatus; 
	const portTickType xTicksToWait = 100 / portTICK_RATE_MS; 
	/* 本任务依然处于死循环中。 */ 
	for( ;; ) 
	{ 
	/* 此调用会发现队列一直为空,因为本任务将立即删除刚写入队列的数据单元。 */ 
	if( uxQueueMessagesWaiting( xQueue ) != 0 ) 
	{ 
		vPrintString( "Queue should have been empty!\r\n" ); 
	} 
	/* 从队列中接收数据
	第一个参数是被读取的队列。队列在调度器启动之前就被创建了,所以先于此任务执行。
	第二个参数是保存接收到的数据的缓冲区地址,本例中即变量lReceivedValue的地址。此变量类型与
	队列数据单元类型相同,所以有足够的大小来存储接收到的数据。
	第三个参数是阻塞超时时间 – 当队列空时,任务转入阻塞状态以等待队列数据有效。本例中常量
	portTICK_RATE_MS用来将100毫秒绝对时间转换为以系统心跳为单位的时间值。
	 */ 
	xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait ); 
	if( xStatus == pdPASS ) 
	{ 
		/* 成功读出数据,打印出来。 */ 
		vPrintStringAndNumber( "Received = ", lReceivedValue ); 
	} 
	else 
	{ 
		/* 等待100ms也没有收到任何数据。
		必然存在错误,因为发送任务在不停地往队列中写入数据 */ 
		vPrintString( "Could not receive from the queue.\r\n" ); 
	} 
	} 
}
main()函数的实现:
其在启动调度器之前创建了一个队列和三
个任务。尽管对任务的优先级的设计使得队列实际上在任何时候都不可能多于一个数据
单元,本例代码还是创建了一个可以保存最多 5 long 型值的队列
/* 声明一个类型为 xQueueHandle 的变量. 其用于保存队列句柄,以便三个任务都可以引用此队列 */ 
xQueueHandle xQueue; 
int main( void ) 
{ 
	/* 创建的队列用于保存最多5个值,每个数据单元都有足够的空间来存储一个long型变量 */ 
	xQueue = xQueueCreate( 5, sizeof( long ) ); 
	if( xQueue != NULL ) 
	{ 
		/* 创建两个写队列任务实例,任务入口参数用于传递发送到队列的值。所以一个实例不停地往队列发送
		100,而另一个任务实例不停地往队列发送200。两个任务的优先级都设为1。 */ 
		xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL ); 
		xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL ); 
		/* 创建一个读队列任务实例。其优先级设为2,高于写任务优先级 */ 
		xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL ); 
		/* 启动调度器,任务开始执行 */ 
		vTaskStartScheduler(); 
	} 
	else 
	{ 
	/* 队列创建失败*/ 
	} 
	/* 如果一切正常,main()函数不应该会执行到这里。但如果执行到这里,很可能是内存堆空间不足导致空闲
	任务无法创建。第五章有讲述更多关于内存管理方面的信息 */ 
	for( ;; ); 
}
写队列任务在每次循环中都调用 taskYIELD() taskYIELD() 通知调度器立即进行任 务切换,而不必等到当前任务的时间片耗尽。某个任务调用 taskYIELD() 等效于其自愿 放弃运行态。由于本例中两个写队列任务具有相同的任务优先级,所以一旦其中一个任 务调用了 taskYIELD() ,另一个任务将会得到执行 调用 taskYIELD()的任务转移到 就绪态,同时另一个任务进入运行态。这样就可以使得这两个任务轮翻地往队列发送数据。

使用队列传递复合数据类型

通常接收方收到数 据后,需要知道数据的来源,并根据数据的来源决定下一步如何处理。一个简单的方式 就是利用队列传递结构体,结构体成员中就包含了数据信息和来源信息。

写队列任务具有最高优先级,所以队列正常情况下一直
是处于满状态。这是因为一旦读队列任务从队列中读走一个数据单元,某个写队列任务
就会立即抢占读队列任务,把刚刚读走的位置重新写入,之后便又转入阻塞态以等待队
列空间有效。
/* 定义队列传递的结构类型。 */ 
typedef struct 
{ 
	unsigned char ucValue; 
	unsigned char ucSource; 
} xData; 
/* 声明两个xData类型的变量,通过队列进行传递。 */ 
static const xData xStructsToSend[ 2 ] = 
{ 
	{ 100, mainSENDER_1 }, /* Used by Sender1. */ 
	{ 200, mainSENDER_2 } /* Used by Sender2. */ 
};

static void vReceiverTask( void *pvParameters ) 
{ 
	/* 声明结构体变量以保存从队列中读出的数据单元 */ 
	xData xReceivedStructure; 
	portBASE_TYPE xStatus; 
	/* This task is also defined within an infinite loop. */ 
	for( ;; ) 
	{ 
		/* 读队列任务的优先级最低,所以其只可能在写队列任务阻塞时得到执行。而写队列任务只会在队列写
		满时才会进入阻塞态,所以读队列任务执行时队列肯定已满。所以队列中数据单元的个数应当等于队列的
		深度 – 本例中队列深度为3 */ 
		if( uxQueueMessagesWaiting( xQueue ) != 3 ) 
		{ 
			vPrintString( "Queue should have been full!\r\n" ); 
		} 
		/* Receive from the queue. 
		第二个参数是存放接收数据的缓存空间。本例简单地采用一个具有足够空间大小的变量的地址。
		第三个参数是阻塞超时时间 – 本例不需要指定超时时间,因为读队列任会只会在队列满时才会得到执行,
		故而不会因队列空而阻塞 */ 
		xStatus = xQueueReceive( xQueue, &xReceivedStructure, 0 ); 
		if( xStatus == pdPASS ) 
		{ 
			/* 数据成功读出,打印输出数值及数据来源。 */ 
			if( xReceivedStructure.ucSource == mainSENDER_1 ) 
			{ 
				vPrintStringAndNumber( "From Sender 1 = ", xReceivedStructure.ucValue ); 
			} 
			else 
			{ 
				vPrintStringAndNumber( "From Sender 2 = ", xReceivedStructure.ucValue ); 
			} 
		} 
		else 
		{ 
			/* 没有读到任何数据。这一定是发生了错误,因为此任务只支在队列满时才会得到执行 */ 
			vPrintString( "Could not receive from the queue.\r\n" ); 
		} 
	} 
}

int main( void ) 
{ 
	/* 创建队列用于保存最多3个xData类型的数据单元。 */ 
	xQueue = xQueueCreate( 3, sizeof( xData ) ); 
	if( xQueue != NULL ) 
	{ 
		/* 为写队列任务创建2个实例。 The 
		任务入口参数用于传递发送到队列中的数据。因此其中一个任务往队列中一直写入
		xStructsToSend[0],而另一个则往队列中一直写入xStructsToSend[1]。这两个任务的优先级都
		设为2,高于读队列任务的优先级 */ 
		xTaskCreate( vSenderTask, "Sender1", 1000, &( xStructsToSend[ 0 ] ), 2, NULL ); 
		xTaskCreate( vSenderTask, "Sender2", 1000, &( xStructsToSend[ 1 ] ), 2, NULL ); 
		/* 创建读队列任务。
		读队列任务优先级设为1,低于写队列任务的优先级。 */ 
		xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 1, NULL ); 
		/* 启动调度器,创建的任务得到执行。 */ 
		vTaskStartScheduler(); 
	} 
	else 
	{ 
	/* 创建队列失败。 */ 
	} 
	/* 如果一切正常,main()函数不应该会执行到这里。但如果执行到这里,很可能是内存堆空间不足导致空闲
	任务无法创建。第五章将提供更多关于内存管理方面的信息 */ 
	for( ;; ); 
}

如果队列存储的数据单元尺寸较大,那最好是利用队列来传递数据的指针而不是对
数据本身在队列上一字节一字节地拷贝进或拷贝出。
但是,当你利用队列传递指针时,一定要十分小心地做到以
下两点:
1、指针指向的内存空间的所有权必须明确
原则上,共享内存在其指针发送到队列之前,其内容只允许被发送任务访问;
共享内存指针从队列中被读出之后,其内容亦只允许被接收任务访问。
2.、指针指向的内存空间必须有效
切忌用指针访问任务栈上分配的空间。因为当栈帧发生改变后,栈上的数据将不再
有效

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

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

相关文章

springboot项目学习-瑞吉外卖(1)

第一天任务如下: 建立基本架构完成登录、退出功能 注意:本博客没有使用网上教程里的mybatis-plus,使用的是mybatis;数据库连接池也没有使用教程里的druid,使用的是spring自带的连接池 基本架构 common包:存…

主干网络篇 | YOLOv8更换主干网络之ShuffleNetV2(包括完整代码+添加步骤+网络结构图)

前言:Hello大家好,我是小哥谈。ShuffleNetV2是一种轻量级的神经网络架构,用于图像分类和目标检测任务。它是ShuffleNet的改进版本,旨在提高模型的性能和效率。ShuffleNetV2相比于之前的版本,在保持模型轻量化的同时&am…

spc x-bar 正态分布 echarts demo

使用echarts,elementUi,vue编写的spc分析的demo示例. 含x-bar和正态分布图,同一数据可以互转 chart.vue <template><div class"app-container"><el-row><el-col :span"4" class"button-container"><el-button clic…

Linux学习——线程池的创建

一&#xff0c;线程池的作用和优点 线程池使用的是一种池化技术&#xff0c;当我们要使用线程时采用线程池创建就一次创建多个线程&#xff0c;在调用当前线程时就让其它的线程进行等待。这样做的优点有如下几点&#xff1a; 1&#xff0c;提高响应速度。线程池提前把线程创建好…

使用Laravel框架创建项目

1.使用Composer创建项目 composer create-project --prefer-dist laravel/laravel blog "5.5.*" 如下图所以&#xff0c;Laravel框架就安装完成了 安装完成后&#xff0c;进入到项目文件夹根目录&#xff0c;打开终端&#xff0c;输入php artisan serve运行项目 p…

Linux操作系统裸机开发-环境搭建

一、配置SSH服务 1、下载安装ssh服务输入以下命令 sudo apt-get install nfs-kernel-server portmap2、建立一个供SSH服务使用的文件夹如以下命令 mkdir linux 3、完成前两步之后需要将其文件路径放到/etc/exports文件里输入以下命令&#xff1a; sudo vi /etc/esports 4.打…

天天说微服务,天天开发RESTful API,那你知道RESTful API是什么东东吗?

RESTful API&#xff08;Representational State Transfer&#xff09;是一种基于网络的架构风格&#xff0c;用于设计和构建Web服务。它是一种轻量级的架构&#xff0c;可以通过HTTP协议进行通信&#xff0c;并支持各种数据格式&#xff0c;例如JSON和XML。 在现代的Web应用程…

三极管工作原理及典型电路

一、三极管的工作原理 三极管&#xff0c;也被称为双极型晶体管或晶体三极管&#xff0c;是一种电流控制元件。主要功能是将微弱的电信号放大成幅度值较大的电信号&#xff0c;工作在饱和区和截止区时同时也被用作无触点开关。 根据结构和工作原理的不同&#xff0c;三极管可以…

Jmeter---分布式

分布式&#xff1a;多台机协作&#xff0c;以集群的方式完成测试任务&#xff0c;可以提高测试效率。 分布式架构&#xff1a;控制机&#xff08;分发任务&#xff09;与多台执行机&#xff08;执行任务&#xff09; 环境搭建&#xff1a; 不同的测试机上安装 Jmeter 配置基…

代码随想录|Day22|回溯02|216.组合总和III、17.电话号码的字母组合

216.组合总和III 本题思路和 77. 组合 类似&#xff0c;在此基础上多了一个和为 n 的判断。 class Solution:def combinationSum3(self, k: int, n: int) -> List[List[int]]:def backtrack(start, path, currentSum):# 递归终止条件&#xff1a;到达叶子节点# 如果和满足条…

HTTPS证书很贵吗?

首先&#xff0c;我们需要明确一点&#xff0c;HTTPS证书的价格并不是一成不变的&#xff0c;它受到多种因素的影响。其中最主要的因素包括证书的类型、颁发机构以及所需的验证级别。 从类型上来看&#xff0c;HTTPS证书主要分为单域名证书、多域名证书和通配符证书。单域名证书…

mmz批量多页抓取数据-AES.CBC算法-爬虫

目标&#xff1a;mmz多页下载 方法&#xff1a;加一个for循环实现多页的下载 问题&#xff1a;浏览器传输服务器时对页码参数做了加密处理 解决方法&#xff1a; 1、判断加密算法模式&#xff08;mmz是AES-CBC算法&#xff09; 2、找到加密的key和iv 代码&#xff1a; i…

基于springboot+vue实现疫情防控物资调配系统项目【项目源码】计算机毕业设计

基于springbootvue实现疫情防控物资调配系统演示 B/S结构的介绍 在确定了项目的主题和研究背景之后&#xff0c;就要确定本系统的架构了。主流的架构有两种&#xff0c;一种是B/S架构&#xff0c;一种是C/S架构。C/S的全称是Client/Server&#xff0c;Client是客户端的意思&am…

HarmonyOS NEXT应用开发—Grid和List内拖拽交换子组件位置

介绍 本示例分别通过onItemDrop()和onDrop()回调&#xff0c;实现子组件在Grid和List中的子组件位置交换。 效果图预览 使用说明&#xff1a; 拖拽Grid中子组件&#xff0c;到目标Grid子组件位置&#xff0c;进行两者位置互换。拖拽List中子组件&#xff0c;到目标List子组件…

插入排序:一种简单而有效的排序算法

插入排序&#xff1a;一种简单而有效的排序算法 一、什么是插入排序&#xff1f;二、插入排序的步骤三、插入排序的C语言实现四、插入排序的性能分析五、插入排序的优化六、总结 在我们日常生活和工作中&#xff0c;排序是一种非常常见的操作。比如&#xff0c;我们可能需要对一…

MasterPDF 强大的多功能软件

哈喽呀&#xff0c;我是苏音今天给大家带来一期免费PDF的工具&#xff0c;可以实现你的大部分需求。 最近有PDF文档相关的的需求&#xff0c;但是之前一直在用WPS&#xff0c;就看能不能实现下面两个功能 1.导出指定页的PDF 2.在某一页PDF中加入指定图片 虽然WPS可以实现将…

免费接口调用 招标信息自动抽取|招标信息|招标数据解析接口

一、开源项目介绍 一款多模态AI能力引擎&#xff0c;专注于提供自然语言处理&#xff08;NLP&#xff09;、情感分析、实体识别、图像识别与分类、OCR识别和语音识别等接口服务。该平台功能强大&#xff0c;支持本地化部署&#xff0c;并鼓励用户体验和开发者共同完善&#xf…

SpringBoot整合Seata注册到Nacos服务

项目引入pom文件 <!-- SpringCloud Seata 组件--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-seata</artifactId><version>${alibaba.seata}</version><exclusions><exc…

Postman接口测试之断言,全网最细教程没有之一!

一、断言 在 postman 中我们是在Tests标签中编写断言&#xff0c;同时右侧封装了常用的断言&#xff0c;当然 Tests 除了可以作为断言&#xff0c;还可以当做后置处理器来编写一些后置处理代码&#xff0c;经常应用于&#xff1a; 【1】获取当前接口的响应&#xff0c;传递给…

智慧城市革命,物联网技术如何改变城市治理与生活方式

随着科技的不断进步&#xff0c;智慧城市已经成为现代城市发展的重要方向之一。物联网技术作为智慧城市的重要支撑&#xff0c;正深刻改变着城市的治理模式和居民的生活方式。本文将探讨智慧城市革命&#xff0c;以及物联网技术如何改变城市治理与生活方式&#xff0c;同时介绍…