FreeRTOS学习(二十三)——消息缓冲区(Message Buffer)

上一节学习了流缓冲区(Stream Buffer),它非常适合UART、DMA等连续字节流数据。

但是流缓冲区有一个特点:

复制代码
它只保存数据

不保存每条数据的边界。

例如连续发送:

复制代码
Hello

和:

复制代码
FreeRTOS

接收任务看到的实际上是一串连续字节。

如果我们希望每次发送的数据都作为一条完整消息 保存,就可以使用FreeRTOS提供的消息缓冲区(Message Buffer)

本节学习消息缓冲区的创建、发送和接收,并理解它和流缓冲区之间的区别。

一、什么是消息缓冲区

消息缓冲区可以理解成:

复制代码
一条消息

↓

完整存进去

↓

完整取出来

例如发送两次:

复制代码
Hello

FreeRTOS

消息缓冲区会保存成:

复制代码
┌─────────┐
│ Hello   │
├─────────┤
│ FreeRTOS│
└─────────┘

接收时第一次得到:

复制代码
Hello

第二次得到:

复制代码
FreeRTOS

两条消息不会混在一起。

二、消息缓冲区和流缓冲区有什么区别

上一节的流缓冲区:

复制代码
Hello + FreeRTOS

↓

HelloFreeRTOS

它关心的是:

复制代码
连续字节。

而消息缓冲区:

复制代码
Hello

FreeRTOS

会分别保存。

它关心的是:

复制代码
一条完整消息。

因此可以简单记:

复制代码
连续字节流

↓

Stream Buffer

一条一条的数据包

↓

Message Buffer

三、创建消息缓冲区

首先包含头文件:

复制代码
#include "message_buffer.h"

定义消息缓冲区句柄:

复制代码
MessageBufferHandle_t MessageBuffer;

创建:

复制代码
MessageBuffer = xMessageBufferCreate(
                    128
                );

这里的:

复制代码
128

表示消息缓冲区的总容量。

也就是:

复制代码
128Byte

左右的存储空间。

需要注意,消息缓冲区内部还需要保存每条消息的长度信息,因此不能简单认为128Byte空间一定能够存放128Byte的有效数据。

四、发送消息

发送消息使用:

复制代码
xMessageBufferSend();

例如:

复制代码
char Message[] = "Hello FreeRTOS";

xMessageBufferSend(
    MessageBuffer,
    Message,
    sizeof(Message),
    portMAX_DELAY
);

第一个参数:

复制代码
MessageBuffer

表示消息缓冲区句柄。

第二个参数:

复制代码
Message

表示需要发送的数据地址。

第三个参数:

复制代码
sizeof(Message)

表示消息长度。

第四个参数:

复制代码
portMAX_DELAY

表示如果空间不足:

复制代码
任务进入阻塞

等待缓冲区出现足够空间。

五、接收消息

接收使用:

复制代码
xMessageBufferReceive();

例如:

复制代码
char Buffer[50];

size_t Length;

Length = xMessageBufferReceive(
             MessageBuffer,
             Buffer,
             sizeof(Buffer),
             portMAX_DELAY
         );

返回值:

复制代码
Length

表示:

复制代码
实际接收到的消息长度。

例如收到:

复制代码
Hello

就会一次性取出这一整条消息,而不是只取其中几个字节。

六、完整实验

创建一个发送任务:

复制代码
void SendTask(void *pvParameters)
{
    char Message[] = "Hello FreeRTOS";

    while(1)
    {
        xMessageBufferSend(
            MessageBuffer,
            Message,
            sizeof(Message),
            portMAX_DELAY
        );

        vTaskDelay(
            pdMS_TO_TICKS(1000)
        );
    }
}

然后创建接收任务:

复制代码
void ReceiveTask(void *pvParameters)
{
    char Buffer[50];
    size_t Length;

    while(1)
    {
        Length = xMessageBufferReceive(
                     MessageBuffer,
                     Buffer,
                     sizeof(Buffer),
                     portMAX_DELAY
                 );

        if(Length > 0)
        {
            printf(
                "Receive: %s\r\n",
                Buffer
            );
        }
    }
}

程序运行以后:

复制代码
SendTask

每隔1秒发送一条消息

接收任务:

复制代码
等待消息

收到以后打印:

复制代码
Receive: Hello FreeRTOS

然后继续等待下一条消息。

七、消息长度不同也可以发送

消息缓冲区并不要求每条消息长度相同。

例如第一次发送:

复制代码
OK

第二次发送:

复制代码
Hello

第三次发送:

复制代码
STM32 FreeRTOS

消息缓冲区会分别记录:

复制代码
OK

Hello

STM32 FreeRTOS

因此特别适合:

复制代码
长度不固定的数据包。

这也是它相比普通队列比较方便的地方之一。

八、接收数组一定要足够大

消息缓冲区有一个很重要的注意事项。

假设发送:

复制代码
20Byte

但是接收数组只有:

复制代码
char Buffer[10];

那么:

复制代码
接收缓冲区太小。

xMessageBufferReceive()不会只取前10Byte然后把剩余部分留到下一次读取,而是本次接收失败,消息仍然保留在消息缓冲区中。

因此实际程序中:

接收数组必须能够容纳可能出现的最大消息。

这是使用消息缓冲区时比较容易踩的坑。

九、消息缓冲区适合什么场景

例如STM32中:

复制代码
UART收到一帧协议数据

可以作为:

复制代码
一条Message

发送给协议处理任务。

网络通信中:

复制代码
收到一个完整数据包

也可以放入:

复制代码
Message Buffer

因此它比较适合:

复制代码
串口协议帧

网络数据包

长度不固定的命令

任务之间的数据包传递

如果只是连续不断的原始字节:

复制代码
UART DMA连续数据流

还是使用:

复制代码
Stream Buffer

更加合适。

十、总结

这一节学习了FreeRTOS中的消息缓冲区。

创建消息缓冲区使用:

复制代码
xMessageBufferCreate();

发送消息:

复制代码
xMessageBufferSend();

接收消息:

复制代码
xMessageBufferReceive();

消息缓冲区与流缓冲区最大的区别就是:流缓冲区只保存连续字节,而消息缓冲区会保留每一条消息的边界。

因此可以简单记住:

复制代码
连续字节流
    ↓
Stream Buffer

独立的数据包
    ↓
Message Buffer

另外,接收消息时一定要保证接收数组足够大,否则无法成功取出完整消息。

相关推荐
我的xiaodoujiao2 小时前
快速学习Python基础知识详细图文教程17--类型注解和断点调试
开发语言·python·学习·测试工具
我是海飞3 小时前
杰理JL703N SSD1306 OLED(128x64) 点阵屏移植说明
单片机·算法·嵌入式·音频·杰理
沸速存储4 小时前
CPU 和 GPU 核心差别在哪?为什么 AI 训练离不开 GPU
服务器·人工智能·科技·嵌入式硬件·电脑
IT古董4 小时前
【MES学习笔记系列】MES 厂商与产品对比
笔记·学习
wuyk5554 小时前
2.队列:先进先出的线性数据结构
c语言·数据结构·stm32·单片机
wangyue_msn_865 小时前
Agent知识学习笔记——03 RAG
笔记·学习·ai编程
呱呱巨基5 小时前
Linux 查找命令
linux·笔记·学习·查找命令
我不会JAVA!5 小时前
【无标题】
嵌入式硬件·h.264
芯岭技术郦5 小时前
OM6625A芯片 Timer 定时器计数模式使用说明
stm32·单片机·嵌入式硬件