FreeRTOS学习笔记八【队列-下】

2023-05-16

FreeRTOS学习笔记八【队列-下】

  • 队列中传输大数据(一个包)或大小可变的数据
    • 传输大数据
    • 传输不同类型和长度的数据
  • 从多个队列中获取数据
    • 队列集合
    • xQueueCreateSet()
    • xQueueAddToSet()
    • xQueueSelectFromSet()
    • 示例
  • 使用队列创建邮箱
    • xQueueOverwrite()
    • xQueuePeek()

队列中传输大数据(一个包)或大小可变的数据

传输大数据

如果存储在队列中的数据项很大,则最好将指向数据的指针存放在队列中,这种方法可以有效的节省数据的拷贝时间以及降低创建队列时所需的空间。但使用指针时必须注意一下两点:

  1. 指针指向的RAM的所有者是明确定义的。
    通过指针在任务之间共享内存时,必须确保各个任务不会同时修改内存的内容。理想情况下,在将指针入队前只允许发送任务访问内存,在指针出队后只允许接收任务访问内存。
  2. 指针指向的RAM是有效的。
    如果指向的内存时动态分配(或者从预先分配的内存池中获得)的,那么应该只有一个任务负责释放任务。在释放该内存后,任何任务后不可以再访问该内存。并且不应该使用指针访问存放在栈上的变量(自动变量)。

下面通过示例来展示使用指针传递数据。
创建一个可以存放5个指针的队列:

QueueHandle_t xPointerQueue;
xPointerQueue = xQueueCreate( 5, sizeof( char * ) );

分配缓冲区,将数据存入缓冲区,然后将指针入队:

void vStringSendingTask( void *pvParameters )
{
	char *pcStringToSend;
	const size_t xMaxStringLength = 50;
	BaseType_t xStringNumber = 0;
	for( ;; )
	{
		/* 分配缓存区 */
		pcStringToSend = ( char * ) prvGetBuffer( xMaxStringLength );
		/* 在缓冲区存放数据 */
		snprintf( pcStringToSend, xMaxStringLength, "String number %d\r\n", xStringNumber );
		
		xStringNumber++;
		/* 指针入队 */
		xQueueSend( xPointerQueue, &pcStringToSend, portMAX_DELAY );
	}
}

从队列获取指针,然后打印数据,最后释放缓存区:

void vStringReceivingTask( void *pvParameters )
{
	char *pcReceivedString;
	for( ;; )
	{
		/* 获取指针 */
		xQueueReceive( xPointerQueue, &pcReceivedString, portMAX_DELAY );
		/* 打印数据 */
		vPrintString( pcReceivedString );
		/* 释放缓存区 */
		prvReleaseBuffer( pcReceivedString );
	}
}

传输不同类型和长度的数据

前面介绍了通过队列传输结构体和指针,通过这两者的组合就可以在单个队列中从任何数据源接收任意数据类型。

从多个队列中获取数据

队列集合

应用程序中通常需要单个任务来接收不同大小、不同含义、不同数据源的数据,在前面介绍了解决这些问题的方法,但有时应用程序运行在受限的设备或者程序不能满足前面的一些要求,因此需要为某些数据使用单独的队列。例如,第三方代码可能会假设某个专用队列存在,这种情况下可以使用队列集。队列集运行从多个队列中获取数据,但任务不会依次轮询灭个队列来确定哪个队列还包含数据。
与使用接收结构体的单个队列实现相同功能的代码比,使用队列集从多个源接收数据的代码并不那么整洁并且效率较低,因此建议仅在有约束条件时而不能满足前面介绍的方法时才使用队列集。

如果要使用队列集功能,需要将FreeRTOSConfig.h中configUSE_QUEUE_SETS设置为1。通过以下几个步骤使用一个队列集:

  1. 创建队列集。
  2. 向集合添加队列。
    信号量也可以添加到队列集中。 信号量将在后面介绍。
  3. 从队列集中读取以确定集合中的哪些队列包含数据。
    当队列集中的队列中有数据时,在调用从队列集中获取数据的函数会返回收到数据的队列的句柄,此时就可直接从该句柄中获取数据。
    注意:如果队列是队列集的成员,则只能使用队列集返回的队列句柄,否则不要直接从队列中读取数据。

