当前位置: 首页 > news >正文

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");  }  
}

相关文章:

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

摘要(From AI): 在嵌入式系统开发中&#xff0c;任务之间的高效通信是实现多任务协作的关键。FreeRTOS 提供了强大的队列机制&#xff0c;支持任务之间安全、灵活地传递数据&#xff0c;是实现任务同步和事件通知的核心工具。本篇博客将全面解析 FreeRTOS 队列的工作原理和应用…...

Project Manager工程管理器详细说明

1前言 STM32CubeMX的Project Manager工程管理器包含:Project工程管理、 Code Generator代码生成、 Advanced Settings高级设置三部分内容。 这三部分内容决定了工程的一些属性,相关的配置信息比较重要,也容易理解。初学者有必要掌握其中内容。 2Project工程管理 Project工…...

vue el-table表格点击某行触发事件操作栏点击和row-click冲突问题

文章为本新手菜鸡的问题记录&#xff0c;如有错误和不足还请大佬指正 文章目录 前言一、点击el-table表格某行&#xff0c;触发事件二、解决el-table的操作栏点击和row-click冲突问题1.问题&#xff1a;2.解决方法 前言 文章主要解决两个问题&#xff1a; 1、点击el-table表格…...

吸猫毛空气净化器哪个好?推荐除猫毛效果好的宠物空气净化器品牌

我家里养了五只猫咪&#xff0c;每天睡醒准备来杯咖啡的时候&#xff0c;总能看见猫毛。最尴尬的是这种现象&#xff0c;哪哪都有。养猫人真的每天都要生活在这个世界里面。平时和猫咪玩耍的时候也没有发现这么多猫毛啊。现在一到冬天不能开窗户&#xff0c;真的超级难受感觉每…...

Reactor 模式的理论与实践

1. 引言 1.1 什么是 Reactor 模式&#xff1f; Reactor 模式是一种用于处理高性能 I/O 的设计模式&#xff0c;专注于通过非阻塞 I/O 和事件驱动机制实现高并发性能。它的核心思想是将 I/O 操作的事件分离出来&#xff0c;通过事件分发器&#xff08;Reactor&#xff09;将事…...

VSCode 汉化教程【简洁易懂】

VSCode【下载】【安装】【汉化】【配置C环境&#xff08;超快&#xff09;】&#xff08;Windows环境&#xff09;-CSDN博客 我们安装完成后默认是英文界面。 找到插件选项卡&#xff0c;搜索“Chinese”&#xff0c;找到简体&#xff08;更具你的需要&#xff09;&#xff08;…...

cookie反爬----普通服务器,阿里系

目录 一.常见COOKIE反爬 普通&#xff1a; 1. 简介 2. 加密原理 二.实战案例 1. 服务器响应cookie信息 1. 逆向目标 2. 逆向分析 2. 阿里系cookie逆向 1. 逆向目标 2. 逆向分析 实战&#xff1a; 无限debugger原理 1. Function("debugger").call() 2. …...

【计算机网络】计算机网络概述

当我们决定要谈谈网络的时候&#xff0c;我想在谈之前&#xff0c;有必要了解一下“协议”这个词。协议&#xff0c;定义了在俩个或者多个通信实体之间交换报文的格式和次序&#xff0c;以及报文发送、接收报文或者其他的事件所采取的动作。定义都比较晦涩&#xff0c;那就让我…...

微信小程序条件渲染与列表渲染的全面教程

微信小程序条件渲染与列表渲染的全面教程 引言 在微信小程序的开发中,条件渲染和列表渲染是构建动态用户界面的重要技术。通过条件渲染,我们可以根据不同的状态展示不同的内容,而列表渲染则使得我们能够高效地展示一组数据。本文将详细讲解这两种渲染方式的用法,结合实例…...

MySQL--存储引擎

