ESP32学习笔记_FreeRTOS(2)——Queue

摘要(From AI):
在嵌入式系统开发中,任务之间的高效通信是实现多任务协作的关键。FreeRTOS 提供了强大的队列机制,支持任务之间安全、灵活地传递数据,是实现任务同步和事件通知的核心工具。本篇博客将全面解析 FreeRTOS 队列的工作原理和应用场景,从基础操作到高级功能(如队列集合和邮箱)的深入探讨

前言:本文档是本人在依照B站UP:Michael_ee的视频教程进行学习时所做的学习笔记,可能存在疏漏和错误,如有发现,望指正。

上一篇:ESP32学习笔记_FreeRTOS(1)——Task的创建和使用

参考资料:

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_vTaskSuspendFreeRTOSConfig.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,函数会立即返回,而不等待

返回值

  1. pdPASS
    • 成功从队列中读取到数据
    • 如果指定了等待时间,可能会发生任务进入 Blocked 状态等待数据写入队列后再成功读取的情况
  2. 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 );

使用说明

  1. 创建队列集合
    队列集合需通过调用 xQueueCreateSet() 显式创建后才能使用

  2. 添加队列和信号量
    使用 xQueueAddToSet() 将标准 FreeRTOS 队列和信号量加入集合

  3. 选择队列或信号量
    调用 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_DELAYINCLUDE_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");  
    }  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值