嵌入式开源组件——LwRB(RingBuffer)的移植和应用

说明:记录自己学习的过程,如有理解上的错误或者不恰当的地方请原谅。

一、简介

LwRB 是一个针对嵌入式系统优化的通用FIFO(先进先出)缓冲区库,之前的名称叫做RingBuffe 

,不知道作者在V2.0.0版本时,修改名称为了LwRB。以下为主要的一些特性:

  • 用 C (C11) 编写,兼容size_t 数据类型

  • 平台无关,无特定架构的代码

  • 实现 FIFO(先进先出)缓冲区

  • 无动态内存分配,数据为静态数组

  • 使用优化的内存复制,而不是循环来读取/写入内存中的数据

  • 作为管道使用时,线程安全,只有单个写入和单个读取条目

  • 作为管道使用时,单写和单读条目的中断安全

  • 适用于从内存到内存的 DMA 传输,缓冲区和应用内存之间零拷贝开销

  • 支持数据查看、读取跳过和写入时提前

  • 实现对事件通知的支持

  • 用户友好的 MIT 许可证

我们重点关注里面标记红色特性,在把LwRB用于串口接收和发送时,零拷贝开销,特别高。

二、相关资料和链接

直接挂上作者写的文档链接,里面有详细的使用技巧介绍、原理讲解,非常生动形象。主页上还有作者开发发的各种其他嵌入式开源小组件。

https://docs.majerle.eu/projects/lwrb/en/latest/index.html

三、移植注意事项

移值平台:DMK5  GD32F303系列芯片(其实和芯片平台无关)

3.1 源码获取

GitHub地址 https://github.com/MaJerle/lwrb 这里我们拉取main分支的代码,他是最新稳定的版本分支。不建议使用develop分支代码,应为为开发状态。

3.2 源码文件介绍

主要关注docs文件和lwrb文件。

docs/examples_src文件重点关注,详细的说明了各个用法例子。docs文件下有许多后缀rst的文件(是一种用于文本数据的文件格式,主要用于Python 编程语言社区的技术文档),这个直接在github网页上就可以了。

docs/lwrb/src文件。我们直接把里面的lwrb.h和lwrb.c文件拎出来,移植就用这两个文件。

3.3 需要修改的地方

把上述的lwrb.h和lwrb.c文件加入工程中。最新稳定V3.0.0版本支持了STDATOMIC 功能,保证数据的原子访问,需要支持这个特性的话,编译器需要支持C11,需要的keil 是AC6的支持。你懂的AC6太麻烦了,我们还是用AC5吧。直接编译会报错:

..\..\User\components\lwrb\lwrb.h(53): error:  #5: cannot open source input file "stdatomic.h": No such file or directory

在lwrb.h上直接定义一个LWRB_DISABLE_ATOMIC,表明不使用atomic即可。

#define LWRB_DISABLE_ATOMIC
#ifdef LWRB_DISABLE_ATOMIC
typedef unsigned long lwrb_ulong_t;
#else
#include <stdatomic.h>
typedef atomic_ulong lwrb_ulong_t;
#endif

可能大家这里就会有疑问了,不使用atomic有什么问题吗?答案是没有的,因为size_t 类型在Cortex-M 上是 32 位,我们使用的GD32或者STM32是 32 位在一个周期内写入的。

3.4 最小示例代码

移植完成后,就可以使用了,只是官网的一个最小使用示例:

#include "lwrb/lwrb.h"

/* Declare rb instance & raw data */
lwrb_t buff;
uint8_t buff_data[8];

/* Application variables */
uint8_t data[2];     /* Application working data */

/* Application code ... */
lwrb_init(&buff, buff_data, sizeof(buff_data)); /* Initialize buffer */

/* Write 4 bytes of data */
lwrb_write(&buff, "0123", 4);

/* Print number of bytes in buffer */
printf("Bytes in buffer: %d\r\n", (int)lwrb_get_full(&buff));

/* Will print "4" *

四、LwRB实现原理

主要参考官方给的文档,文档展示了几种不同的缓冲区使用情况,来提供给我们理解内部是如何管理数据,这里自己搬运一下,配合上自己的理解。https://docs.majerle.eu/projects/lwrb/en/latest/user-manual/how-it-works.html

先明确一下这几个缩写的含义:

  • R: 代表读指针。在读取/写入操作时读取。仅在读取操作时修改
  • W: 代表写指针。在进行读/写操作时读取。仅在写操作时修改
  • S: 代表缓冲区大小。在初始化的时候就固定了
  • W和R指针的值位于0到S-1之间。例如上述图中定义了一个大小为8的缓冲区作为数组,W 和 R 指针的有效数值范围为 0 - 7。
    • R 和 W 将在在 S 处溢出,因此R 和 W有效范围总是 0、1、2、3、......、S - 2、S - 1、0、1、2、3、......、S - 2、S - 1、0、......
    • 当S为4时,R和W的取值范围为:0、1、2、3、0、1、2、3、0、1.............
  • 缓冲区可容纳的最大字节数总是 S - 1,因此缓冲区最多可容纳 7 个字节
  • R 和 W 指针总是指向下一个读/写操作
  • 当 W == R 时,缓冲区为空
  • 当 W == R - 1 时,缓冲区被认为是满的
    • W == R - 1 只有在 W 和 R 溢出缓冲区大小 S 时才有效。
    • 总是将 S 加到计算出的数字上,然后使用模数 S 得出最终值

具体的模数应该怎么计算呢?

//假设S为4 
2 - 3 = (2 - 3 + S) % S = (2 - 3 + 4) % 4 = (-1 + 4) % 4 = 3

我们来看具体的情况:

  • 示例A:缓冲区为空,因为 W == R = 0 
  • 示例B:缓冲区有 W - R = 4 - 0 = 4 个字节,因为 W > R
  • 示例C:缓冲区满,因为 W == R - 1 或 7 == 0 - 1 ,也就是上面说到模数计算公式R代入0,来进行计算 ,7 = (0 - 1 + S) % S = (0 - 1 + 8) % 8 = (-1 + 8) % 8 = 7
  • 示例D:缓冲区保存有 S - (R - W) = 8 - (5 - 3) = 6 个字节,因为 R > W
  • 示例E:缓冲区满,因为 W == R - 1 或者叫(4 = 5 - 1) ,也就是 S - (R - W) = 8 - (5 - 4) ) = 7 字节。

注意:这里我们重点区分一下,缓冲区满的C和E的情况不同,C示例的缓冲区满,是W>R的条件下,直接用R-1计算是负数,需要进行取模计算公式来满足W==R-1。而E示例的缓冲区满R>W的条件下的,相当于缓冲区到了尾部后又重新到了头部,所以直接利用R-1计算可以得到满足W==R-1,这个时候的利用 S - (R - W)来计算出缓冲区内大小为7字节,缓冲区满了。

五、应用

我主要用于串口数据的接收和发送上面,配置DMA接收半满中断、串口IDLE中断、DAM发送完成中断,可以实现高效的串口数据发送和接收,后续会出两篇博文详细介绍这方面的使用和原理。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值