目录 1 MySQL体系结构 2 存储引擎简介 3 存储引擎特点 3.1 InnoDB 3.1.1 介绍 3.1.2 特点 3.1.3 文件 3.2 逻辑存储结构 3.3 MyISAM 3.3.1 介绍 3.3.2 特点 3.3.3 文件 3.4 Memory 3.3.1 介绍 3.3.2 特点 3.3.3 文件 4 存储引擎选择 Innodb MyISAM MEMORY …...

洛谷 B2038:奇偶 ASCII 值判断

【题目来源】https://www.luogu.com.cn/problem/B2038http://shnoip.openjudge.cn/level1/39/【题目描述】 任意输入一个字符&#xff0c;判断其 ASCII 是否是奇数&#xff0c;若是&#xff0c;输出 YES&#xff0c;否则&#xff0c;输出 NO。 例如&#xff0c;字符 A 的 ASCII…...

软件测试面试之常规问题

1.描述一下测试过程 类似题目:测试的生命周期 思路:这是一个“范围”很大的题目&#xff0c;而且回答时间一般在3分钟之内&#xff0c;不可能非常详细的描述整个过程&#xff0c;因此答题的思路要从整体结构入手&#xff0c;不要过细。为了保证答案的准确性&#xff0c;可以引…...

Android 天气APP(三十七)新版AS编译、更新镜像源、仓库源、修复部分BUG

上一篇&#xff1a;Android 天气APP&#xff08;三十六&#xff09;运行到本地AS、更新项目版本依赖、去掉ButterKnife 新版AS编译、更新镜像源、仓库源、修复部分BUG 前言正文一、更新镜像源① 腾讯源③ 阿里源 二、更新仓库源三、修复城市重名BUG四、地图加载问题五、源码 前…...

网络性能及IO性能测试工具

文章目录 简介IO性能pidstatiostatfioblktrace 网络性能ipeftrek 简介 网络性能和IO性能测试工具在现代计算环境中至关重要。无论是评估网络带宽、优化数据传输速度&#xff0c;还是检测磁盘读写性能&#xff0c;选择适合的工具至关重要。本文将介绍各种网络性能和IO性能测试工…...

1+X应急响应(网络)常见网络攻击-SQL注入:

常见网络攻击-SQL注入&#xff1a; SQL注入概述&#xff1a; 动态网站的工作流程&#xff1a; SQL注入的起源&#xff1a; SQL典型的攻击手段&#xff1a; SQL注入的危害&#xff1a; SQL注入的函数&#xff1a; SQL注入类型&#xff1a; 提交方式分类&#xff1a; Get注入&am…...

流式上传与分片上传的原理与实现

&#x1f680; 博主介绍&#xff1a;大家好&#xff0c;我是无休居士&#xff01;一枚任职于一线Top3互联网大厂的Java开发工程师&#xff01; &#x1f680; &#x1f31f; 在这里&#xff0c;你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人&#xff0c;我不仅热衷…...

基于深度学习CNN算法的花卉分类识别系统01--带数据集-pyqt5UI界面-全套源码

文章目录 基于深度学习算法的花卉分类识别系统一、项目摘要二、项目运行效果三、项目文件介绍四、项目环境配置1、项目环境库2、环境配置视频教程 五、项目系统架构六、项目构建流程1、数据集2、算法网络Mobilenet3、网络模型训练4、训练好的模型预测5、UI界面设计-pyqt56、项目…...

【数电】常见组合逻辑电路设计和分析

1.基于数据选择器设计 1.1卡诺图降维 1.2实例分析 1.2.1例题一 1.2.2例题二 1.2.2例题三 2.基于译码器设计 3.参考资料 1.《数字电子技术基础 第六版》 阎石王红 2.卡诺图的降维_哔哩哔哩_bilibili...

攻防世界-web ics-06 [解法思路]

进入环境 点击左边的列表只有报表中心有反应 注意看url直接就是index.php?id1 我先试了sqlmap不行&#xff0c;然后就沉淀了一下 想到了id后面的参数问题&#xff0c;我谁便改了几个数都没反应 就想着用bp抓包爆一下这个参数&#xff0c;用了一个数字10000的字典 发现2333…...

