├── .gitignore ├── .gitmodules ├── README.md ├── SConscript ├── docs ├── images │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── buff_cases.svg │ ├── image-20210823113505048.png │ └── image-20210824172755979.png ├── lwrb使用说明.md └── lwrb工程学习.md └── examples ├── event.c ├── example_events.c ├── example_index.c ├── example_minimal.c ├── getting_started.c ├── lwrb_peek.c ├── zero_copy_from_lwrb_memory.c └── zero_copy_to_lwrb_memory.c /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lwrb"] 2 | path = lwrb 3 | url = git@github.com:MaJerle/lwrb.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 1. 介绍 2 | 3 | lwrb2rtt 是一个轻量级的环形缓冲区管理软件包,提供了通用的 FIFO 环形缓冲区实现。 4 | 5 | 此项目是完成开源的 lwrb 与 rt-thread 的匹配。 6 | 7 | 原工程地址:https://github.com/MaJerle/lwrb/tree/develop 8 | 9 | 项目文档:https://docs.majerle.eu/projects/lwrb/en/latest/ 10 | 11 | ## 1.1 特点 12 | 13 | - 用 ANSI C99 编写,与 `size_t` 大小的数据类型兼容。 14 | - 具有平台独立性,无与硬件耦合代码。 15 | - FIFO (先进先出)缓冲区的实现。 16 | - 无动态内存申请,数据存储在静态数组里。 17 | - 使用效率更高的内存复制,而不是循环地从内存读取数据或向内存写入数据。 18 | - 当用作管道,即只有一个写端口和一个读端口时是线程安全的。 19 | - 当用作管道,即只有一个写端口和一个读端口时是中断安全的。 20 | - 适用于 DMA 传输,缓冲区和应用程序内存之间的复制开销为 0 。 21 | - 支持数据预览,跳过读取和前进写入。 22 | - 支持事件通知 23 | - 对用户友好的 MIT 许可证。 24 | 25 | ## 1.2 目录结构 26 | 27 | | 名称 | 说明 | 28 | | -------- | ---------------- | 29 | | examples | 例程目录 | 30 | | lwrb | 原 lwrb 工程目录 | 31 | 32 | ## 1.3 许可证 33 | 34 | lwrb2rtt 遵循 MIT 许可。 35 | 36 | ## 1.4 依赖 37 | 38 | 无依赖 39 | 40 | # 2. 如何打开 lwrb2rtt 41 | 42 | 使用 lwrb2rtt 需要在 RT-Thread 的包管理器中选择它,具体路径如下: 43 | 44 | ``` 45 | RT-Thread online packages 46 | tools packages ---> 47 | [*] lwrb2rtt: Lightweight ring buffer manager. ---> 48 | [*] Enable lwrb2rtt samples 49 | ``` 50 | 51 | - `Enable lwrb2rtt samples`: 开启 lwrb2rtt 例程。 52 | 53 | 然后让 RT-Thread 的包管理器自动更新,或者使用 `pkgs --update` 命令更新到 BSP 中。 54 | 55 | # 3. 联系方式 & 感谢 56 | 57 | - 维护:Jackistang 58 | - 主页:[https://github.com/Jackistang](https://github.com/Jackistang) 59 | - 邮箱:tangjia.jackis@qq.com -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | 2 | from building import * 3 | 4 | cwd = GetCurrentDir() 5 | 6 | # init src and inc vars 7 | src = [] 8 | inc = [] 9 | 10 | inc += [cwd + "/lwrb/lwrb/src/include"] 11 | src += Glob("lwrb/lwrb/src/lwrb/lwrb.c") 12 | 13 | if GetDepend('LWRB2RTT_USING_SAMPLES'): 14 | src += Glob("examples/*.c") 15 | 16 | group = DefineGroup('lwrb2rtt', src, depend = ['PKG_USING_LWRB2RTT'], CPPPATH = inc) 17 | 18 | Return('group') -------------------------------------------------------------------------------- /docs/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackistang/lwrb2rtt/72ff2a7a26cb0a177befb10bc50e819d335b33ae/docs/images/1.png -------------------------------------------------------------------------------- /docs/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackistang/lwrb2rtt/72ff2a7a26cb0a177befb10bc50e819d335b33ae/docs/images/2.png -------------------------------------------------------------------------------- /docs/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackistang/lwrb2rtt/72ff2a7a26cb0a177befb10bc50e819d335b33ae/docs/images/3.png -------------------------------------------------------------------------------- /docs/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackistang/lwrb2rtt/72ff2a7a26cb0a177befb10bc50e819d335b33ae/docs/images/4.png -------------------------------------------------------------------------------- /docs/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackistang/lwrb2rtt/72ff2a7a26cb0a177befb10bc50e819d335b33ae/docs/images/5.png -------------------------------------------------------------------------------- /docs/images/buff_cases.svg: -------------------------------------------------------------------------------- 1 | 2 |
R
R
W
[Not supported by viewer]
R
R
W
[Not supported by viewer]
R
R
W
[Not supported by viewer]
R
R
W
[Not supported by viewer]
R
R
W
[Not supported by viewer]
1
[Not supported by viewer]
2
2
3
3
6
6
5
5
4
4
7
7
0
0
1
[Not supported by viewer]
2
2
3
3
6
6
5
5
4
4
7
7
0
0
1
[Not supported by viewer]
2
2
3
3
6
6
5
5
4
4
0
0
1
[Not supported by viewer]
2
2
3
3
6
6
5
5
4
4
7
7
0
0
7
7
B
B
A
A
C
C
D
D
1
[Not supported by viewer]
2
2
3
3
6
6
5
5
4
4
7
7
0
0
E
E
-------------------------------------------------------------------------------- /docs/images/image-20210823113505048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackistang/lwrb2rtt/72ff2a7a26cb0a177befb10bc50e819d335b33ae/docs/images/image-20210823113505048.png -------------------------------------------------------------------------------- /docs/images/image-20210824172755979.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jackistang/lwrb2rtt/72ff2a7a26cb0a177befb10bc50e819d335b33ae/docs/images/image-20210824172755979.png -------------------------------------------------------------------------------- /docs/lwrb使用说明.md: -------------------------------------------------------------------------------- 1 | # LwRB 使用说明 2 | 3 | LwRB (lightweight ring buffer) 是一个轻量级的环形缓冲区,除了支持 ring buffer 常规的读写操作,它还有自己的特色,比如支持**读写事件通知**,支持 DMA **零拷贝**收发数据。下面依次介绍相应功能的使用方式: 4 | 5 | ## LwRB 原理浅析 6 | 7 | 详细内容请参考 [LwRB - How it works](https://docs.majerle.eu/projects/lwrb/en/latest/user-manual/how-it-works.html),以下仅写出我的理解。 8 | 9 | LwRB 内部有一块地址连续的缓冲区,这个缓冲区是用户调用 `lwrb_init()` 时指定的,接口如下: 10 | 11 | ```C 12 | uint8_t lwrb_init(LWRB_VOLATILE lwrb_t* buff, void* buffdata, size_t size); 13 | ``` 14 | 15 | 而且 LwRB 内部维护了一个读(R)、写(W)指针,分别指向**下一个可读**、**下一个可写**的位置。写入一个字节数据时,将数据放入写指针的位置,并将写指针加 1 ,若超出缓冲区范围,则回滚到缓冲区起始处。 16 | 17 | ![Different buffer corner cases](images/buff_cases.svg) 18 | 19 | 例如上图例程,缓冲区大小 `S` 为 8 : 20 | 21 | - 初始时:`R` 与 `W` 相等,代表 LwRB 为空。 22 | - 写入 4 个字节:`W` 移动到 4 的位置,此时 LwRB 存储了 4 个字节。 23 | - 写入 3 个字节:`W` 移动到 7 的位置,此时 LwRB 存储了 7 个字节,而且 `W == R-1` , 或者说 `W == (R + S - 1) % S`,代表 LwRB 已满。 24 | - 读出 5 个字节,并再次写入 4 个字节:`W` 移动到 3,`R` 移动到 5,此时 LwRB 存储了 6 个字节。 25 | - 写入 1 个字节:`W` 移动到 4,此时 LwRB 存储了 7 个字节,而且 `W == R-1` ,或者说 `W == (R + S - 1) % S`,代表 LwRB 已满。 26 | 27 | 需要注意的是,lwrb 内部 `W == R` 代表 LwRB 为空,`W == (R + S - 1) % S` 代表 LwRB 为满。 28 | 29 | ## 读写操作 30 | 31 | LwRB 里的读写操作接口为: 32 | 33 | ```C 34 | /* Read/Write functions */ 35 | size_t lwrb_write(LWRB_VOLATILE lwrb_t* buff, const void* data, size_t btw); 36 | size_t lwrb_read(LWRB_VOLATILE lwrb_t* buff, void* data, size_t btr); 37 | size_t lwrb_peek(LWRB_VOLATILE lwrb_t* buff, size_t skip_count, void* data, size_t btp); 38 | ``` 39 | 40 | - `lwrb_write()` 用于向 LwRB 里写入数据,并移动写指针。 41 | - `lwrb_read()` 用于从 LwRB 里读出数据,并移动读指针。 42 | - `lwrb_peek()` 用于从 LwRB 里读出数据,**不移动**读指针。 43 | 44 | 这里需要区分 `lwrb_read()` 和 `lwrb_peek()` 的不同之处,read 是真正地从 LwRB 里把数据取出来,会修改读指针,read 之后该数据就被 LwRB 移除了;而 peek 操作从 LwRB 里读数据,但并不取出来,不会修改读指针,peek 之后数据仍在 LwRB 里。 45 | 46 | `lwrb_peek()` 函数还有一些特殊的用法,函数原型如下: 47 | 48 | ```C 49 | size_t lwrb_peek(LWRB_VOLATILE lwrb_t* buff, size_t skip_count, void* data, size_t btp); 50 | ``` 51 | 52 | - `buff` - LwRB 对象 53 | - `skip_count` - 跳过几个数据再开始读取 54 | - `data` - 读取数据缓冲区 55 | - `btp` - 需要读取的字节数(bytes to peek) 56 | 57 | 同时该函数返回实际读取的字节数。以下来理解如何使用: 58 | 59 | 假设 LwRB 内部状态如下:`W` 处于 4 的位置,`R` 处于 6 的位置。 60 | 61 | ![](images/image-20210823113505048.png) 62 | 63 | 此时我们想要从 LwRB 里读取 4 个字节,调用 `lwrb_peek(&buff, 0, data, 4);` ,则最终得到的数据为:`6, 7, 0, 1` ,该函数内部帮我们处理了**读指针回滚**的情况,这是目前许多 ring buffer 不具备的功能。而且该函数还支持跳过最开始处的字节,例如调用 `lwrb_peek(&buff, 2, data, 4);` ,则最终得到的数据为:`0, 1, 2, 3`,该函数内部自动跳过了读指针对开始处的 `6, 7` 这两个字节数据。 64 | 65 | 例程可参考:getting_started.c 和 lwrb_peek.c 。 66 | 67 | ## 读写事件通知 68 | 69 | 详细内容请参考 [LwRB - Events](https://docs.majerle.eu/projects/lwrb/en/latest/user-manual/events.html#events),以下仅写出我的理解。 70 | 71 | 读写事件通知接口如下: 72 | 73 | ```C 74 | typedef void (*lwrb_evt_fn)(LWRB_VOLATILE struct lwrb* buff, lwrb_evt_type_t evt, size_t bp); 75 | void lwrb_set_evt_fn(LWRB_VOLATILE lwrb_t* buff, lwrb_evt_fn fn); 76 | ``` 77 | 78 | lwrb 的事件通知功能是用户调用 `lwrb_set_evt_fn()` 向 lwrb 注册一个回调函数,然后 lwrb 每次进行读写数据时,都会通过这个回调函数通知用户当前写入了多少个字节,或读取了多少个字节。 79 | 80 | 该功能可用于**打印日志**,**释放信号量**等,更多使用场景还待挖掘。 81 | 82 | 例程可参考:event.c 。 83 | 84 | ## DMA 零拷贝收发数据 85 | 86 | 详细内容请参考 [LwRB - DMA for embedded systems](https://docs.majerle.eu/projects/lwrb/en/latest/user-manual/hw-dma-usage.html),以下仅写出我的理解。 87 | 88 | LwRB 提供的**零拷贝**功能,按照工作方式可分为两种类型: 89 | 90 | - 从 LwRB 里零拷贝读取数据 91 | - 向 LwRB 里零拷贝写入数据 92 | 93 | 先介绍**从 LwRB 里零拷贝读取数据**,相关接口为: 94 | 95 | ```C 96 | /* Read data block management */ 97 | void* lwrb_get_linear_block_read_address(LWRB_VOLATILE lwrb_t* buff); 98 | size_t lwrb_get_linear_block_read_length(LWRB_VOLATILE lwrb_t* buff); 99 | size_t lwrb_skip(LWRB_VOLATILE lwrb_t* buff, size_t len); 100 | ``` 101 | 102 | 假设当前 LwRB 内部状态如下图所示: 103 | 104 | ![](./images/1.png) 105 | 106 | LwRB 内部缓冲区已满,`W` 处于 4 的位置,`R` 处于 5 的位置。 107 | 108 | 接口 `lwrb_get_linear_block_read_address()` 可获取当前 LwRB 内部缓冲区第一个可读取字节的地址,例如上图中的 `R`,而 `lwrb_get_linear_block_read_length()` 函数获取当前 LwRB 内部缓冲区**可读取的**,且**地址连续的**字节数,例如上图中会得到结果 3,表示可连续读取后续的数据 `5, 6, 7` 。 109 | 110 | 利用这两个函数,我们就得到了一个指针 `p` 和一个大小 `len` ,`p` 指向数据的起始地址,`len` 代表数据的字节大小,将 `p` 和 `len` 传给 DMA,即可直接数据发送了。 111 | 112 | ![](./images/2.png) 113 | 114 | DMA 发送完成后,我们还需要更新 LwRB 里的 `R` 指针,通过 `lwrb_skip()` 函数移动 `R` 指针,如下图所示: 115 | 116 | ![](./images/3.png) 117 | 118 | 至此,我们就完成了从 LwRB 里零拷贝读取数据的流程,例程可参考:zero_copy_from_lwrb_memory.c 。 119 | 120 | **向 LwRB 里零拷贝写入数据**的操作也类似,相关接口为: 121 | 122 | ```C 123 | /* Write data block management */ 124 | void* lwrb_get_linear_block_write_address(LWRB_VOLATILE lwrb_t* buff); 125 | size_t lwrb_get_linear_block_write_length(LWRB_VOLATILE lwrb_t* buff); 126 | size_t lwrb_advance(LWRB_VOLATILE lwrb_t* buff, size_t len); 127 | ``` 128 | 129 | 假设当前 LwRB 内部状态如下图所示: 130 | 131 | ![](./images/4.png) 132 | 133 | LwRB 内部缓冲区为空,`R` 和 `W` 都处于 4 的位置。 134 | 135 | 接口 `lwrb_get_linear_block_write_address()` 可获取 LwRB 内部缓冲区第一个可写入字节的地址,例如上图中的 `W`,而 `lwrb_get_linear_block_read_length()` 函数返回 LwRB 当前可**连续地址写入**的大小,例如上图中的结果为 4,表示可连续写入 `4, 5, 6, 7` 处的数据。 136 | 137 | 利用这两个函数,我们就得到了一片**地址连续**的缓冲区,包括其首地址 `p` 和大小 `len`,将其传入 DMA 即可开始接收数据了。 138 | 139 | DMA 接收完成后,我们还需要更新 LwRB 内部的 `W` 指针,通过 `lwrb_advance()` 函数移动 `W` 指针,如下图所示: 140 | 141 | ![](./images/5.png) 142 | 143 | 至此,我们就完成了向 LwRB 里零拷贝写入数据的流程,例程可参考:zero_copy_to_lwrb_memory.c 。 144 | 145 | ## 线程安全 146 | 147 | 当只有一个写线程和一个读线程时,LwRB 是线程安全的,不需要额外加锁。 148 | 149 | 详细内容请参考 [LwRB - Thread safety](https://docs.majerle.eu/projects/lwrb/en/latest/user-manual/thread-safety.html)。 150 | 151 | -------------------------------------------------------------------------------- /docs/lwrb工程学习.md: -------------------------------------------------------------------------------- 1 | # LwRB 工程学习 2 | 3 | 本文是对 LwRB 开源项目的工程学习,包括项目文件如何组织,使用了哪些第三方库,以及是如何测试的。通过学习这些内容能够让自己知道一个好的开源项目应该有哪些工程架构。 4 | 5 | LwRB 内部包括 Sanitizer(地址检查,未定义行为检查),gcov & lcov(代码覆盖率报告),CMake 模块 sanitizers-cmake 和 CodeCoverage,文档生成方式。 6 | 7 | ## Sanitizer 8 | 9 | ### ASan 10 | 11 | ASan(Address Sanitizer)简介:用于检测程序里的地址错误,例如 [Ues after free](https://github.com/google/sanitizers/wiki/AddressSanitizerExampleUseAfterFree), [Heap buffer overflow](https://github.com/google/sanitizers/wiki/AddressSanitizerExampleHeapOutOfBounds), [Global buffer overflow](https://github.com/google/sanitizers/wiki/AddressSanitizerExampleGlobalOutOfBounds) 等等。 12 | 13 | GCC 如何使用 ASan:编译时加上选项[-fsanitize=address](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html) 14 | 15 | 参考:https://zhuanlan.zhihu.com/p/360135083 16 | 17 | ### UBSan 18 | 19 | UBSan(Undefined Behavior Sanitizer)简介:用于检测程序里未定义的行为,需要编译器支持。 20 | 21 | GCC 如何使用 UBSan:编译时加上选项 [-fsanitize=undefined](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html) 22 | 23 | ### cmake 如何快速使用 ASan 和 UBSan 24 | 25 | 使用 cmake 模块 [sanitizers-cmake](https://github.com/arsenm/sanitizers-cmake) ,将 sanitizers-cmake 的路径添加到 `CMAKE_MODULE_PATH` 里,通过 26 | 27 | ```cmake 28 | find_package(Sanitizers) 29 | ``` 30 | 31 | 命令找到 Sanitizers 模块,然后将变量 `SANITIZE_ADDRESS` 设置为 `"On"` 开启 ASan,将变量 `SANITIZE_UNDEFINED` 设置为 "On" 开启 UBSan 。 32 | 33 | ```cmak 34 | set(SANITIZE_ADDRESS "On") 35 | set(SANITIZE_UNDEFINED "On") 36 | ``` 37 | 38 | 最后将 \ 加入 Sanitizers 模块即可。 39 | 40 | ```cmake 41 | add_sanitizers() 42 | ``` 43 | 44 | ## gcov & lcov 45 | 46 | **gcov** - GCC 配套发布的代码覆盖率分析工具。 47 | 48 | **lcov** - 用于将 gcov 的分析内容转换成 html 格式的代码覆盖率报告,提高覆盖率检测结果的可读性。 49 | 50 | ```shell 51 | sudo apt install gcov lcov 52 | ``` 53 | 54 | 流程一般为: 55 | 56 | - 编译程序,添加编译选项 `-fprofile-arcs -ftest-coverage`,告知编译器生成 gcov 需要的额外信息。 57 | - 运行可执行程序,此时会在当前生成 `*.gcda` 文件, 58 | - 使用 `gcov *.c` 命令生成代码覆盖数据,生成相应的 `*.c.gcov` 文件,此时该文件里就包含 gcov 格式的代码覆盖报告。 59 | - 可以使用 lcov 将 `*.c.gcov` 转换成更直观的表现形式,`lcov -d . -t '表格名称' -o '文件名称.info' -b . -c` 。 60 | - 利用 `gethtml`(lcov 工具)生成 html 图形文件。`genhtml -o 输出目录名 文件名.info` 。 61 | 62 | 上述使用还是挺麻烦的,**cmake** 提供了一个模块来很方便地生成代码覆盖率 html 文件,使用了 [CodeCoverage](https://github.com/bilke/cmake-modules) 模块。 63 | 64 | - 首先将 CodeCoverage.cmake 文件路径添加到 `CMAKE_MODULE_PATH` 65 | - 导入 CodeCoverage,`include(CodeCoverage)` 66 | - 添加编译选项 `append_coverage_compiler_flags()`,该命令会添加 `-fprofile-arcs -ftest-coverage` 编译选项到 GCC 67 | - 将不需要报告的目录文件排除,通过将文件或目录加入 `COVERAGE_EXCLUDES` 变量,或通过 `setup_target_for_coverage_lcov()` 函数的 EXCLUDE 参数。 68 | 69 | ```cmake 70 | 方法1: 71 | set(COVERAGE_EXCLUDES 72 | '${PROJECT_SOURCE_DIR}/src/dir1/*' 73 | '/path/to/my/src/dir2/*') 74 | 75 | 方法2: 76 | setup_target_for_coverage_lcov( 77 | NAME coverage 78 | EXECUTABLE testrunner 79 | EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") 80 | ``` 81 | 82 | - 然后根据需求选择 `setup_target_for_coverage_*()` 函数使用,本次使用 lcov。 83 | 84 | ```cmake 85 | if(WITH_COVERAGE) 86 | setup_target_for_coverage_lcov( 87 | NAME coverage 88 | EXECUTABLE ctest 89 | EXCLUDE "${CMAKE_SOURCE_DIR}/*" "${THIRD_PARTY_DIR}/*") 90 | endif(WITH_COVERAGE) 91 | ``` 92 | 93 | `NAME` 是名称,后续会使用该名称,例如下述的 `make coverage`;`EXECUTABLE` 是在 `${PROJECT_BINARY_DIR}` 会执行的命令,用于执行程序以生成相应的代码覆盖率数据。 94 | 95 | - 之后运行 `make coverage` 执行 CodeCoverage 。 96 | - 打开 `coverage/index.html` 即可看到可视化的代码覆盖率报告。如下图所示: 97 | 98 | ![](images/image-20210824172755979.png) 99 | 100 | 褐色表示未执行代码,蓝色表示已执行代码,而前面的数字表示代码执行的次数。 101 | 102 | 参考: 103 | 104 | - [GCOV+LCOV 代码调试和覆盖率统计工具](https://blog.csdn.net/gatieme/article/details/78368667) 105 | - [CodeCoverage.cmake](https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake) 106 | 107 | ## cmake 测试集成工具 **ctest** 108 | 109 | ctest 能够同时运行**多个**测试程序,能够以**不同的命令行参数**运行测试程序。首先通过 `enable_testing()` 运行 ctest 功能,然后通过 `add_test()` 添加需要执行的测试程序。如下述代码所示: 110 | 111 | ```cmake 112 | enable_testing() 113 | 114 | add_test(NAME boo COMMAND boo) 115 | add_test(NAME boo COMMAND boo arg1 arg2) 116 | ``` 117 | 118 | ## 文档生成工具 119 | 120 | Sphinx + Read the Docs,reStructureText 标记语法 121 | 122 | reStructureText 编辑器:VSCode + reStructuredText 插件 123 | 124 | 主流标记语法: 125 | 126 | - reStructureText 语法 127 | - Markdown 语法 128 | 129 | Read the Docs 支持两种文档生成器: 130 | 131 | - Sphinx:文档生成器,支持生成 html, PDF, ePub 等等,默认支持 reStructureText 语法,使用插件可支持 Markdown 语法。 132 | - MkDocs:文档生成器,默认支持 Markdown 语法,使用难度较低。 133 | 134 | 135 | 136 | 参考: 137 | 138 | - [从 Markdown 到 reStructuredText](https://macplay.github.io/posts/cong-markdown-dao-restructuredtext/) 139 | - [Read the Docs: Documentation Simplified](https://docs.readthedocs.io/en/stable/index.html) 140 | 141 | 142 | 143 | 144 | 145 | ```shell 146 | pip install mkdocs 147 | 148 | mkdocs new . 149 | ``` 150 | 151 | 152 | 153 | 注册 Read the Docs 时选择 github 注册,会自动开通权限。 154 | 155 | Read the Docs 自动导入文档,手动导入文档。 156 | 157 | 158 | 159 | ```shell 160 | echo "# MkDocs-ReadTheDocs-demo" >> README.md 161 | git init 162 | git add README.md 163 | git commit -m "first commit" 164 | git branch -M main 165 | git remote add origin git@github.com:Jackistang/MkDocs-ReadTheDocs-demo.git 166 | git push -u origin main 167 | ``` 168 | 169 | 170 | 171 | 高级设置 - 文档类型 - Mkdocs 172 | 173 | -------------------------------------------------------------------------------- /examples/event.c: -------------------------------------------------------------------------------- 1 | #include "lwrb/lwrb.h" 2 | 3 | #include 4 | 5 | /* 6 | - Notify application layer that LwRB operation has been executed and send debug message 7 | 8 | - Unlock semaphore when sufficient amount of bytes have been written/read from/to buffer when application uses operating system 9 | 10 | - Write notification to message queue at operating system level to wakeup another task 11 | 12 | 13 | */ 14 | 15 | /** 16 | * \brief Buffer event function 17 | */ 18 | static void 19 | my_buff_evt_fn(lwrb_t* buff, lwrb_evt_type_t type, size_t len) { 20 | switch (type) { 21 | case LWRB_EVT_RESET: 22 | rt_kprintf("[EVT] Buffer reset event!\r\n"); 23 | break; 24 | case LWRB_EVT_READ: 25 | rt_kprintf("[EVT] Buffer read event: %d byte(s)!\r\n", (int)len); 26 | break; 27 | case LWRB_EVT_WRITE: 28 | rt_kprintf("[EVT] Buffer write event: %d byte(s)!\r\n", (int)len); 29 | break; 30 | default: break; 31 | } 32 | } 33 | 34 | static void lwrb_event(void) 35 | { 36 | /* Later in the code... */ 37 | lwrb_t buff; 38 | uint8_t buff_data[8]; 39 | 40 | /* Init buffer and set event function */ 41 | lwrb_init(&buff, buff_data, sizeof(buff_data)); 42 | lwrb_set_evt_fn(&buff, my_buff_evt_fn); 43 | 44 | lwrb_write(&buff, "1234", 4); 45 | 46 | uint8_t data[2]; 47 | lwrb_read(&buff, data, sizeof(data)); 48 | 49 | lwrb_reset(&buff); 50 | } 51 | 52 | MSH_CMD_EXPORT(lwrb_event, lwrb event example); 53 | -------------------------------------------------------------------------------- /examples/example_events.c: -------------------------------------------------------------------------------- 1 | #include "lwrb/lwrb.h" 2 | 3 | #include 4 | 5 | /** 6 | * \brief Buffer event function 7 | */ 8 | static void 9 | my_buff_evt_fn(lwrb_t *buff, lwrb_evt_type_t type, size_t len) 10 | { 11 | switch (type) 12 | { 13 | case LWRB_EVT_RESET: 14 | rt_kprintf("[EVT] Buffer reset event!\r\n"); 15 | break; 16 | case LWRB_EVT_READ: 17 | rt_kprintf("[EVT] Buffer read event: %d byte(s)!\r\n", (int)len); 18 | break; 19 | case LWRB_EVT_WRITE: 20 | rt_kprintf("[EVT] Buffer write event: %d byte(s)!\r\n", (int)len); 21 | break; 22 | default: 23 | break; 24 | } 25 | } 26 | 27 | static void lwrb2rtt_example_events(void) 28 | { 29 | /* Later in the code... */ 30 | lwrb_t buff; 31 | uint8_t buff_data[8]; 32 | 33 | /* Init buffer and set event function */ 34 | lwrb_init(&buff, buff_data, sizeof(buff_data)); 35 | lwrb_set_evt_fn(&buff, my_buff_evt_fn); 36 | 37 | /* Write 4 bytes of data */ 38 | lwrb_write(&buff, "0123", 4); 39 | 40 | /* Print number of bytes in buffer */ 41 | rt_kprintf("Bytes in buffer: %d\r\n", (int)lwrb_get_full(&buff)); 42 | 43 | /* Will print "4" */ 44 | 45 | lwrb_write(&buff, "0123", 4); 46 | lwrb_reset(&buff); 47 | } 48 | MSH_CMD_EXPORT(lwrb2rtt_example_events, lwrb events example); 49 | 50 | 51 | -------------------------------------------------------------------------------- /examples/example_index.c: -------------------------------------------------------------------------------- 1 | #include "lwrb/lwrb.h" 2 | 3 | #include 4 | 5 | static void lwrb2rtt_example_index(void) 6 | { 7 | /* Declare rb instance & raw data */ 8 | lwrb_t buff; 9 | uint8_t buff_data[8]; 10 | 11 | /* Application variables */ 12 | uint8_t data[2]; 13 | size_t len; 14 | 15 | /* Application code ... */ 16 | lwrb_init(&buff, buff_data, sizeof(buff_data)); /* Initialize buffer */ 17 | 18 | /* Write 4 bytes of data */ 19 | lwrb_write(&buff, "0123", 4); 20 | 21 | /* Try to read buffer */ 22 | /* len holds number of bytes read */ 23 | /* Read until len == 0, when buffer is empty */ 24 | while ((len = lwrb_read(&buff, data, sizeof(data))) > 0) 25 | { 26 | rt_kprintf("Successfully read %d bytes\r\n", (int)len); 27 | } 28 | } 29 | MSH_CMD_EXPORT(lwrb2rtt_example_index, lwrb minimal index); 30 | -------------------------------------------------------------------------------- /examples/example_minimal.c: -------------------------------------------------------------------------------- 1 | #include "lwrb/lwrb.h" 2 | 3 | #include 4 | 5 | static void lwrb2rtt_example_minimal(void) 6 | { 7 | /* Declare rb instance & raw data */ 8 | lwrb_t buff; 9 | uint8_t buff_data[8]; 10 | 11 | /* Application code ... */ 12 | lwrb_init(&buff, buff_data, sizeof(buff_data)); /* Initialize buffer */ 13 | 14 | /* Write 4 bytes of data */ 15 | lwrb_write(&buff, "0123", 4); 16 | 17 | /* Print number of bytes in buffer */ 18 | rt_kprintf("Bytes in buffer: %d\r\n", (int)lwrb_get_full(&buff)); 19 | 20 | /* Will print "4" */ 21 | } 22 | MSH_CMD_EXPORT(lwrb2rtt_example_minimal, lwrb minimal example); 23 | -------------------------------------------------------------------------------- /examples/getting_started.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void lwrb_getting_started(void) 5 | { 6 | /* Declare rb instance & raw data */ 7 | lwrb_t buff; 8 | uint8_t buff_data[8]; 9 | 10 | /* Application variables */ 11 | uint8_t data[2]; 12 | size_t len; 13 | 14 | /* Application code ... */ 15 | lwrb_init(&buff, buff_data, sizeof(buff_data)); /* Initialize buffer */ 16 | 17 | /* Write 4 bytes of data */ 18 | lwrb_write(&buff, "0123", 4); 19 | rt_kprintf("Write data: 48, 49, 50, 51\r\n"); 20 | 21 | /* Try to peek buffer */ 22 | lwrb_peek(&buff, 0, data, sizeof(data)); 23 | rt_kprintf("Peek data skip 0 count: %d, %d\r\n", data[0], data[1]); 24 | lwrb_peek(&buff, 1, data, sizeof(data)); 25 | rt_kprintf("Peek data skip 1 count: %d, %d\r\n", data[0], data[1]); 26 | 27 | /* Try to read buffer */ 28 | /* len holds number of bytes read */ 29 | /* Read until len == 0, when buffer is empty */ 30 | while ((len = lwrb_read(&buff, data, sizeof(data))) > 0) { 31 | rt_kprintf("Successfully read %d bytes, data: %d, %d\r\n", (int)len, data[0], data[1]); 32 | } 33 | 34 | } 35 | MSH_CMD_EXPORT(lwrb_getting_started, lwrb getting started example); 36 | -------------------------------------------------------------------------------- /examples/lwrb_peek.c: -------------------------------------------------------------------------------- 1 | #include "lwrb/lwrb.h" 2 | 3 | #include 4 | 5 | static void lwrb_peek(void) 6 | { 7 | /* Declare rb instance & raw data */ 8 | lwrb_t buff; 9 | uint8_t buff_data[8]; 10 | 11 | /* Application variables */ 12 | uint8_t data[8]; 13 | size_t len; 14 | 15 | /* Application code ... */ 16 | lwrb_init(&buff, buff_data, sizeof(buff_data)); /* Initialize buffer */ 17 | 18 | /* Change the W, R to initial position. */ 19 | lwrb_write(&buff, "0123456", 7); 20 | lwrb_read(&buff, data, 6); 21 | memset(data, 0, sizeof(data)); 22 | lwrb_write(&buff, "70123", 5); 23 | printf("Initial: R = %ld, W = %ld\r\n", buff.r, buff.w); 24 | 25 | /* Try to peek buffer */ 26 | lwrb_peek(&buff, 0, data, 4); 27 | printf("Peek data skip 0 count: %c, %c, %c, %c\r\n", data[0], data[1], data[2], data[3]); 28 | lwrb_peek(&buff, 2, data, 4); 29 | printf("Peek data skip 2 count: %c, %c, %c, %c\r\n", data[0], data[1], data[2], data[3]); 30 | 31 | return; 32 | } 33 | MSH_CMD_EXPORT(lwrb_peek, lwrb peek example); 34 | 35 | -------------------------------------------------------------------------------- /examples/zero_copy_from_lwrb_memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lwrb/lwrb.h" 3 | 4 | static void send_data(uint8_t *data, size_t len) 5 | { 6 | rt_kprintf("Send data len: %d, data: ", (int)len); 7 | for (size_t i = 0; i < len; i++) 8 | rt_kprintf(" %c", data[i]); 9 | rt_kprintf("\r\n"); 10 | } 11 | 12 | static void lwrb_zero_copy_from_lwrb_memory(void) 13 | { 14 | /* Declare rb instance & raw data */ 15 | lwrb_t buff; 16 | uint8_t buff_data[8]; 17 | 18 | size_t len; 19 | uint8_t* data; 20 | 21 | uint8_t read_data[8]; 22 | 23 | /* Initialize buffer, use buff_data as data array */ 24 | lwrb_init(&buff, buff_data, sizeof(buff_data)); 25 | 26 | /* Use write, read operations, process data */ 27 | lwrb_write(&buff, "0123456", 7); 28 | lwrb_read(&buff, read_data, 5); 29 | lwrb_write(&buff, "70123", 5); 30 | 31 | /* IMAGE PART A */ 32 | rt_kprintf("PART A: R = %ld, W = %ld\r\n", buff.r, buff.w); 33 | 34 | /* At this stage, we have buffer as on image above */ 35 | /* R = 5, W = 4, buffer is considered full */ 36 | 37 | /* Get length of linear memory at read pointer */ 38 | /* Function returns 3 as we can read 3 bytes from buffer in sequence */ 39 | /* When function returns 0, there is no memory available in the buffer for read anymore */ 40 | if ((len = lwrb_get_linear_block_read_length(&buff)) > 0) { 41 | /* Get pointer to first element in linear block at read address */ 42 | /* Function returns &buff_data[5] */ 43 | data = lwrb_get_linear_block_read_address(&buff); 44 | 45 | /* Send data via DMA and wait to finish (for sake of example) */ 46 | send_data(data, len); 47 | 48 | /* Now skip sent bytes from buffer = move read pointer */ 49 | lwrb_skip(&buff, len); 50 | 51 | /* Now R points to top of buffer, R = 0 */ 52 | /* At this point, we are at image part B */ 53 | } 54 | 55 | /* IMAGE PART B */ 56 | rt_kprintf("PART B: R = %ld, W = %ld\r\n", buff.r, buff.w); 57 | 58 | /* Get length of linear memory at read pointer */ 59 | /* Function returns 4 as we can read 4 bytes from buffer in sequence */ 60 | /* When function returns 0, there is no memory available in the buffer for read anymore */ 61 | if ((len = lwrb_get_linear_block_read_length(&buff)) > 0) { 62 | /* Get pointer to first element in linear block at read address */ 63 | /* Function returns &buff_data[0] */ 64 | data = lwrb_get_linear_block_read_address(&buff); 65 | 66 | /* Send data via DMA and wait to finish (for sake of example) */ 67 | send_data(data, len); 68 | 69 | /* Now skip sent bytes from buffer = move read pointer */ 70 | /* Read pointer is moved for len bytes */ 71 | lwrb_skip(&buff, len); 72 | 73 | /* Now R points to 4, that is R == W and buffer is now empty */ 74 | /* At this point, we are at image part C */ 75 | } 76 | 77 | /* IMAGE PART C */ 78 | rt_kprintf("PART C: R = %ld, W = %ld\r\n", buff.r, buff.w); 79 | 80 | /* Buffer is considered empty as R == W */ 81 | 82 | } 83 | MSH_CMD_EXPORT(lwrb_zero_copy_from_lwrb_memory, lwrb zero copy from lwrb memory example); 84 | -------------------------------------------------------------------------------- /examples/zero_copy_to_lwrb_memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lwrb/lwrb.h" 3 | 4 | static void receive_data(uint8_t *data, size_t len) 5 | { 6 | static uint8_t count = 3; 7 | 8 | rt_kprintf("Receive data len: %d, data: ", (int)len); 9 | uint8_t *p = data; 10 | uint8_t *end = data + len - 1; 11 | while (p <= end) { 12 | count = (count + 1) % 8; 13 | *p = '0' + count; 14 | rt_kprintf(" %c", *p); 15 | 16 | p++; 17 | } 18 | rt_kprintf("\r\n"); 19 | } 20 | 21 | static void lwrb_zero_copy_to_lwrb_memory(void) 22 | { 23 | /* Declare rb instance & raw data */ 24 | lwrb_t buff; 25 | uint8_t buff_data[8]; 26 | 27 | size_t len; 28 | uint8_t* data; 29 | 30 | uint8_t read_data[8]; 31 | 32 | /* Initialize buffer, use buff_data as data array */ 33 | lwrb_init(&buff, buff_data, sizeof(buff_data)); 34 | 35 | /* Use write, read operations, process data */ 36 | lwrb_write(&buff, "1234", 4); 37 | lwrb_read(&buff, read_data, 4); 38 | 39 | /* IMAGE PART A */ 40 | rt_kprintf("PART A: R = %ld, W = %ld\r\n", buff.r, buff.w); 41 | 42 | /* At this stage, we have buffer as on image above */ 43 | /* R = 4, W = 4, buffer is considered empty */ 44 | 45 | /* Get length of linear memory at write pointer */ 46 | /* Function returns 4 as we can write 4 bytes to buffer in sequence */ 47 | /* When function returns 0, there is no memory available in the buffer for write anymore */ 48 | if ((len = lwrb_get_linear_block_write_length(&buff)) > 0) { 49 | /* Get pointer to first element in linear block at write address */ 50 | /* Function returns &buff_data[4] */ 51 | data = lwrb_get_linear_block_write_address(&buff); 52 | 53 | /* Receive data via DMA and wait to finish (for sake of example) */ 54 | /* Any other hardware may directly write to data array */ 55 | /* Data array has len bytes length */ 56 | /* Or use memcpy(data, my_array, len); */ 57 | receive_data(data, len); 58 | 59 | /* Now advance buffer for written bytes to buffer = move write pointer */ 60 | /* Write pointer is moved for len bytes */ 61 | lwrb_advance(&buff, len); 62 | 63 | /* Now W points to top of buffer, W = 0 */ 64 | /* At this point, we are at image part B */ 65 | } 66 | 67 | /* IMAGE PART B */ 68 | rt_kprintf("PART B: R = %ld, W = %ld\r\n", buff.r, buff.w); 69 | 70 | /* Get length of linear memory at write pointer */ 71 | /* Function returns 3 as we can write 3 bytes to buffer in sequence */ 72 | /* When function returns 0, there is no memory available in the buffer for write anymore */ 73 | if ((len = lwrb_get_linear_block_write_length(&buff)) > 0) { 74 | /* Get pointer to first element in linear block at write address */ 75 | /* Function returns &buff_data[0] */ 76 | data = lwrb_get_linear_block_write_address(&buff); 77 | 78 | /* Receive data via DMA and wait to finish (for sake of example) */ 79 | /* Any other hardware may directly write to data array */ 80 | /* Data array has len bytes length */ 81 | /* Or use memcpy(data, my_array, len); */ 82 | receive_data(data, len); 83 | 84 | /* Now advance buffer for written bytes to buffer = move write pointer */ 85 | /* Write pointer is moved for len bytes */ 86 | lwrb_advance(&buff, len); 87 | 88 | /* Now W points to 3, R points to 4, that is R == W + 1 and buffer is now full */ 89 | /* At this point, we are at image part C */ 90 | } 91 | 92 | /* IMAGE PART C */ 93 | rt_kprintf("PART C: R = %ld, W = %ld\r\n", buff.r, buff.w); 94 | 95 | /* Buffer is considered full as R == W + 1 */ 96 | 97 | } 98 | MSH_CMD_EXPORT(lwrb_zero_copy_to_lwrb_memory, lwrb zero copy to lwrb memory example); 99 | --------------------------------------------------------------------------------