freertos学习02-队列 stream buffer message buffer

2023-05-16

1、freertos数据传递简介

在freertos中,各个模块都是独立的任务,那么任务之间怎么进行大量的数据通信呢?在V10版本给出了三种方法。

  • 队列queue,发送固定长度的数据串
  • stream buffer,为新增的特性,发送不定长度的数据串
  • message buffer,为新增的特性,发送不定长度的数据串,同时带有发送长度信息

以上三者,都可以用于任务-任务,任务-中断,都遵循FIFO先进先出原则,数据传递的方式为拷贝,像ucos中数据传递采用的传输指针,拷贝的方式效率有所降低,而好处也很明显,避免了同一数据可能存在的同时读写造成的问题。只要拷贝完成,源数据的改变不影响接收方数据的有效性,只不过接收方接收到的数据可能并非最新数据。

传输的中的数据只要被成功接收,该数据就会消亡。这种在1发多收的情况下会出现,比如,task1 不停的在发送数据,task1,task2在接收task1的数据。
在这里插入图片描述
一发多收的情况下,同一个接收方不能完全接收到所有的信息。信息一旦被接收后,就消亡了,要个新消息腾出空间。

2、队列 stream buffer message buffer区别

队列可以引用到各种场合,是最基础的数据传递方式。

官方网站给出了stream buffers 和message buffers的简介https://www.freertos.org/FreeRTOS-V10.html
stream buffers主要应用于一个发送者一个接收者。比如从中断发送给task,或者从一个cpu核发送到另一个cpu核。

message buffers是基于streambuffers实现的,stream buffers传输连续的数据,而message buffers 传送带离散的带有长度的消息,接收方可以读取到当前消息的长度。

这三者的初步对比分析,由于资料及应用时间有限,可能存在不到位的地方,后续根据使用情况继续完善。

对象特点优势缺陷
queuetask-task,中断-task,固定长度,传输的是拷贝,可以1对多,多对1,多对多应用场合多长度固定
stream bufferstask-task,中断-task,不固定长度,传输的是拷贝,传送持续的数据(文件,图片),建议1对1传输量大,可自定义接收长度场景有限制
queuetask-task,中断-task,不固定长度,自身带有长度信息,传输的是拷贝拷贝非固定长度,带有长度标签,适合传输协议帧数据

通过以上的简单分析,个人觉得最好的方式是message buffer,牛的地方是可以发送不定长数据,接收的时候可以读到数据的当前长度,可以根据长度来区分不同的信息。当在传输一些协议的时候,比较优势,相比于queue,灵活性强太多了

3、api接口

3.1 queue

创建

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
UBaseType_t uxItemSize );

参数含义

uxQueueLength: 队列可存储消息的最大数量
uxItemSize :单个消息的长度,为字节数量

发送

BaseType_t xQueueSend( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );

参数含义:

xQueue:队列句柄
pvItemToQueue:要传输的消息的指针
xTicksToWait :消息满的最大等待时间,如果消息满了,改值非0的话,任务会阻塞,直到时间到达portMAX_DELAY无限等待

返回值含义:

pdPASS:队列发送成功
errQUEUE_FULL:队列满了

从中断函数发送

BaseType_t xQueueSendFromISR( QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken );

参数含义:

xQueue:队列句柄
pvItemToQueue:要传输的的消息的指针
pxHigherPriorityTaskWoken : 是否需要切换上下文的标志位,需要先定义一个变量来存贮这个值,pdTRUE 或者pdFALSE

返回值:

pdPASS:队列发送成功
errQUEUE_FULL:队列满了

注意在使用的使用的时候,清除完中断标志位后,需要调用切换上下文的函数。