传智杯 3-初赛:终端

题目描述&#xff1a; 有一天您厌烦了电脑上又丑又没用的终端&#xff0c;打算自己实现一个 Terminal。具体来说&#xff0c;它需要支持如下命令: 1. touch filename&#xff1a;如果名为 filename 的文件不存在&#xff0c;就创建一个这样的文件&#xff0c;如果已经存在同名…...

《数据结构》学习系列——图(中)

系列文章目录 目录 图的遍历深度优先遍历递归算法堆栈算法 广度优先搜索 拓扑排序定义定理算法思想伪代码 关键路径基本概念关键活动有关量数学公式伪代码时间复杂性 图的遍历 从给定连通图的某一顶点出发&#xff0c;沿着一些边访问遍图中所有的顶点&#xff0c;且使每个顶点…...

网络安全,文明上网(2)加强网络安全意识

前言 在当今这个数据驱动的时代&#xff0c;对网络安全保持高度警觉已经成为每个人的基本要求。 网络安全意识&#xff1a;信息时代的必备防御 网络已经成为我们生活中不可或缺的一部分&#xff0c;信息技术的快速进步使得我们对网络的依赖性日益增强。然而&#xff0c;网络安全…...

Laravel对接SLS日志服务

Laravel对接SLS日志服务&#xff08;写入和读取&#xff09; 1、下载阿里云的sdk #通过composer下载 composer require alibabacloud/aliyun-log-php-sdk#对应的git仓库 https://github.com/aliyun/aliyun-log-php-sdk2、创建sdk请求的service <?phpnamespace App\Ser…...

Kafka 工作流程解析:从 Broker 工作原理、节点的服役、退役、副本的生成到数据存储与读写优化

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…...

基于 Flink 的车辆超速监测与数据存储的小实战

基于 Flink 的车辆超速监测与数据存储的小实战 一、实战背景与目标 在智能交通管理领域&#xff0c;实时监控车辆行驶速度并精准识别超速行为对于保障道路交通安全和维护交通秩序具有至关重要的意义。本项目旨在构建一个高效的数据处理系统&#xff0c;能够从 Kafka 的 topic…...

Shell 脚本基础(7):重定向详解

内容预览 ≧∀≦ゞ Shell 脚本基础&#xff08;7&#xff09;&#xff1a;重定向详解声明1. 重定向基础概念1.1 输出重定向&#xff08;> 和 >>&#xff09;覆盖写入&#xff08;>&#xff09;追加写入&#xff08;>>&#xff09;输出到终端和文件&#xff0…...

04. 流程控制

一、流程控制 流程控制就是用来控制程序运行中各语句执行顺序的语句。基本的流程结构为&#xff1a;顺序结构&#xff0c;分支结构&#xff08;或称选择结构&#xff09;&#xff0c;循环结构。 顺序结构&#xff1a;程序自上到下执行&#xff0c;中间没有任何判断和跳转&…...

基于卡尔曼滤波器的 PID 控制

基于卡尔曼滤波器的PID控制算法结合了经典控制理论和现代信号处理技术。卡尔曼滤波器&#xff08;Kalman Filter, KF&#xff09;可以对噪声数据进行平滑处理&#xff0c;从而改善PID控制器的性能&#xff0c;特别是在处理具有噪声和不确定性的系统时。以下是详细的设计过程&am…...

基于信创环境的信息化系统运行监控及运维需求及策略

随着信息技术的快速发展和国家对信息安全的日益重视&#xff0c;信创环境&#xff08;信息技术应用创新环境&#xff09;的建设已成为行业发展的重要趋势。本指南旨在为运维团队在基于信创环境的系统建设及运维过程中提供参考&#xff0c;确保项目顺利实施并满足各项技术指标和…...

leetCode 283.移动零

题目 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]示例 2: 输入: nums [0] 输出:…...

Kali Linux语言设置成中文

