Freertos学习08-queue基本发送与接受

发布时间 2023-07-03 11:35:45作者: seekwhale13

一、前言

队列是任务间通信的主要形式。 它们可以用于在任务之间以及中断和任务之间发送消息。队列是一个先进先出(FIFO)的数据结构,类似于现实生活中的排队。任务可以将数据项放入队列的末尾,然后另一个任务可以从队列的开头取出这些数据项。这种方式可以实现任务之间的数据共享和通信。

  本节主要涉及以下内容:

  • 队列的特点
  • 队列的基本创建
  • 队列数据的发送与接收

二、队列特性

1.队列的长度与宽度:队列的项数被称为“长度”,每项占用内存大小称为“宽度”。
2.数据采用先进先出:写数据时放到尾部,读数据时从头部读,也可强制写数据与头部。
3.优先级:队列可以与任务的优先级相关联。当多个任务等待队列中的数据时,具有更高优先级的任务将首先获得数据。
  具体操作过程如下:
image

三、函数API

3.1创建队列

  函数原型如下:

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
                            UBaseType_t uxItemSize );
参数 说明
uxQueueLength 队列长度,最多能存多少个数据(item)
uxItemSize 每个数据(item)的大小
返回值 非零,成功创建队列;NULL,堆内存不足,无法创建队列

3.2删除队列

  用于删除使用xQueueCreate()或xQueueCreateStatic()创建的队列,将会释放内存,函数原型如下:

void vQueueDelete( TaskHandle_t pxQueueToDelete );
参数 说明
pxQueueToDelete 队列句柄
返回值 无返回值

3.3写队列

  将数据写入到队列的后面,函数原型如下:

BaseType_t xQueueSendToFront(QueueHandle_t xQueue,
                             const void * pvItemToQueue, 
                             TickType_t xTicksToWait );
参数 说明
xQueue 队列句柄
pvItemToQueue 指向要复制到队列中的数据的指针。
xTicksToWait 如果队列无法写入新数据,队列将进入阻塞态,xTicksToWait表示进入阻塞态的最大时间;如果xTicksToWait设置为0,无法写入数据时将立即返回;如果设置为portMAX_DELAY,将会一直等待直至有空间可写
返回值 非零,成功创建队列;NULL,堆内存不足,无法创建队列

3.4读队列

  使用xQueueReceive()函数读队列,读到一个数据后,队列中该数据会被移除。函数原型如下:

BaseType_t xQueueReceive( QueueHandle_t xQueue,
                          void *pvBuffer, 
                          TickType_t xTicksToWait );
参数 说明
xQueue 队列句柄
pvBuffer buff指针,将队列中的数据复制到该缓冲区
xTicksToWait 如果队列为空无法读取数据,队列将进入阻塞态,xTicksToWait表示进入阻塞态的最大时间;如果xTicksToWait设置为0,无法读出数据时将立即返回;如果设置为portMAX_DELAY,将会一直等待直至有数据可读
返回值 pdPASS,成功读取数据,或者并非第一时间读取了数据,但在xTicksToWait耗尽前读取到了数据,也返回成功;errQUEUE_EMPTY,无法读取数据因为队列为空,或者未能在xTicksToWait耗尽前成功读取数据

  如果队列数据需要传给多个函数,可使用xQueuePeek()读取,peek本意:一瞥,窥视,此函数读出数据后并不删除,方便多函数调用同一队列数据。函数原型

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

四、示例测试

1.传输结构体

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/gpio.h"
#include "freertos/queue.h"

typedef struct A_STRUCT
{
	char id;
	char data;
} xStruct; // 先构建结构体

void queue_send(void *pvParam)
{
	QueueHandle_t QHandle;
	QHandle = (QueueHandle_t)pvParam;

	BaseType_t xStatus;
	xStruct xUSB = {1, 78};

	while (1)
	{
		xStatus = xQueueSend(QHandle, &xUSB, 0);
		if (xStatus != pdPASS)
			printf("发送失败!\n");
		else
			printf("发送成功!\n");

		xUSB.id++;
		if (xUSB.id == 8)
			xUSB.id = 0;
		vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时;
	}
}

void queue_rec(void *pvParam)
{
	QueueHandle_t QHandle;
	QHandle = (QueueHandle_t)pvParam;

	BaseType_t xStatus;
	xStruct xUSB = {0, 0};

	while (1)
	{
		if (uxQueueMessagesWaiting(QHandle) != 0)
		{
			xStatus = xQueueReceive(QHandle, &xUSB, 0);
			if (xStatus != pdPASS)
				printf("接收失败!\n");
			else
				printf("rec id=%d data=%d!\n", xUSB.id, xUSB.data);

			vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时;
		}
		else
			printf("queue为空!\n");
	}
}

void app_main(void)
{

	QueueHandle_t QHandle;

	QHandle = xQueueCreate(5, sizeof(xStruct)); // 创建队列

	if (QHandle != NULL)
	{
		printf("队列创建成功!\n");
		xTaskCreate(queue_send, "mytask1", 4096, (void *)QHandle, 0, NULL);
		xTaskCreate(queue_rec, "mytask2", 4096, (void *)QHandle, 0, NULL);
	}
	else
	{
		printf("创建队列失败!\n");
	}
}

2.队列数据的多进单出

  队列可由多个函数写入数据,并由单一函数读取,例如函数1负责汇报温度信息,函数2负责汇报湿度信息,由函数3统一读取并打印。测试代码中接受任务的优先级为3,而两个发送任务的优先级为1,这代表接受数据的优先级更高,队列中经常为空;亦或反过来,令发送任务优先级更高,接受优先级低,此时,队列出现常常为满的情况,发送任务进入阻塞态。

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "freertos/queue.h"

