【CI130x 离在线】FreeRTOS的流缓冲(StreamBuffer)

news/2025/10/27 0:05:02/文章来源:https://www.cnblogs.com/FBsharl/p/19167643

FreeRTOS StreamBuffer 详解

概述

StreamBuffer(流缓冲区)是 FreeRTOS 提供的一种轻量级数据流传输机制,用于在任务间或中断与任务间高效传输字节流数据。

基本特性

1. 数据结构

  • 字节流存储: 以 FIFO 方式存储字节数据

  • 单读写者: 设计为单生产者单消费者模式

  • 无结构数据: 不区分消息边界,纯字节流

2. 与 Message Buffer 的区别

c
 
 
// Stream Buffer - 纯字节流
+---+---+---+---+---+---+
| A | B | C | D | E | F |  // 连续字节流
+---+---+---+---+---+---+// Message Buffer - 带长度信息
+-----+---+---+---+-----+---+---+---+
| Len | A | B | C | Len | X | Y | Z |  // 离散消息
+-----+---+---+---+-----+---+---+---+

核心 API 函数

创建函数

c
 
 
// 创建流缓冲区
StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes );

发送函数

c
 
 
// 发送数据到流缓冲区
size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,const void *pvTxData,size_t xDataLengthBytes,TickType_t xTicksToWait );

接收函数

c
 
 
// 从流缓冲区接收数据
size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,void *pvRxData,size_t xBufferLengthBytes,TickType_t xTicksToWait );

中断安全版本

c
 
 
// 中断中发送
size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer,const void *pvTxData,size_t xDataLengthBytes,BaseType_t *pxHigherPriorityTaskWoken );// 中断中接收
size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer,void *pvRxData,size_t xBufferLengthBytes,BaseType_t *pxHigherPriorityTaskWoken );

关键概念

1. 触发级别 (Trigger Level)

  • 定义何时唤醒等待的任务

  • 当缓冲区中的数据量 ≥ 触发级别时,唤醒等待接收的任务

  • 创建时设置,可影响性能和行为

2. 阻塞机制

  • 当缓冲区空时,接收任务可阻塞等待

  • 当缓冲区满时,发送任务可阻塞等待

  • 支持超时设置

使用示例

基本使用

c
 
 
// 创建流缓冲区(100字节,触发级别10字节)
StreamBufferHandle_t xStreamBuffer;
xStreamBuffer = xStreamBufferCreate(100, 10);// 任务1:发送数据
void vSenderTask(void *pvParameters)
{const char *pcData = "Hello Stream Buffer";size_t xBytesSent;xBytesSent = xStreamBufferSend(xStreamBuffer, pcData, strlen(pcData), portMAX_DELAY);
}// 任务2:接收数据
void vReceiverTask(void *pvParameters)
{char cBuffer[50];size_t xBytesReceived;xBytesReceived = xStreamBufferReceive(xStreamBuffer, cBuffer, sizeof(cBuffer), portMAX_DELAY);cBuffer[xBytesReceived] = '\0'; // 添加字符串结束符
}

中断使用