要将Kali Linux设置成中国地区&#xff08;简体中文&#xff09;&#xff0c;可以按照以下步骤进行操作&#xff1a; 一、更新软件包列表 打开Kali Linux的终端。 输入以下命令来更新软件包列表&#xff1a; sudo apt-get update 二、安装语言包&#xff08;如果有就不用安装了…...

c#窗体列表框(combobox)应用——省市区列表选择实例

效果如下&#xff1a; designer.cs代码如下&#xff1a; using System.Collections.Generic;namespace 删除 {public partial class 省市区选择{private Dictionary<string, List<string>> provinceCityDictionary;private Dictionary<string,List<string&…...

kali中信息收集的一些常用工具

这里只是代表个人所见&#xff0c;所以肯定会有其他的没提到&#xff0c;希望大家体谅 前言 信息收集分为主动和被动的 主动就是通过自己的机器去和对方比如通信后获得的数据 被动是指不是在自己这里获取的&#xff0c;可以是第三方平台获取到的&#xff0c;与目标没有通信 …...

【CS61A 2024秋】Python入门课,全过程记录P2(Week3开始,更新中2024/11/24)

文章目录 关于基本介绍&#x1f44b;Week 3Mon Environments阅读材料Lab 02: Higher-Order Functions, Lambda ExpressionsQ1: WWPD: The Truth Will PrevailQ2: WWPD: Higher-Order FunctionsQ3: WWPD: Lambda 关于 个人博客&#xff0c;里面偶尔更新&#xff0c;最近比较忙。…...

python学习笔记(8)算法(1)数组

一、数组 数组是存储于一个连续空间且具有相同数据类型的元素集合。若将有限个类型相同的变量的集合命名&#xff0c;那么这个名称为数组名。组成数组的各个变量称为数组的分量&#xff0c;也称为数组的元素&#xff0c;有时也称为下标变量。用于区分数组的各个元素的数字编号…...

C++(进阶) 第1章 继承

C&#xff08;进阶) 第1章 继承 文章目录 前言一、继承1.什么是继承2.继承的使用 二、继承方式1.private成员变量的&#xff08;3种继承方式&#xff09;继承2. private继承方式3.继承基类成员访问⽅式的变化 三、基类和派生类间的转换1.切片 四、 继承中的作⽤域1.隐藏规则&am…...

【Linux | 计网】TCP协议详解:从定义到连接管理机制

目录 1.TCP协议的定义&#xff1a; 2.TCP 协议段格式 3.TCP两种通信方式 4.确认应答(ACK)机制 解决“后发先至”问题 5.超时重传机制 那么, 超时的时间如何确定? 6.连接管理机制&#xff1a; 6.1.三次握手&#xff1a; 为什么需要3次握手&#xff0c;一次两次不行吗…...

SElinux学习

一、SElinux简介 SELinux是Security-Enhanced Linux的缩写&#xff0c;意思是安全强化的linux&#xff0c;传统的访问控制在我们开启权限后&#xff0c;系统进程可以直接访问.当我们对权限设置不严谨时&#xff0c;这种访问方式就是系统的安全漏洞。 所以在开启SElinux后 会…...

Spark核心组件解析:Executor、RDD与缓存优化

Spark核心组件解析&#xff1a;Executor、RDD与缓存优化 Spark Executor Executor 是 Spark 中用于执行任务&#xff08;task&#xff09;的执行单元&#xff0c;运行在 worker 上&#xff0c;但并不等同于 worker。实际上&#xff0c;Executor 是一组计算资源&#xff08;如…...

阿里云cdn配置记录和nodejs手动安装

cdn 登录阿里云 域名解析权限 开启cdn&#xff0c;接引导流程&#xff0c; 源可以设置 域名或者ip等 配置好域名解析 上传https证书 图片不显示&#xff0c;后端开发需要配置 回源配置的回源协议 &#xff0c;配置跟随客服端【如果浏览器多次重定向错误&#xff0c;客服或者改…...

