Skip to content

八、流缓冲区和消息缓冲区

8.1 流缓冲区

通过流缓冲区,可以将字节流从中断服务程序传递到任务, 也可以将其从一项任务传递到另一项任务。字节流可以是任意长度,不一定 有开头或结尾。可以一次写入任意数量的字节, 也可以一次读取任意数量的字节。数据通过复制的方式传递:数据由发送方复制到缓冲区中, 然后由接收方从缓冲区中读取。

8.1.1 API说明

函数名功能备注
xStreamBufferCreate创建一个流数据缓冲区返回一个缓冲区句柄,供后续使用
xStreamBufferSend向流缓冲区写入数据如果缓冲区已满,可选择阻塞或立即返回
xStreamBufferReceive从流缓冲区读取数据如果缓冲区为空,可选择阻塞或立即返回
xStreamBufferReset重置流缓冲区,清空所有数据需确保无任务正在操作此缓冲区
xStreamBufferSpacesAvailable查询缓冲区中剩余可写空间返回缓冲区剩余字节数
xStreamBufferBytesAvailable查询缓冲区中可读数据量返回缓冲区中尚未读取的字节数
vStreamBufferDelete删除流缓冲区并释放内存清理操作,需在确保无任务使用的情况下执行

以下是流数据缓冲区相关函数的详细说明,包含函数原型、参数解释和返回值:

xStreamBufferCreate

功能: 创建一个流数据缓冲区,用于存储和传输数据。

原型

c
StreamBufferHandle_t xStreamBufferCreate(size_t xBufferSizeBytes, size_t xTriggerLevelBytes);

参数

  • xBufferSizeBytes:缓冲区的总大小(以字节为单位)。
  • xTriggerLevelBytes:触发读取任务的最小字节数(推荐设为 1)。

返回值

成功:返回创建的流缓冲区句柄。 失败:返回 NULL。

xStreamBufferSend

功能: 向流缓冲区写入数据。

原型

c
size_t xStreamBufferSend(StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait);

参数

  • xStreamBuffer:目标流缓冲区的句柄。
  • pvTxData:指向要写入数据的指针。
  • xDataLengthBytes:要写入的数据长度(字节数)。
  • xTicksToWait:写入时的最大阻塞时间(Tick 数,0 表示立即返回)。

返回值

成功:实际写入的字节数。 失败:0 表示写入失败(缓冲区已满)。

xStreamBufferReceive

功能: 从流缓冲区中读取数据。

原型

c

size_t xStreamBufferReceive(StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait);

参数

  • xStreamBuffer:目标流缓冲区的句柄。
  • pvRxData:指向接收数据的缓冲区指针。
  • xBufferLengthBytes:接收缓冲区的大小(字节数)。
  • xTicksToWait:读取时的最大阻塞时间(Tick 数,0 表示立即返回)。

返回值

成功:实际读取的字节数。 失败:0 表示读取失败(缓冲区为空)。

xStreamBufferReset

功能: 重置流缓冲区,清空所有数据。 原型

c
BaseType_t xStreamBufferReset(StreamBufferHandle_t xStreamBuffer);

参数

  • xStreamBuffer:目标流缓冲区的句柄。 返回值
  • pdPASS:重置成功。
  • pdFAIL:重置失败(通常是因为缓冲区正在被任务或中断使用)。

xStreamBufferSpacesAvailable

功能: 查询流缓冲区中剩余的可写空间。

原型

c

size_t xStreamBufferSpacesAvailable(StreamBufferHandle_t xStreamBuffer);

参数

  • xStreamBuffer:目标流缓冲区的句柄。 返回值:剩余可写空间的字节数。

xStreamBufferBytesAvailable

功能: 查询流缓冲区中可读取的数据量。 原型

c
size_t xStreamBufferBytesAvailable(StreamBufferHandle_t xStreamBuffer);

参数

  • xStreamBuffer:目标流缓冲区的句柄。 返回值:当前缓冲区中可读取的数据字节数。

vStreamBufferDelete

功能: 删除一个流缓冲区并释放其内存。 原型

c

void vStreamBufferDelete(StreamBufferHandle_t xStreamBuffer);

参数

  • xStreamBuffer:目标流缓冲区的句柄。 返回值:无返回值。