xQueueCreateSet()

在使用队列集之前必须显示创建它之后才能使用。QueueSetHandle_t是队列集句柄的类型,通过调用xQueueCreateSet()可以创建一个队列集,它的原型如下:

QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength );

参数:

  • uxEventQueueLength
    当队列集中的队列收到数据时,接收到数据队列的句柄会被发送到队列集。uxEventQueueLength定义创建的队列集可以容纳的最大句柄数。只有当集合中的队列接收到数据时,才会将队列句柄发送到队列集。如果队列已满,则无法将队列句柄发送到队列集。因此队列集可容纳的最大句柄数就是队列集中每个队列的数据项数之和。例如,集合中有5个队列,每个队列可以容纳3个数据项,那么uxEventQueueLength的值就是15。信号量也可以添加到队列,二值信号量的长度为1,计数信号量的长度是最大计数值,例如,集合中有一个二值信号量,一个长度为10的队列,那么uxEventQueueLength = 10 + 1 = 11。

返回值:

  • NULL 队列集创建失败,因为没有足够的内存来存储队列集。
  • 非NULL,队列集创建成功,返回的是队列集的句柄。

xQueueAddToSet()

xQueueAddToSet()将队列或信号量添加到队列集。原型如下:

BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet );

参数:

  • xQueueOrSemaphore
    要添加到队列集的队列或信号量的句柄。
    队列句柄和信号量句柄都可以强制转换为QueueSetMemberHandle_t类型。
  • xQueueSet
    队列集的句柄。

返回值:

  • pdPASS
    仅当队列或信号量成功添加到队列集时,才会返回pdPASS。
  • pdFAIL
    如果无法将队列或信号量添加到队列集,则将返回pdFAIL。
    队列和二值信号量只能在为空时添加到集合中。 计数信号量只能在计数为零时添加到集合中。 队列和信号量都只能被添加到一个集合中,不可以同时添加到两个队列集。

xQueueSelectFromSet()

xQueueSelectFromSet()从队列集中获取队列的句柄。当集合中队列或信号量收到数据时,队列或信号量的句柄会被发送到队列集,在任务调用xQueueSelectFromSet()时返回句柄,当该函数返回的句柄时,表示该句柄的队列(信号量)已有数据,可以直接从中获取数据。
注意:只能从xQueueSelectFromSet()返回的句柄中获取数据,而不能直接从加入集合中的队列或信号量里获取数据。同时每次从返回的句柄中只能获取一项数据。
xQueueSelectFromSet()的原型如下:

QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait );

参数:

  • xQueueSet
    队列集的句柄。
  • xTicksToWait
    如果xTicksToWait为0,并且队列集中没有队列收到数据,立即返回,如果xTicksToWait不为0,表示队列集中没有队列收到数据的最长等大时间。指定的时间可以通过pdMS_TO_TICKS()转换。如果将FreeRTOSConfig.h中INCLUDE_vTaskSuspend设置为1,将xTicksToWait设置为portMAX_DELAY,任务将无限期地等待。

返回值:

  • 非NULL值表示收到数据的队列或信号量的句柄。 句柄返回QueueSetMemberHandle_t类型,但可以强制转换为QueueHandle_t类型或SemaphoreHandle_t类型。
  • NULL则无法从队列集中读取句柄。 如果xTicksToWait不为零,则该时间内都没有收到数据。

示例

示例中创建了两个发送任务和一个接收任务。 两个发送任务分别在两个队列上发送数据, 两个队列被添加到队列集中,接收任务从队列集中读取以确定两个队列中的哪一个包含数据。

static QueueHandle_t xQueue1 = NULL, xQueue2 = NULL;
/* 队列集的句柄 */
static QueueSetHandle_t xQueueSet = NULL;

