Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread学习营
内核学习营
ringbuffer_环形缓冲区
听说你不知道 RT-Thread 有个 ringbuffer
发布于 2020-07-13 00:40:30 浏览:3949
订阅该版
[tocm] 在嵌入式开发中,我们经常需要用到 FIFO 数据结构来存储数据,比如任务间的通信、串口数据收发等场合。很多小伙伴不知道 RT-Thread 为我们提供了一个 ringbuffer 数据结构,代码位于: - components/drivers/src/ringbuffer.c - components/drivers/include/ipc/ringbuffer.h RingBuffer 其实就是先进先出(FIFO)的循环缓冲区。把一段线性的存储空间当作一个环形的存储空间使用,可以提高存储空间的利用率。 ![在这里插入图片描述](https://oss-club.rt-thread.org/uploads/20220714/c6cc6a1cc5ac63a29f5235ad78e5d632c6a207e2.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1Y2t5ZGFyY3k=,size_16,color_FFFFFF,t_70#pic_center) ### 数据结构 RT-Thread 定义了 rt_ringbuffer 结构体,包括四组成员:缓冲区指针 buffer_ptr、缓冲区大小 buffer_size、读指针、写指针。 ```c struct rt_ringbuffer { rt_uint8_t *buffer_ptr; rt_uint16_t read_mirror : 1; rt_uint16_t read_index : 15; rt_uint16_t write_mirror : 1; rt_uint16_t write_index : 15; rt_int16_t buffer_size; }; ``` 对于读、写指针,rt_ringbuffer 结构体使用位域来定义 read 和 write 的索引值和镜像位。更具体来说,使用 MSB(最高有效位)作为 read_index 和 write_index 变量的镜像位。通过这种方式,为缓冲区添加了虚拟镜像,用于指示 read 和 write 指针指向的是普通缓冲区还是镜像缓冲区。 - 如果 write_index 和 read_index 相等,但在不同镜像,说明缓冲区满了; - 如果 write_index 和 read_index 相等,但在相同镜像,说明缓冲区空了。 为了让大家更好地理解,我给大家画了个图: ![在这里插入图片描述](https://oss-club.rt-thread.org/uploads/20220714/b817a8b160a1e56000d2a038f0a1ea351f10e6b8.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x1Y2t5ZGFyY3k=,size_16,color_FFFFFF,t_70#pic_center) ### 接口函数 #### 初始化与重置 ```c void rt_ringbuffer_init(struct rt_ringbuffer *rb, rt_uint8_t *pool, rt_int16_t size); void rt_ringbuffer_reset(struct rt_ringbuffer *rb); ``` 这两个函数适用于以静态方式初始化或重置 ringbuffer,需要事先准备好 ringbuffer 对象和一段内存空间。 #### 创建和销毁 ```c struct rt_ringbuffer* rt_ringbuffer_create(rt_uint16_t length); void rt_ringbuffer_destroy(struct rt_ringbuffer *rb); ``` 这两个函数适用于以动态方式创建和销毁 ringbuffer,将在堆空间申请相关资源,并返回一个 ringbuffer 指针。 #### 写入数据 ```c rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb, const rt_uint8_t *ptr, rt_uint16_t length); rt_size_t rt_ringbuffer_put_force(struct rt_ringbuffer *rb, const rt_uint8_t *ptr, rt_uint16_t length); rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch); rt_size_t rt_ringbuffer_putchar_force(struct rt_ringbuffer *rb, const rt_uint8_t ch); ``` 往 ringbuffer 写入数据可以使用这组函数,其中 `_put` 为写入一串字符,`_putchar` 为写入一个字符,带 `_force` 的函数则表示如果缓冲区满了,将直接用新数据覆盖旧数据(谨慎使用)。 #### 读出数据 ```c rt_size_t rt_ringbuffer_get(struct rt_ringbuffer *rb, rt_uint8_t *ptr, rt_uint16_t length); rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer *rb, rt_uint8_t *ch); ``` 从 ringbuffer 读出数据可以使用这组函数,其中 `_get` 为读出一串字符,`_getchar` 为读出一个字符。 #### 获取长度 读写操作前可以先判断是否有数据可读或者有位置可写,ringbuffer 提供了三个接口获取长度,包括获取 ringbuffer 的总长度、数据长度、空闲长度。 **获取缓冲区数据长度** ```c rt_size_t rt_ringbuffer_data_len(struct rt_ringbuffer *rb); ``` **获取缓冲区总长度** ```c rt_uint16_t rt_ringbuffer_get_size(struct rt_ringbuffer *rb); ``` **获取缓冲区空闲长度** ```c #define rt_ringbuffer_space_len(rb) ((rb)->buffer_size - rt_ringbuffer_data_len(rb)) ``` ### 应用示例 下面通过一个简单的示例,来看看在程序中该如何使用 ringbuffer。首先创建一个 ringbuffer 对象,然后 Producer 线程往 ringbuffer 写入数据,Consumer 线程从 ringbuffer 读出数据。这是一个典型生产者-消费者模型。 ```c #include
#include
#include
#define RING_BUFFER_LEN 8 static struct rt_ringbuffer *rb; static char *str = "Hello, World"; static void consumer_thread_entry(void *arg) { char ch; while (1) { if (1 == rt_ringbuffer_getchar(rb, &ch)) { rt_kprintf("[Consumer] <- %c\n", ch); } rt_thread_mdelay(500); } } static void ringbuffer_sample(int argc, char** argv) { rt_thread_t tid; rt_uint16_t i = 0; rb = rt_ringbuffer_create(RING_BUFFER_LEN); if (rb == RT_NULL) { rt_kprintf("Can't create ringbffer"); return; } tid = rt_thread_create("consumer", consumer_thread_entry, RT_NULL, 1024, RT_THREAD_PRIORITY_MAX/3, 20); if (tid == RT_NULL) { rt_ringbuffer_destroy(rb); } rt_thread_startup(tid); while (str[i] != '\0') { rt_kprintf("[Producer] -> %c\n", str[i]); rt_ringbuffer_putchar(rb, str[i++]); rt_thread_mdelay(500); } rt_thread_delete(tid); rt_ringbuffer_destroy(rb); } #ifdef RT_USING_FINSH MSH_CMD_EXPORT(ringbuffer_sample, Start a producer and a consumer with a ringbuffer); #endif ``` 运行结果: ![在这里插入图片描述](https://oss-club.rt-thread.org/uploads/20220714/d332df8eb842a18bd7d2a44f8f5a75f5323df64f.png#pic_center)
6
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
阿基米东
https://github.com/luhuadong
文章
7
回答
52
被采纳
3
关注TA
发私信
相关文章
1
stm32f407+lan8720 lwip2.0 作业提交
2
玩溜GD32303E-EVAL BSP系列(五)----设备连接网络
3
onenet应用连不上云端
4
【文件系统】晴天文件匹配
5
rtt semc sdram 基于操作系统怎么初始化驱动设备
6
RT-Thread移植笔记
7
RT-Thread内核移植+LoIIs+STM32F103C8+StdLib
8
EVN编译报错,求助大神
9
【内核和外设学习营】十里 简单LED闪亮测试
10
【内核和外设学习营】 十里 串口指令控制RGB灯点亮的颜色
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
8
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部