8.1.2 示例代码

c
// 任务通知值按位判断-代替队列邮箱或者事件组
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/stream_buffer.h"

static const char *TAG = "main";

StreamBufferHandle_t xStreamBuffer;

void task1(void *pvParameters)
{
    ESP_LOGI(TAG, "-------------------------------");
    ESP_LOGI(TAG, "task1启动!");

    char tx_buf[50];
    int i = 0;
    while (1)
    {
        i++;
        sprintf(tx_buf, "Hello World!-%d", i);
        // 写入数据到流缓冲区
        int send_len = xStreamBufferSend(xStreamBuffer, (void *)tx_buf, sizeof(tx_buf), portMAX_DELAY);
        ESP_LOGI(TAG, "task1发送数据长度: %d", send_len);
        vTaskDelay(pdMS_TO_TICKS(3000));
    }
}

void task2(void *pvParameters)
{
    ESP_LOGI(TAG, "-------------------------------");
    ESP_LOGI(TAG, "task2启动!");
    char rx_buf[50];

    while (1)
    {
        memset(rx_buf, 0, sizeof(rx_buf));
        // 读取数据
        int recv_len = xStreamBufferReceive(xStreamBuffer, (void *)rx_buf, sizeof(rx_buf), portMAX_DELAY);
        if (recv_len > 0)
        {
            rx_buf[recv_len] = 0;
            ESP_LOGI(TAG, "task2接收数据: %s ", rx_buf);
        }
    }
}