void vSenderTask1( void *pvParameters )
{
	const TickType_t xBlockTime = pdMS_TO_TICKS( 100 );
	const char * const pcMessage = "Message from vSenderTask1\r\n";
	for( ;; )
	{
		vTaskDelay( xBlockTime );
		/* 在队列1中发送数据 */
		xQueueSend( xQueue1, &pcMessage, 0 );
	}
}

void vSenderTask2( void *pvParameters )
{
	const TickType_t xBlockTime = pdMS_TO_TICKS( 200 );
	const char * const pcMessage = "Message from vSenderTask2\r\n";
	for( ;; )
	{
		vTaskDelay( xBlockTime );
		/* 在队列2中发送数据*/
		xQueueSend( xQueue2, &pcMessage, 0 );
	}
}
void vReceiverTask( void *pvParameters )
{
	QueueHandle_t xQueueThatContainsData;
	char *pcReceivedString;
	for( ;; )
	{
		/* 获取收到数据的队列的句柄 */
		xQueueThatContainsData = ( QueueHandle_t ) xQueueSelectFromSet( xQueueSet,
		portMAX_DELAY );
		/* 从队列中获取数据 */
		xQueueReceive( xQueueThatContainsData, &pcReceivedString, 0 );
		
		vPrintString( pcReceivedString );
	}
}

int main( void )
{
	/* 创建两个队列 */
	xQueue1 = xQueueCreate( 1, sizeof( char * ) );
	xQueue2 = xQueueCreate( 1, sizeof( char * ) );
	/* 创建队列集 */
	xQueueSet = xQueueCreateSet( 1 * 2 );
	/* 添加队列到队列集 */
	xQueueAddToSet( xQueue1, xQueueSet );
	xQueueAddToSet( xQueue2, xQueueSet );
	/* 创建两个发送任务 */
	xTaskCreate( vSenderTask1, "Sender1", 1000, NULL, 1, NULL );
	xTaskCreate( vSenderTask2, "Sender2", 1000, NULL, 1, NULL );
	/* 创建一个接收任务 */
	xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );
	vTaskStartScheduler();
	for( ;; );
	return 0;
}

运行结果:
在这里插入图片描述

使用队列创建邮箱

“邮箱”用于表示长度为1的队列。由于队列在应用程序中的使用方式,队列可能会被描述为邮箱,而不是因为它与队列有功能差异:

  • 队列用于将数据从一个任务发送到另一个任务,或从中断服务例程发送到任务。发件方将一个数据项放入队列中,接收方从队列中删除该数据项。数据通过队列从发送方传递到接收方。
  • 邮箱用于保存任何任务或任何中断服务例程都可以读取的数据。数据不会经过邮箱,而是保留在邮箱中,直到被覆盖。发件方覆盖邮箱中的数据,接收方从邮箱中读取数据,但不从邮箱中删除该数据。
    创建一个邮箱的方法如下:
/* 实际使用中可以可以是任意类型数据,不一定像本例这样, 这里包含一个更新数据时时间戳,和存放的数据*/
typedef struct xExampleStructure
{
	TickType_t xTimeStamp;
	uint32_t ulValue;
} Example_t;
QueueHandle_t xMailbox;
void vAFunction( void )
{
	/* 创建一个邮箱 */
	xMailbox = xQueueCreate( 1, sizeof( Example_t ) );
}

下面介绍两个允许将队列用作邮箱的队列API函数。

xQueueOverwrite()

与xQueueSendToBack()函数一样,xQueueOverwrite()函数将数据发送到队列。 与xQueueSendToBack()不同的是如果队列已满,则xQueueOverwrite()将覆盖队列中已有的数据。xQueueOverwrite()只应与长度为1的队列一起使用。 该限制避免了实现在队满时需要选择队列中的哪个数据项被覆盖的实现。
注意:不要从中断服务程序调用xQueueOverwrite()。中断安全版本为xQueueOverwriteFromISR()。
函数原型如下:

