完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
一,什么是 ringbuff
ringbuff: 翻译成中文就是环形缓冲区。 网上关于 ringbuff 的介绍已经非常多了,我也分享一下我对 ringbuff 的认识。ringbuff 可以理解为学校操场上的跑道,ringbuff 初始化就是新建了一个 400 的环形操场跑道,跑道上有 2 名同学 (write 和 read),每次写入一个数据,write 往前跑一步,每次读一个数据,read 往前跑一步,read永远不可以超过 write。刚开始起点都是 0 ,如果 write 写的速度慢了,被 read 追上了,那么 ringbuff 缓存数据就为空。 如果 write 写的速度快了,read 读慢了,被 write 跑了一圈又追上了,那么 ringbuff 缓存区就是满。 二. 怎么使用 ringbuff 在知道怎么使用 ringbuff 前先要看一下,ringbuff 的结构体: 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; }; *buffer_ptr : 缓冲区指针 read_mirror :读取镜像。可以理解为一张白纸,读完了就翻过一页再读 read_index :读数据的位置 write_mirror : 写入镜像。可以理解为一张白纸,写完了就翻过一页再写 write_index : 写入数据的位置 buffer_size : 缓存区大小 这里有一个 C 语言 的小知识点:位域 。 这里使用了一个 u16 存储了 read_mirror 和 read_index , 所以 index 最大只能到 32767 。这个大小满足绝大数的场景。 ##### 1 . 初始化 ringbuff void rt_ringbuffer_init(struct rt_ringbuffer *rb, rt_uint8_t *pool, rt_int16_t size) *rb : 环形缓冲区句柄 *pool : 缓存区指针 size : 缓存区大小 ##### 2 . 往 ringbuff 写入数据 rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb, const rt_uint8_t *ptr, rt_uint16_t length) *rb : 环形缓冲区句柄 *ptr : 写入数据的指针 length : 写入数据的长度 ##### 3 . 强制往 ringbuff 写入数据 rt_size_t rt_ringbuffer_put_force(struct rt_ringbuffer *rb, const rt_uint8_t *ptr, rt_uint16_t length) *rb : 环形缓冲区句柄 *ptr : 写入数据的指针 length : 写入数据的长度 强制写数据使用场景:ringbuff 缓存区中的数据已经满了,但是还要写入数据。这种情况将会导致前面写入的数据被覆盖,导致数据丢失,请谨慎使用。 ##### 4. 从 ringbuff 读取数据 rt_size_t rt_ringbuffer_get(struct rt_ringbuffer *rb, rt_uint8_t *ptr, rt_uint16_t length) *rb : 环形缓冲区句柄 *ptr : 读数据的存放的位置 length : 读取的长度 ##### 5. 往 ringbuff 写入一个字节的数据 rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch) *rb : 环形缓冲区句柄 ch : 将要写入的数据 ##### 6. 强制往 ringbuff 写入一个字节的数据 rt_size_t rt_ringbuffer_putchar_force(struct rt_ringbuffer *rb, const rt_uint8_t ch) *rb : 环形缓冲区句柄 ch : 将要写入的数据 注意强制写入将会导致数据被覆盖。 ##### 7. 从 ringbuff 读取一个字节的数据 rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer *rb, rt_uint8_t *ch) *rb : 环形缓冲区句柄 *ch : 存储读取的一个字节的变量 ##### 8. 获取 ringbuff 中已经使用的控件 rt_size_t rt_ringbuffer_data_len(struct rt_ringbuffer *rb) *rb : 环形缓冲区句柄 ##### 9. 复位 ringbuff void rt_ringbuffer_reset(struct rt_ringbuffer *rb) *rb : 环形缓冲区句柄 ##### 10. 创建 ringbuff struct rt_ringbuffer* rt_ringbuffer_create(rt_uint16_t size) size : 唤醒缓冲区的大小 rt_ringbuffer_create 在函数内部实现了 pool 然后调用了 rt_ringbuffer_init ##### 11. 销毁 ringbuff void rt_ringbuffer_destroy(struct rt_ringbuffer *rb) 这个只能销毁调用 rt_ringbuffer_create 创建的 ring_buff ##### 12 . 获取 ringbuff 的大小 rt_inline rt_uint16_t rt_ringbuffer_get_size(struct rt_ringbuffer *rb) 三. 原理分析 ringbuff 1. 初始化 ringbuff void rt_ringbuffer_init(struct rt_ringbuffer *rb, rt_uint8_t *pool, rt_int16_t size) { /* initialize read and write index */ rb->read_mirror = rb->read_index = 0;// 初始化时 读下标设置为 0 rb->write_mirror = rb->write_index = 0;// 初始化时 写下标设置为 0 /* set buffer pool and size */ rb->buffer_ptr = pool;// 设置 ring_buff 的缓存区地址 rb->buffer_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);// 保证 size 的大小是对齐的 } 2. 往 ringbuff 写入数据 rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb, const rt_uint8_t *ptr, rt_uint16_t length) { rt_uint16_t size; /* whether has enough space */ size = rt_ringbuffer_space_len(rb);// 获取 ring_buff 中可用空间的大小 /* no space */ if (size == 0) return 0; // 如果空间不够 直接返回 /* drop some data */ if (size < length) // 如果缓存区的控件不够保存这一次数据, 则把能够写入的这一部分数据写进去 length = size; if (rb->buffer_size - rb->write_index > length) {// 这里判断的是数据能够在第一个镜像写完,就直接写入当前的镜像,可以理解为在白纸上写数据,这个时候不需要翻页 /* read_index - write_index = empty space */ memcpy(&rb->buffer_ptr[rb->write_index], ptr, length); /* this should not cause overflow because there is enough space for * length of data in current mirror */ rb->write_index += length; return length; // 返回写入数据的长度 } memcpy(&rb->buffer_ptr[rb->write_index],// 把能够写入第一个镜像的数据线写入第一个镜像的缓存区 &ptr[0], rb->buffer_size - rb->write_index); memcpy(&rb->buffer_ptr[0],// 前一个镜像写满了,就要从头写了,这里就体现了循环 &ptr[rb->buffer_size - rb->write_index], length - (rb->buffer_size - rb->write_index)); /* we are going into the other side of the mirror */ rb->write_mirror = ~rb->write_mirror;// 完成 镜像 0 和 1 的翻转 rb->write_index = length - (rb->buffer_size - rb->write_index);// 重新设置写数据的小标 return length;// 返回写入数据的长度 } 3. 强制往 ringbuff 写入数据 rt_size_t rt_ringbuffer_put_force(struct rt_ringbuffer *rb, const rt_uint8_t *ptr, rt_uint16_t length) { rt_uint16_t space_length; space_length = rt_ringbuffer_space_len(rb); // 获取可用缓存区大小 if (length > rb->buffer_size) // 如果长度超过了当前 ring_buff 的最大值 { ptr = &ptr[length - rb->buffer_size];// 获取超出部分的地址 length = rb->buffer_size;// lenght 设置为 ring_buff 的大小 } if (rb->buffer_size - rb->write_index > length)// 如果足够存储这次将要写入的数据 { /* read_index - write_index = empty space */ memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);// 拷贝数据 /* this should not cause overflow because there is enough space for * length of data in current mirror */ rb->write_index += length;// 移动 write index if (length > space_length) // 如果长度大于可用缓存区大小 rb->read_index = rb->write_index;// 因为数据已经被覆盖掉了,所以 read 的 index 必须往前移动 return length; // 返回写入数据的长度 } memcpy(&rb->buffer_ptr[rb->write_index],// 拷贝数据在当前镜像 &ptr[0], rb->buffer_size - rb->write_index); memcpy(&rb->buffer_ptr[0],// 拷贝数据到下一个镜像 &ptr[rb->buffer_size - rb->write_index], length - (rb->buffer_size - rb->write_index)); /* we are going into the other side of the mirror */ rb->write_mirror = ~rb->write_mirror;// 镜像翻转 rb->write_index = length - (rb->buffer_size - rb->write_index); // 写完数据后移动 write index if (length > space_length) // 如果长度大于可用空间 { rb->read_mirror = ~rb->read_mirror; // 翻转读镜像 rb->read_index = rb->write_index; // 移动 read_index 到 write_index } return length; } 4. 从 ringbuff 获取数据 rt_size_t rt_ringbuffer_get(struct rt_ringbuffer *rb, rt_uint8_t *ptr, rt_uint16_t length) { rt_size_t size; /* whether has enough data */ size = rt_ringbuffer_data_len(rb);// 检查当前 ringbuff 中是否有足够的空间 /* no data */ if (size == 0) return 0; // 如果没有数据则直接返回 /* less data */ if (size < length) length = size;// 如果已经存在的数据小于想要获取的长度,那么就会把长度设置为 size if (rb->buffer_size - rb->read_index > length)// 如果在一个镜像中就能获取到所有的数据 { /* copy all of data */ memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);// 拷贝数据 /* this should not cause overflow because there is enough space for * length of data in current mirror */ rb->read_index += length; // 移动读下标 return length;// 返回读取到的长度 } /* 能执行到这里说明,一个镜像内无法读取到所有的数据 */ memcpy(&ptr[0], &rb->buffer_ptr[rb->read_index], rb->buffer_size - rb->read_index);// 拷贝当前镜像的数据 memcpy(&ptr[rb->buffer_size - rb->read_index], &rb->buffer_ptr[0], length - (rb->buffer_size - rb->read_index));// 拷贝剩余的数据 /* we are going into the other side of the mirror */ rb->read_mirror = ~rb->read_mirror;// 翻转镜像 rb->read_index = length - (rb->buffer_size - rb->read_index);// 移动读下标 return length; } 5. 往 ringbuff 中写入一个字符 rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch) { /* whether has enough space */ if (!rt_ringbuffer_space_len(rb)) // 没有足够的空间就直接返回了 return 0; rb->buffer_ptr[rb->write_index] = ch;// 把这个字符写入到缓冲区的指定位置 /* flip mirror */ if (rb->write_index == rb->buffer_size-1)// 检查写入这个字符后,当前镜像是否写满 { rb->write_mirror = ~rb->write_mirror;// 翻转镜像 rb->write_index = 0;// 设置下标为0 } else { rb->write_index++; // 下标加1 } return 1; // 写入一个字符,返回 1 } 6. 往 ringbuff 强制写入一个字符 rt_size_t rt_ringbuffer_putchar_force(struct rt_ringbuffer *rb, const rt_uint8_t ch) { enum rt_ringbuffer_state old_state; old_state = rt_ringbuffer_status(rb);// 获取状态 rb->buffer_ptr[rb->write_index] = ch;// 写入数据 /* flip mirror */ if (rb->write_index == rb->buffer_size-1) // 检查当前镜像是不是满了 { rb->write_mirror = ~rb->write_mirror; // 翻转写镜像 rb->write_index = 0;// 翻转之后设置下标为 0 if (old_state == RT_RINGBUFFER_FULL) // 如果 ringbuff 的状态是满 { rb->read_mirror = ~rb->read_mirror; // 翻转读镜像 rb->read_index = rb->write_index; // 设置读下标和写下标一致 } } else { rb->write_index++; // 写下标加1 if (old_state == RT_RINGBUFFER_FULL) rb->read_index = rb->write_index;// 如果满,设置读下标等于写下标 } return 1; // 写入一个字符,返回1 } 7. 从 ringbuff 获取一个字符 rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer *rb, rt_uint8_t *ch) { /* ringbuffer is empty */ if (!rt_ringbuffer_data_len(rb)) // 检查 ringbuff 是否为空 return 0; /* put character */ *ch = rb->buffer_ptr[rb->read_index];// 获取当前读下标的数据 if (rb->read_index == rb->buffer_size-1)// 如果当前镜像满了 { rb->read_mirror = ~rb->read_mirror;// 翻转镜像 rb->read_index = 0; // 设置读数据的下标为0 } else { rb->read_index++; // 下标加1 } return 1;// 读取一个字节,返回1 } 8. 获取 ringbuff 中数据的长度 rt_size_t rt_ringbuffer_data_len(struct rt_ringbuffer *rb) { switch (rt_ringbuffer_status(rb)) // 获取 ringbuff 的状态 { case RT_RINGBUFFER_EMPTY: return 0; // 空就返回 0 case RT_RINGBUFFER_FULL: return rb->buffer_size; // 满就返回缓冲区的大小 case RT_RINGBUFFER_HALFFULL:// 半满 default: if (rb->write_index > rb->read_index) // 如果在同一镜像 return rb->write_index - rb->read_index;// 返回下标差 else return rb->buffer_size - (rb->read_index - rb->write_index);// 如果不在同一镜像,通过计算获取数据的长度 }; } 9. 重置 ringbuff void rt_ringbuffer_reset(struct rt_ringbuffer *rb) { rb->read_mirror = 0; rb->read_index = 0; rb->write_mirror = 0; rb->write_index = 0; } // 所有的值都设置为 0 10. 创建一个 ringbuff struct rt_ringbuffer* rt_ringbuffer_create(rt_uint16_t size) { struct rt_ringbuffer *rb; rt_uint8_t *pool; size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);// 大小做字节对齐 rb = (struct rt_ringbuffer *)rt_malloc(sizeof(struct rt_ringbuffer));// 申请内存 if (rb == RT_NULL) goto exit; pool = (rt_uint8_t *)rt_malloc(size);// 申请数据缓冲区内存 if (pool == RT_NULL) { rt_free(rb); rb = RT_NULL; goto exit; } rt_ringbuffer_init(rb, pool, size);// 初始化 ringff exit: return rb; } 这个 API 简化了使用 ringbuff 所需要的 形参。本质还是调用了 rt_ringbuffer_init 11. 摧毁 ringbuff void rt_ringbuffer_destroy(struct rt_ringbuffer *rb) { rt_free(rb->buffer_ptr); rt_free(rb);// 释放申请的内存 } 四. 总结 ringbuff 读写数据互不干扰,比普通的 buff使用更加灵活。 ringbuff 的使用场景非常多,可以解决读写速度不一致的问题,RT-Thread 的组件框架中也使用到了,RT-Thread 已经提供了就不需要我们自己再重复造轮子了,这部分代码也很少,想要数量掌握,还是建议读一下源码。 |
|
相关推荐
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
1033 浏览 0 评论
2877 浏览 0 评论
图腾柱PFC无法上升至400V,且电感电流为正弦波形,但是幅值极小
9386 浏览 0 评论
飞凌嵌入式ElfBoard-Vim编辑器之静态链接和动态链接
2898 浏览 0 评论
使用 LinkBoy 将程序导出为 C 语言代码并烧录至 Arduino ESP32 开发板
2302 浏览 1 评论
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-2 09:20 , Processed in 0.671933 second(s), Total 71, Slave 54 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖
2416