// 监控缓冲区
void task3(void *pvParameters)
{
    while (1)
    {
        // 读取数据
        size_t xBytesAvailable = xStreamBufferBytesAvailable(xStreamBuffer);
        if (xBytesAvailable > 0)
        {
            ESP_LOGI(TAG, "监控缓冲区中有数据: %d", xBytesAvailable);
        }
        else
        {
            ESP_LOGI(TAG, "监控缓冲区中无数据");
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}
void app_main(void)
{
    xStreamBuffer = xStreamBufferCreate(1024, 300); // 创建一个 1024 字节的流缓冲区,每次可读取 1 字节
    if (xStreamBuffer != NULL)
    {
        ESP_LOGI(TAG, "创建流缓冲区成功");
        xTaskCreate(task1, "task1", 1024 * 4, NULL, 1, NULL);
        xTaskCreate(task2, "task2", 1024 * 4, NULL, 1, NULL);
        xTaskCreate(task3, "task3", 1024 * 2, NULL, 1, NULL);
    }
    else
    {
        ESP_LOGI(TAG, "创建流缓冲区失败");
    }
}

8.2 消息缓冲区

消息缓冲区允许长度可变的离散消息从中断服务程序传递至 一个任务,或从一个任务传递至另一个任务。例如,长度为 10、20 和 123 字节的消息 都可以在同一个消息缓冲区写入或读取 。与使用流缓冲区不同的是, 长度为 10 个字节的消息只能作为 10 个字节的消息读取,而不能 以单独的字节读取。消息缓冲区构建在流缓冲区之上(即它们使用 流缓冲区实现)。

4.6.1 API说明

以下是消息缓冲区的核心 API,以及其功能和参数说明。

函数名功能备注
xMessageBufferCreate创建一个消息缓冲区返回一个缓冲区句柄,供后续使用
xMessageBufferSend向消息缓冲区写入数据如果缓冲区空间不足,可选择阻塞或立即返回
xMessageBufferReceive从消息缓冲区读取数据如果缓冲区为空,可选择阻塞或立即返回
xMessageBufferSpaceAvailable查询消息缓冲区中剩余可用空间返回剩余字节数
xMessageBufferReset重置消息缓冲区,清空所有数据需确保没有任务正在操作缓冲区
vMessageBufferDelete删除消息缓冲区并释放内存必须在没有任务使用此缓冲区的情况下执行

xMessageBufferCreate

功能: 创建一个消息缓冲区,用于传输带长度前缀的消息。

原型

c
MessageBufferHandle_t xMessageBufferCreate(size_t xBufferSizeBytes);

参数

  • xBufferSizeBytes:缓冲区的总大小(字节)。

返回值

成功:返回创建的消息缓冲区句柄。 失败:返回 NULL。

xMessageBufferSend

功能: 向消息缓冲区中写入数据。

原型

c
size_t xMessageBufferSend(MessageBufferHandle_t xMessageBuffer, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait);

参数

  • xMessageBuffer:目标消息缓冲区的句柄。
  • pvTxData:指向要写入数据的指针。
  • xDataLengthBytes:要写入的数据长度(字节)。
  • xTicksToWait:写入时的最大阻塞时间(Tick 数,0 表示立即返回)。

返回值

  • 成功:实际写入的字节数。
  • 失败:0 表示写入失败(缓冲区空间不足)。

xMessageBufferReceive

功能: 从消息缓冲区中读取数据。 原型:

c
size_t xMessageBufferReceive(MessageBufferHandle_t xMessageBuffer, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait);

参数

  • xMessageBuffer:目标消息缓冲区的句柄。
  • pvRxData:指向接收数据的缓冲区指针。
  • xBufferLengthBytes:接收缓冲区的大小(字节)。
  • xTicksToWait:读取时的最大阻塞时间(Tick 数,0 表示立即返回)。

返回值

  • 成功:实际读取的字节数。
  • 失败:0 表示读取失败(缓冲区为空)。

注意,当接收缓冲区小于消息长度时,无法接收到消息。

xMessageBufferSpaceAvailable

功能: 查询消息缓冲区中剩余的可用空间。 原型

c
size_t xMessageBufferSpaceAvailable(MessageBufferHandle_t xMessageBuffer);

参数

  • xMessageBuffer:目标消息缓冲区的句柄。 返回值:剩余可用空间的字节数。

xMessageBufferReset

功能: 重置消息缓冲区,清空所有数据。

原型

c

BaseType_t xMessageBufferReset(MessageBufferHandle_t xMessageBuffer);

参数:- xMessageBuffer:目标消息缓冲区的句柄。 返回值

  • pdPASS:重置成功。
  • pdFAIL:重置失败(缓冲区正在被使用)。

vMessageBufferDelete

功能: 删除一个消息缓冲区并释放其内存。

原型

c
void vMessageBufferDelete(MessageBufferHandle_t xMessageBuffer);

参数:xMessageBuffer:目标消息缓冲区的句柄。

返回值:无返回值。

4.6.2 示例代码

c
// 任务通知值按位判断-代替队列邮箱或者事件组
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/message_buffer.h"

static const char *TAG = "main";

MessageBufferHandle_t xMessageBuffer;

void task1(void *pvParameters)
{
    ESP_LOGI(TAG, "-------------------------------");
    ESP_LOGI(TAG, "task1启动!");

    char tx_buf[50];
    for (int i = 0; i < 3; i++)
    {
        sprintf(tx_buf, "Hello World!-%d", i);
        // 写入数据到流缓冲区
        int send_len = xMessageBufferSend(xMessageBuffer, (void *)tx_buf, sizeof(tx_buf), portMAX_DELAY);
        ESP_LOGI(TAG, "task1发送数据长度: %d", send_len);
    }

    vTaskDelete(NULL);
}

void task2(void *pvParameters)
{
    ESP_LOGI(TAG, "-------------------------------");
    ESP_LOGI(TAG, "task2启动!");
    char rx_buf[200];
    vTaskDelay(pdMS_TO_TICKS(3000));
    while (1)
    {
        memset(rx_buf, 0, sizeof(rx_buf));
        // 读取数据
        int recv_len = xMessageBufferReceive(xMessageBuffer, (void *)rx_buf, sizeof(rx_buf), portMAX_DELAY);
        if (recv_len > 0)
        {
            rx_buf[recv_len] = 0;
            ESP_LOGI(TAG, "task2接收数据: %s ", rx_buf);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
}


void app_main(void)
{
    xMessageBuffer = xMessageBufferCreate(1024); // 创建消息缓冲区
    if (xMessageBuffer != NULL)
    {
        ESP_LOGI(TAG, "创建消息缓冲区成功");
        xTaskCreate(task1, "task1", 1024 * 4, NULL, 1, NULL);
        xTaskCreate(task2, "task2", 1024 * 4, NULL, 1, NULL);
    }
    else
    {
        ESP_LOGI(TAG, "创建流缓冲区失败");
    }
}