IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> freertos学习02-队列 stream buffer message buffer -> 正文阅读

[嵌入式]freertos学习02-队列 stream buffer message buffer

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字节数据。运行结果如下:
在这里插入图片描述
三个发送端的数据都可以收到。实际应用的时候,可以根据接收数据的长度来判读发送方。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-04-23 11:00:36  更:2022-04-23 11:00:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:36:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码