void vBufferISR( void )
{
char cIn;
BaseType_t xHigherPriorityTaskWoken;
/* No tasks have yet been unblocked. */
xHigherPriorityTaskWoken = pdFALSE;
/* Loop until the buffer is empty. */
do
{
/* Obtain a byte from the buffer. */
cIn = INPUT_BYTE( RX_REGISTER_ADDRESS );
/* Write the byte to the queue. xHigherPriorityTaskWoken will get set to
pdTRUE if writing to the queue causes a task to leave the Blocked state,
and the task leaving the Blocked state has a priority higher than the
currently executing task (the task that was interrupted). */
xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
} while( INPUT_BYTE( BUFFER_COUNT ) );
/* Clear the interrupt source here. */
/* Now the buffer is empty, and the interrupt source has been cleared, a context
switch should be performed if xHigherPriorityTaskWoken is equal to pdTRUE.
NOTE: The syntax required to perform a context switch from an ISR varies from
port to port, and from compiler to compiler. Check the web documentation and
examples for the port being used to find the syntax required for your
application. */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

接收函数

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

参数说明:

xQueue:队列句柄
pvBuffer:接收消息buffer的指针
xTicksToWait :最大等待时间,等待的时候可以,当前任务被阻塞portMAX_DELAY无限等待

返回值:

pdPASS:数据接收完毕
errQUEUE_EMPTY:队列为空

从中断接收函数

BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,
void *pvBuffer,
BaseType_t *pxHigherPriorityTaskWoken );

参数含义:

xQueue:队列句柄
pvItemToQueue:要传输的的消息的指针
pxHigherPriorityTaskWoken : 是否需要切换上下文的标志位,需要先定义一个变量来存贮这个值,pdTRUE 或者pdFALSE

返回值:

pdPASS:队列发送成功
errQUEUE_FULL:队列满了

注意在使用的使用的时候,清除完中断标志位后,需要调用切换上下文的函数。同从中断发送队列函数

3.2 stream buffers

创建函数

StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes,
size_t xTriggerLevelBytes );

参数说明:

xBufferSizeBytes :buffer的最大容量字节数
xTriggerLevelBytes:最小有效触发字节,意思是,当buffer至少有大于等于这个值的字节数时,消息才能被接收,该值最小为1,如果设置为0,内部会自动将其设置为1

返回值:

NULL:创建失败

发送函数

size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
const void *pvTxData,
size_t xDataLengthBytes,
TickType_t xTicksToWait );

参数说明:

xStreamBuffer :句柄
pvTxData:待发送消息的指针
xDataLengthBytes:拷贝到stream buffer的字节数量
xTicksToWait:stream buffer 满了后发送的等待时间

返回值:

最终拷贝到stream buffer的字节数量

接收函数

size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
void *pvRxData,
size_t xBufferLengthBytes,
TickType_t xTicksToWait );

参数说明:

xStreamBuffer:句柄
pvRxData:接收buffer的指针
xBufferLengthBytes:一次最大的接收长度
xTicksToWait:等待时间

返回值:

实际接收到的长度

从中断发送

size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer,
const void *pvTxData,
size_t xDataLengthBytes,
BaseType_t *pxHigherPriorityTaskWoken );

参数说明:

xStreamBuffer:句柄
pvTxData:待发送消息的指针
xDataLengthBytes:拷贝到stream buffer的字节数量
pxHigherPriorityTaskWoken :上下文切换的标志位

返回值:

实际写入的字节数量

从中断接收

size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer,
void *pvRxData,
size_t xBufferLengthBytes,
BaseType_t *pxHigherPriorityTaskWoken );

参数说明:

xStreamBuffer:句柄
pvRxData:接收buffer的指针
xBufferLengthBytes:一次最大接收长度
pxHigherPriorityTaskWoken :上下文切换的标志位

3.3message buffers

创建

MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );

参数说明:

xBufferSizeBytes :buffer的最大容量,buffer占用的空间为xBufferSizeBytes+4.4字节uint32存放实际buff有效数据的长度

返回值:

NULL:创建失败,一般是堆空间不够

发送

size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer,
const void *pvTxData,
size_t xDataLengthBytes,
TickType_t xTicksToWait );

