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

摘要(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_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");  }  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/60656.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Binder技术概览

Android中的Binder是一种基于远程过程调用&#xff08;Remote Procedure Call, RPC&#xff09;的轻量级通信机制&#xff0c;核心用于 Android 系统中的进程间通信&#xff08;Inter-Process Communication, IPC&#xff09;。Binder 是 Android 系统中不可或缺的一部分&#…

MySQL学习入门

好的&#xff01;以下是一个详细的 MySQL 入门学习文档&#xff0c;针对 Python 开发者&#xff0c;包含使用 SQLite 进行学习的示例。文档将涵盖实际开发中需要使用的各种相关操作和知识&#xff0c;帮助您从基础开始&#xff0c;逐步掌握在 Python 中使用 MySQL 的技能。 MyS…

AI模型---安装cuda与cuDNN

1.安装cuda 先打开cmd 输入nvidia-smi 查看显卡支持cuda对应的版本&#xff1a; 然后去英伟达官网下载cuda&#xff08;外网多刷几次&#xff09; https://developer.nvidia.com/cuda-toolkit-archive 注意对应版本 安装过程中如果显示如下图&#xff1a; 请安装visual Stu…

大数据背景下信息通信网络安全管理管理策略研究

摘要&#xff1a; 在互联网以及计算机技术的支持下我国已经进入了大数据时代&#xff0c;各行各业在发展的进程之中更多的借助云计算技术慢慢完成了信息发展。在信息化发展的进程中&#xff0c;因为网络的开放性&#xff0c;促使信息通信网络安全工作面临一定危胁&#xff0c;安…

ajax (一)

什么是 AJAX [ˈeɪdʒks] &#xff1f; 概念&#xff1a;AJAX是浏览器与服务器进行 数据通信 的技术&#xff0c;动态数据交互 怎么用AJAX? 1. 先使用 axios [k‘sio ʊ s] 库&#xff0c; 与服务器进行 数据通信 ⚫ 基于 XMLHttpRequest 封装、代码简单、月下载量在 1…

k8s rainbond centos7/win10 -20241124

参考 https://www.rainbond.com/ 国内一站式云原生平台 对centos7环境支持不太行 [lighthouseVM-16-5-centos ~]$ curl -o install.sh https://get.rainbond.com && bash ./install.sh 2024-11-24 09:56:57 ERROR: Ops! Docker daemon is not running. Start docke…

使用 Maven 开发 IntelliJ IDEA 插件

使用 Maven 开发 IntelliJ IDEA 插件的完整流程 1. 创建 Maven 项目 1.1 使用 IntelliJ 创建 Maven 项目 打开 IntelliJ IDEA&#xff0c;点击 File > New > Project。选择 Maven&#xff0c;填写项目名称和 GroupId&#xff0c;例如&#xff1a; GroupId: com.exampl…

Load-Balanced-Online-OJ(负载均衡式在线OJ)

负载均衡式在线OJ 前言1. 项目介绍2. 所用技术与环境所用技术栈开发环境 3. 项目宏观结构3.1 项目核心模块3.2 项目的宏观结构 4. comm公共模块4.1 日志&#xff08;log.hpp &#xff09;4.1.1 日志主要内容4.1.2 日志使用方式4.1.2 日志代码 4.2 工具&#xff08;util.hpp&…

微信小程序上传微信官方审核流程(1)

1&#xff0c;打开微信开发者工具 2&#xff0c;微信开发者工具右上角有一个上传按钮&#xff0c;点击上传按钮 3&#xff0c;点击完上传按钮会弹出一个上传成功的提示&#xff0c;点击提示框中的确定按钮 4&#xff0c;点击完确定按钮后会显示填写版本好和项目备注 5&#x…

EXISTS 和 IN 的使用方法、特性及查询效率比较

在 SQL Server 中&#xff0c;EXISTS 和 IN 是两个常用的子查询操作符&#xff0c;用于检查某个值是否存在于一个子查询的结果集中。尽管它们在功能上有相似之处&#xff0c;但在使用方法、特性、查询效率和生成查询计划方面存在一些重要的区别。本文将详细探讨这两个操作符的使…

flink学习(3)——方法的使用—对流的处理(map,flatMap,filter)

map 数据 86.149.9.216 10001 17/05/2015:10:05:30 GET /presentations/logstash-monitorama-2013/images/github-contributions.png 83.149.9.216 10002 17/05/2015:10:06:53 GET /presentations/logstash-monitorama-2013/css/print/paper.css 83.149.9.216 10002 17/05/20…

数据结构(Java版)第一期:时间复杂度和空间复杂度

目录 一、数据结构的概念 1.1. 什么是数据结构 1.2. 算法与数据结构的关系 二、算法效率 三、时间复杂度 3.1. 大O的渐进表⽰法 3.2. 计算冒泡排序的时间复杂度 3.3. 计算二分查找的时间复杂度 四、空间复杂度 4.1. 空间复杂度 4.2. 冒泡排序的空间复杂度 4.3.…

微信小程序全局配置:导航栏、下拉刷新与上拉触底设置教程

微信小程序全局配置:导航栏、下拉刷新与上拉触底设置教程 引言 微信小程序作为一种新兴的轻量级应用,凭借其便捷性和丰富的功能受到了广泛的欢迎。在开发小程序的过程中,合理配置全局属性是提升用户体验的关键。本文将深入探讨小程序的全局配置中的window选项,重点介绍导…

语言模型中的多模态链式推理

神经网络的公式推导 简介摘要引言多模态思维链推理的挑战多模态CoT框架多模态CoT模型架构细节编码模块融合模块解码模块 实验结果运行代码补充细节安装包下载Flan-T5数据集准备rougenltkall-MiniLM-L6-v2运行 简介 本文主要对2023一篇论文《Multimodal Chain-of-Thought Reason…

LLaMA-Mesh: Unifying 3D Mesh Generation with Language Models 论文解读

目录 一、概述 二、相关工作 1、LLMs到多模态 2、3D对象生成 3、自回归的Mesh生成 三、LLaMA-Mesh 1、3D表示 2、预训练模型 3、有监督的微调数据集 4、数据集演示 四、实验 1、生成的多样性 2、不同模型text-to-Mesh的比较 3、通用语境的评估 一、概述 该论文首…

【Go】-go中的锁机制

目录 一、锁的基础知识 1. 互斥量/互斥锁 2. CAS&#xff08;compare and swap&#xff09; 3. 自旋锁 4. 读写锁 5. 乐观锁 & 悲观锁 6. 死锁 二、go中锁机制 1. Mutex-互斥锁 2. RWMutex-读写锁 2.1 RWMutex流程概览 2.2 写锁饥饿问题 2.3. golang的读写锁源…

VSCode 新建 Python 包/模块 Pylance 无法解析

问题描述&#xff1a; 利用 VSCode 写代码&#xff0c;在项目里新建一个 Python 包或者模块&#xff0c;然后在其他文件里正常导入这个包或者模块时出现&#xff1a; Import “xxxx” could not be resolved Pylance (reportMissingImports) 也就是说 Pylance 此时无法解析我们…

深入理解 Java 阻塞队列:使用场景、原理与性能优化

在并发编程中&#xff0c;线程安全的队列是解决线程间任务传递和调度的关键工具之一。阻塞队列&#xff08;BlockingQueue&#xff09;作为一种线程安全的队列&#xff0c;实现了在并发环境下对共享数据的安全访问&#xff0c;广泛应用于生产者-消费者模型、任务调度和多线程计…

.NET9 - 新功能体验(二)

书接上回&#xff0c;我们继续来聊聊.NET9和C#13带来的新变化。 01、新的泛型约束 allows ref struct 这是在 C# 13 中&#xff0c;引入的一项新的泛型约束功能&#xff0c;允许对泛型类型参数应用 ref struct 约束。 可能这样说不够直观&#xff0c;简单来说就是Span、ReadO…

C++游戏《密室逃脱2.0》预告

这里是2.0预告区域&#xff0c;发布时将直接以此文章界面发布&#xff0c;可以提前点赞收藏。 你们所期待的2.0就要来啦&#xff01; 是的&#xff0c;没错&#xff0c;今年年末就要出2.0版本了&#xff0c;时间大约在12月底。玩法有更新&#xff0c;更新如下&#xff1a; 增…