/* 定义2种数据来源(ID) */
typedef enum
{
   temperature,
   humidity
} ID_t;

/* 定义在队列中传输的数据的格式 */
typedef struct
{
   ID_t eDataID;
   int32_t lDataValue;
} Data_t;

void queue_send1(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t xStruct_temp = {temperature, 37}; // 定义温度信息结构体
   BaseType_t xStatus;

   while (1)
   {
      if (xStruct_temp.lDataValue++ > 43) // 模拟温度变化
         xStruct_temp.lDataValue = 26;
      xStatus = xQueueSend(QHandle, &xStruct_temp, 0);
      if (xStatus != pdPASS)
         printf("温度发送失败!\n");
      else
         printf("温度发送成功!\n");
      vTaskDelay(1000 / portTICK_PERIOD_MS);
   }
}

void queue_send2(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;
   Data_t xStruct_humi = {humidity, 37}; // 定义湿度信息结构体

   BaseType_t xStatus;

   while (1)
   {
      if (xStruct_humi.lDataValue++ > 99) // 模拟湿度变化
         xStruct_humi.lDataValue = 10;
      xStatus = xQueueSend(QHandle, &xStruct_humi, 0);
      if (xStatus != pdPASS)
         printf("湿度发送失败!\n");
      else
         printf("湿度发送成功!\n");
      vTaskDelay(1000 / portTICK_PERIOD_MS);
   }
}

void queue_rec(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t Date_receive;
   BaseType_t xStatus;

   while (1)
   {

      xStatus = xQueueReceive(QHandle, &Date_receive, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("接收失败!\n");
      else
      {
         if (Date_receive.eDataID == temperature)
            printf("当前温度啊啊啊:%ld'C\n", Date_receive.lDataValue);
         else
            printf("当前湿度:%ld%%\n", Date_receive.lDataValue);
      }
   }
}

void app_main(void)
{

   QueueHandle_t QHandle;

   QHandle = xQueueCreate(5, sizeof(Data_t)); // 创建队列,5长度,int宽度

   if (QHandle != NULL)
   {
      printf("队列创建成功!\n");
      xTaskCreate(queue_send1, "queue_send1", 4096, (void *)QHandle, 1, NULL);
      xTaskCreate(queue_send2, "queue_send2", 4096, (void *)QHandle, 1, NULL);
      xTaskCreate(queue_rec, "queue_rec", 4096, (void *)QHandle, 2, NULL);
   }
   else
   {
      printf("创建队列失败!\n");
   }
}

3.队列数据的单进多出,有时后我们会用到一些集成传感器,例如惯性传感器可反馈偏航角,角加速度,磁场方向等信息,并将这些信息分散至不同的任务进行处理。令发送任务优先级为2,接受任务优先级为1,因此发送任务优先,队列出现满的情况,因此发送任务进入阻塞态,接受任务得以运行,当读取完某个数据后,该数据将被立即删除,队列出现空位,发送任务从阻塞态进入就绪态运行准备发送下一次数据。注意,多个任务用xQueuePeek()读取,该函数读取后不删除,只需要在最后一个任务使用xQueueReceive(),即读取后立即删除。

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "freertos/queue.h"

/* 定义在队列中传输的数据的格式 */
typedef struct
{
   int32_t Angle;
   float Ang_acc;
   int32_t magnetism;

} Data_t;

void queue_send1(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t xStruct = {28, 1.7, 5}; // 定义温度信息结构体
   BaseType_t xStatus;

   while (1)
   {
      xStatus = xQueueSend(QHandle, &xStruct, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("发送失败!\n");
      else
         printf("发送成功!\n");
   }
}

void queue_rec1(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t Date_receive;
   BaseType_t xStatus;

   while (1)
   {

      xStatus = xQueueReceive(QHandle, &Date_receive, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("接收失败!\n");
      else
      {
         printf("111当前角度:%ld\n", Date_receive.Angle);
      }
   }
}

void queue_rec2(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t Date_receive;
   BaseType_t xStatus;

   while (1)
   {

      xStatus = xQueueReceive(QHandle, &Date_receive, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("接收失败!\n");
      else
      {
         printf("222当前角加速度%f\n", Date_receive.Ang_acc);
      }
   }
}

void queue_rec3(void *pvParam)
{
   QueueHandle_t QHandle;
   QHandle = (QueueHandle_t)pvParam;

   Data_t Date_receive;
   BaseType_t xStatus;

   while (1)
   {

      xStatus = xQueueReceive(QHandle, &Date_receive, portMAX_DELAY);
      if (xStatus != pdPASS)
         printf("接收失败!\n");
      else
      {
         printf("当前磁场方向:%ld\n", Date_receive.magnetism);
      }
   }
}

void app_main(void)
{

   QueueHandle_t QHandle;

   QHandle = xQueueCreate(5, sizeof(Data_t)); // 创建队列,5长度,int宽度

   if (QHandle != NULL)
   {
      printf("队列创建成功!\n");
      xTaskCreate(queue_send1, "queue_send1", 4096, (void *)QHandle, 2, NULL);
      xTaskCreate(queue_rec1, "queue_rec1", 4096, (void *)QHandle, 1, NULL);
      xTaskCreate(queue_rec2, "queue_rec2", 4096, (void *)QHandle, 1, NULL);
      xTaskCreate(queue_rec3, "queue_rec3", 4096, (void *)QHandle, 1, NULL);
   }
   else
   {
      printf("创建队列失败!\n");
   }
}

THE END!