参数说明:

xMessageBuffer:句柄
pvTxData:发送消息的指针
xDataLengthBytes:发送消息的长度
xTicksToWait :buffer满了等待时间

返回值:

实际写入buffer的数量,如果messagebuff没有足够的长度存储pvTxData,则返回值将会是0

接收

size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,
void *pvRxData,
size_t xBufferLengthBytes,
TickType_t xTicksToWait );

参数说明:

xMessageBuffer:句柄
pvTxData:接收消息的指针
xDataLengthBytes:接收buffer的长度,如果长度小于messge的长度,返回值为0
xTicksToWait :等待接收时间

返回值

读取到的长度,如果message超过接收buff的长度,返回值将会是0,消息仍然存在消息buffer中。如果message buffer 空,接收超过了xTicksToWait时间,则返回的也是0.

从中断发送

size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer,
const void *pvTxData,
size_t xDataLengthBytes,
BaseType_t *pxHigherPriorityTaskWoken )

参数说明

xMessageBuffer:句柄
pvTxData:发送消息的指针
xDataLengthBytes:发送消息的长度
pxHigherPriorityTaskWoken :上下文切换的标志位,具体见queue章节

返回值

写入到messagebuff的长度。如果messagebuff没有足够的长度存储pvTxData,则返回值将会是0

从中断接收

size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer,
void *pvRxData,
size_t xBufferLengthBytes,
BaseType_t *pxHigherPriorityTaskWoken );

参数说明

xMessageBuffer:句柄
pvTxData:接收消息的指针
xDataLengthBytes:接收buffer的长度
pxHigherPriorityTaskWoken :上下文切换的标志位,具体见queue章节

返回值

读取到的长度

4、queue实验

4.1一发二收

void que_tx(void *pvParameters)
{
	uint8_t tx[ 8 ]={0,1,2,3,4,5,6,7};
	uint8_t tx1[ 8 ]={2,3,4,5,6,7,8,9};
	uint32_t err;
	while(1)
	{
		err = xQueueSend(queue_1,tx,0);
		if(err == pdPASS)
			printf("que_tx1:queue sende\r\n");
		
		err = xQueueSend(queue_1,tx1,0);
		if(err == pdPASS)
			printf("que_tx1:queue sende\r\n");
		vTaskDelay(400);
	}

}


void que_rx(void *pvParameters)
{
	uint8_t rx[ 8 ]={0};
	uint32_t err;
	while(1)
	{
		err = xQueueReceive(queue_1,rx,0);
		xSemaphoreTake(mutex1,portMAX_DELAY);
		if(err == pdPASS)
		{
		printf("que_rx:rx_data<-- ,");
			for (int i = 0;i<8;i++)
				printf("%d ",rx[i]);
			printf("\r\n");
		
		}
		xSemaphoreGive(mutex1);
		vTaskDelay(800);
	}

}


void que_rx1(void *pvParameters)
{
	uint8_t rx[ 8 ]={0};
	uint32_t err;
	while(1)
	{
		err = xQueueReceive(queue_1,rx,0);
		xSemaphoreTake(mutex1,portMAX_DELAY);
		if(err == pdPASS)
		{
		printf("que_rx1:rx_data<-- ,");
			for (int i = 0;i<8;i++)
				printf("%d ",rx[i]);
			printf("\r\n");
		
		}
		xSemaphoreGive(mutex1);
		vTaskDelay(800);
	}

}

在这里插入图片描述
最终结果如图,如果不采用互斥信号量,打印的数据会乱。可见最好使用1发1收

4.2二发一收

