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

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

相关推荐
Awh-3 分钟前
ARM 裸机开发 DAY10 学习笔记
arm开发·笔记·学习
我命由我123453 分钟前
动火作业极简理解
学习·职场和发展·产品运营·求职招聘·职场发展·产品经理·学习方法
深圳市恒锐丰科技杨生13 分钟前
LR010N04SD10 N 沟道 SGT‑MOSFET|Long‑Tek 龙夏电子
嵌入式硬件·硬件工程
西城微科方案开发33 分钟前
方案商|西城微科胎压计方案开发
单片机·嵌入式硬件
深圳市尚想信息技术有限公司38 分钟前
HITENX海速芯TM56M152A-SOP8单片机MCU微控制器IC,高精度ADC-16位PWM国产主控芯片
单片机·嵌入式硬件
艾芯微科技1 小时前
CJX3134K 长晶双 N 沟道 MOS 管详解|SOT-563 20V 小型电源开关方案
网络·单片机·嵌入式硬件·集成测试·51单片机
andrsted1 小时前
练题簿助学教学:在线刷题、随堂考试,让一门课拥有自己的题库
学习·微信小程序·小程序·学习方法·刷题
apple_692658851 小时前
GitHub fqapp 番茄开源魔改版:小短剧+漫剧+听书聚合工具 安装指南使用
学习·开源
番茄灭世神1 小时前
PN学堂GD32教程第14篇——Arm单片机启动流程
arm开发·单片机·嵌入式硬件
lhh_qrsly2 小时前
一、电机控制怎么学
驱动开发·嵌入式硬件