【实战】基于urllib和BeautifulSoup爬取jsp网站的数据

文章目录 前言目标网站分析目标网页爬取数据解析导出数据其他问题处理分页检索及多关键字搜索去重cookie问题工具封装经验总结前言 网络数据爬取大致分为两类: 静态爬取:该种方式针对那种架构比较老的网站,使用模版方式,通过浏览器F12只能找到静态页面,找不到返回json数…...

cpp-httplib 框架

cpp-httplib 概述 主要特点 单文件库&#xff1a;cpp-httplib 是一个单文件头文件库&#xff0c;易于集成到现有项目中。你只需将 httplib.h 包含到项目中即可开始使用支持 HTTP 客户端和服务器&#xff1a;它不仅支持作为 HTTP 客户端发送请求&#xff0c;也支持构建 HTTP 服…...

GEE 案例——TVDI(Temperature Vegetation Dryness Index)指数

目录 简介 方法论 代码解释 函数 案例代码 单景影像的TVDI 影像集合批量计算TVDI 引用 结果 简介 本文中代码包含两个可用于计算 TVDI 的主要函数。其中一个函数用于仅根据一幅 NDVI 和一幅 LST 图像生成 TVDI(singleTVDI),另一个函数用于为多幅 NDVI 和多幅 LST …...

Java语言程序设计 选填题知识点总结

第一章 javac.exe是JDK提供的编译器public static void main (String args[])是Java应用程序主类中正确的main方法Java源文件是由若干个书写形式互相独立的类组成的Java语言的名字是印度尼西亚一个盛产咖啡的岛名Java源文件中可以有一个或多个类Java源文件的扩展名是.java如果…...

基于Springboot+Vue社区养老服务管理系统(源码+lw+讲解部署+PPT)

前言 详细视频演示 论文参考 系统介绍 系统概述 核心功能 用户角色与功能 具体实现截图 1. 服务信息查看功能 主要代码实现 截图&#xff1a; 2. 服务申请功能 主要代码实现 截图&#xff1a; 3. 公告信息查看功能 主要代码实现 截图&#xff1a; 4. 服务信息…...

Linux基本指令的使用

当然可以&#xff01;以下是一些常用的Linux指令及其示例&#xff1a; 1. ls 列出目录内容。 ls 显示当前目录下的文件和文件夹。 ls -l 以详细格式列出文件和文件夹的信息&#xff08;如权限、拥有者、大小等&#xff09;。 2. cd 改变当前目录。 cd /path/to/dire…...

Momenta C++面试题及参考答案

vtable 的创建时机 在 C 中&#xff0c;vtable&#xff08;虚函数表&#xff09;是在编译阶段创建的。当一个类包含虚函数时&#xff0c;编译器会为这个类生成一个 vtable。vtable 本质上是一个函数指针数组&#xff0c;其中每个元素指向一个虚函数的实现。这个表的布局是由编译…...

AI 在软件开发流程中的优势、挑战及应对策略

AI 在软件开发流程中的优势、挑战及应对策略 随着人工智能技术的飞速发展&#xff0c;AI大模型正在逐步渗透到软件开发的各个环节&#xff0c;从代码自动生成到智能测试&#xff0c;AI的应用正在重塑传统的软件开发流程。本篇文章将分析AI在软件开发流程中带来的优势&#xff0…...

langchain runnable

LangChain 文档详细解析 LangChain 是一个用于构建与语言模型&#xff08;如GPT-4&#xff09;交互的框架。本文档介绍了LangChain v0.1版本中的Runnable接口及其相关功能。 目录 Runnable接口输入和输出模式使用示例异步方法事件流并行处理 1. Runnable接口 为了简化自定义…...

nginx配置不缓存资源

方法1 location / {index index.html index.htm;add_header Cache-Control no-cache,no-store;try_files $uri $uri/ /index.html;#include mime.types;if ($request_filename ~* .*\.(htm|html)$) {add_header Cache-Control "private, no-store, no-cache, must-revali…...