void que_tx(void *pvParameters)
{
	uint8_t tx[ 8 ]={0,1,2,3,4,5,6,7};
	uint8_t tx1[ 8 ]={2,3,4,5,6,7,8,9};
	uint32_t err;
	while(1)
	{
		err = xQueueSend(queue_1,tx,0);
		if(err == pdPASS)
			printf("que_tx1:queue sende\r\n");
		
		err = xQueueSend(queue_1,tx1,0);
		if(err == pdPASS)
			printf("que_tx1:queue sende\r\n");
		vTaskDelay(400);
	}

}
void que_tx1(void *pvParameters)
{
	uint8_t tx[ 8 ]={11,12,13,14,15,16,17,18};
	uint32_t err;
	while(1)
	{
		err = xQueueSend(queue_1,tx,0);
		if(err == pdPASS)
			printf("que_tx1:queue sende\r\n");
		

		vTaskDelay(400);
	}

}

void que_rx(void *pvParameters)
{
	uint8_t rx[ 8 ]={0};
	uint32_t err;
	while(1)
	{
		err = xQueueReceive(queue_1,rx,0);
		xSemaphoreTake(mutex1,portMAX_DELAY);
		if(err == pdPASS)
		{
		printf("que_rx:rx_data<-- ,");
			for (int i = 0;i<8;i++)
				printf("%d ",rx[i]);
			printf("\r\n");
		
		}
		xSemaphoreGive(mutex1);
		vTaskDelay(800);
	}

}

在这里插入图片描述
最终结果如图,收发都比较正常

4.3中断发送

static	uint8_t test[ 8 ]={22,23,24,25,26,27,28,29};
void TIMER0_UP_IRQHandler(void)
{
	BaseType_t flag = pdFALSE;
   if( timer_interrupt_flag_get(TIMER0,TIMER_INT_FLAG_UP) == SET)
   {
	   if(queue_1!=NULL)
			xQueueSendFromISR(queue_1,test,&flag);
   
   }
	timer_interrupt_flag_clear(TIMER0,TIMER_INT_FLAG_UP);
   portYIELD_FROM_ISR(flag);
}

这里需要注意的是判断queue_1是否为空,要确定队列已经初始化了才能进行发送。可能进入中断了,队列还没完成初始化,这样会造成硬件故障。
在这里插入图片描述
结果如上图所示,可见,多发单收是没有任何问题的。

5、stream buffers实验

5.1 单发单收

	stream_1 = xStreamBufferCreate(100,12);
	void que_tx1(void *pvParameters)
{
	uint8_t tx[ 8 ]={21,22,23,24,25,26,27,28};
	uint32_t err;
	while(1)
	{
		err = xStreamBufferSend(stream_1,tx,sizeof(tx),portMAX_DELAY);
		if(err >0)
			printf("que_tx1 %d:queue sende\r\n",err);
		

		vTaskDelay(400);
	}

}
void que_rx(void *pvParameters)
{
	uint8_t rx[ 11 ]={0};
	uint32_t err;
	while(1)
	{
		err = xStreamBufferReceive(stream_1,rx,sizeof(rx),portMAX_DELAY);
		xSemaphoreTake(mutex1,portMAX_DELAY);
		if(err >0)
		{
		printf("que_rx:rx_data %d <-- ,",err);
			for (int i = 0;i<sizeof(rx);i++)
			{
				printf("%d ",rx[i]);
				rx[i] = 0;
			}

			printf("\r\n");
		
		}
		xSemaphoreGive(mutex1);
//		vTaskDelay(100);
	}

}

运行结果如下:
在这里插入图片描述
这个实验没有理解,一开始设定的stream buffer 触发长度为12,按理收当收完第一个11字节数据后,message buffer中的数据只有5字节,5<15这时候接收到的数据应该是0啊。反复测试都是这个结果,问题还未知,先放着。

6、message buffers实验

6.1单发单收

message_1 = xMessageBufferCreate(100);
void que_tx1(void *pvParameters)
{
	uint8_t tx[ 8 ]={21,22,23,24,25,26,27,28};
	uint32_t err;
	while(1)
	{
		err = xMessageBufferSend(message_1,tx,sizeof(tx),portMAX_DELAY);
		if(err >0)
			printf("que_tx1 %d:queue sende\r\n",err);
		

		vTaskDelay(400);
	}

}