c
 
 
// 中断服务程序
void vUART_RX_ISR(void)
{BaseType_t xHigherPriorityTaskWoken = pdFALSE;char cReceivedByte;// 从UART读取字节cReceivedByte = UART_ReadByte();// 发送到流缓冲区xStreamBufferSendFromISR(xStreamBuffer, &cReceivedByte, 1, &xHigherPriorityTaskWoken);// 如果需要,进行任务切换portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

优势特点

1. 轻量高效

  • 比队列更节省内存

  • 避免数据拷贝,直接操作缓冲区

2. 灵活的数据处理

  • 支持任意长度数据读写

  • 可部分读取/写入

3. 集成通知机制

  • 使用任务通知,无需额外同步对象

  • 减少资源消耗

适用场景

  1. 串口数据传输

  2. 网络数据包处理

  3. 文件流操作

  4. 音频/视频流处理

  5. 任何需要流式数据传输的场景

注意事项

  1. 单生产者单消费者: 不适合多对多通信

  2. 数据边界: 需要应用层处理消息边界

  3. 内存管理: 需要合理设置缓冲区大小

  4. 阻塞时间: 根据系统需求设置合适的超时时间

Stream Buffer 是 FreeRTOS 中处理流式数据的理想选择,特别适合需要高效字节流传输的嵌入式应用场景。

 
 
 

FreeRTOS 消息队列 vs 消息缓冲区 (Message Buffer)

架构设计差异

消息队列 (Queue)

c
 
 
// 传统消息队列 - 固定大小元素
typedef struct {uint8_t data[20];uint32_t timestamp;
} Message_t;QueueHandle_t xQueue = xQueueCreate(10, sizeof(Message_t));

消息缓冲区 (Message Buffer)

c
 
 
// 消息缓冲区 - 变长消息
MessageBufferHandle_t xMessageBuffer = xMessageBufferCreate(100);

核心区别对比

 
 
特性 消息队列 (Queue) 消息缓冲区 (Message Buffer)
数据单元 固定大小元素 变长消息
内存使用 预分配固定槽位 动态使用缓冲区空间
数据拷贝 整个元素拷贝 只拷贝有效数据
消息边界 自动维护 自动维护
API复杂度 相对简单 更灵活

具体差异分析

1. 数据存储方式

消息队列

c
 
 
// 队列内存布局 - 固定槽位
+---------+---------+---------+
| Slot1   | Slot2   | Slot3   |  // 每个槽位大小固定
+---------+---------+---------+
// 即使消息很小,也占用整个槽位

消息缓冲区

c
 
 
// 消息缓冲区内存布局 - 紧凑存储
+----+---------+----+--------------+
| L1 | Msg1    | L2 | LongerMsg2   |  // 只存储实际数据+长度
+----+---------+----+--------------+
// 高效利用内存,支持变长消息

2. API 使用对比

消息队列示例

c
 
 
// 发送固定大小数据
typedef struct {char data[50];uint8_t priority;
} QueueMessage_t;QueueMessage_t xMessage;
strcpy(xMessage.data, "Hello");
xMessage.priority = 1;
xQueueSend(xQueue, &xMessage, portMAX_DELAY);  // 发送整个结构体// 接收
QueueMessage_t xReceivedMessage;
xQueueReceive(xQueue, &xReceivedMessage, portMAX_DELAY);

消息缓冲区示例

c
 
 
// 发送变长数据
const char *pcMessage = "Hello Message Buffer";
xMessageBufferSend(xMessageBuffer, pcMessage, strlen(pcMessage), portMAX_DELAY);// 接收 - 自动处理消息边界
char pcBuffer[50];
size_t xReceivedBytes = xMessageBufferReceive(xMessageBuffer, pcBuffer, sizeof(pcBuffer), portMAX_DELAY);
pcBuffer[xReceivedBytes] = '\0';

3. 性能特点

消息队列优势

  • 确定性内存使用

  • 更快的操作(固定大小)

  • 适合小尺寸固定数据

消息缓冲区优势

  • 内存使用更高效

  • 支持变长数据

  • 适合大数据块传输

实际应用场景选择

使用消息队列的场景

c
 
 
// 1. 固定大小的控制命令
typedef struct {Command_t eCommand;uint32_t ulParameter;
} ControlMessage_t;// 2. 传感器数据(固定格式)
typedef struct {float fTemperature;float fHumidity;uint32_t ulTimestamp;
} SensorData_t;// 3. 状态机事件
typedef struct {State_t eNewState;Event_t eTriggerEvent;
} StateEvent_t;

使用消息缓冲区的场景

c
 
 
// 1. 变长字符串消息
xMessageBufferSend(xMsgBuffer, "Error: File not found", 21, 0);// 2. 数据包传输(如网络包)
xMessageBufferSend(xNetworkBuffer, pucPacketData, usPacketLength, 0);// 3. 文件数据块
xMessageBufferSend(xFileBuffer, pucFileChunk, xChunkSize, 0);// 4. 调试信息
char pcDebugMsg[100];
int len = snprintf(pcDebugMsg, sizeof(pcDebugMsg), "Task %s ran for %d ms", pcTaskName, xRunTime);
xMessageBufferSend(xDebugBuffer, pcDebugMsg, len, 0);

底层实现差异

消息队列实现

  • 基于数组的环形缓冲区

  • 每个元素大小固定

  • 使用互斥机制保护

消息缓冲区实现

  • 基于流缓冲区 + 长度前缀

  • 自动添加/解析消息长度

  • 使用相同的通知机制

c
 
 
// 消息缓冲区本质上是在流缓冲区基础上添加长度信息
+---------+-----------------+
| 长度字段 | 实际消息数据     |
+---------+-----------------+
| 4字节   | 变长数据         |
+---------+-----------------+

总结建议

选择消息队列当

  • 数据格式固定且大小已知

  • 需要最高性能

  • 消息尺寸较小

  • 系统资源充足

选择消息缓冲区当

  • 消息长度变化很大

  • 需要高效内存使用

  • 传输大数据块

  • 处理字符串或原始数据流

在实际项目中,经常根据具体的数据特性混合使用两者,以达到最佳的性能和资源利用率。

 
 
 
 
 
 
 
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/947151.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RT-Thread Nano源码浅析

了解RT-Thread Nano源码构成。一、了解源码 从官方下载到RT-Thread Nano源码目录如下图所示1、bsp文件夹 bsp文件夹里面存放的是板级支持包(board support package),用于存放RT-Thread为各种半导体厂商的评估板写好…

《从 “被动听” 到 “主动学”:课堂听讲助力大学生思维成长》

阅读完三篇文章,我首先想到的就是大一到大二的学习状态变化,突然发现上学期的我可能是刚高考完的原因,脑海中还深深印刻着高中生上课必须要认真听讲的烙印。虽然上课给带手机,但是一看到老师走进教室,总是下意识地…

用AI批量生成产品视频!Python+Google Veo 3.1 API让电商转化率飙升

今天跟大家分享一个超实用的电商运营技巧:如何用Python和Google Veo 3.1 AI,把枯燥的产品图片批量变成生动的营销视频。用户原创内容(UGC)现在特别火,对销售的拉动效果非常明显,有了这个工具,你也能轻松制作大量…

关于SQLite - 世界上装机量最多的数据库

关于SQLite - 世界上装机量最多的数据库? 使用C语言开发,使得它小巧精致而高效,直接采用偏底层的语言,使用文件的逻辑,实现SQL数据库的逻辑; 使用方:包括但不限于 Python、Java、C# 等; 无服务器的,…

模拟IIC与硬件IIIC哪个更常用?

在实际项目中,两种方式都被广泛使用,但模拟IIC(软件IIC)的应用场景更多、更普遍。 下面我们来详细解释一下两者的区别、优缺点和适用场景。 硬件IIC 硬件IIC是指由微控制器内部的专用IIC外设电路来实现的。你只需要…

251019 NOIP 模拟赛 T2 | dp 及其优化、调整法最优解性质、数形结合

OJ 传送门 原题: QOJ 5500 题意 有 \(n\) 个屋子排成一列,每个屋子里一个人,每个屋子可以开酒吧。 每个人会去自己左右两侧最近的(分别)酒吧消费。 一个方案的价值为 \(\sum _ {酒吧} 来这个酒吧的人数 \times p_i…

常见问题解决 --- 未识别函数

常见问题解决 --- 未识别函数

小作业 14(2018 北京高考文科)

已知椭圆 \(M:\dfrac{x^2}{3}+y^2=1\),斜率为 \(k\) 的直线 \(l\) 与椭圆 \(M\) 有两个不同的交点 \(A\),\(B\)。设 \(P(-2,0)\),直线 \(PA\) 与椭圆 \(M\) 的另一个交点为 \(C\),直线 \(PB\) 与椭圆 \(M\) 的另一…

第六章习题

下面的机器数都以16进制表示w func1(w) func2(w)机器数 ; 值 机器数 ; 值 机器数 ; 值0000007F;127 0000007F;127 0000007F;12700000080;128 00000080;128 FFFFFF80;-128000000FF;255 000000FF;255 FFFFFFFF;-10000010…

速通 花卉鉴赏 短文

真鲜花灵感来自 P&KU3(上) 第二日《时空花园》一题。 顺带一提,我在查阅资料的时候并没有找到百度百科花钟对应 list 的一个 reference,最后在 wikipedia 里有 reference 的里面选了一些写,如果有知道的朋友可…

Agent常见模式 - 智慧园区

我希望用最直白的方式,让大家理解这四种常见的模式,并且真实商业落地和你想的可能完全相反。 模式讲解 第一种,工作流LLM 这种方式最直观,就是在你原有的工作流上插入Agent,替换掉原来代码做不了,必须人做的事请…

react-router7.9.4使用

安装 新版路由简化了安装,只需要以下命令即可 npm i react-router创建路由文件 在项目根目录下创建路由文件 src\router\index.ts import { lazy } from react import Root from ../components/root import { createH…

AI元人文:从战略能力到价值对话的实现框架

AI元人文:从战略能力到价值对话的实现框架 核心洞见:无需从零创造,“价值对话AI”可通过重构已验证的战略型AI技术基石实现。关键在于对其能力进行目标升华与伦理重塑。 一、四项核心重构架构重构:从利益博弈到价值…

Loneliness

Spending years with a group of people who are fundamentally dissimilar is loneliness. Feeling the passage of time is also so lonely. Memories and regrets can even be so lonely. The speed of time never …

Java流程控制——用户交互Scanner

Java流程控制——用户交互Scanner用户交互Scanner 使用Scanner类实现获取用户输入 Scanner s = new Scanner(System.in);可通过Scanner类的next()与nextLinevitable()方法获取输入的字符串, 在读取前一般用hasNext()与…

概率论测试

后续会上传批改版本

2025.10.26总结

今天做了23年5月份上半场的75道选择,怎么说,没及格,无论是知识点的广度上还是深度上都差的很远,以目前的水平考试,能不能通过完全靠运气。 下午题看了几眼,一共五六道比较固定的答题,熟悉题型和做题方法后应该就…

Python---开发桌面应用程序

早就知道可以使用Python的PyQT构建桌面应用程序了,但是没有具体实际过,以前都是使用前端来开发桌面应用程序的,安装依赖麻烦的要死,打包的时候还各种繁琐。 没想到使用Python来开发桌面应用程序如此的简单。 直接撸…

Python实现验证码识别的完整流程解析

验证码(CAPTCHA)是网络安全防护中最常见的技术之一,主要用于防止恶意程序批量请求服务。然而,随着深度学习和图像处理技术的发展,验证码的自动识别已成为人工智能领域的一个重要应用场景。本文将通过 Python 展示…