BaseType_t xQueueOverwrite( QueueHandle_t xQueue, const void * pvItemToQueue );

参数:

  • xQueue
    队列的句柄。
  • pvItemToQueue
    指向写入队列的数据。

返回值:

  • 只会返回pdPASS,因为队满时也会写入数据。

使用示例:

void vUpdateMailbox( uint32_t ulNewValue )
{
	Example_t xData;
	
	xData.ulValue = ulNewValue;

	xData.xTimeStamp = xTaskGetTickCount();
	
	xQueueOverwrite( xMailbox, &xData );
}

xQueuePeek()

xQueuePeek()用于从队列接收(读取)数据,但不删除数据。 它从队列头部接收数据,而不修改存储在队列中的数据,或者数据存储在队列中的顺序。
注意:不要从中断服务程序调用xQueuePeek()。 中断安全版本为xQueuePeekFromISR()。
函数原型:

BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );

它的参数和返回值与xQueueReceive()的相同。
使用示例:

BaseType_t vReadMailbox( Example_t *pxData )
{
	TickType_t xPreviousTimeStamp;
	BaseType_t xDataUpdated;
	/* 记录上一次数据更新时间 */
	xPreviousTimeStamp = pxData->xTimeStamp;
	/* 获取数据 */
	xQueuePeek( xMailbox, pxData, portMAX_DELAY );
	/* 如果数据已更新返回pdTRUE,否则返回pdFALSE */
	if( pxData->xTimeStamp > xPreviousTimeStamp )
	{
		xDataUpdated = pdTRUE;
	}
	else
	{
		xDataUpdated = pdFALSE;
	}
	return xDataUpdated;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS学习笔记八【队列-下】 的相关文章

随机推荐

  • STM32F446ZET6关于使用PA11,PA12,PB14,PB15的使用注意事项

    STM32F446ZET6关于使用PA11 xff0c PA12 xff0c PB14 xff0c PB15的使用注意事项 这两天在用PB14做输入时 xff0c 发现总是不灵敏 xff0c 开始还以为布线有问题 xff0c 检查后发现不是
  • numpy基本方法总结

    NumPy基本方法 一 数组方法 创建数组 xff1a arange 创建一维数组 xff1b array 创建一维或多维数组 xff0c 其参数是类似于数组的对象 xff0c 如列表等 读取数组元素 xff1a 如a 0 a 0 0 数组
  • 如何实现Qt上位机软件串口的按字节数据处理?

    1 Qt串口类的选择 最近在做一个小项目 xff0c 需要用到Qt做上位机软件 xff0c 本人也是边学边做 xff0c 买了本 QtCreator快速入门 看了看就动手了 xff0c 由于初学不是很了解Qt5的官方资源 xff0c 一开始
  • 如何使用HAL库手动修改OSC引脚为PD0/1?

    CubeMX不能直接重映射OSC引脚为PD0 1 xff0c 那么 xff0c 如何使用HAL库手动修改OSC引脚为PD0 1 如下图所示配置即可 xff1a
  • “野火FreeRTOS教程”第7章补充知识点-异常流程

    一 知识点 1 Cortex M3 4在复位后CONTROL寄存器初始值为0 xff0c 也就是说MCU会处于线程模式 具有特权访问权限且使用主栈指针 MSP 2 当进入异常时CM3会自动入栈 xff0c 如下图所示 xff1a 3 当异常
  • mini四旋翼飞行器DIY日志

    一 方案 功能描述 xff1a 具备mini四旋翼飞行器的基本功能 xff0c 可以拓展其他模块实现定高 对航向角yaw的校准 xff0c 将所有io引出并设计出最小系统板子功能 xff0c 将设计I2C总线挂载选择电路便于调试和使用 可以
  • Quartus II 13.1.0.162三件套安装包

    QuartusSetup 13 1 0 162 链接 https pan baidu com s 1B01zWG76kfNcGLA0VmwyMw 提取码 jjdd ModelSimSetup 13 1 0 162 链接 https pan
  • DSP Builder安装时的注意事项

    注意事项1 DSP Builder是以组件的形式安装在altera下面的 xff0c 并且altera要与matlab 32位 xff01 xff01 安装在同一路径下 xff0c 这个路径不要带有中文 xff0c 或者空格字符 xff0c
  • Maven3.6.1下载与配置,超详细

    Maven3 6 1下载与配置 xff0c 超详细Maven3 6 1下载与配置 xff0c 超详细Maven3 6 1下载与配置 xff0c 超详细 Maven下载与配置 1 官网下载对应版本 xff0c 推荐下载免安装版 下载地址 ht
  • 【GIS】GIS矢量空间分析(上)

    0 GIS的基本概念 栅格数据与矢量数据 上图中 xff0c a为图形模拟表示的地理对象 xff0c b为控件对象对应的栅格数据模型表示 xff0c c为对应的矢量数据模型表示 矢量模型的表达源于原型空间实体本身 xff0c 通常以坐标来定
  • ROS简介-从零开始讲解ROS(适合超零基础阅读)

    1 前言 笔者以前是机械专业 xff0c 对于计算机方面的学习是少之又少 xff0c 接触机器人的学习之后 xff0c 比如路径规划 算法等 xff0c 发现很难入门 xff0c 不过慢慢摸爬滚打之后还是有了一些认识 xff0c 俗话说的好
  • C++primer plus第六版课后编程练习答案14.1

    include lt iostream gt include lt string gt using namespace std template lt class T1 class T2 gt class Pair private T1 a
  • 区块链——脱坑truffle

    使用truffle构建一个智能合约 实现输出 helloworld 的功能 网上有很多帖子 但也有很多坑 这里展示我的搭建过程 帮助大家绕过那些麻烦 一 安装web3 solc truffle npm g install solc npm
  • ucosii消息队列使用

    ucosii消息队列简介 ucosii的消息队列源码定义在os q c文件 xff1b 接口全部声明在ucos ii h xff0c 总共有如下接口 xff1a span class token keyword void span span
  • rtthrea-ucosii-freertos三系统学习总结

    三系统基本功能对比 RT thread ucosii freertos 任务调度 抢占式 时间片式 xff08 多级队列位图调度 xff09 抢占式 xff08 纯位图调度 xff09 抢占式 时间片式 xff08 遍历列表调度 xff09
  • VScode的git安装与配置

    git下载与安装 1 在官网下载git的最新版本 2 按照提示一步步安装完成 具体步骤参看以下文章 xff1a 转https blog csdn net qq 40342589 article details 105676715 VScod
  • 将python代码封装成c版本的dll动态链接库

    前言 将python程序打包成DLL文件 xff0c 然后用C 43 43 调用生成的DLL文件 xff0c 这是一种用C 43 43 调用python的方法 xff0c 这一块比较容易遇到坑 网上关于这一块的教程不是很多 xff0c 而且
  • gazebo加载异常或者加载缓慢

    在我们安装完ros的时候会在命令行里运行gazebo命令会打开gazebo一个空白世界 xff0c 往往会卡在如图所示的界面 xff1a 这是模型文件加载缓慢需要很长时间 xff0c 这是在ubuntu16 04下的情况 xff0c 在ub
  • FreeRTOS学习笔记二【源码文件介绍及命名规范】

    FreeRTOS学习笔记二 源码文件介绍及命名规范 源码文件介绍源码中的命名规范两个重要数据类型变量名函数名宏名 参考 本文介绍源码中的命名规范 xff0c 下篇开始介绍FreeRTOS中的任务管理 源码文件介绍 Free RTOS源码中包
  • FreeRTOS学习笔记八【队列-下】

    FreeRTOS学习笔记八 队列 下 队列中传输大数据 一个包 或大小可变的数据传输大数据传输不同类型和长度的数据 从多个队列中获取数据队列集合xQueueCreateSet xQueueAddToSet xQueueSelectFromS