在 FreeRTOS 中使用字符串队列



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

您能否启发我应该如何创建和使用队列以及我应该考虑哪些注意事项?

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

在像 FreeRTOSQueue这样的"原始"内存 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这样的标准库容器通常将其内容存储在错误定位的块中,因此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 queuen");

}

从队列接收:

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

从我的角度来看,使用固定大小缓冲区的容器可能不太理想。向队列发送/从队列接收的每个操作都将涉及整个缓冲区的完整副本。它还需要更大和精心选择的堆栈大小。

在我看来,freeRTOS 消息缓冲区非常方便,可以解决将几乎任意大小的数据块从一个任务发送到另一个任务的需求。

最新更新