一、前言
队列是任务间通信的主要形式。 它们可以用于在任务之间以及中断和任务之间发送消息。队列是一个先进先出(FIFO)的数据结构,类似于现实生活中的排队。任务可以将数据项放入队列的末尾,然后另一个任务可以从队列的开头取出这些数据项。这种方式可以实现任务之间的数据共享和通信。
本节主要涉及以下内容:
- 队列的特点
- 队列的基本创建
- 队列数据的发送与接收
二、队列特性
1.队列的长度与宽度:队列的项数被称为“长度”,每项占用内存大小称为“宽度”。
2.数据采用先进先出:写数据时放到尾部,读数据时从头部读,也可强制写数据与头部。
3.优先级:队列可以与任务的优先级相关联。当多个任务等待队列中的数据时,具有更高优先级的任务将首先获得数据。
具体操作过程如下:
三、函数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!
标签:QHandle,Freertos,队列,08,queue,printf,xStatus,void From: https://www.cnblogs.com/seekwhale13/p/17521286.html