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

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

相关推荐
山甫aa8 小时前
锁一篇搞定:乐观锁、悲观锁、CAS、数据库锁与分布式锁【从零开始的 Web 后端学习】
java·数据库·分布式·后端·学习·web
m0_564876848 小时前
CMake学习
学习
只愿云淡风清9 小时前
正则表达式学习笔记 02:基础语法
笔记·学习·正则表达式
wangqiaowq12 小时前
Ontology 学习 二
学习
Zw-awa17 小时前
协议是人为约定:同一个字节能有三种意思
stm32
该学习了咕咕18 小时前
顶刊论文学习笔记
学习
Linux-lucky20 小时前
45-Linux学习之旅之防火墙与WEB加固
linux·前端·网络·学习·ubuntu
li星野20 小时前
【学习记录】k-Wave 时间步长、CFL 稳定性与参数实验全解析
学习
国科安芯1 天前
83 倍:ASC0108S 在推挽和开漏之间,差的不是速度,是回程
单片机·嵌入式硬件·架构·电平转换·抗辐射
心祭丫丫1 天前
【每天一个 Python 小案例17】Python 实现通讯录
开发语言·人工智能·笔记·python·学习