摘要(From AI):
在嵌入式系统开发中,任务之间的高效通信是实现多任务协作的关键。FreeRTOS 提供了强大的队列机制,支持任务之间安全、灵活地传递数据,是实现任务同步和事件通知的核心工具。本篇博客将全面解析 FreeRTOS 队列的工作原理和应用场景,从基础操作到高级功能(如队列集合和邮箱)的深入探讨
前言:本文档是本人在依照B站UP:Michael_ee的视频教程进行学习时所做的学习笔记,可能存在疏漏和错误,如有发现,望指正。
上一篇:ESP32学习笔记_FreeRTOS(1)——Task的创建和使用
文章目录
- Queue
- Queue Delivery Data
- Creating a Queue
- xQueueCreate()
- Sending Data to a Queue
- xQueueSend(), xQueueSendToFront(),xQueueSendToBack()
- Receiving Data from a Queue
- xQueueReceive()
- Checking Queue Parameters
- uxQueueMessagesWaiting()
- Example Code: Working with FreeRTOS Queues
- Queue Multiple In Single Out
- Queue Set
- xQueueCreateSet()
- xQueueAddToSet()
- xQueueSelectFromSet()
- Example Code:Queue Sets in FreeRTOS
- Queue Mailbox
- xQueueOverwrite()
- xQueuePeek()
- Example Code:Implementing a Shared Mailbox with Multiple Readers and One Writer
参考资料:
BiliBili——Michael_ee视频教程
freeRTOS官网
espressif 在线文档
Queue
FreeRTOS 中的队列 (Queue) 是一种重要的任务间通信机制,用于在任务与任务或任务与中断之间传递数据。它通过先进先出 (FIFO) 的方式存储和传递信息,并提供线程安全的访问保障,避免了多任务同时操作共享资源时的竞争问题。队列支持阻塞操作,任务可以等待数据或空位出现,从而简化了任务的同步与协作
队列在嵌入式系统中有广泛的应用,例如传感器数据采集、事件通知、日志记录等。通过队列,生产者任务可以将数据发送到队列中,消费者任务从中读取并处理数据。这种机制既提高了系统的可靠性,又降低了任务间通信的复杂度,是实时操作系统中不可或缺的功能之一
Queue Delivery Data
队列本质上就类似于一个 Buffer
Creating a Queue
xQueueCreate()
xQueueCreate()
是 FreeRTOS 中用于创建队列的函数。队列是 FreeRTOS 中用于在任务之间或任务与中断之间传递数据的机制。该函数会自动从 FreeRTOS 的堆中分配所需的 RAM,用于存储队列的状态和数据项
#include “FreeRTOS.h”
#include “queue.h”QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
uxQueueLength
队列可以容纳的最大数据项数
uxItemSize
队列中每个数据项的大小(以字节为单位)
QueueHandle_t
队列创建成功,返回的值是用于引用队列的句柄,如果是 NULL,则队列创建失败
Sending Data to a Queue
xQueueSend(), xQueueSendToFront(),xQueueSendToBack()
这三个函数用于将数据项发送(写入)到 FreeRTOS 队列中。
xQueueSend()
和xQueueSendToBack()
:- 功能相同,均用于将数据发送到队列的末尾(队尾)。
xQueueSend()
是较早的版本,推荐使用xQueueSendToBack()
替代。
xQueueSendToFront()
:- 用于将数据发送到队列的前端(队首)
#include “FreeRTOS.h”
#include “queue.h”BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );BaseType_t xQueueSendToBack( QueueHandle_t xQueue,const void * pvItemToQueue,TickType_t xTicksToWait );
xQueue
- 队列的句柄,由
xQueueCreate()
或xQueueCreateStatic()
创建队列时返回。
- 队列的句柄,由
pvItemToQueue
- 指向要写入队列的数据的指针
- 队列创建时已经指定了每个数据项的大小,因此将从
pvItemToQueue
中复制相应字节数到队列存储区
xTicksToWait
- 如果队列已满,任务进入 Blocked 状态等待空间可用时的最大等待时间
- 以“系统时钟节拍(ticks)”为单位。
- 使用宏
pdMS_TO_TICKS()
将毫秒时间转换为时钟节拍时间 - 设置为
portMAX_DELAY
时,任务将无限期等待,前提是INCLUDE_vTaskSuspend
在FreeRTOSConfig.h
中设置为 1
- 使用宏
- 若
xTicksToWait
为 0,函数会立即返回,而不等待
返回值:
pdPASS
数据成功写入队列;如果指定了等待时间,可能会发生任务进入 Blocked 状态等待队列有空间后再成功写入的情况
errQUEUE_FULL
队列已满,无法写入数据;如果指定了等待时间,但在等待期间队列仍未腾出空间,函数返回此值
Receiving Data from a Queue
xQueueReceive()
xQueueReceive()
用于从 FreeRTOS 队列中接收(读取)数据项
#include “FreeRTOS.h”
#include “queue.h”BaseType_t xQueueReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait );
参数说明:
xQueue
- 队列的句柄,由
xQueueCreate()
或xQueueCreateStatic()
创建队列时返回 - 指定要从中读取数据的队列
- 队列的句柄,由
pvBuffer
- 指向一个缓冲区的指针,用于存储从队列中读取的数据。
- 缓冲区的大小必须至少等于队列中每个数据项的大小(在创建队列时由
uxItemSize
指定)
xTicksToWait
- 如果队列为空,任务进入 Blocked 状态等待数据可用的最大等待时间
- 以“系统时钟节拍(ticks)”为单位
- 若
xTicksToWait
为 0,函数会立即返回,而不等待
返回值:
pdPASS
- 成功从队列中读取到数据
- 如果指定了等待时间,可能会发生任务进入 Blocked 状态等待数据写入队列后再成功读取的情况
errQUEUE_EMPTY
- 队列为空,未能读取到数据
- 如果指定了等待时间,但在等待期间队列中始终无数据写入,函数返回此值
Checking Queue Parameters
uxQueueMessagesWaiting()
uxQueueMessagesWaiting() 用于查询当前队列中存储的项数量
#include “FreeRTOS.h”
#include “queue.h”UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
- 返回队列中当前存储的项数量(即队列中未被读取的消息数)
Example Code: Working with FreeRTOS Queues
// 传递数据为整形
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h" #include "freertos/queue.h" // 包括队列头文件 void sendTask(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 BaseType_t xStatus; int i = 0; while (true) { xStatus = xQueueSend(myQueueHandle, &i, 0); // 发送数据到Queue,等待时间为0 if (xStatus == pdPASS) printf("Send %d to queue.\n", i); else printf("Send %d to queue failed.\n", i); i++; if (i == 8) i = 0; printf("sendTask is running.\n"); vTaskDelay(3000 / portTICK_PERIOD_MS); }
} void receiveTask(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 BaseType_t xStatus; int i = 0; // 因为Queue中的数据类型是int,所以要用int类型接收 while (true) { if (uxQueueMessagesWaiting(myQueueHandle) != 0) { xStatus = xQueueReceive(myQueueHandle, &i, 0); // 从Queue中接收数据 if (xStatus == pdPASS) printf("Receive %d from queue.\n", i); else printf("Receive data from queue failed.\n"); } else printf("Queue is empty.\n"); // 如果Queue为空,则打印Queue为空 printf("receiveTask is running.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void app_main(void)
{ TaskHandle_t myHandle = NULL; TaskHandle_t myHandle2 = NULL; QueueHandle_t myQueueHandle = xQueueCreate(5, sizeof(int)); // 创建一个队列,队列长度为5,每个元素大小为int if (myQueueHandle != NULL) { printf("Queue created successfully.\n"); // 当Queue创建成功后,创建两个任务,一个发送任务,一个接收任务 xTaskCreate(sendTask, "sendTask", 1024 * 5, (void *)myQueueHandle, 1, &myHandle); xTaskCreate(receiveTask, "receiveTask", 1024 * 5, (void *)myQueueHandle, 1, &myHandle2); } else { printf("Queue creation failed.\n"); }
}/* --------------------------------------- */// 传递数据为结构体
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h" #include "freertos/queue.h" // 包括队列头文件 typedef struct AData
{ int m_id; int m_data;
} xStruct; void sendTask(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 BaseType_t xStatus; xStruct xUSB = {1, 2}; while (true) { xStatus = xQueueSend(myQueueHandle, &xUSB, 0); // 发送数据到Queue,等待时间为0 if (xStatus == pdPASS) printf("Send id %d to queue.\n", xUSB.m_id); else printf("Send id %d to queue failed.\n", xUSB.m_id); xUSB.m_id++; printf("sendTask is running.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void receiveTask(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 BaseType_t xStatus; xStruct xUSB = {0, 0}; // 因为Queue中的数据类型是int,所以要用int类型接收 while (true) { if (uxQueueMessagesWaiting(myQueueHandle) != 0) { xStatus = xQueueReceive(myQueueHandle, &xUSB, 0); // 从Queue中接收数据 if (xStatus == pdPASS) printf("Receive id %d from queue.Data is %d\n", xUSB.m_id, xUSB.m_data); else printf("Receive data from queue failed.\n"); } else printf("Queue is empty.\n"); // 如果Queue为空,则打印Queue为空 printf("receiveTask is running.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void app_main(void)
{ TaskHandle_t myHandle = NULL; TaskHandle_t myHandle2 = NULL; QueueHandle_t myQueueHandle = xQueueCreate(5, sizeof(xStruct)); // 创建一个队列,队列长度为5,每个元素大小为xStruct的大小 if (myQueueHandle != NULL) { printf("Queue created successfully.\n"); // 当Queue创建成功后,创建两个任务,一个发送任务,一个接收任务 xTaskCreate(sendTask, "sendTask", 1024 * 5, (void *)myQueueHandle, 1, &myHandle); xTaskCreate(receiveTask, "receiveTask", 1024 * 5, (void *)myQueueHandle, 1, &myHandle2); } else { printf("Queue creation failed.\n"); }
}/* --------------------------------------- */// 传递数据为指针
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h" #include "freertos/queue.h" // 包括队列头文件 void sendTask(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 char* pStrToSend = NULL; int id = 0; BaseType_t xStatus; while (true) { pStrToSend = (char*)malloc(50); snprintf(pStrToSend, 50, "Hello World! id:%d\n", id); xStatus = xQueueSend(myQueueHandle, &pStrToSend, 0); if (xStatus == pdPASS) printf("Send id %d to queue.\n", id); else printf("Send id %d to queue failed.\n", id); id++; printf("sendTask is running.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void receiveTask(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 BaseType_t xStatus; char* pStrToReceive = NULL; while (true) { if (uxQueueMessagesWaiting(myQueueHandle) != 0) { xStatus = xQueueReceive(myQueueHandle, &pStrToReceive, 0); // 从Queue中接收数据 if (xStatus == pdPASS) { printf("Receive data: %s\n", pStrToReceive); free(pStrToReceive);// 释放内存,防止内存泄漏// 注意要在接收数据后再释放内存,否则会收不到数据 } else printf("Receive data from queue failed.\n"); } else printf("Queue is empty.\n"); // 如果Queue为空,则打印Queue为空 printf("receiveTask is running.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void app_main(void)
{ TaskHandle_t myHandle = NULL; TaskHandle_t myHandle2 = NULL; QueueHandle_t myQueueHandle = xQueueCreate(5, sizeof(char*)); // 创建一个队列,队列长度为5,每个元素大小为char*的大小 if (myQueueHandle != NULL) { printf("Queue created successfully.\n"); // 当Queue创建成功后,创建两个任务,一个发送任务,一个接收任务 xTaskCreate(sendTask, "sendTask", 1024 * 5, (void *)myQueueHandle, 1, &myHandle); xTaskCreate(receiveTask, "receiveTask", 1024 * 5, (void *)myQueueHandle, 1, &myHandle2); } else { printf("Queue creation failed.\n"); }
}
Queue Multiple In Single Out
Queue 可以同时有多个输入,一个输出,可以根据实际需求进行调整
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h" #include "freertos/queue.h" // 包括队列头文件 void sendTask1(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 int id = 111; BaseType_t xStatus; while (true) { xStatus = xQueueSend(myQueueHandle, &id, 0); if (xStatus == pdPASS) printf("Send id %d to queue.\n", id); else printf("Send id %d to queue failed.\n", id); printf("sendTask is running.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void sendTask2(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 int id = 222; BaseType_t xStatus; while (true) { xStatus = xQueueSend(myQueueHandle, &id, 0); if (xStatus == pdPASS) printf("Send id %d to queue.\n", id); else printf("Send id %d to queue failed.\n", id); printf("sendTask2 is running.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void receiveTask(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 BaseType_t xStatus; int id = 0; while (true) { xStatus = xQueueReceive(myQueueHandle, &id, portMAX_DELAY); // 从Queue中接收数据,等待时间无限长,直至收到数据 // 同时由于接收优先级高于发送,所以当Queue不为空时,必定能收到数据 // 优先级高的任务会先执行,之后再执行优先级低的任务 // 所以此时只有在收到数据后,才能执行下一次发送 // 实际使用时,应根据实际需求,设置合理的优先级 if (xStatus == pdPASS) { printf("Receive data: %d\n", id); } else printf("Receive data from queue failed.\n"); printf("receiveTask is running.\n"); }
} void app_main(void)
{ TaskHandle_t myHandle = NULL; TaskHandle_t myHandle2 = NULL; TaskHandle_t myHandle3 = NULL; QueueHandle_t myQueueHandle = xQueueCreate(5, sizeof(int)); // 创建一个队列,队列长度为5,每个元素大小为int的大小 if (myQueueHandle != NULL) { printf("Queue created successfully.\n"); // 当Queue创建成功后,创建两个任务,一个发送任务,一个接收任务 xTaskCreate(sendTask1, "sendTask1", 1024 * 5, (void *)myQueueHandle, 1, &myHandle); xTaskCreate(sendTask2, "sendTask2", 1024 * 5, (void *)myQueueHandle, 1, &myHandle2); xTaskCreate(receiveTask, "receiveTask", 1024 * 5, (void *)myQueueHandle, 2, &myHandle3); // 接收优先级高于发送 } else { printf("Queue creation failed.\n"); }
}
Queue Set
队列集合(Queue Set)适用于当有多个 Task 同时向各自的 Queue 中写入数据,但都需要被一个 Task 读取时的情况
在需要单任务从多个源(队列或信号量)中高效获取数据时,Queue Set 比传统方法更优
xQueueCreateSet()
队列集合(Queue Sets)是一种机制,允许实时操作系统(RTOS)的任务在多个队列或信号量的读取操作上同时阻塞(挂起)
#include “FreeRTOS.h”
#include “queue.h”QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength );
使用说明
-
创建队列集合
队列集合需通过调用xQueueCreateSet()
显式创建后才能使用 -
添加队列和信号量
使用xQueueAddToSet()
将标准 FreeRTOS 队列和信号量加入集合 -
选择队列或信号量
调用xQueueSelectFromSet()
判断集合中是否有队列或信号量可被成功读取或获取
参数
uxEventQueueLength
队列集合存储其包含的队列和信号量上的事件,uxEventQueueLength
指定事件队列的最大长度。为防止事件丢失,需根据以下规则设置:队列长度之和 + 二元信号量或互斥量的长度(1) + 计数信号量的最大计数值。
返回值
NULL
创建失败其他值
创建成功,返回队列集合的句柄
xQueueAddToSet()
xQueueAddToSet()
用于将队列或信号量添加到先前由 xQueueCreateSet()
创建的队列集合中
#include “FreeRTOS.h”
#include “queue.h”BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,QueueSetHandle_t xQueueSet );
参数
xQueueOrSemaphore
:要添加到队列集合中的队列或信号量的句柄,需强制转换为QueueSetMemberHandle_t
类型。xQueueSet
:目标队列集合的句柄。
返回值
pdPASS
:成功将队列或信号量添加到队列集合中。pdFAIL
:添加失败,因为该队列或信号量已属于另一个队列集合。
注意事项
- 在调用
xQueueSelectFromSet()
并获取到集合成员的句柄之前,不可对队列集合中的队列执行接收操作或对信号量执行获取操作 - 在
FreeRTOSConfig.h
文件中,必须将configUSE_QUEUE_SETS
设置为 1,否则无法使用xQueueAddToSet()
xQueueSelectFromSet()
xQueueSelectFromSet() 用于从队列集合中选择一个队列或信号量
#include “FreeRTOS.h”
#include “queue.h”QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet,const TickType_t xTicksToWait );
参数
xQueueSet
队列集合的句柄,任务将可能在此集合上阻塞xTicksToWait
任务等待的最大时间(以系统时钟节拍为单位),在此期间任务将处于阻塞状态
返回值
NULL
在 xTicksToWait 指定的时间内,集合中的队列或信号量未变为可用。
其他值
集合中可用的队列(或信号量)的句柄,强制转换为 QueueSetMemberHandle_t 类型。
Example Code:Queue Sets in FreeRTOS
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h" #include "freertos/queue.h" // 包括队列头文件 void sendTask1(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 int id = 111; BaseType_t xStatus; while (true) { xStatus = xQueueSend(myQueueHandle, &id, 0); if (xStatus == pdPASS) printf("Send id %d to queue.\n", id); else printf("Send id %d to queue failed.\n", id); printf("sendTask is running.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void sendTask2(void *pvPamra)
{ QueueHandle_t myQueueHandle = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 int id = 222; BaseType_t xStatus; while (true) { xStatus = xQueueSend(myQueueHandle, &id, 0); if (xStatus == pdPASS) printf("Send id %d to queue.\n", id); else printf("Send id %d to queue failed.\n", id); printf("sendTask2 is running.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void receiveTask(void *pvPamra)
{ QueueSetHandle_t myQueueSetHandle = (QueueSetHandle_t)pvPamra; // 从pvPamra中取出队列集句柄 BaseType_t xStatus; int id = 0; QueueSetMemberHandle_t myQueueMemberHandle; // 用于接收数据 while (true) { myQueueMemberHandle = xQueueSelectFromSet(myQueueSetHandle, portMAX_DELAY); // 从队列集中选择一个队列 xStatus = xQueueReceive(myQueueMemberHandle, &id, portMAX_DELAY); // 从队列中接收数据 if (xStatus == pdPASS) { printf("Receive data: %d\n", id); } else printf("Receive data from queue failed.\n"); printf("receiveTask is running.\n"); }
} void app_main(void)
{ TaskHandle_t myHandle = NULL; TaskHandle_t myHandle2 = NULL; TaskHandle_t myHandle3 = NULL; QueueHandle_t myQueueHandle1 = xQueueCreate(5, sizeof(int)); // 创建一个队列,队列长度为5,每个元素大小为int的大小 QueueHandle_t myQueueHandle2 = xQueueCreate(5, sizeof(int)); QueueSetHandle_t myQueueSetHandle = xQueueCreateSet(10); // 创建一个队列集,队列集大小为队列长度之和 xQueueAddToSet(myQueueHandle1, myQueueSetHandle); xQueueAddToSet(myQueueHandle2, myQueueSetHandle); if (myQueueHandle1 != NULL && myQueueHandle2 != NULL && myQueueSetHandle != NULL) { printf("Queue created successfully.\n"); // 当Queue创建成功后,创建两个任务,一个发送任务,一个接收任务 xTaskCreate(sendTask1, "sendTask1", 1024 * 5, (void *)myQueueHandle1, 1, &myHandle); xTaskCreate(sendTask2, "sendTask2", 1024 * 5, (void *)myQueueHandle2, 1, &myHandle2); xTaskCreate(receiveTask, "receiveTask", 1024 * 5, (void *)myQueueSetHandle, 2, &myHandle3); // 接收优先级高于发送 } else { printf("Queue creation failed.\n"); }
}
Queue Mailbox
Mailbox 不会传递数据,而是一直保留这个数据,这个数据可以被任何 Task 和中断服务读取,并根据 Task 决定程序运行情况
发送方可以 overwrite Mailbox 里的值,而接收方只可以读取,而不能移除这个值
xQueueOverwrite()
xQueueOverwrite() 是 xQueueSendToBack()
的变体,即使队列已满也会向队列写入数据,并覆盖队列中已有的数据。此函数主要适用于长度为 1 的队列(即队列只有“空”或“满”两种状态,比如 Mailbox)
#include “FreeRTOS.h”
#include “queue.h”BaseType_t xQueueOverwrite( QueueHandle_t xQueue,const void *pvItemToQueue );
参数
xQueue
目标队列的句柄
pvItemToQueue
指向要写入队列项的指针。队列在创建时定义了每个项的大小,此函数会将该大小的数据从 pvItemToQueue
拷贝到队列存储区域
返回值
xQueueOverwrite()
是对 xQueueGenericSend()
的宏封装,返回值与 xQueueSendToFront()
相同,但由于 xQueueOverwrite()
在队列已满时也能写入,pdPASS 是唯一可能的返回值
xQueuePeek()
xQueuePeek()
用于从队列中读取一项数据,但不会将该项从队列中移除
#include “FreeRTOS.h”
#include “queue.h”BaseType_t xQueuePeek( QueueHandle_t xQueue,void *pvBuffer, TickType_t xTicksToWait );
参数
xQueue
队列的句柄,用于指定从哪个队列中读取数据
pvBuffer
指向用于接收队列数据的内存指针。内存大小必须至少等于队列项的大小(在使用 xQueueCreate()
或 xQueueCreateStatic()
创建队列时由 uxItemSize
参数定义)
xTicksToWait
任务等待数据变为可用的最大时间(以系统时钟节拍为单位)
- 任务等待数据变为可用的最大时间(以系统时钟节拍为单位)。
- 如果设置为 0,函数会立即返回。
- 如果设置为
portMAX_DELAY
且INCLUDE_vTaskSuspend
为 1,则任务会无限期等待。
返回值
pdPASS
**数据成功从队列中读取。
- 如果
xTicksToWait
非零,任务可能会进入阻塞状态等待数据变为可用
errQUEUE_EMPTY
队列为空,无法读取数据 - 如果
xTicksToWait
非零,任务可能已进入阻塞状态,但在超时时间内没有数据可用
注意事项
- 使用此函数不会移除队列中的数据,因此适用于需要查看但不消费队列数据的场景
- 缓存大小必须匹配队列项的大小,否则可能导致数据损坏或访问越界
Example Code:Implementing a Shared Mailbox with Multiple Readers and One Writer
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h" #include "freertos/queue.h" // 包括队列头文件 void writeTask(void *pvPamra)
{ QueueHandle_t Mailbox = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 int id = 0; BaseType_t xStatus; while (true) { xStatus = xQueueOverwrite(Mailbox, &id); // 覆盖写入 if (xStatus == pdPASS) printf("Send id %d to queue.\n", id); else printf("Send id %d to queue failed.\n", id); id++; vTaskDelay(6000 / portTICK_PERIOD_MS); }
} void readTask(void *pvPamra)
{ QueueHandle_t Mailbox = (QueueHandle_t)pvPamra; // 从pvPamra中取出队列句柄 int id = 0; BaseType_t xStatus; while (true) { xStatus = xQueuePeek(Mailbox, &id, 0); // 读取队列中的数据 if (xStatus == pdPASS) printf("Read id %d from queue.\n", id); else printf("Read id %d from queue failed.\n", id); vTaskDelay(1000 / portTICK_PERIOD_MS); }
} void app_main(void)
{ QueueHandle_t MailboxHandle = NULL; TaskHandle_t writeTaskHandle = NULL; TaskHandle_t readTaskHandle = NULL; TaskHandle_t readTaskHandle2 = NULL; TaskHandle_t readTaskHandle3 = NULL; MailboxHandle = xQueueCreate(1, sizeof(int)); // 创建一个邮箱,邮箱大小为1,每个元素大小为int的大小 if (MailboxHandle != NULL) { printf("Queue created successfully.\n"); // 当Queue创建成功后,创建两个任务,一个发送任务,一个接收任务 xTaskCreate(writeTask, "writeTask", 1024 * 5, (void *)MailboxHandle, 1, &writeTaskHandle); xTaskCreate(readTask, "readTask1", 1024 * 5, (void *)MailboxHandle, 2, &readTaskHandle); // 在实现的功能相同时,多个Task可以共用一个函数体 xTaskCreate(readTask, "readTask2", 1024 * 5, (void *)MailboxHandle, 2, &readTaskHandle2); xTaskCreate(readTask, "readTask3", 1024 * 5, (void *)MailboxHandle, 2, &readTaskHandle3); } else { printf("Queue creation failed.\n"); }
}