在 FreeRTOS 中使用字符串队列

Using Queue of string in FreeRTOS

我正在使用 Ardunio/ESP32,我对 FreeRTOS 还很陌生。我想有一个任务,只负责在串口上打印文本,其他任务可以推送消息。 因此,我决定使用具有 10 个项目容量的字符数组队列(或 std::string)。 但是我不确定队列存储分配对于不同长度的元素是如何工作的!

请问我应该如何创建和使用队列,我应该考虑哪些因素?

TL;DR:创建一个指向 std::string 的指针队列,并在任一侧处理 new/delete。确保生产者和消费者都使用共享内存 space.

在像 FreeRTOS Queue 这样的“原始”内存 API 中使用 std::string 的问题实际上并不是对象大小的问题。事实上 std::string 对象的大小是固定的,与对象存储的字符数组的大小无关。不相信我?尝试在您自己的机器上编译和 运行 这个简单的程序:

#include <iostream>

int main(int argc, char **argv)
{
    std::string str1 = "short";
    std::string str2 = "very very very very very very long";

    std::cout << "str1 length = " << sizeof(str1) << "\n";
    std::cout << "str2 length = " << sizeof(str2) << "\n";
    return 0;
}

你会得到这样的东西(实际大小会因你的平台而异):

str1 length = 24
str2 length = 24

请注意,如果您使用 str1.size(),此结果会有所不同。

原因是像 std::string 这样的标准库容器通常将它们的内容存储在 malloc 块中,因此 std::string 对象本身将只存储一个指向包含字符串中的字符数据。 sizeof() 在这种情况下从编译器的角度告诉您对象的大小,这将是指针和其他固定元数据(即结构的字段)的大小。 .size() 方法从实现中告诉您字符串的大小,其中将包括对分配的字符数组的字符串长度计算。

您不能将 std::string 对象复制到 xQueueSend() 变体中的原因是生命周期管理的问题。 FreeRTOS API 通过原始 memcpy 执行 *Send*Receive。标准库容器通常不是 POD 类型,这意味着它们必须通过专用的复制构造函数进行复制。如果不这样做,您可能会使对象的某些内部状态无效,除非您真的知道自己在做什么。

因此,完成这项工作的最简单方法如下所示:

  1. xQueueCreate() 上,将您的对象大小设置为 sizeof(std::string *):
xQueue = xQueueCreate(NUM_OBJECTS, sizeof(std::string *));
  1. xQueueSend()上,通过运算符new创建一个std::string,并传递一个地址给这个要复制的指针。不要删除对象。
std::string *pStr = new std::string("hello world!");
xQueueSend(xQueue, &pStr, (TickType_t)0);
  1. xQueueReceive()上,复制指针。用这个指针做你需要做的,然后delete它。
std::string *pStr = NULL;
xQueueReceive(xQueue, &pStr, (TickType_t)10);
// do stuff with pStr
delete pStr;

你好,在这个例子中,我将使用结构在队列中发送一个字符串数据:

首先我们创建结构:

typedef struct log_payload_t
{
    uint8_t message[256];
} log_payload_t;

创建队列:

xQueueHandle log_queue;
int main(int argc, char **argv)
{
   log_queue = xQueueCreate(10, sizeof(log_payload_t));
}

将字符串发送到队列:

log_payload_t payload;

memset(payload.message, 0, 256);
memcpy(payload.message, "Example text", strlen(str));

if (xQueueSend(log_queue, &payload, 0))
    {
            printf("added message to log queue  \n");
    }
    else
    {
            printf("failed to add message to log queue\n");
        
    }

从队列接收:

log_payload_t payload;
if (xQueueReceive(log_queue, &payload, 0))
{
    printf("Log Queue Data %s \n", payload.message);

}
else
{
    vTaskDelay(10);
}           

在我看来,使用固定大小缓冲区的容器可能不是最佳选择。 sending/receiving to/from 队列的每个操作都将涉及整个缓冲区的完整副本。它还需要更大且精心选择的筹码量。

freeRTOS message buffers 在我看来非常方便地解决您将几乎任意大小的数据块从一个任务发送到另一个任务的需求。