void que_rx(void *pvParameters)
{
	uint8_t rx[ 11 ]={0};
	uint32_t err;
	while(1)
	{
		err = xMessageBufferReceive(message_1,rx,sizeof(rx),portMAX_DELAY);
		xSemaphoreTake(mutex1,portMAX_DELAY);
		if(err >0)
		{
		printf("que_rx:rx_data %d <-- ,",err);
			for (int i = 0;i<sizeof(rx);i++)
			{
				printf("%d ",rx[i]);
				rx[i] = 0;
			}

			printf("\r\n");
		
		}
		xSemaphoreGive(mutex1);
//		vTaskDelay(100);
	}

}

设置messagebuf的最大容量为100字节。发送任务每次发8字节,接收任务最大每次接收11字节

在这里插入图片描述
结果如上图,发送接收比较正常。

6.2多发单收

在6.1中增加一个发送任务

void que_tx(void *pvParameters)
{
	uint8_t tx[ 10 ]={1,2,3,4,5,6,7,8,9,10};
	uint8_t tx1[ 8 ]={2,3,4,5,6,7,8,9};
	uint32_t err;
	while(1)
	{
		err = xMessageBufferSend(message_1,tx,sizeof(tx),portMAX_DELAY);
		if(err >0)
			printf("que_tx %d:queue sende\r\n",err);
		vTaskDelay(400);
	}

}

在这里插入图片描述
接收结果也比较正常,可以分别收到8字节和10字节长度的数据。比queue应用范围更广。

6.3中断发送

在6.2中增加如下代码,

static	uint8_t test[ 9 ]={24,25,26,27,28,29,30,31,32};
void TIMER0_UP_IRQHandler(void)
{
	BaseType_t flag = pdFALSE;
   if( timer_interrupt_flag_get(TIMER0,TIMER_INT_FLAG_UP) == SET)
   {
	   if(message_1!=NULL)
		   xMessageBufferSendFromISR(message_1,test,sizeof(test),&flag);

   
   }
	timer_interrupt_flag_clear(TIMER0,TIMER_INT_FLAG_UP);
   portYIELD_FROM_ISR(flag);
}

从中断发送9字节数据。运行结果如下:
在这里插入图片描述
三个发送端的数据都可以收到。实际应用的时候,可以根据接收数据的长度来判读发送方。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

freertos学习02-队列 stream buffer message buffer 的相关文章

随机推荐

  • Debian 11 KDE Plasma桌面环境,编译Fcitx5(只编译,不安装)

    Debian 11 KDE Plasma桌面环境 xff0c 编译Fcitx5 xff08 只编译 xff0c 不安装 xff09 安装依赖的工具和库 安装前需要检查电脑上是不是具备这些工具 xff0c 或者库 xff08 这是我根据实际情
  • 快速解决 vsftpd nologin 虚拟用户 拒绝访问 无法登录

    搭建好了vsftpd服务之后 xff0c 新建了一个nologin虚拟用户 xff0c 然而在另一台win7上用WinSCP连接ftp时提示错误 useradd d home ftp s sbin nologin ftpUser1 pass
  • 云计算的部署

    一 云计算的服务和交付模式 基础设施即服务 xff08 Iaas xff09 平台即服务 xff08 Paas xff09 软件即服务 xff08 Saas xff09 衍生出 xff1a 存储即服务 数据库即服务 安全即服务 通信即服务
  • MapReduce的数据流程、执行流程

    MapReduce的大体流程是这样的 xff0c 如图所示 xff1a 由图片可以看到mapreduce执行下来主要包含这样几个步骤 1 首先对输入数据源进行切片 2 master调度worker执行map任务 3 worker读取输入源片
  • 免费下载中国知网、万方学术论文的几种方法(福利合集)

    在国内 xff0c 中国知网收录了最多的期刊论文和硕博士论文 无论学霸学渣 xff0c 都得上去下载论文 如果你的学校在知网购买了相应的下载版权 xff0c 那恭喜你 xff0c 你通过校园网就能免费下载了 但一旦你回了家 xff0c 或学
  • 使用apt离线安装deb包

    文章目录 apt 下载的deb路径阻止apt自动删除缓存文件的方法只下载不安装的方法离线安装deb包离线安装gcc1 下载依赖2 打包下载的deb文件 xff0c 上传到没有外网连接的服务器3 安装deb包 apt 下载的deb路径 默认存
  • haar分类

    今天说一说haar分类算法 首先介绍haar like特征 haar like的特征有边缘特征 线性特征 中心特征和对角线特征 我们使用特征模板来表示特征的计算 xff0c 如图所示 xff1a 这些特征分别对应着不同的矩阵以便于进行计算
  • POI window excel 打开提示部分内容有问题, 是否尝试尽量恢复

    问题如下 window excel 打开报错如下 但是WPS打开正常 问题在于 window excel 冻结窗口只能设置一行 WPS可以设置多行 设置冻结窗口如下 冻结第一行 sheet createFreezePane 0 1 0 1
  • 解决从数据库中取出json数据有转义符

    不处理从数据库取出数据如下 String s1 61 34 34 MsgId 34 1 34 TotalCount 34 10 34 FilterCount 34 8 34 SentCount 34 7 34 ErrorCount 34 0
  • 查询数据报错 com.mysql.cj.exceptions.DataConversionException

    com mysql cj exceptions DataConversionException Caused by java sql SQLDataException Cannot determine value type from str
  • 微信调用接口报错:"errcode":45009,"errmsg":"reach max api daily quota limit hints:

    api请求次数达到最大上限 每个帐号每月共10次清零操作机会 xff0c 清零生效一次即用掉一次机会 xff08 10次包括了平台上的清零和调用接口API的清零 xff09 https developers weixin qq com do
  • @FeignClient注解 中属性 contextId使用

    64 FeignClient注解 中属性 contextId 比如我们有个user服务 xff0c 但user服务中有很多个接口 xff0c 我们不想将所有的调用接口都定义在一个类中 xff0c 比如 xff1a Client span c
  • toString和toJSONString的区别

    Map span class token generics function span class token punctuation lt span String span class token punctuation span Int
  • Neutron运营商网络和租户网络详解

    由租户创建并且管理的网络 xff0c Neutron称之为租户网络 但是Openstack不是万能的 xff0c Neutron也不是万能的 还有很多网络不在Neutron管理范围内 xff08 Neutron称之为外部网络 xff09 有
  • mysql in查询太慢, 使用join优化

    mysql中查询 in 参数太多 导致查询很慢 使用join优化 在实例中in查询话费2s 优化后0 4s span class token keyword SELECT span span class token operator spa
  • Springboot 多数据源事务,切换数据源+事务

    项目有多个数据源 根据配置文件配置的连接数来自动生成多数据源配置 并且使用 aop切换数据源 使用的是 AbstractRoutingDataSource 重写 determineCurrentLookupKey 方法 在切换数据源之前 6
  • Redisson自定义序列化方式

    redissonClient span class token punctuation span span class token function getBucket span span class token punctuation s
  • 方法区使用举例

    span class token keyword public span span class token keyword class span span class token class name MethodAreaDemo span
  • mysql动态字段行转列

    动态行转列 table schema id name s 001 是否吃饭了 s 002 你的汽车品牌 table schema value id user id schema id schema value span class toke
  • freertos学习02-队列 stream buffer message buffer

    1 freertos数据传递简介 在freertos中 xff0c 各个模块都是独立的任务 xff0c 那么任务之间怎么进行大量的数据通信呢 xff1f 在V10版本给出了三种方法 队列queue xff0c 发送固定长度的数据串strea