BouffaloSDK 使用指南
欢迎来到 Bouffalo Lab BouffaloSDK 文档中心。
环境搭建
本节主要介绍 BouffaloSDK 的环境搭建,支持 windows 和 linux, macos 参考 linux。在阅读下面之前,需要保证你的电脑已经安装了 git 工具。
下载 SDK
BouffaloSDK 下载可以从 github 或者 gitee 上获取
1$ cd ~
2$ git clone https://github.com/bouffalolab/bouffalo_sdk.git # 使用国内镜像请将 github 更换成 gitee
安装
BouffaloSDK 编译需要用到 gcc 工具链,cmake , make,ninja(可选,用于加速代码编译)构建系统,下面讲述如何安装和配置。
Windows 环境
下载 GCC 工具链
1$ cd D:/
2$ git clone https://gitee.com/bouffalolab/toolchain_gcc_t-head_windows.git
安装 cmake
无需安装, bouffalo_sdk/tools/cmake 目录下已经提供 windows 版本 cmake 工具
安装 make
无需安装, bouffalo_sdk/tools/make 目录下已经提供 windows 版本 make 工具
安装 ninja
无需安装, bouffalo_sdk/tools/ninja 目录下已经提供 windows 版本 ninja 工具
配置环境变量
将 make、ninja、gcc 工具链路径配置到系统环境变量中

完成上述步骤以后,需要检查是否真的安装成功。打开 powershell 或者 cmd,输入下列命令:
1$ make -v
此时应该显示如下内容表示安装成功:
1 GNU Make 4.2.1
2 Built for x86_64-w64-mingw32
3 Copyright (C) 1988-2016 Free Software Foundation, Inc.
4 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
5 This is free software: you are free to change and redistribute it.
6 There is NO WARRANTY, to the extent permitted by law.
1$ riscv64-unknown-elf-gcc -v
此时应该显示如下内容表示安装成功:
1 Using built-in specs.
2 COLLECT_GCC=riscv64-unknown-elf-gcc
3 COLLECT_LTO_WRAPPER=/mnt/d/EmbeddedSoftware/Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/bin/../libexec/gcc/riscv64-unknown-elf/10.2.0/lto-wrapper
4 Target: riscv64-unknown-elf
5 Configured with: /lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/./source/riscv/riscv-gcc/configure --target=riscv64-unknown-elf --with-gmp=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-mpfr=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-mpc=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-libexpat-prefix=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-libmpfr-prefix=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-pkgversion='Xuantie-900 elf newlib gcc Toolchain V2.2.5 B-20220323' CXXFLAGS='-g -O2 -DTHEAD_VERSION_NUMBER=2.2.5' --enable-libgcctf --prefix=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5 --disable-shared --disable-threads --enable-languages=c,c++ --with-system-zlib --enable-tls --with-newlib --with-sysroot=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/riscv64-unknown-elf --with-native-system-header-dir=/include --disable-libmudflap --disable-libssp --disable-libquadmath --disable-libgomp --disable-nls --disable-tm-clone-registry --src=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/./source/riscv/riscv-gcc --enable-multilib --with-abi=lp64d --with-arch=rv64gcxthead 'CFLAGS_FOR_TARGET=-Os -mcmodel=medany' 'CXXFLAGS_FOR_TARGET=-Os -mcmodel=medany'
6 Thread model: single
7 Supported LTO compression algorithms: zlib
8 gcc version 10.2.0 (Xuantie-900 elf newlib gcc Toolchain V2.2.5 B-20220323)
Linux 环境
下载 GCC 工具链
1$ cd ~
2$ git clone https://gitee.com/bouffalolab/toolchain_gcc_t-head_linux.git
3$ sudo cp -rf toolchain_gcc_t-head_linux/ /usr/bin
4$ echo "export PATH=\"$PATH:/usr/bin/toolchain_gcc_t-head_linux/bin\"" >> ~/.bashrc
5$ source ~/.bashrc
安装 cmake
无需安装, bouffalo_sdk/tools/cmake 目录下已经提供 linux 版本 cmake 工具
安装 make
1$ cd ~
2$ sudo apt install make -y
安装 ninja
1$ cd ~
2$ sudo apt install ninja-build -y
配置环境变量
上述安装好以后就已经配置到系统环境变量了,无需手动配置
完成上述步骤以后,需要检查是否真的安装成功,在 linux 终端中输入下列命令:
1$ make -v
此时应该显示如下内容表示安装成功:
1 GNU Make 4.1
2 Built for x86_64-pc-linux-gnu
3 Copyright (C) 1988-2014 Free Software Foundation, Inc.
4 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
5 This is free software: you are free to change and redistribute it.
6 There is NO WARRANTY, to the extent permitted by law.
1$ riscv64-unknown-elf-gcc -v
此时应该显示如下内容表示安装成功:
1 Using built-in specs.
2 COLLECT_GCC=riscv64-unknown-elf-gcc
3 COLLECT_LTO_WRAPPER=/mnt/d/EmbeddedSoftware/Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/bin/../libexec/gcc/riscv64-unknown-elf/10.2.0/lto-wrapper
4 Target: riscv64-unknown-elf
5 Configured with: /lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/./source/riscv/riscv-gcc/configure --target=riscv64-unknown-elf --with-gmp=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-mpfr=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-mpc=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-libexpat-prefix=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-libmpfr-prefix=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/build-Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/lib-for-gcc-x86_64-linux --with-pkgversion='Xuantie-900 elf newlib gcc Toolchain V2.2.5 B-20220323' CXXFLAGS='-g -O2 -DTHEAD_VERSION_NUMBER=2.2.5' --enable-libgcctf --prefix=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5 --disable-shared --disable-threads --enable-languages=c,c++ --with-system-zlib --enable-tls --with-newlib --with-sysroot=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/build-riscv-gcc-riscv64-unknown-elf/Xuantie-900-gcc-elf-newlib-x86_64-V2.2.5/riscv64-unknown-elf --with-native-system-header-dir=/include --disable-libmudflap --disable-libssp --disable-libquadmath --disable-libgomp --disable-nls --disable-tm-clone-registry --src=/lhome/software/toolsbuild/slave2/workspace/Toolchain/build-gnu-riscv_4/./source/riscv/riscv-gcc --enable-multilib --with-abi=lp64d --with-arch=rv64gcxthead 'CFLAGS_FOR_TARGET=-Os -mcmodel=medany' 'CXXFLAGS_FOR_TARGET=-Os -mcmodel=medany'
6 Thread model: single
7 Supported LTO compression algorithms: zlib
8 gcc version 10.2.0 (Xuantie-900 elf newlib gcc Toolchain V2.2.5 B-20220323)
编译
BouffaloSDK 编译代码提供两种编译方式:make 和 ninja
使用 make 编译
1 $ cd examples/helloworld
2 $ make CHIP=chip_name BOARD=board_name ## chip_name 为芯片型号,可以填写 bl702、bl616、bl808、bl606p, board_name 为开发板名称,详见 bsp/board 目录
使用 ninja 编译
1 $ cd examples/helloworld
2 $ make ninja CHIP=chip_name BOARD=board_name ## chip_name 为芯片型号,可以填写 bl702、bl616、bl808、bl606p, board_name 为开发板名称,详见 bsp/board 目录
小心
如果使用 BL808 或者 BL606P,需要在上面基础上添加 CPU_ID=id ,id 可以为 m0 或者 d0
小心
chip_name 相同系列只需要填 pin 脚最少的芯片名称,比如使用 bl618 则填写 bl616,使用 bl706 则填写 bl702
烧录
BouffaloSDK 烧录代码提供两种:命令行和界面。需要注意,如果使用第三方 usb转串口模块,部分不支持 2M 波特率烧录,例如 CH340,请降低波特率使用(小于500K)
备注
在烧录之前需要保证芯片进入烧录模式:按住 boot 键,按下 reset 键,松开 reset 键,松开 boot,即可进入烧录模式
命令行烧录
1 $ cd examples/helloworld
2 $ make flash CHIP=chip_name COMX=port_name ## port_name 为串口号名称
小心
如果使用 BL808 或者 BL606P,需要在上面基础上添加 CPU_ID=id ,id 可以为 m0 或者 d0
小心
如果使用 WSL ,烧录请使用界面 windows 版本
界面烧录
windows 环境请用后缀带 .exe 文件,其余同理

双击打开
点击 Broswe,导入烧录文件(位于每个 demo 下,名称为 flash_prog_cfg.ini)
选择芯片型号
选择烧录的串口号
选择波特率
点击 download
界面提示绿色的 100% 表示烧录完成
如果烧录失败,请检查烧录口是否正确,芯片是否进入烧录模式,供电是否正常,usb转串口模块是否支持设置的波特率

运行
烧录完成以后,按下 reset 键,程序就运行了,并且可以使用串口工具查看 log,默认波特率 2M。推荐使用微软商店串口调试助手或者 mobaxterm
调试
BouffaloSDK 当前仅支持使用 eclipse + cklink 调试。参考 Build and Debug with Eclipse
芯片默认 JTAG PIN 列表
CHIP/Pin |
BL602/BL604 |
BL702/BL704/BL706 |
BL616/BL618 |
BL808 |
---|---|---|---|---|
TMS |
GPIO12 |
GPIO0 |
GPIO0 |
GPIO6 |
TCK |
GPIO14 |
GPIO2 |
GPIO1 |
GPIO12 |
TDO |
GPIO11 |
GPIO9 |
GPIO2 |
GPIO7 |
TDI |
GPIO17 |
GPIO1 |
GPIO3 |
GPIO13 |
Build and Debug with Eclipse
本节主要介绍如何使用 Eclipse 进行开发。
环境搭建
安装 GCC 工具链,cmake, make 和 ninja 流程参考 环境搭建
下载 Eclipse , 选择 Eclipse IDE for C/C++ Developers , 并根据你的电脑版本进行下载。

解压 Eclipse 并双击打开 Eclipse.exe

选择工作路径并点击 Launch

点击左上角 File 选择 Import,选择 General > Existing project into workspace 并点击 Next

导入 bl_mcu_sdk

编译
双击 Build Targets 中的 make 即可编译 helloworld。双击 clean 即可清除 helloworld 下的缓存

如果需要更换 demo ,右击 Build Targets 中的 make,修改中间方框里的内容即可,内容为指定 demo 下 Makefile 文件的路径

调试
从 T-HEAD 官网下载最新版本 CKLink 驱动
将下载的 CKLink 安装包解压,并双击 Setup ,一路 next 即可,安装完成以后,桌面会有一个 T-HeadDebugServer 图标


将板子的 JTAG 引脚和 CKLink 调试器连接以后,点击 三角 按钮,如果变成 圆圈,则表示 JTAG 连接成功。如果失败,则需要检查 JTAG 线序是否正确。

完成上述步骤以后,如果是在 flash 上调试,需要先将编译的 bin 文件烧录到芯片中并复位 ,然后才能够进行下面的调试环节。
在 Eclipse 中 点击 debug 图标,并点击 Debug Configurations

左侧打开 GDB Hardware Debugging,选择对应芯片的配置按钮,并导入编译生成的 elf, 最后点击 Debug

最终进入调试界面

CMake 框架的使用
本节主要介绍如何使用 CMake 框架。BouffaloSDK 中为用户封装了以下函数接口,基本满足常用场景的使用。
Function |
Description |
---|---|
sdk_generate_library |
生成库,库名如果函数没有形参则使用当前库所在目录名 |
sdk_library_add_sources |
为库添加源文件 |
sdk_library_add_sources_ifdef |
为库添加源文件(满足 if 条件) |
sdk_add_include_directories |
添加头文件路径 |
sdk_add_include_directories_ifdef |
添加头文件路径(满足 if 条件) |
sdk_add_compile_definitions |
添加宏定义,不要带 -D |
sdk_add_compile_definitions_ifdef |
添加宏定义(满足 if 条件) |
sdk_add_compile_options |
添加编译选项 |
sdk_add_compile_options_ifdef |
添加编译选项(满足 if 条件) |
sdk_add_link_options |
添加链接选项 |
sdk_add_link_options_ifdef |
添加链接选项(满足 if 条件) |
sdk_add_link_libraries |
添加静态库 |
sdk_add_link_libraries_ifdef |
添加静态库(满足 if 条件) |
sdk_add_subdirectory_ifdef |
编译子目录下的 cmakelist(满足 if 条件) |
sdk_add_static_library |
添加外部静态库 |
sdk_set_linker_script |
设置链接脚本 |
sdk_set_main_file |
设置 main 函数所在文件 |
project |
工程编译 |
target_source(app PRIVATE xxx) |
添加源文件到 app 库中,当用户需要自己添加一些源文件又不想创建 cmakelist 单独编译成库,可以使用该项 |
新建工程
用户可以复制一份工程,如 helloworld,并修改 SDK_DEMO_PATH 变量为你使用的路径即可创建一份全新的工程
为工程添加组件
如果需要编译相关组件,如 FATFS、LVGL ,需要在 proj.conf 文件中添加组件的使能,举例如下:
1 set(CONFIG_FATFS 1)
2 set(CONFIG_LVGL 1)
使能条件编译项
用户自定义的 cmake 条件编译项( 使用了 cmake 的 if 语法)、或者使用了 sdk 带 ifdef 结尾的函数, 使能方式同上
bouffalo_flash_cube 的使用
BouffaloSDK 采用新的 flash tool ( bouffalo_flash_cube ),并且烧录依赖 Flash prog cfg.ini 文件。
相比于 Bouffalo Lab DevCube 繁琐的功能,bouffalo_flash_cube 只用于下载代码,在用户执行 make flash
时会调用 bouffalo_flash_cube 下的可执行文件,并根据 Flash prog cfg.ini 进行烧录,
本节主要介绍一下 Flash prog cfg.ini 的语法。更详细的介绍参考 tools/bflb_tools/bouffalo_flash_cube/docs/FlashCube_User_Guide.pdf
语法
Flash prog cfg.ini 正常使用只需要创建一个 KEY,例如 [FW],并且填写 filedir 和 address 就可以使用了。
其中 filedir 的填写方式有以下几种:
bin 文件全路径 + bin 文件名称
bin 文件相对路径 + bin 文件名称
bin 文件名称添加 _$(CHIPNAME) 后缀可以自动识别成不同芯片(仅在 bin 文件名称前缀不同的时候使用)
bin 文件名称添加 * 通配符,可以自动补全bin 文件名称(仅在 bin 文件名称前缀不同的时候使用)
常规 MCU 使用(不使用无线功能)
1 [cfg]
2 # 0: no erase, 1:programmed section erase, 2: chip erase
3 erase = 1
4 # skip mode set first para is skip addr, second para is skip len, multi-segment region with ; separated
5 skip_mode = 0x0, 0x0
6 # 0: not use isp mode, #1: isp mode
7 boot2_isp_mode = 0
8
9 [FW]
10 filedir = ./build/build_out/xxx*_$(CHIPNAME).bin
11 address = 0x0000
cfg 表示烧录时的一些配置,正常不需要改动
FW 要烧录的应用固件,必须使用 FW 名称。
filedir 表示应用固件所在相对路径,正常来说是编译完后放在 build/build_out 目录。
_$(CHIPNAME).bin
用于自动区分不同芯片。xxx
表示应用固件名称,与 CMakeLists.txt 中 project(xxx) 中名称一致。*
表示正则匹配,可用可不用。address 必须使用 0 地址
常规 IOT 使用(使用无线功能)
1 [cfg]
2 # 0: no erase, 1:programmed section erase, 2: chip erase
3 erase = 1
4 # skip mode set first para is skip addr, second para is skip len, multi-segment region with ; separated
5 skip_mode = 0x0, 0x0
6 # 0: not use isp mode, #1: isp mode
7 boot2_isp_mode = 0
8
9 [boot2]
10 filedir = ./build/build_out/boot2_*.bin
11 address = 0x000000
12
13 [partition]
14 filedir = ./build/build_out/partition*.bin
15 address = 0xE000
16
17 [FW]
18 filedir = ./build/build_out/xxx*_$(CHIPNAME).bin
19 address = 0x10000
20
21 [mfg]
22 filedir = ./build/build_out/mfg*.bin
23 address = 0x210000
cfg 表示烧录时的一些配置,正常不需要改动
FW 要烧录的应用固件,必须使用 FW 名称。
filedir 表示应用固件所在相对路径,正常来说是编译完后放在 build/build_out 目录。
_$(CHIPNAME).bin
用于区分不同芯片。xxx
表示应用固件名称,与 CMakeLists.txt 中 project(xxx) 中名称一致。address 由 partition_xxx.toml 指定
boot2 要烧录的 boot2 固件,必须使用 boot2 名称。
filedir 表示 boot2 固件所在相对路径,正常来说是编译完后放在 build/build_out 目录。 自动从 bsp/board/board_name/config 目录拷贝。
address 必须使用 0 地址
partition 要烧录的 partition 固件,必须使用 partition 名称。
filedir 表示 partition 固件所在相对路径,正常来说是编译完后放在 build/build_out 目录。 自动从 bsp/board/board_name/config 目录 partition_xxx.toml 转换成 bin 文件并拷贝。
address 由 partition_xxx.toml 指定
mfg 要烧录的 mfg 固件,必须使用 mfg 名称。 mfg 可选,可以不烧录
filedir 表示 mfg 固件所在相对路径,正常来说是编译完后放在 build/build_out 目录。 自动从 bsp/board/board_name/config 目录拷贝。
address 由 partition_xxx.toml 指定
备注
如果使用了 partition,address 可以使用 @partition 代替绝对地址,@partition 将会自动从 partition_xxx.toml 找到对应的地址
1 [cfg]
2 # 0: no erase, 1:programmed section erase, 2: chip erase
3 erase = 1
4 # skip mode set first para is skip addr, second para is skip len, multi-segment region with ; separated
5 skip_mode = 0x0, 0x0
6 # 0: not use isp mode, #1: isp mode
7 boot2_isp_mode = 0
8
9 [boot2]
10 filedir = ./build/build_out/boot2_*.bin
11 address = 0x000000
12
13 [partition]
14 filedir = ./build/build_out/partition*.bin
15 address = 0xE000
16
17 [FW]
18 filedir = ./build/build_out/xxx*_$(CHIPNAME).bin
19 address = 0x10000
20
21 [mfg]
22 filedir = ./build/build_out/mfg*.bin
23 address = 0x210000
多个运行固件烧录
禁止使用通配符 * 以及 _$(CHIPNAME)
前缀,因为 bin 文件名称前缀相同。
1 [cfg]
2 # 0: no erase, 1:programmed section erase, 2: chip erase
3 erase = 1
4 # skip mode set first para is skip addr, second para is skip len, multi-segment region with ; separated
5 skip_mode = 0x0, 0x0
6 # 0: not use isp mode, #1: isp mode
7 boot2_isp_mode = 0
8
9 [FW1]
10 filedir = ./build/build_out/xxx0.bin
11 address = 0x00000
12
13 [FW2]
14 filedir = ./build/build_out/xxx1.bin
15 address = 0x10000
16
17 [FW3]
18 filedir = ./build/build_out/xxx2.bin
19 address = 0x20000
Repo 的使用
Repo 是谷歌用 Python 脚本写的调用 git 的一个脚本,可以实现管理多个 git 库,简单的几行命令便可以拉取很多仓库的代码。
安装
下载 repo 。
windows 下将 repo.exe 文件(在 win 目录中)所在的路径配置到系统环境变量。

Linux 下执行以下命令。
1 cd repo/linux
2 chmod +x repo
3 mv repo /usr/bin/repo
下载代码
请联系商务申请账号。
同步代码
后续只需要使用 repo sync
即可同步代码。
LHAL
LHAL 是 BouffaloSDK 为统一通用外设而实现的驱动库,不同芯片使用同一套接口,方便用户使用和移植到其他平台。
LHAL API 文档请参考 doxygen for lhal 。
BFLOG
简介
BFLOG 是一个移植简单,功能多样的多线程日志记录库
具有同步异步两种工作模式,异步模式如果缓冲区溢出会将最早的一条记录完整删除
总体三部分、记录器、定向输出、格式化器
一个记录器可多重定向输出,可同时输出到缓冲区、IO外设、文件、文件大小划分、文件时间划分
定向到文件输出,支持设定保留数量,支持按文件大小划分,支持按时间划分
每个定向输出可单独设定格式化器、输出等级、TAG标签过滤、输出方式、颜色
格式化器支持简单格式、用户自定义格式、YAML格式(实现中)、CSV格式(规划中)
六级日志等级控制, FATAL、ERROR、WARNING、INFO、DEBUG、TRACE
支持等级、TAG标签、函数、行数、文件名、TICK数、TIME、线程信息输出
支持等级、TAG标签过滤功能
可对不需要的等级、功能、标签进行裁剪,缩小代码体积

log 样式
配置BFLOG相关功能
如果需要配置BFLOG的相关功能需要在对应的工程目录下 proj.conf 文件中添加对应的代码,举例如下:
以下是一个不支持文件输出的 proj.conf 配置
1# 使能 BFLOG
2set(CONFIG_BFLOG 1)
3# 使能参数检查, 可不开启
4set(CONFIG_BFLOG_DEBUG 1)
此外在 proj.conf 配置中添加以下配置可以使用用户的 bflog_user.h 配置文件,配置文件默认为 bflog_default.h
1# 使能 BFLOG_USER 配置文件
2set(CONFIG_BFLOG_USER 1)
Macros
BFLOG_CSI
Control Sequence Introducer
用于控制终端
1 #define BFLOG_CSI_START "\033["
2 #define BFLOG_CSI_CUU "A"
3 #define BFLOG_CSI_CUD "B"
4 #define BFLOG_CSI_CUF "C"
5 #define BFLOG_CSI_CUB "D"
6 #define BFLOG_CSI_CNL "E"
7 #define BFLOG_CSI_CPL "F"
8 #define BFLOG_CSI_CHA "G"
9 #define BFLOG_CSI_CUP "H"
10 #define BFLOG_CSI_ED "J"
11 #define BFLOG_CSI_EL "K"
12 #define BFLOG_CSI_SU "S"
13 #define BFLOG_CSI_SD "T"
14 #define BFLOG_CSI_SGR "m"
BFLOG_SGR
Select Graphic Rendition
用于文字图形
1 #define BFLOG_SGR_RESET "0"
2 #define BFLOG_SGR_BOLD "1"
3 #define BFLOG_SGR_FAINT "2"
4 #define BFLOG_SGR_ITALICS "3"
5 #define BFLOG_SGR_UNDERLINE "4"
6 #define BFLOG_SGR_BLINKS "5"
7 #define BFLOG_SGR_BLINKR "6"
8 #define BFLOG_SGR_REVERSE "7"
9 #define BFLOG_SGR_HIDE "8"
10 #define BFLOG_SGR_STRIKE "9"
11 #define BFLOG_SGR_NORMAL "22"
12 #define BFLOG_SGR_FG_BLACK "30"
13 #define BFLOG_SGR_FG_RED "31"
14 #define BFLOG_SGR_FG_GREEN "32"
15 #define BFLOG_SGR_FG_YELLOW "33"
16 #define BFLOG_SGR_FG_BLUE "34"
17 #define BFLOG_SGR_FG_MAGENTA "35"
18 #define BFLOG_SGR_FG_CYAN "36"
19 #define BFLOG_SGR_FG_WHITE "37"
20 #define BFLOG_SGR_BG_BLACK "40"
21 #define BFLOG_SGR_BG_RED "41"
22 #define BFLOG_SGR_BG_GREEN "42"
23 #define BFLOG_SGR_BG_YELLOW "43"
24 #define BFLOG_SGR_BG_BLUE "44"
25 #define BFLOG_SGR_BG_MAGENTA "45"
26 #define BFLOG_SGR_BG_CYAN "46"
27 #define BFLOG_SGR_BG_WHITE "47"
BFLOG_COLOR
一系列颜色用于配置使用
1 #define BFLOG_COLOR_START BFLOG_CSI_START
2 #define BFLOG_COLOR_END BFLOG_CSI_SGR
3 #define BFLOG_CLOLR_SEP ";"
4 #define BFLOG_COLOR_DEFAULT
5 #define BFLOG_COLOR_RESET BFLOG_SGR_RESET BFLOG_CLOLR_SEP
6 #define BFLOG_COLOR_FG_NONE
7 #define BFLOG_COLOR_FG_BLACK BFLOG_SGR_FG_BLACK BFLOG_CLOLR_SEP
8 #define BFLOG_COLOR_FG_RED BFLOG_SGR_FG_RED BFLOG_CLOLR_SEP
9 #define BFLOG_COLOR_FG_GREEN BFLOG_SGR_FG_GREEN BFLOG_CLOLR_SEP
10 #define BFLOG_COLOR_FG_YELLOW BFLOG_SGR_FG_YELLOW BFLOG_CLOLR_SEP
11 #define BFLOG_COLOR_FG_BLUE BFLOG_SGR_FG_BLUE BFLOG_CLOLR_SEP
12 #define BFLOG_COLOR_FG_MAGENTA BFLOG_SGR_FG_MAGENTA BFLOG_CLOLR_SEP
13 #define BFLOG_COLOR_FG_CYAN BFLOG_SGR_FG_CYAN BFLOG_CLOLR_SEP
14 #define BFLOG_COLOR_FG_WHITE BFLOG_SGR_FG_WHITE BFLOG_CLOLR_SEP
15 #define BFLOG_COLOR_BG_NONE
16 #define BFLOG_COLOR_BG_BLACK BFLOG_SGR_BG_BLACK BFLOG_CLOLR_SEP
17 #define BFLOG_COLOR_BG_RED BFLOG_SGR_BG_RED BFLOG_CLOLR_SEP
18 #define BFLOG_COLOR_BG_GREEN BFLOG_SGR_BG_GREEN BFLOG_CLOLR_SEP
19 #define BFLOG_COLOR_BG_YELLOW BFLOG_SGR_BG_YELLOW BFLOG_CLOLR_SEP
20 #define BFLOG_COLOR_BG_BLUE BFLOG_SGR_BG_BLUE BFLOG_CLOLR_SEP
21 #define BFLOG_COLOR_BG_MAGENTA BFLOG_SGR_BG_MAGENTA BFLOG_CLOLR_SEP
22 #define BFLOG_COLOR_BG_CYAN BFLOG_SGR_BG_CYAN BFLOG_CLOLR_SEP
23 #define BFLOG_COLOR_BG_WHITE BFLOG_SGR_BG_WHITE BFLOG_CLOLR_SEP
BFLOG_COLOR_CONTROL
默认配置的各等级LOG颜色
1 #ifndef BFLOG_COLOR_FATAL
2 #define BFLOG_COLOR_FATAL BFLOG_COLOR_FG_MAGENTA BFLOG_COLOR_BG_NONE BFLOG_SGR_BLINKS
3 #endif
4
5 #ifndef BFLOG_COLOR_ERROR
6 #define BFLOG_COLOR_ERROR BFLOG_COLOR_FG_RED BFLOG_COLOR_BG_NONE BFLOG_SGR_NORMAL
7 #endif
8
9 #ifndef BFLOG_COLOR_WARN
10 #define BFLOG_COLOR_WARN BFLOG_COLOR_FG_YELLOW BFLOG_COLOR_BG_NONE BFLOG_SGR_NORMAL
11 #endif
12
13 #ifndef BFLOG_COLOR_INFO
14 #define BFLOG_COLOR_INFO BFLOG_COLOR_FG_NONE BFLOG_COLOR_BG_NONE BFLOG_SGR_RESET
15 #endif
16
17 #ifndef BFLOG_COLOR_DEBUG
18 #define BFLOG_COLOR_DEBUG BFLOG_COLOR_FG_WHITE BFLOG_COLOR_BG_NONE BFLOG_SGR_NORMAL
19 #endif
20
21 #ifndef BFLOG_COLOR_TRACE
22 #define BFLOG_COLOR_TRACE BFLOG_COLOR_FG_WHITE BFLOG_COLOR_BG_NONE BFLOG_SGR_FAINT
23 #endif
BFLOG_LEVEL_STRING
默认配置的各等级提示信息
1 #ifndef BFLOG_LEVEL_FATAL_STRING
2 #define BFLOG_LEVEL_FATAL_STRING "FATL"
3 #endif
4
5 #ifndef BFLOG_LEVEL_ERROR_STRING
6 #define BFLOG_LEVEL_ERROR_STRING "ERRO"
7 #endif
8
9 #ifndef BFLOG_LEVEL_WARN_STRING
10 #define BFLOG_LEVEL_WARN_STRING "WARN"
11 #endif
12
13 #ifndef BFLOG_LEVEL_INFO_STRING
14 #define BFLOG_LEVEL_INFO_STRING "INFO"
15 #endif
16
17 #ifndef BFLOG_LEVEL_DEBUG_STRING
18 #define BFLOG_LEVEL_DEBUG_STRING "DBUG"
19 #endif
20
21 #ifndef BFLOG_LEVEL_TRACE_STRING
22 #define BFLOG_LEVEL_TRACE_STRING "TRAC"
23 #endif
BFLOG_LEVEL
用于配置 recorder 和 direct 的 LOG等级
1 #define BFLOG_LEVEL_FATAL 0x00 /*!< level fatal, create a panic */
2 #define BFLOG_LEVEL_ERROR 0x01 /*!< level error */
3 #define BFLOG_LEVEL_WARN 0x02 /*!< level warning */
4 #define BFLOG_LEVEL_INFO 0x03 /*!< level information */
5 #define BFLOG_LEVEL_DEBUG 0x04 /*!< level debug */
6 #define BFLOG_LEVEL_TRACE 0x05 /*!< level trace information */
BFLOG_FLAG
用于配置 recorder 和 direct 的功能
1 #define BFLOG_FLAG_LEVEL ((uint8_t)0x01) /*!< supported print level */
2 #define BFLOG_FLAG_TAG ((uint8_t)0x02) /*!< supported record tag */
3 #define BFLOG_FLAG_FUNC ((uint8_t)0x04) /*!< supported record function */
4 #define BFLOG_FLAG_LINE ((uint8_t)0x08) /*!< supported record line */
5 #define BFLOG_FLAG_FILE ((uint8_t)0x10) /*!< supported record file */
6 #define BFLOG_FLAG_CLK ((uint8_t)0x20) /*!< supported record clock */
7 #define BFLOG_FLAG_TIME ((uint8_t)0x40) /*!< supported record time */
8 #define BFLOG_FLAG_THREAD ((uint8_t)0x80) /*!< supported record thread */
BFLOG_MODE
用于配置 recorder 的模式
1 #define BFLOG_MODE_SYNC ((uint8_t)0x00)
2 #define BFLOG_MODE_ASYNC ((uint8_t)0x01)
BFLOG_COMMAND
配置命令,用于 bflog_control 第二个参数
1 #define BFLOG_CMD_FLAG ((uint32_t)0x01)
2 #define BFLOG_CMD_LEVEL ((uint32_t)0x02)
3 #define BFLOG_CMD_QUEUE_POOL ((uint32_t)0x03)
4 #define BFLOG_CMD_QUEUE_SIZE ((uint32_t)0x04)
5 #define BFLOG_CMD_QUEUE_RST ((uint32_t)0x05)
6 #define BFLOG_CMD_ENTER_CRITICAL ((uint32_t)0x06)
7 #define BFLOG_CMD_EXIT_CRITICAL ((uint32_t)0x07)
8 #define BFLOG_CMD_FLUSH_NOTICE ((uint32_t)0x08)
9 #define BFLOG_CMD_MODE ((uint32_t)0x09)
BFLOG_DIRECT_COMMAND
配置命令,用于 bflog_direct_control 第二个参数
1 #define BFLOG_DIRECT_CMD_ILLEGAL ((uint32_t)0x00)
2 #define BFLOG_DIRECT_CMD_LEVEL ((uint32_t)0x02)
3 #define BFLOG_DIRECT_CMD_LOCK ((uint32_t)0x06)
4 #define BFLOG_DIRECT_CMD_UNLOCK ((uint32_t)0x07)
5 #define BFLOG_DIRECT_CMD_COLOR ((uint32_t)0x0A)
BFLOG_DIRECT_TYPE
要创建的direct类型,用于 bflog_direct_create 第二个参数
1 #define BFLOG_DIRECT_TYPE_BUFFER ((uint8_t)0x01)
2 #define BFLOG_DIRECT_TYPE_STREAM ((uint8_t)0x02)
3 #define BFLOG_DIRECT_TYPE_FILE ((uint8_t)0x03)
4 #define BFLOG_DIRECT_TYPE_FILE_TIME ((uint8_t)0x04)
5 #define BFLOG_DIRECT_TYPE_FILE_SIZE ((uint8_t)0x05)
BFLOG_DIRECT_COLOR
是否启用颜色输出,用于 bflog_direct_create 第三个参数
1 #define BFLOG_DIRECT_COLOR_DISABLE ((uint8_t)0)
2 #define BFLOG_DIRECT_COLOR_ENABLE ((uint8_t)1)
BFLOG_LAYOUT_TYPE
要创建的layout类型,用于 bflog_layout_create 第二个参数
1 #define BFLOG_LAYOUT_TYPE_SIMPLE ((uint8_t)0)
2 #define BFLOG_LAYOUT_TYPE_FORMAT ((uint8_t)1)
3 #define BFLOG_LAYOUT_TYPE_YAML ((uint8_t)2)
Port Functions
移植要实现的接口函数
bflog_clock
获取当前cpu时钟数
1 uint64_t bflog_clock(void);
bflog_time
获取当前UTC时间戳
1 uint32_t bflog_time(void);
bflog_thread
获取当前线程名称
1 char *bflog_thread(void);
Global Functions
bflog_global_filter
用于对标签过滤器进行全局的开关,会影响所有的recorder和direct
1 int bflog_global_filter(void *tag_string, uint8_t enable);
parameter |
description |
---|---|
tag_string |
标签字符串的指针 |
enable |
是否使能 |
1 bflog_global_filter("YOURTAG", true);
2 bflog_global_filter("YOURTAG", false);
Recorder Functions
recorder负责收集日志, 具有illegal、ready、running、suspend四种状态 running状态可以收集日志, ready、suspend状态可以对其进行配置 除level配置操作外, 其他配置操作必须在ready、suspend下
bflog_create
创建一个recorder, 需要定义一个bflog_t结构体并将其指针传入,定义一块内存数组用于换冲
成功返回0,失败返回-1
1 int bflog_create(bflog_t *log, void *pool, uint16_t size, uint8_t mode);
parameter |
description |
---|---|
log |
recorder 指针 |
pool |
用于缓冲的数组 |
size |
用户缓冲的数组大小 |
mode |
BFLOG_MODE |
1 #include "bflog.h"
2
3 #define EXAMPLE_LOG_POOL_SIZE 4096
4
5 bflog_t example_recorder;
6 static uint32_t example_pool[EXAMPLE_LOG_POOL_SIZE / 4];
7
8 /*!< 创建一个记录器, 配置内存池, 内存池大小, 模式为同步 */
9 if (0 != bflog_create((void *)&example_recorder, example_pool, EXAMPLE_LOG_POOL_SIZE, BFLOG_MODE_SYNC)) {
10 printf("bflog_create faild\r\n");
11 }
bflog_delete
删除一个recorder
处于ready、suspend状态下
线程安全
成功返回0,失败返回-1
1 int bflog_delete(bflog_t *log);
parameter |
description |
---|---|
log |
recorder 指针 |
bflog_append
将一个direct添加到此recorder,可以添加多个direct,但一个direct只能被添加到一个recorder
处于ready、suspend状态下
线程安全
成功返回0,失败返回-1
1 int bflog_append(bflog_t *log, bflog_direct_t *direct);
parameter |
description |
---|---|
log |
recorder 指针 |
direct |
direct 指针 |
bflog_remove
将一个direct从recorder移除
处于ready、suspend状态下
线程安全
成功返回0,失败返回-1
1 int bflog_remove(bflog_t *log, bflog_direct_t *direct);
parameter |
description |
---|---|
log |
recorder 指针 |
direct |
direct 指针 |
bflog_suspend
将一个recorder挂起
处于ready、running、suspend状态下
线程安全
成功返回0,失败返回-1
1 int bflog_suspend(bflog_t *log);
parameter |
description |
---|---|
log |
recorder 指针 |
bflog_resume
将一个recorder恢复
处于ready、running、suspend状态下
线程安全
成功返回0,失败返回-1
1 int bflog_resume(bflog_t *log);
parameter |
description |
---|---|
log |
recorder 指针 |
bflog_control
对一个recorder进行配置
处于ready、suspend状态下;当command为BFLOG_CMD_LEVEL可以处于running状态下
线程安全
成功返回0,失败返回-1
1 int bflog_control(bflog_t *log, uint32_t command, uint32_t param);
parameter |
description |
---|---|
log |
recorder 指针 |
command |
配置命令 |
param |
配置参数 |
command param 可以填入以下参数:
command |
param |
description |
---|---|---|
BFLOG_CMD_FLAG |
BFLOG_FLAG |
配置recorder记录功能 |
BFLOG_CMD_LEVEL |
BFLOG_LEVEL |
配置recorder记录的等级 |
BFLOG_CMD_QUEUE_POOL |
内存池地址 |
配置用于缓冲的内存池地址 |
BFLOG_CMD_QUEUE_SIZE |
内存池大小(byte) |
配置用于缓冲的内存池大小 |
BFLOG_CMD_QUEUE_RST |
NULL |
复位缓冲区,清除缓冲区的所有内容 |
BFLOG_CMD_ENTER_CRITICAL |
int (* enter_critical)(void) 函数指针 |
配置进入临界区的函数 |
BFLOG_CMD_EXIT_CRITICAL |
int (* exit_critical)(void) 函数指针 |
配置退出临界区的函数 |
BFLOG_CMD_FLUSH_NOTICE |
int (* flush_notice)(void) 函数指针 |
配置清除缓冲区提示函数,用于多线程flush线程工作 |
BFLOG_CMD_MODE |
BFLOG_MODE |
配置recorder工作模式 |
bflog_filter
配置recorder的tag filter, 配置是否启用对应tag_string的log输出
处于ready、suspend、running状态下
线程安全
成功返回0,失败返回-1
1 int bflog_filter(bflog_t *log, void *tag_string, uint8_t enable);
parameter |
description |
---|---|
log |
recorder 指针 |
tag_string |
tag 字符串 |
enable |
是否使能 |
bflog
将log信息添加到recorder, sync模式会直接调用bflog_flush, async模式调用配置的flush_notice函数
处于running状态下
线程安全
成功返回0,失败返回-1
1 int bflog(void *log, uint8_t level, void *tag, const char *const file, const char *const func, const long line, const char *format, ...);
parameter |
description |
---|---|
log |
recorder 指针 |
level |
此条log的等级 |
tag |
此条log的tag结构体指针 |
file |
此条log的文件名字符串指针 |
func |
此条log的函数名字符串指针 |
line |
此条log的行号 |
format |
此条log的格式化字符串 |
… |
此条log的变长参数列表 |
bflog_flush
将队列中存储的所有log信息全部输出到recorder配置的所有的direct
处于running状态下
线程安全
成功返回0,失败返回-1
1 int bflog_flush(void *log);
parameter |
description |
---|---|
log |
recorder 指针 |
BLE
概述
- BLE支持的特性:
- 蓝牙HOST特性
GAP支持的角色:Peripheral与Central,Observer与Broadcaster
GATT支持的角色:Server与Client
支持配对包括蓝牙4.2中的安全连接特性
支持永久存储蓝牙特定的设置和数据
- 蓝牙mesh特性
支持Relay, Friend Node, Low-Power Node (LPN) and GATT Proxy角色
支持两种Provisioning bearers(PB-ADV & PB-GATT)
- BLE协议栈的架构:
- 总共有3个主要层,共同构成了一个完整的蓝牙低能耗协议栈
Host:这一层位于应用程序之下,由多个(非实时)网络和传输协议组成,使应用程序能够以标准和互操作的方式与对等设备通信。
Controller:控制器实现了链路层(LE LL),这是一种低层次的实时协议,它与无线电硬件一起提供了空中通信的标准互操作。LL处理包的接收和传输,保证数据的传递,并处理所有LL控制程序。
Radio Hardware:实现所需的模拟和数字基带功能块,允许链路层固件在频谱的2.4GHz波段发送和接收。
- 主控Host:
- 蓝牙Host层实现了所有高级协议和配置文件,最重要的是它为应用程序提供了高级API
HCI:Host与controller接口
L2CAP:逻辑链路控制和适应协议
GATT:通用属性配置层(Generic Attribute Profile)
GAP:通用访问配置层(Generic Access Profile)
SMP:安全管理器配置层(Security Manager Specification)
- 应用Application
- 应用层是用户开发实际蓝牙应用的地方,包含必要的协议栈参数设置,以及各种功能函数的调用。我们分别从蓝牙从机和蓝牙主机两种设备来分析。
- 蓝牙从机
相关硬件和基础服务初始化
设置广播参数:广播数据,广播间隔,扫描回应等参数或者数据
设置Profile:添加从机服务、特征值,还有设置回调函数用于接收主机数据等
设置配对参数(可选)
启动广播,开始运行
等待相关事件,及事件处理,例如收到主机发来的数据,被链接等等
- 蓝牙主机
相关硬件和基础服务初始化
设置扫描参数
设置连接参数
设置配对参数(可选)
启动协议栈,开始运行
等待相关事件,及事件处理,例如扫描事件,从机的Notify事件等等。
CLI 命令
ble_init
命令功能:ble通用初始化,在所有ble cli操作前,需要先输入该命令
参数:无
示例:输入命令
ble_init
ble_auth
命令功能:注册SMP接口函数
参数:无
示例:输入命令
ble_auth
ble_unpair
命令功能:清除配对key
第一个参数表示设备地址类型 - 0:设备表示public地址类型 - 1:表示设备地址为random类型 - 2:表示设备地址为可解析的地址或者Public地址 - 3:表示设备地址为可解析的地址或者random地址
第二个参数代表设备地址,高字节在前低字节在后,如果为0,代表清除所有设备的key
示例:输入命令
ble_unpair 0 0
ble_start_adv
命令功能表示:开启广播
第一个参数表示广播类型 - 0:adv_ind 可连接可被扫描; - 1:adv_scan_ind 不可被连接可被扫描; - 2:adv_nonconn_ind 不可被连接不可被扫描; - 3:adv_direct_ind 可被指定的设备连接不可被扫描
第二个参数表示广播模式 - 0:General discoverable; - 1:non discoverable; - 2:limit discoverable;
第三个参数表示广播间隙最小值,其计算方式为 0.625ms * N,范围应在20 ms to 10.24 s之间
第四个参数表示广播间隙最大值,其计算方式为 0.625ms * N,范围应在20 ms to 10.24 s之间
示例:输入命令
ble_start_adv 0 0 0x80 0x80
ble_stop_adv
命令功能:停止ADV广播
参数:无
示例:输入命令
ble_stop_adv
![]()
ble_start_scan
命令功能:表示扫描广播设备
第一个参数表示扫描类型 - 0:表示scan passive type只监听广播数据 - 1:表示scan active,不仅监听当满足条件还会发scan_req包
第二个参数表示过滤设备广播包 - 0:表示不启用重复过滤 - 1:表示启用重复过滤 - 2:仅仅接收白名单列表发起的广播和scan response包,除了指定连接地址不是自己的adv_direct_ind广播包 - 4:使用扩展过滤策略,过滤设备
第三个参数表示扫描间隙,其计算方式为 0.625ms * N,范围在2.5 ms to 10.24 s之间,其应该大于等于扫描窗口
第四个参数表示扫描窗口,其计算方式为 0.625ms * N,范围在2.5 ms to 10.24 s之间,其应该小于等于扫描间隙
示例:输入命令
ble_start_scan 0 0 0x80 0x40
ble_stop_scan
命令功能:停止扫描
参数:无
示例:系统进入SCAN后,输入命令
ble_stop_scan
ble_conn_update
命令功能:表示更新连接参数
第一个参数表示连接间隙的最小值,其计算方式为 N * 1.25 ms,其范围在7.5 ms to 4 s
第二个参数表示连接间隙的最大值,其计算方式为 N * 1.25 ms,其范围在7.5 ms to 4 s
第三个参数表示从设备时延多少个连接事件范围是0~499,比如:该值设置为1,表明延时一个事件的时间进行数据交互,作用是降低交互频率更省电
第四个参数表示连接超时时间,计算方式 N * 10 ms,范围是100 ms to 32 s
示例:连接成功后,输入命令
ble_conn_update 0x28 0x28 0x0 0xf4
ble_security
命令功能:设置SMP的加密等级
第一个参数表示加密等级,总共有5个等级 - 0:仅用于BR/EDR,比如SDP服务; - 1:表示不需要加密不需要认证的过程; - 2:表示需要加密不需要认证的过程 - 3:表示需要加密和认证,比如双方需要输入PIN码 - 4:表示需要加密和认证,通过128bit的key - 示例:连接成功后,输入命令
ble_security 2
ble_get_device_name
命令功能:获取本地设备名称
参数:无
示例:输入命令
ble_get_device_name
ble_set_device_name
命令功能:设置本地设备名称
参数:需要设置的设备名字
参数:无
示例:输入命令
ble_set_device_name bl602
ble_read_local_address
命令功能:读取本地设备地址
参数:无
示例:输入命令
ble_read_local_address
ble_set_adv_channel
命令功能:设置ADV通道
参数:需要设定的ADV通道数,其值范围为1-7,参数大小为1byte,bit0代表通道37,bit1代表通道38,bit2代表通道39
示例:输入命令
ble_set_adv_channel 4
![]()
ble_connect
命令功能:连接指定地址的设备
第一个参数表示设备地址类型 - 0:设备表示public地址类型 - 1:表示设备地址为random类型 - 2:表示设备地址为可解析的地址或者Public地址 - 3:表示设备地址为可解析的地址或者random地址
第二个参数代表设备地址,高字节在前低字节在后
示例:输入命令
ble_connect 0 18B905DE96E0
ble_disconnect
命令功能:断开指定地址的设备的连接
第一个参数表示设备地址类型 - 0:设备表示public地址类型 - 1:表示设备地址为random类型 - 2:表示设备地址为可解析的地址或者Public地址 - 3:表示设备地址为可解析的地址或者random地址
第二个参数代表设备地址,高字节在前低字节在后
示例:连接成功后,输入命令
ble_disconnect 0 18B905DE96E0
ble_select_conn
命令功能:多个连接中,将某一个连接对象设置为当前连接对象
第一个参数表示设备地址类型 - 0:设备表示public地址类型 - 1:表示设备地址为random类型 - 2:表示设备地址为可解析的地址或者Public地址 - 3:表示设备地址为可解析的地址或者random地址
第二个参数代表设备地址,高字节在前低字节在后
示例:多个设备连接成功后,输入命令
ble_select_conn 1 5F10546C8D83
,将选定的连接对象设置为当前连接对象,后续的ble_read等操作将会作用在该连接上
ble_auth_cancel
命令功能:取消加密认证过程
参数:无
示例:当在SMP过程中,输入命令
ble_auth_cancel
ble_auth_passkey_confirm
命令功能:接收到passkey后回复远端,并且对端设备在配对过程中也有显示该passkey; 例如:配对过程本地打印 Confirm passkey for 48:95:E6:73:1C:1A (random): 745491;可发送该函数进行回复
参数:无
示例:当在SMP过程中,对应security level为3,需要输入命令
ble_auth_passkey_confirm
ble_auth_pairing_confirm
命令功能:接收到远端配对请求,用此函数回复远端配对请求,例如:配对过程本地打印 Confirm pairing for 00:1B:DC:F2:20:E9 (public);可发送该函数进行回复
参数:无
示例:当在SMP过程中,对应的security level为2,输入命令
ble_auth_pairing_confirm
,
ble_auth_passkey
命令功能:请求输入passkey
参数:passkey值,其范围为0-999999
示例:当用ble_security 3命令进行配对,且SMP配对方法为PASSKEY_INPUT(代码中实现方法:用ble_auth注册smp接口函数时,在数据结构bt_conn_auth_cb中将函数passkey_entry填充,passkey_display与passkey_confirm不填充,其它接口函数默认即可),串口将打印出Enter passkey for XX:XX:XX:XX:XX:XX (public),此时输入命令
ble_auth_passkey 111111
完成配对
ble_exchange_mtu
命令功能:交换mtu大小
参数: 无
示例:连接成功后,输入命令
ble_exchange_mtu
ble_discover
命令功能:查询指定的服务或特性
第一个参数表示需要查询的类型 - 0:primary - 1:secondary - 2:include - 3:Characteristic - 4:Descriptor
第二个参数表示2BYTES的uuid
第三个参数表示起始句柄,占2BYTES
第四个参数表示结束句柄,占2BYTES
示例:连接成功后,输入命令
ble_discover 0 0x1800 0x1 0xffff
ble_read
命令功能:读取指定句柄的数据
第一个参数表示句柄
第二个参数表示偏移量
示例:连接成功后,输入命令
ble_read 0x5 0
ble_write
命令功能:指定句柄写入相应的数据
第一个参数表示句柄,占2bytes
第二个参数表示偏移量,占2bytes
第三个参数表示数据长度,占2bytes,最大不超过512
第四个参数表示需要写入的数据
示例:连接成功后,写入2个字节的数据,命令为
ble_write 0xf 0 2 0102
,其中01为一个byte,02为一个byte
ble_write_without_rsp
命令功能:指定句柄写入相应的数据并且不需要回复
第一参数表示是否启动sign write命令 - 0:不使能sign write命令 - 1:使能sign write命令
第二个参数表示句柄,占2bytes,
第三个参数表示数据的长度,占2bytes,最大不超过512
第四个参数表示写入的数据
示例:连接成功后,写入2个字节的数据,命令为
ble_write_without_rsp 0 0xf 2 0102
,其中01为一个byte,02为一个byte![]()
ble_subscribe
命令功能:订阅CCC
第一个参数表示CCC句柄
第二个参数表示订阅值的句柄
第三个参数表示订阅类型 - 1:表示notification - 2:表示indication
示例:连接成功后,输入命令
ble_subscribe 0xf 0xd 0x1
,表示使能CCC的notification![]()
ble_unsubscribe
命令功能:取消订阅CCC
参数:无
示例:输入命令
ble_unsubscribe
ble_set_data_len
命令功能:设置pdu数据长度
第一个参数表示有效荷载传输的最大值,范围为0x001B - 0x00FB
第二个参数表示有效荷载传输的最大时间,范围值为0x0148 - 0x4290
示例:当连接成功后,发送命令
ble_set_data_len 0xfb 0x0848
ble_conn_info
命令功能:获取所有的连接信息
参数:无
示例:当连接成功后,发送命令
ble_conn_info
,获取已连接的设备
ble_disable
命令功能:注销BLE
参数:无
示例:当无scan/adv/connect事件,发送命令
ble_disable
ble_set_tx_pwr
命令功能:设置发送功率
第一个参数表示设置功率值
示例:发送命令
ble_set_tx_pwr 0xa
Functions
ble stack 采用 zephyr ble stack,因此 API 请参考 zephyr bluetooth api 。
BLE_MESH
CLI 命令
blemesh_init
命令功能:blemesh通用初始化,在所有blemesh cli操作前,需要先输入该命令
参数:无
示例:输入命令
blemesh_init
blemesh_pb
命令功能:设置 Provisioning 承载
第一个参数表示 Provisioning 承载方式 - 1:承载方式 PB-ADV - 2:承载方式 PB-GATT
第二个参数表示使能
示例:输入命令
blemesh_pb 2 1
Functions
ble stack 采用 zephyr ble mesh,因此 API 请参考 zephyr bluetooth mesh api 。
WIFI6
CLI 命令
wifi_sta_connect
用于连接 AP,连接成功后,会打印出分配的 ip 地址
第一个参数表示 ssid
第二个参数表示 pwd
例如:


wifi_scan
用于扫描 AP ,不需要跟参数
例如:

ping
用于 ping 网络
第一个参数填写 ip 地址或者域名地址
例如:

Functions
Peripherals
外设例程如果没有特殊说明,则表示适用于博流所有系列芯片。
ADC
ADC - poll
本 demo 主要演示 adc poll 单端模式下读取电压值。默认扫描通道 0 ~ 通道10。 需要注意,有些芯片不一定支持全部通道。
硬件连接
本 demo 使用到的 gpio 参考 board_adc_gpio_init
。
软件实现
更详细的代码请参考 examples/peripherals/adc/adc_poll
1board_init();
board_init
中会开启 ADC IP 时钟,并选择 ADC 时钟源和分频(ADC 时钟必须小于等于 500K)。
1board_adc_gpio_init();
配置相关引脚为 ADC 功能
1adc = bflb_device_get_by_name("adc");
2
3/* adc clock = XCLK / 2 / 32 */
4struct bflb_adc_config_s cfg;
5cfg.clk_div = ADC_CLK_DIV_32;
6cfg.scan_conv_mode = true;
7cfg.continuous_conv_mode = false;
8cfg.differential_mode = false;
9cfg.resolution = ADC_RESOLUTION_16B;
10cfg.vref = ADC_VREF_3P2V;
11
12bflb_adc_init(adc, &cfg);
获取 adc 句柄,并初始化 adc 配置,设置 adc 采样频率为 500K
1bflb_adc_channel_config(adc, chan, TEST_ADC_CHANNELS);
配置 adc 通道信息,使用的对数根据 TEST_ADC_CHANNELS 可配,默认开启通道 0 ~ 10,根据
board_adc_gpio_init
需要选择性关闭其他通道。
1for (uint32_t i = 0; i < TEST_COUNT; i++) {
2 bflb_adc_start_conversion(adc);
3
4 while (bflb_adc_get_count(adc) < TEST_ADC_CHANNELS) {
5 bflb_mtimer_delay_ms(1);
6 }
7
8 for (size_t j = 0; j < TEST_ADC_CHANNELS; j++) {
9 struct bflb_adc_result_s result;
10 uint32_t raw_data = bflb_adc_read_raw(adc);
11 printf("raw data:%08x\r\n", raw_data);
12 bflb_adc_parse_result(adc, &raw_data, &result, 1);
13 printf("pos chan %d,%d mv \r\n", result.pos_chan, result.millivolt);
14 }
15
16 bflb_adc_stop_conversion(adc);
17 bflb_mtimer_delay_ms(100);
18}
调用
bflb_adc_start_conversion(adc)
启用 adc 的转换调用
bflb_adc_get_count(adc)
读取转换完成的个数调用
bflb_adc_read_raw(adc)
读取一次 adc 的转换值调用
bflb_adc_parse_result(adc, &raw_data, &result, 1)
对 adc 的转换结果进行解析,解析的值保存到result
结构体中调用
bflb_adc_stop_conversion(adc)
停止 adc 转换
编译和烧录
参考 环境搭建
实验现象
打印 raw data,通道号以及通道对应的电压值。
ADC - dma
本 demo 主要演示 adc dma 单端模式下读取电压值。默认扫描通道 0 ~ 通道10。 需要注意,有些芯片不一定支持全部通道。
硬件连接
本 demo 使用到的 gpio 参考 board_adc_gpio_init
。
软件实现
更详细的代码请参考 examples/peripherals/adc/adc_dma
1board_init();
board_init
中会开启 ADC IP 时钟,并选择 ADC 时钟源和分频(ADC 时钟必须小于等于 500K)。
1board_adc_gpio_init();
配置相关引脚为 ADC 功能
1adc = bflb_device_get_by_name("adc");
2
3/* adc clock = XCLK / 2 / 32 */
4struct bflb_adc_config_s cfg;
5cfg.clk_div = ADC_CLK_DIV_32;
6cfg.scan_conv_mode = true;
7cfg.continuous_conv_mode = true;
8cfg.differential_mode = false;
9cfg.resolution = ADC_RESOLUTION_16B;
10cfg.vref = ADC_VREF_3P2V;
11
12bflb_adc_init(adc, &cfg);
获取 adc 句柄,并初始化 adc 配置,设置 adc 采样频率为 500K
1bflb_adc_channel_config(adc, chan, TEST_ADC_CHANNELS);
配置 adc 通道信息,使用的通道数通过 TEST_ADC_CHANNELS 可配,默认开启通道 0 ~ 10,根据
board_adc_gpio_init
需要选择性关闭其他通道。
1bflb_adc_link_rxdma(adc, true);
使能 adc rx dma 功能
1dma0_ch0 = bflb_device_get_by_name("dma0_ch0");
2
3struct bflb_dma_channel_config_s config;
4
5config.direction = DMA_PERIPH_TO_MEMORY;
6config.src_req = DMA_REQUEST_ADC;
7config.dst_req = DMA_REQUEST_NONE;
8config.src_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
9config.dst_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
10config.src_burst_count = DMA_BURST_INCR1;
11config.dst_burst_count = DMA_BURST_INCR1;
12config.src_width = DMA_DATA_WIDTH_32BIT;
13config.dst_width = DMA_DATA_WIDTH_32BIT;
14bflb_dma_channel_init(dma0_ch0, &config);
15
16bflb_dma_channel_irq_attach(dma0_ch0, dma0_ch0_isr, NULL);
配置 DMA CH0 为 ADC RX
注册 dma 通道中断
1struct bflb_dma_channel_lli_pool_s lli[1]; /* max trasnfer size 4064 * 1 */
2struct bflb_dma_channel_lli_transfer_s transfers[1];
3
4memset(raw_data, 0, sizeof(raw_data));
5
6transfers[0].src_addr = (uint32_t)DMA_ADDR_ADC_RDR;
7transfers[0].dst_addr = (uint32_t)raw_data;
8transfers[0].nbytes = sizeof(raw_data);
9
10bflb_dma_channel_lli_reload(dma0_ch0, lli, 1, transfers, 1);
11bflb_dma_channel_start(dma0_ch0);
12
13bflb_adc_start_conversion(adc);
14
15while (dma_tc_flag0 != 1) {
16 bflb_mtimer_delay_ms(1);
17}
18
19bflb_adc_stop_conversion(adc);
分配一块 lli 内存池,个数为1,最多可以传输 4064 * 1 字节
配置一块内存进行传输
调用
bflb_dma_channel_lli_reload
初始化调用
bflb_dma_channel_start
启动传输调用
bflb_adc_start_conversion
启用 adc 的转换等待传输完成并进入中断
调用
bflb_adc_stop_conversion
停止 adc 转换
1for (size_t j = 0; j < TEST_ADC_CHANNELS * TEST_COUNT; j++) {
2 struct bflb_adc_result_s result;
3 printf("raw data:%08x\r\n", raw_data[j]);
4 bflb_adc_parse_result(adc, &raw_data[j], &result, 1);
5 printf("pos chan %d,%d mv \r\n", result.pos_chan, result.millivolt);
6}
调用
bflb_adc_parse_result
对 adc 的转换结果进行解析,解析的值保存到result
结构体中打印通道号和电压值
编译和烧录
参考 环境搭建
实验现象
打印 raw data,通道号以及通道对应的电压值。
ADC - int
本 demo 主要演示 adc 中断模式下读取电压值。默认扫描通道 0 ~ 通道10。 需要注意,有些芯片不一定支持全部通道。
硬件连接
本 demo 使用到的 gpio 参考 board_adc_gpio_init
。
软件实现
更详细的代码请参考 examples/peripherals/adc/adc_int
1board_init();
board_init
中会开启 ADC IP 时钟,并选择 ADC 时钟源和分频(ADC 时钟必须小于等于 500K)。
1board_adc_gpio_init();
配置相关引脚为 ADC 功能
1adc = bflb_device_get_by_name("adc");
2
3/* adc clock = XCLK / 2 / 32 */
4struct bflb_adc_config_s cfg;
5cfg.clk_div = ADC_CLK_DIV_32;
6cfg.scan_conv_mode = true;
7cfg.continuous_conv_mode = false;
8cfg.differential_mode = false;
9cfg.resolution = ADC_RESOLUTION_16B;
10cfg.vref = ADC_VREF_3P2V;
11
12bflb_adc_init(adc, &cfg);
获取 adc 句柄,并初始化 adc 配置,设置 adc 采样频率为 500K
1bflb_adc_channel_config(adc, chan, TEST_ADC_CHANNELS);
配置 adc 通道信息,使用的对数根据 TEST_ADC_CHANNELS 可配,默认开启通道 0 ~ 10,根据
board_adc_gpio_init
需要选择性关闭其他通道。
1bflb_adc_rxint_mask(adc, false);
2bflb_irq_attach(adc->irq_num, adc_isr, NULL);
3bflb_irq_enable(adc->irq_num);
调用 bflb_adc_rxint_mask 打开 adc 转换完成中断
调用 bflb_irq_attach 连接中断处理函数
调用 bflb_irq_enable 使能中断
1for (size_t i = 0; i < TEST_COUNT; i++) {
2 read_count = 0;
3 bflb_adc_start_conversion(adc);
4
5 while (read_count < TEST_ADC_CHANNELS) {
6 bflb_mtimer_delay_ms(1);
7 }
8 for (size_t j = 0; j < TEST_ADC_CHANNELS; j++) {
9 struct bflb_adc_result_s result;
10 printf("raw data:%08x\r\n", raw_data[j]);
11 bflb_adc_parse_result(adc, (uint32_t *)&raw_data[j], &result, 1);
12 printf("pos chan %d,%d mv \r\n", result.pos_chan, result.millivolt);
13 }
14 bflb_adc_stop_conversion(adc);
15 bflb_mtimer_delay_ms(100);
16}
调用
bflb_adc_start_conversion(adc)
启用 adc 的转换调用
bflb_adc_parse_result(adc, (uint32_t *)&raw_data[j], &result, 1)
对 adc 的转换结果进行解析,解析的值保存到result
结构体中调用
bflb_adc_stop_conversion(adc)
停止 adc 转换
编译和烧录
参考 环境搭建
实验现象
打印 raw data,通道号以及通道对应的电压值。
ADC - poll_diff_mode
本 demo 主要演示 adc poll 差分模式下读取通道 2 和 通道 3 的电压值。
硬件连接
本 demo 使用到的 gpio 参考 board_adc_gpio_init
。
软件实现
更详细的代码请参考 examples/peripherals/adc/adc_poll_diff_mode
1board_init();
board_init
中会开启 ADC IP 时钟,并选择 ADC 时钟源和分频(ADC 时钟必须小于等于 500K)。
1board_adc_gpio_init();
配置相关引脚为 ADC 功能
1adc = bflb_device_get_by_name("adc");
2
3/* adc clock = XCLK / 2 / 32 */
4struct bflb_adc_config_s cfg;
5cfg.clk_div = ADC_CLK_DIV_32;
6cfg.scan_conv_mode = true;
7cfg.continuous_conv_mode = false;
8cfg.differential_mode = true;
9cfg.resolution = ADC_RESOLUTION_16B;
10cfg.vref = ADC_VREF_3P2V;
11
12bflb_adc_init(adc, &cfg);
获取 adc 句柄,并初始化 adc 配置,设置 adc 采样频率为 500K
1bflb_adc_channel_config(adc, chan, TEST_ADC_CHANNELS);
配置通道 2 和 通道 3 信息。
1for (uint32_t i = 0; i < TEST_COUNT; i++) {
2 bflb_adc_start_conversion(adc);
3
4 while (bflb_adc_get_count(adc) < TEST_ADC_CHANNELS) {
5 bflb_mtimer_delay_ms(1);
6 }
7
8 for (size_t j = 0; j < TEST_ADC_CHANNELS; j++) {
9 struct bflb_adc_result_s result;
10 uint32_t raw_data = bflb_adc_read_raw(adc);
11 printf("raw data:%08x\r\n", raw_data);
12 bflb_adc_parse_result(adc, &raw_data, &result, 1);
13 printf("pos chan %d,neg chan %d,%d mv \r\n", result.pos_chan, result.neg_chan, result.millivolt);
14 }
15
16 bflb_adc_stop_conversion(adc);
17 bflb_mtimer_delay_ms(100);
18}
调用
bflb_adc_start_conversion(adc)
启用 adc 的转换调用
bflb_adc_get_count(adc)
读取转换完成的个数调用
bflb_adc_read_raw(adc)
读取一次 adc 的转换值调用
bflb_adc_parse_result(adc, &raw_data, &result, 1)
对 adc 的转换结果进行解析,解析的值保存到result
结构体中调用
bflb_adc_stop_conversion(adc)
停止 adc 转换
编译和烧录
参考 环境搭建
实验现象
打印 raw data,正极和负极通道号以及对应的电压差值。
ADC - tsen
本 demo 主要演示通过 adc 测量二极管的电压差,计算得到环境温度。
软件实现
更详细的代码请参考 examples/peripherals/adc/adc_tsen
1board_init();
board_init
中会开启 ADC IP 时钟,并选择 ADC 时钟源和分频(ADC 时钟必须小于等于 500K)。
1adc = bflb_device_get_by_name("adc");
2
3/* adc clock = XCLK / 2 / 32 */
4struct bflb_adc_config_s cfg;
5cfg.clk_div = ADC_CLK_DIV_32;
6cfg.scan_conv_mode = false;
7cfg.continuous_conv_mode = false;
8cfg.differential_mode = false;
9cfg.resolution = ADC_RESOLUTION_16B;
10cfg.vref = ADC_VREF_2P0V;
11
12struct bflb_adc_channel_s chan;
13
14chan.pos_chan = ADC_CHANNEL_TSEN_P;
15chan.neg_chan = ADC_CHANNEL_GND;
16
17bflb_adc_init(adc, &cfg);
获取 adc 句柄,并初始化 adc 配置(参考电压必须设置为2.0V),设置 adc 采样频率为 500K。
1bflb_adc_channel_config(adc, chan, 1);
配置 adc 通道信息。
1bflb_adc_tsen_init(adc, ADC_TSEN_MOD_INTERNAL_DIODE);
开启 tsen 功能,使用内部二极管测量电压值。
1 for (i = 0; i < 50; i++) {
2 average_filter += bflb_adc_tsen_get_temp(adc);
3 bflb_mtimer_delay_ms(10);
4 }
5
6 printf("temp = %d\r\n", (uint32_t)(average_filter / 50.0));
7 average_filter = 0;
调用
bflb_adc_tsen_get_temp(adc)
获取 adc tsen 计算得到的环境温度值。
编译和烧录
参考 环境搭建
实验现象
打印计算得到的环境温度。
ADC - vbat
本 demo 主要演示 adc 测量芯片 VDD33 的电压值。
软件实现
更详细的代码请参考 examples/peripherals/adc/adc_vbat
1board_init();
board_init
中会开启 ADC IP 时钟,并选择 ADC 时钟源和分频(ADC 时钟必须小于等于 500K)。
1adc = bflb_device_get_by_name("adc");
2
3/* adc clock = XCLK / 2 / 32 */
4struct bflb_adc_config_s cfg;
5cfg.clk_div = ADC_CLK_DIV_32;
6cfg.scan_conv_mode = false;
7cfg.continuous_conv_mode = false;
8cfg.differential_mode = false;
9cfg.resolution = ADC_RESOLUTION_16B;
10cfg.vref = ADC_VREF_3P2V;
11
12struct bflb_adc_channel_s chan;
13
14chan.pos_chan = ADC_CHANNEL_VABT_HALF;
15chan.neg_chan = ADC_CHANNEL_GND;
16
17bflb_adc_init(adc, &cfg);
获取 adc 句柄,并初始化 adc 配置,设置 adc 采样频率为 500K。
1bflb_adc_channel_config(adc, chan, 1);
配置 adc 通道信息。
1bflb_adc_vbat_enable(adc);
开启 vbat 功能。
1struct bflb_adc_result_s result;
2for (uint16_t i = 0; i < 10; i++) {
3 bflb_adc_start_conversion(adc);
4 while (bflb_adc_get_count(adc) == 0) {
5 bflb_mtimer_delay_ms(1);
6 }
7 uint32_t raw_data = bflb_adc_read_raw(adc);
8
9 bflb_adc_parse_result(adc, &raw_data, &result, 1);
10 printf("vBat = %d mV\r\n", (uint32_t)(result.millivolt * 2));
11 bflb_adc_stop_conversion(adc);
12
13 bflb_mtimer_delay_ms(500);
14}
调用
bflb_adc_start_conversion(adc)
启用 adc 的转换调用
bflb_adc_get_count(adc)
读取转换完成的个数调用
bflb_adc_read_raw(adc)
读取一次 adc 的转换值调用
bflb_adc_parse_result(adc, &raw_data, &result, 1)
对 adc 的转换结果进行解析,解析的值保存到result
结构体中调用
bflb_adc_stop_conversion(adc)
停止 adc 转换
编译和烧录
参考 环境搭建
实验现象
打印芯片 VDD33 的电压值。
ADC 各通道对应的 GPIO 如下表:
名称 |
芯片系列 |
GPIO |
---|---|---|
Channel0 |
BL702 |
GPIO 8 |
BL808 |
GPIO 17 |
|
BL616 |
GPIO 20 |
|
Channel1 |
BL702 |
GPIO 15 |
BL808 |
GPIO 5 |
|
BL616 |
GPIO 19 |
|
Channel2 |
BL702 |
GPIO 17 |
BL808 |
GPIO 4 |
|
BL616 |
GPIO 2(Bootstrap 引脚) |
|
Channel3 |
BL702 |
GPIO 11 |
BL808 |
GPIO 11 |
|
BL616 |
GPIO 3 |
|
Channel4 |
BL702 |
GPIO 12 |
BL808 |
GPIO 6 |
|
BL616 |
GPIO 14 |
|
Channel5 |
BL702 |
GPIO 14 |
BL808 |
GPIO 40 |
|
BL616 |
GPIO 13 |
|
Channel6 |
BL702 |
GPIO 7 |
BL808 |
GPIO 12 |
|
BL616 |
GPIO 12 |
|
Channel7 |
BL702 |
GPIO 9 |
BL808 |
GPIO 13 |
|
BL616 |
GPIO 10 |
|
Channel8 |
BL702 |
GPIO 18 |
BL808 |
GPIO 16 |
|
BL616 |
GPIO 1 |
|
Channel9 |
BL702 |
GPIO 19 |
BL808 |
GPIO 18 |
|
BL616 |
GPIO 0 |
|
Channel10 |
BL702 |
GPIO 20 |
BL808 |
GPIO 19 |
|
BL616 |
GPIO 27 |
|
Channel11 |
BL702 |
GPIO 21 |
BL808 |
GPIO 34 |
|
BL616 |
GPIO 28 |
DAC
DAC - poll
本 demo 主要介绍基于 DAC 轮询模式生成正弦波。
硬件连接
本 demo 使用到的 gpio 参考 board_adc_gpio_init
。
软件实现
更详细的代码请参考 examples/peripherals/dac/dac_polling
1board_init();
board_init
中会开启 DAC IP 时钟,并选择 DAC 时钟源和分频。
1board_dac_gpio_init();
配置相关引脚为 DAC 功能
1dac = bflb_device_get_by_name("dac");
2
3bflb_dac_init(dac, DAC_SAMPLING_FREQ_32K);
获取 dac 句柄,并初始化 dac 频率为 32K
1bflb_dac_channel_enable(dac, DAC_CHANNEL_A);
配置 dac 通道信息,当前使用的 A 通道
1for (uint16_t i = 0; i < sizeof(SIN_LIST) / sizeof(uint16_t); i++) {
2 bflb_dac_set_value(dac, DAC_CHANNEL_A, SIN_LIST[i]);
3 bflb_mtimer_delay_us(100);
4}
调用
bflb_dac_set_value(dac, DAC_CHANNEL_A, SIN_LIST[i])
,将需要转换的数据通过通道 A 输出
编译和烧录
参考 环境搭建
实验现象
DAC Channel A 对应的 GPIO 输出正弦波。
DAC - dma
本 demo 主要介绍基于 DAC DMA 模式生成正弦波。
硬件连接
本 demo 使用到的 gpio 参考 board_dac_gpio_init
。
软件实现
更详细的代码请参考 examples/peripherals/dac/dac_dma
1board_init();
board_init
中会开启 DAC IP 时钟,并选择 DAC 时钟源和分频。
1board_dac_gpio_init();
配置相关引脚为 DAC 功能
1dac = bflb_device_get_by_name("dac");
2
3/* 512K / 1 = 512K */
4bflb_dac_init(dac, DAC_CLK_DIV_1);
5bflb_dac_channel_enable(dac, DAC_CHANNEL_A);
6bflb_dac_channel_enable(dac, DAC_CHANNEL_B);
7bflb_dac_link_txdma(dac, true);
获取 dac 句柄,并初始化 dac 频率为 512K
bflb_dac_channel_enable
配置 dac 通道信息,当前使用的 A 通道和 B 通道bflb_dac_link_txdma
开启 dac txdma 功能
1dma0_ch0 = bflb_device_get_by_name("dma0_ch0");
2
3struct bflb_dma_channel_config_s config;
4
5config.direction = DMA_MEMORY_TO_PERIPH;
6config.src_req = DMA_REQUEST_NONE;
7config.dst_req = DMA_REQUEST_DAC;
8config.src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
9config.dst_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
10config.src_burst_count = DMA_BURST_INCR1;
11config.dst_burst_count = DMA_BURST_INCR1;
12config.src_width = DMA_DATA_WIDTH_16BIT;
13config.dst_width = DMA_DATA_WIDTH_16BIT;
14bflb_dma_channel_init(dma0_ch0, &config);
15
16bflb_dma_channel_irq_attach(dma0_ch0, dma0_ch0_isr, NULL);
配置 DMA CH0 为 DAC
注册 dma 通道中断
1struct bflb_dma_channel_lli_pool_s lli[1]; /* max trasnfer size 4064 * 1 */
2struct bflb_dma_channel_lli_transfer_s transfers[1];
3
4transfers[0].src_addr = (uint32_t)SIN_LIST;
5transfers[0].dst_addr = (uint32_t)DMA_ADDR_DAC_TDR;
6transfers[0].nbytes = sizeof(SIN_LIST);
7bflb_l1c_dcache_clean_range((void*)SIN_LIST,sizeof(SIN_LIST));
8
9bflb_dma_channel_lli_reload(dma0_ch0, lli, 1, transfers, 1);
10bflb_dma_channel_start(dma0_ch0);
11
12while (dma_tc_flag0 != 1) {
13 bflb_mtimer_delay_ms(1);
14}
分配一块 lli 内存池,个数为1,最多可以传输 4064 * 1 字节
配置一块内存进行传输
调用
bflb_dma_channel_lli_reload
初始化调用
bflb_dma_channel_start
启动传输等待传输完成并进入中断
编译和烧录
参考 环境搭建
实验现象
DAC Channel A 和 B 对应的 GPIO 输出正弦波。
DAC 各通道对应的 GPIO 如下表:
名称 |
芯片系列 |
精度 |
GPIO |
---|---|---|---|
ChannelA |
BL702 |
10-bit |
GPIO 11 |
BL808 |
10-bit |
GPIO 11 |
|
BL616 |
12-bit |
GPIO 3 |
|
ChannelB |
BL702 |
10-bit |
GPIO 17 |
BL808 |
10-bit |
GPIO 4 |
|
BL616 |
12-bit |
GPIO 2 |
GPIO
GPIO - input/output
本 demo 主要介绍 GPIO 0 输出和 GPIO 1 输入功能。
硬件连接
使用杜邦线将 GPIO 0 和 GPIO 1 引脚连接。
软件实现
更详细的代码请参考 examples/peripherals/gpio/gpio_input_output
1board_init();
board_init
中开启时钟
1gpio = bflb_device_get_by_name("gpio");
2
3bflb_gpio_init(gpio, GPIO_PIN_0, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
4bflb_gpio_init(gpio, GPIO_PIN_1, GPIO_INPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
配置 GPIO 0 为 GPIO_OUTPUT 功能,GPIO 1 为 GPIO_INPUT 功能。
1while (1) {
2 bflb_gpio_set(gpio, GPIO_PIN_0);
3 printf("GPIO_PIN_1=%x\r\n", bflb_gpio_read(gpio, GPIO_PIN_1));
4 bflb_mtimer_delay_ms(2000);
5
6 bflb_gpio_reset(gpio, GPIO_PIN_0);
7 printf("GPIO_PIN_1=%x\r\n", bflb_gpio_read(gpio, GPIO_PIN_1));
8 bflb_mtimer_delay_ms(2000);
9}
bflb_gpio_set(gpio, GPIO_PIN_0)
将 GPIO 0 引脚置位bflb_gpio_read(gpio, GPIO_PIN_1)
读取 GPIO 1 引脚电平bflb_gpio_reset(gpio, GPIO_PIN_0)
将 GPIO 0 引脚置 0
编译和烧录
参考 环境搭建
实验现象
打印 GPIO 1 引脚电平。
GPIO - interrupt
本 demo 主要介绍 GPIO 0 的同步低电平中断类型。
硬件连接
将 GPIO 0 和 GND 连接。
软件实现
更详细的代码请参考 examples/peripherals/gpio/gpio_interrupt
1board_init();
board_init
中开启时钟
1gpio = bflb_device_get_by_name("gpio");
2
3bflb_gpio_int_init(gpio, GPIO_PIN_0, GPIO_INT_TRIG_MODE_SYNC_LOW_LEVEL);
设置 GPIO 0 的中断类型。
1bflb_gpio_int_mask(gpio, GPIO_PIN_0, false);
2
3bflb_irq_attach(gpio->irq_num, gpio_isr, gpio);
4bflb_irq_enable(gpio->irq_num);
bflb_gpio_int_mask(gpio, GPIO_PIN_0, false)
打开 GPIO 0 中断bflb_irq_attach(gpio->irq_num, gpio_isr, gpio)
注册 GPIO 中断函数bflb_irq_enable(gpio->irq_num)
使能 GPIO 中断
编译和烧录
参考 环境搭建
实验现象
将 GPIO 0 引脚电平拉低,进入中断并打印中断次数。
各开发板支持的 GPIO 引脚如下表:
开发板 |
GPIO |
---|---|
BL61x_MB_V1 |
GPIO 0-34(其中 GPIO 2 为 BOOT 引脚,GPIO16/17默认为晶振引脚) |
I2C
I2C - 10-bit
本 demo 主要介绍 I2C 10-bit slave 模式数据传输。
硬件连接
本 demo 使用到的 gpio 参考 board_i2c0_gpio_init
,将 USB 转 I2C 模块与开发板连接,具体引脚连接方式如下表(以BL616为例):
开发板 I2C 引脚 |
USB 转 I2C 模块 |
---|---|
SCL(GPIO14) |
SCL |
SDA(GPIO15) |
SDA |
软件实现
更详细的代码请参考 examples/peripherals/i2c/i2c_10_bit
1board_init();
board_init
中会开启 I2C IP 时钟,并选择 I2C 时钟源和分频。
1board_i2c0_gpio_init();
配置相关引脚为 I2C 功能
1i2c0 = bflb_device_get_by_name("i2c0");
2
3bflb_i2c_init(i2c0, 400000);
获取 i2c0 句柄,并初始化 i2c0 频率为 400K
1struct bflb_i2c_msg_s msgs[2];
2uint8_t subaddr[2] = { 0x00, 0x04};
3uint8_t write_data[I2C_10BIT_TRANSFER_LENGTH];
4
5/* Write buffer init */
6write_data[0] = 0x55;
7write_data[1] = 0x11;
8write_data[2] = 0x22;
9for (size_t i = 3; i < I2C_10BIT_TRANSFER_LENGTH; i++) {
10 write_data[i] = i;
11}
12
13/* Write data */
14msgs[0].addr = I2C_10BIT_SLAVE_ADDR;
15msgs[0].flags = I2C_M_NOSTOP | I2C_M_TEN;
16msgs[0].buffer = subaddr;
17msgs[0].length = 2;
18
19msgs[1].addr = I2C_10BIT_SLAVE_ADDR;
20msgs[1].flags = 0;
21msgs[1].buffer = write_data;
22msgs[1].length = I2C_10BIT_TRANSFER_LENGTH;
23
24bflb_i2c_transfer(i2c0, msgs, 2);
初始化发送数据(write_data)和配置从设备信息(msgs)
bflb_i2c_transfer(i2c0, msgs, 2)
开启 i2c 传输
编译和烧录
参考 环境搭建
实验现象
通过串口(波特率大于115200)发送``04 00 06 01 03 55``命令给 USB 转 I2C 模块,设置 I2C 从机 10-bit 模式数据传输。 按下开发板中 RST 按键,串口打印开发板发送的 write_data 数据。
I2C - eeprom
本 demo 主要介绍 I2C 读写 eeprom。
硬件连接
本 demo 使用到的 gpio 参考 board_i2c0_gpio_init
,将 eeprom 模块与开发板连接,具体引脚连接方式如下表(以BL616为例):
开发板 I2C 引脚 |
eeprom 模块 |
---|---|
SCL(GPIO14) |
SCL |
SDA(GPIO15) |
SDA |
GND |
GND |
VCC |
VCC |
软件实现
更详细的代码请参考 examples/peripherals/i2c/i2c_eeprom
1board_init();
board_init
中会开启 I2C IP 时钟,并选择 I2C 时钟源和分频。
1board_i2c0_gpio_init();
配置相关引脚为 I2C 功能
1i2c0 = bflb_device_get_by_name("i2c0");
2
3bflb_i2c_init(i2c0, 400000);
获取 i2c0 句柄,并初始化 i2c0 频率为 400K
1struct bflb_i2c_msg_s msgs[2];
2uint8_t subaddr[2] = { 0x00, EEPROM_SELECT_PAGE0};
3uint8_t write_data[256];
4
5/* Write and read buffer init */
6for (size_t i = 0; i < 256; i++) {
7 write_data[i] = i;
8 read_data[i] = 0;
9}
10
11/* Write page 0 */
12msgs[0].addr = 0x50;
13msgs[0].flags = I2C_M_NOSTOP;
14msgs[0].buffer = subaddr;
15msgs[0].length = 2;
16
17msgs[1].addr = 0x50;
18msgs[1].flags = 0;
19msgs[1].buffer = write_data;
20msgs[1].length = EEPROM_TRANSFER_LENGTH;
21
22bflb_i2c_transfer(i2c0, msgs, 2);
初始化发送数据(write_data),接收buffer和配置从设备信息(msgs)
bflb_i2c_transfer(i2c0, msgs, 2)
开启 i2c 传输
1uint8_t read_data[256];
2
3/* Read page 0 */
4msgs[1].addr = 0x50;
5msgs[1].flags = I2C_M_READ;
6msgs[1].buffer = read_data;
7msgs[1].length = EEPROM_TRANSFER_LENGTH;
8bflb_i2c_transfer(i2c0, msgs, 2);
读取从设备寄存器地址中的数据,存放至 read_data 中
bflb_i2c_transfer(i2c0, msgs, 2)
开启 i2c 传输
1/* Check read data */
2for (uint8_t i = 0; i < EEPROM_TRANSFER_LENGTH; i++) {
3 if (write_data[i] != read_data[i]) {
4 printf("check fail, %d write: %02x, read: %02x\r\n", i, write_data[i], read_data[i]);
5 }
6}
检查发送和读取的数据是否一致
编译和烧录
参考 环境搭建
实验现象
按下 RST 按键,数据传输完成后,打印“write over”,“read over”和“check over”。
I2C - eeprom_dma
本 demo 主要介绍 I2C 使用 DMA 的方式读写 eeprom。
硬件连接
本 demo 使用到的 gpio 参考 board_i2c0_gpio_init
,将 eeprom 模块与开发板连接,具体引脚连接方式如下表(以BL616为例):
开发板 I2C 引脚 |
eeprom 模块 |
---|---|
SCL(GPIO14) |
SCL |
SDA(GPIO15) |
SDA |
GND |
GND |
VCC |
VCC |
软件实现
更详细的代码请参考 examples/peripherals/i2c/i2c_eeprom_dma
1board_init();
board_init
中会开启 I2C IP 时钟,并选择 I2C 时钟源和分频。
1board_i2c0_gpio_init();
配置相关引脚为 I2C 功能
1/* Send and receive buffer init */
2for (size_t i = 0; i < 32; i++) {
3 ((uint8_t *)send_buffer)[i] = i;
4 ((uint8_t *)receive_buffer)[i] = 0;
5}
6
7i2c0 = bflb_device_get_by_name("i2c0");
8
9bflb_i2c_init(i2c0, 400000);
10bflb_i2c_link_txdma(i2c0, true);
11bflb_i2c_link_rxdma(i2c0, true);
初始化发送和接收 buffer
获取 i2c0 句柄,并初始化 i2c0 频率为 400K
bflb_i2c_link_txdma(i2c0, true)
开启 I2C TX DMA 功能bflb_i2c_link_rxdma(i2c0, true)
开启 I2C RX DMA 功能
1/* Write page 0 */
2dma0_ch0 = bflb_device_get_by_name("dma0_ch0");
3
4struct bflb_dma_channel_config_s tx_config;
5
6tx_config.direction = DMA_MEMORY_TO_PERIPH;
7tx_config.src_req = DMA_REQUEST_NONE;
8tx_config.dst_req = DMA_REQUEST_I2C0_TX;
9tx_config.src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
10tx_config.dst_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
11tx_config.src_burst_count = DMA_BURST_INCR1;
12tx_config.dst_burst_count = DMA_BURST_INCR1;
13tx_config.src_width = DMA_DATA_WIDTH_32BIT;
14tx_config.dst_width = DMA_DATA_WIDTH_32BIT;
15bflb_dma_channel_init(dma0_ch0, &tx_config);
16
17bflb_dma_channel_irq_attach(dma0_ch0, dma0_ch0_isr, NULL);
对于 TX, DMA 的配置如下:传输方向(direction)为内存到外设(MEMORY_TO_PERIPH),源请求(src_req)为内存,目标请求(dst_req)为 DMA_REQUEST_I2C0_TX
调用
bflb_dma_channel_init(dma0_ch0, &tx_config)
初始化 DMA调用
bflb_dma_channel_irq_attach(dma0_ch0, dma0_ch0_isr, NULL)
注册 dma 通道 0 中断
1struct bflb_dma_channel_lli_pool_s tx_llipool[20]; /* max trasnfer size 4064 * 20 */
2struct bflb_dma_channel_lli_transfer_s tx_transfers[1];
3tx_transfers[0].src_addr = (uint32_t)send_buffer;
4tx_transfers[0].dst_addr = (uint32_t)DMA_ADDR_I2C0_TDR;
5tx_transfers[0].nbytes = 32;
6bflb_dma_channel_lli_reload(dma0_ch0, tx_llipool, 20, tx_transfers, 1);
7
8msgs[0].addr = 0x50;
9msgs[0].flags = I2C_M_NOSTOP;
10msgs[0].buffer = subaddr;
11msgs[0].length = 2;
12
13msgs[1].addr = 0x50;
14msgs[1].flags = I2C_M_DMA;
15msgs[1].buffer = NULL;
16msgs[1].length = 32;
17bflb_i2c_transfer(i2c0, msgs, 2);
18
19bflb_dma_channel_start(dma0_ch0);
分配二十块 lli 内存池,最多可以传输 4064 * 20 字节
配置一块内存(tx_transfers)进行传输,源地址(src_addr)为存储发送数据的内存地址(send_buffer),目标地址(dst_addr)为 I2C TX FIFO地址(DMA_ADDR_I2C0_TDR)
调用
bflb_dma_channel_lli_reload(dma0_ch0, tx_llipool, 20, tx_transfers, 1)
初始化调用
bflb_i2c_transfer(i2c0, msgs, 2)
开启 I2C 传输调用
bflb_dma_channel_start(dma0_ch0)
启动 DMA 传输
1/* Read page 0 */
2dma0_ch1 = bflb_device_get_by_name("dma0_ch1");
3
4struct bflb_dma_channel_config_s rx_config;
5
6rx_config.direction = DMA_PERIPH_TO_MEMORY;
7rx_config.src_req = DMA_REQUEST_I2C0_RX;
8rx_config.dst_req = DMA_REQUEST_NONE;
9rx_config.src_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
10rx_config.dst_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
11rx_config.src_burst_count = DMA_BURST_INCR1;
12rx_config.dst_burst_count = DMA_BURST_INCR1;
13rx_config.src_width = DMA_DATA_WIDTH_32BIT;
14rx_config.dst_width = DMA_DATA_WIDTH_32BIT;
15bflb_dma_channel_init(dma0_ch1, &rx_config);
16
17bflb_dma_channel_irq_attach(dma0_ch1, dma0_ch1_isr, NULL);
对于 RX, DMA 的配置如下:传输方向(direction)为外设到内存(PERIPH_TO_MEMORY),源请求(src_req)为 DMA_REQUEST_I2C0_RX ,目标请求(dst_req)为内存
调用
bflb_dma_channel_init(dma0_ch1, &rx_config)
初始化 DMA调用
bflb_dma_channel_irq_attach(dma0_ch1, dma0_ch1_isr, NULL)
注册 dma 通道 1 中断
1struct bflb_dma_channel_lli_pool_s rx_llipool[20];
2struct bflb_dma_channel_lli_transfer_s rx_transfers[1];
3rx_transfers[0].src_addr = (uint32_t)DMA_ADDR_I2C0_RDR;
4rx_transfers[0].dst_addr = (uint32_t)receive_buffer;
5rx_transfers[0].nbytes = 32;
6
7bflb_dma_channel_lli_reload(dma0_ch1, rx_llipool, 20, rx_transfers, 1);
8
9msgs[1].addr = 0x50;
10msgs[1].flags = I2C_M_DMA | I2C_M_READ;
11msgs[1].buffer = NULL;
12msgs[1].length = 32;
13bflb_i2c_transfer(i2c0, msgs, 2);
14
15bflb_dma_channel_start(dma0_ch1);
分配二十块 lli 内存池,最多可以传输 4064 * 20 字节
配置一块内存(rx_transfers)进行传输,源地址(src_addr)为 I2C RX FIFO地址(DMA_ADDR_I2C0_RDR),目标地址(dst_addr)为存储接收数据的内存地址(receive_buffer)
调用
bflb_dma_channel_lli_reload(dma0_ch1, rx_llipool, 20, rx_transfers, 1)
初始化调用
bflb_i2c_transfer(i2c0, msgs, 2)
开启 I2C 传输调用
bflb_dma_channel_start(dma0_ch1)
启动 DMA 传输
1while (dma_tc_flag1 == 0) {
2}
3while ((bflb_i2c_get_intstatus(i2c0) & I2C_INTSTS_END) == 0) {
4}
5bflb_i2c_deinit(i2c0);
数据传输完成后,复位 I2C 模块
1/* Check read data */
2for (uint8_t i = 0; i < 32; i++) {
3 if (((uint8_t *)send_buffer)[i] != ((uint8_t *)receive_buffer)[i]) {
4 printf("check fail, %d write: %02x, read: %02x\r\n", i, ((uint8_t *)send_buffer)[i], ((uint8_t *)receive_buffer)[i]);
5 }
6}
检查发送和读取的数据是否一致
编译和烧录
参考 环境搭建
实验现象
按下 RST 按键,数据传输完成后,打印“write over”,“read over”和“check over”。
I2C - eeprom_interrupt
本 demo 主要介绍 I2C 使用中断的方式读写 eeprom。
硬件连接
本 demo 使用到的 gpio 参考 board_i2c0_gpio_init
,将 eeprom 模块与开发板连接,具体引脚连接方式如下表(以BL616为例):
开发板 I2C 引脚 |
eeprom 模块 |
---|---|
SCL(GPIO14) |
SCL |
SDA(GPIO15) |
SDA |
GND |
GND |
VCC |
VCC |
软件实现
更详细的代码请参考 examples/peripherals/i2c/i2c_eeprom_interrupt
1board_init();
board_init
中会开启 I2C IP 时钟,并选择 I2C 时钟源和分频。
1board_i2c0_gpio_init();
配置相关引脚为 I2C 功能
1i2c0 = bflb_device_get_by_name("i2c0");
2
3bflb_i2c_init(i2c0, 400000);
获取 i2c0 句柄,并初始化 i2c0 频率为 400K
1/* Set i2c interrupt */
2bflb_i2c_int_mask(i2c0, I2C_INTEN_END | I2C_INTEN_TX_FIFO | I2C_INTEN_RX_FIFO | I2C_INTEN_NACK | I2C_INTEN_ARB | I2C_INTEN_FER, false);
3bflb_irq_attach(i2c0->irq_num, i2c_isr, NULL);
4bflb_irq_enable(i2c0->irq_num);
调用
bflb_i2c_int_mask(i2c0, I2C_INTEN_END | I2C_INTEN_TX_FIFO | I2C_INTEN_RX_FIFO | I2C_INTEN_NACK | I2C_INTEN_ARB | I2C_INTEN_FER, false)
打开 I2C 中断注册 I2C 中断
1uint8_t write_data[256];
2uint8_t read_data[256];
3
4/* Write and read buffer init */
5for (size_t i = 0; i < 256; i++) {
6 write_data[i] = i;
7 read_data[i] = 0;
8}
初始化发送和接收 buffer
1/* Write page 0 */
2subaddr[1] = EEPROM_SELECT_PAGE0;
3
4msgs[0].addr = 0x50;
5msgs[0].flags = I2C_M_NOSTOP;
6msgs[0].buffer = subaddr;
7msgs[0].length = 2;
8
9msgs[1].addr = 0x50;
10msgs[1].flags = 0;
11msgs[1].buffer = write_data;
12msgs[1].length = EEPROM_TRANSFER_LENGTH;
13
14bflb_i2c_transfer(i2c0, msgs, 2);
15if (txFifoFlag) {
16 printf("TX FIFO Ready interrupt generated\r\n");
17 txFifoFlag = 0;
18}
19if (rxFifoFlag) {
20 printf("RX FIFO Ready interrupt generated\r\n");
21 rxFifoFlag = 0;
22}
23printf("write over\r\n\r\n");
24bflb_mtimer_delay_ms(100);
bflb_i2c_transfer(i2c0, msgs, 2)
开启 i2c 传输
1/* Unmask interrupt */
2bflb_i2c_int_mask(i2c0, I2C_INTEN_END | I2C_INTEN_TX_FIFO | I2C_INTEN_RX_FIFO | I2C_INTEN_NACK | I2C_INTEN_ARB | I2C_INTEN_FER, false);
3
4/* Write page 1 */
5subaddr[1] = EEPROM_SELECT_PAGE1;
6
7msgs[1].addr = 0x50;
8msgs[1].flags = 0;
9msgs[1].buffer = write_data + EEPROM_TRANSFER_LENGTH;
10msgs[1].length = EEPROM_TRANSFER_LENGTH;
11
12bflb_i2c_transfer(i2c0, msgs, 2);
13if (txFifoFlag) {
14 printf("TX FIFO Ready interrupt generated\r\n");
15 txFifoFlag = 0;
16}
17if (rxFifoFlag) {
18 printf("RX FIFO Ready interrupt generated\r\n");
19 rxFifoFlag = 0;
20}
21printf("write over\r\n\r\n");
22bflb_mtimer_delay_ms(100);
开启 I2C 中断,进行第二次数据传输
1/* Unmask interrupt */
2bflb_i2c_int_mask(i2c0, I2C_INTEN_END | I2C_INTEN_TX_FIFO | I2C_INTEN_RX_FIFO | I2C_INTEN_NACK | I2C_INTEN_ARB | I2C_INTEN_FER, false);
3
4/* Read page 0 */
5subaddr[1] = EEPROM_SELECT_PAGE0;
6
7msgs[1].addr = 0x50;
8msgs[1].flags = I2C_M_READ;
9msgs[1].buffer = read_data;
10msgs[1].length = EEPROM_TRANSFER_LENGTH;
11bflb_i2c_transfer(i2c0, msgs, 2);
12if (txFifoFlag) {
13 printf("TX FIFO Ready interrupt generated\r\n");
14 txFifoFlag = 0;
15}
16if (rxFifoFlag) {
17 printf("RX FIFO Ready interrupt generated\r\n");
18 rxFifoFlag = 0;
19}
20printf("read over\r\n\r\n");
读取 eeprom 的数据
1/* Unmask interrupt */
2bflb_i2c_int_mask(i2c0, I2C_INTEN_END | I2C_INTEN_TX_FIFO | I2C_INTEN_RX_FIFO | I2C_INTEN_NACK | I2C_INTEN_ARB | I2C_INTEN_FER, false);
3
4/* Read page 1 */
5subaddr[1] = EEPROM_SELECT_PAGE1;
6
7msgs[1].addr = 0x50;
8msgs[1].flags = I2C_M_READ;
9msgs[1].buffer = read_data + EEPROM_TRANSFER_LENGTH;
10msgs[1].length = EEPROM_TRANSFER_LENGTH;
11bflb_i2c_transfer(i2c0, msgs, 2);
12if (txFifoFlag) {
13 printf("TX FIFO Ready interrupt generated\r\n");
14 txFifoFlag = 0;
15}
16if (rxFifoFlag) {
17 printf("RX FIFO Ready interrupt generated\r\n");
18 rxFifoFlag = 0;
19}
第二次读取数据
1/* Check read data */
2for (uint8_t i = 0; i < 2 * EEPROM_TRANSFER_LENGTH; i++) {
3 if (write_data[i] != read_data[i]) {
4 printf("check fail, %d write: %02x, read: %02x\r\n", i, write_data[i], read_data[i]);
5 }
6}
检查发送和读取的数据是否一致
编译和烧录
参考 环境搭建
实验现象
按下 RST 按键,数据传输完成后,打印“write over”,“read over”和“check over”。
I2C 信号引脚对应的 GPIO 如下表:
信号 |
芯片系列 |
GPIO |
---|---|---|
SCL |
BL702 |
|
BL808 |
||
BL616 |
GPIO 14 |
|
SDA |
BL702 |
|
BL808 |
||
BL616 |
GPIO 15 |
IR
IR - nec
本 demo 主要介绍 IR 以 nec 协议收发数据。
硬件连接
本 demo 使用到的 gpio 参考 board_ir_gpio_init
,将红外发射二极管和接收头与 IR 引脚连接,具体连接方式如下表(以BL808为例):
开发板 IR 引脚 |
外接模块 |
---|---|
VCC |
红外接收头 VCC |
GND |
红外接收头 GND |
RX(GPIO17) |
红外接收头 OUT |
VCC |
红外发射二极管正极 |
TX(GPIO11) |
红外发射二极管负极 |
软件实现
更详细的代码请参考 examples/peripherals/ir/ir_nec
1board_init();
board_init
中会开启 IR 时钟,并选择 IR 时钟源和分频。
1board_ir_gpio_init();
配置相关引脚为 IR 功能
1uint32_t tx_buffer[1] = { 0xE916FF00 };
2struct bflb_ir_tx_config_s tx_cfg;
3
4irtx = bflb_device_get_by_name("irtx");
5
6/* TX init */
7tx_cfg.tx_mode = IR_TX_NEC;
8bflb_ir_tx_init(irtx, &tx_cfg);
获取 irtx 句柄
设置 tx_mode 为 NEC 模式,调用
bflb_ir_tx_init(irtx, &tx_cfg)
初始化 ir tx
1uint64_t rx_data;
2uint8_t rx_len;
3struct bflb_ir_rx_config_s rx_cfg;
4
5irrx = bflb_device_get_by_name("irrx");
6
7/* RX init */
8rx_cfg.rx_mode = IR_RX_NEC;
9rx_cfg.input_inverse = true;
10rx_cfg.deglitch_enable = false;
11bflb_ir_rx_init(irrx, &rx_cfg);
12
13/* Enable rx, wait for sending */
14bflb_ir_rx_enable(irrx, true);
获取 irrx 句柄
设置 rx_mode 为 NEC 模式,调用
bflb_ir_rx_init(irrx, &rx_cfg)
初始化 ir rx调用
bflb_ir_rx_enable(irrx, true)
使能 ir rx,等待数据发送
1bflb_ir_send(irtx, tx_buffer, 1);
2rx_len = bflb_ir_receive(irrx, &rx_data);
调用
bflb_ir_send(irtx, tx_buffer, 1)
发送 tx_buffer 中的数据调用
bflb_ir_receive(irrx, &rx_data)
将接收到的数据存放在 rx_data 中
1/* Check data received */
2if (rx_data != tx_buffer[0]) {
3 printf("Data error! receive bit: %d, value: 0x%016lx\r\n", rx_len, rx_data);
4} else {
5 printf("Received correctly. receive bit: %d, value: 0x%016lx\r\n", rx_len, rx_data);
6}
检查发送和接收的数据是否一致
编译和烧录
参考 环境搭建
实验现象
按下开发板中 RST 按键,串口打印接收到的数据。
IR - rc5
本 demo 主要介绍 IR 以 rc5 协议收发数据。
硬件连接
本 demo 使用到的 gpio 参考 board_ir_gpio_init
,将红外发射二极管和接收头与 IR 引脚连接,具体连接方式如下表(以BL808为例):
开发板 IR 引脚 |
外接模块 |
---|---|
VCC |
红外接收头 VCC |
GND |
红外接收头 GND |
RX(GPIO17) |
红外接收头 OUT |
VCC |
红外发射二极管正极 |
TX(GPIO11) |
红外发射二极管负极 |
软件实现
更详细的代码请参考 examples/peripherals/ir/ir_rc5
1board_init();
board_init
中会开启 IR 时钟,并选择 IR 时钟源和分频。
1board_ir_gpio_init();
配置相关引脚为 IR 功能
1uint32_t tx_buffer[1] = { 0x123D };
2struct bflb_ir_tx_config_s tx_cfg;
3
4irtx = bflb_device_get_by_name("irtx");
5
6/* TX init */
7tx_cfg.tx_mode = IR_TX_RC5;
8bflb_ir_tx_init(irtx, &tx_cfg);
获取 irtx 句柄
设置 tx_mode 为 RC5 模式,调用
bflb_ir_tx_init(irtx, &tx_cfg)
初始化 ir tx
1uint64_t rx_data;
2uint8_t rx_len;
3struct bflb_ir_rx_config_s rx_cfg;
4
5irrx = bflb_device_get_by_name("irrx");
6
7/* RX init */
8rx_cfg.rx_mode = IR_RX_RC5;
9rx_cfg.input_inverse = true;
10rx_cfg.deglitch_enable = false;
11bflb_ir_rx_init(irrx, &rx_cfg);
12
13/* Enable rx, wait for sending */
14bflb_ir_rx_enable(irrx, true);
获取 irrx 句柄
设置 rx_mode 为 RC5 模式,调用
bflb_ir_rx_init(irrx, &rx_cfg)
初始化 ir rx调用
bflb_ir_rx_enable(irrx, true)
使能 ir rx,等待数据发送
1bflb_ir_send(irtx, tx_buffer, 1);
2rx_len = bflb_ir_receive(irrx, &rx_data);
调用
bflb_ir_send(irtx, tx_buffer, 1)
发送 tx_buffer 中的数据调用
bflb_ir_receive(irrx, &rx_data)
将接收到的数据存放在 rx_data 中
1/* Check data received */
2if (rx_data != tx_buffer[0]) {
3 printf("Data error! receive bit: %d, value: 0x%016lx\r\n", rx_len, rx_data);
4} else {
5 printf("Received correctly. receive bit: %d, value: 0x%016lx\r\n", rx_len, rx_data);
6}
检查发送和接收的数据是否一致
编译和烧录
参考 环境搭建
实验现象
按下开发板中 RST 按键,串口打印接收到的数据。
IR - swm
本 demo 主要介绍 IR 以软件模式收发数据。
硬件连接
本 demo 使用到的 gpio 参考 board_ir_gpio_init
,将红外发射二极管和接收头与 IR 引脚连接,具体连接方式如下表(以BL808为例):
开发板 IR 引脚 |
外接模块 |
---|---|
VCC |
红外接收头 VCC |
GND |
红外接收头 GND |
RX(GPIO17) |
红外接收头 OUT |
VCC |
红外发射二极管正极 |
TX(GPIO11) |
红外发射二极管负极 |
软件实现
更详细的代码请参考 examples/peripherals/ir/ir_swm
1board_init();
board_init
中会开启 IR 时钟,并选择 IR 时钟源和分频。
1board_ir_gpio_init();
配置相关引脚为 IR 功能
1uint16_t tx_buffer[] = { 1777, 1777, 3555, 3555, 1777, 1777, 1777, 1777, 1777, 1777,
2 3555, 1777, 1777, 1777, 1777, 3555, 3555, 1777, 1777, 3555, 1777 };
3struct bflb_ir_tx_config_s tx_cfg;
4
5irtx = bflb_device_get_by_name("irtx");
6
7/* TX init */
8tx_cfg.tx_mode = IR_TX_SWM;
9bflb_ir_tx_init(irtx, &tx_cfg);
获取 irtx 句柄
设置 tx_mode 为 SWM 模式,调用
bflb_ir_tx_init(irtx, &tx_cfg)
初始化 ir tx
1uint16_t rx_buffer[30];
2uint8_t rx_len;
3struct bflb_ir_rx_config_s rx_cfg;
4
5irrx = bflb_device_get_by_name("irrx");
6
7/* RX init */
8rx_cfg.rx_mode = IR_RX_SWM;
9rx_cfg.input_inverse = true;
10rx_cfg.deglitch_enable = false;
11rx_cfg.end_threshold = 3999;
12bflb_ir_rx_init(irrx, &rx_cfg);
13
14/* Enable rx, wait for sending */
15bflb_ir_rx_enable(irrx, true);
获取 irrx 句柄
设置 rx_mode 为 SWM 模式,调用
bflb_ir_rx_init(irrx, &rx_cfg)
初始化 ir rx调用
bflb_ir_rx_enable(irrx, true)
使能 ir rx,等待数据发送
1bflb_ir_swm_send(irtx, tx_buffer, sizeof(tx_buffer) / sizeof(tx_buffer[0]));
2rx_len = bflb_ir_swm_receive(irrx, rx_buffer, 30);
调用
bflb_ir_swm_send(irtx, tx_buffer, sizeof(tx_buffer) / sizeof(tx_buffer[0]))
发送 tx_buffer 中的数据调用
bflb_ir_swm_receive(irrx, rx_buffer, 30)
将接收到的数据存放在 rx_buffer 中
编译和烧录
参考 环境搭建
实验现象
按下开发板中 RST 按键,串口打印接收到的数据。
IR - tx_dma
本 demo 主要介绍 IR 使用 DMA 的方式发送数据。
硬件连接
本 demo 使用到的 gpio 参考 board_ir_gpio_init
,将红外发射二极管与 IR 引脚连接,具体连接方式如下表(以BL808为例):
开发板 IR 引脚 |
外接模块 |
---|---|
VCC |
红外发射二极管正极 |
TX(GPIO11) |
红外发射二极管负极 |
软件实现
更详细的代码请参考 examples/peripherals/ir/ir_tx_dma
1board_init();
board_init
中会开启 IR 时钟,并选择 IR 时钟源和分频。
1board_ir_gpio_init();
配置相关引脚为 IR 功能
1struct bflb_ir_tx_config_s tx_cfg = {
2 .tx_mode = IR_TX_CUSTOMIZE,
3 .data_bits = 0,
4 .tail_inverse = 0,
5 .tail_enable = 0,
6 .head_inverse = 0,
7 .head_enable = 0,
8 .logic1_inverse = 1,
9 .logic0_inverse = 1,
10 .data_enable = 1,
11 .swm_enable = 0,
12 .output_modulation = 1,
13 .output_inverse = 0,
14 .freerun_enable = 1,
15 .continue_enable = 1,
16 .fifo_width = IR_TX_FIFO_WIDTH_24BIT,
17 .fifo_threshold = 1,
18 .logic0_pulse_width_1 = 0,
19 .logic0_pulse_width_0 = 0,
20 .logic1_pulse_width_1 = 2,
21 .logic1_pulse_width_0 = 0,
22 .head_pulse_width_1 = 0,
23 .head_pulse_width_0 = 0,
24 .tail_pulse_width_1 = 0,
25 .tail_pulse_width_0 = 0,
26 .modu_width_1 = 17,
27 .modu_width_0 = 34,
28 .pulse_width_unit = 1124,
29};
30
31irtx = bflb_device_get_by_name("irtx");
32
33/* TX init */
34bflb_ir_tx_init(irtx, &tx_cfg);
35bflb_ir_link_txdma(irtx, true);
36bflb_ir_tx_enable(irtx, true);
获取 irtx 句柄
设置 tx_mode 为 IR_TX_CUSTOMIZE 模式,调用
bflb_ir_tx_init(irtx, &tx_cfg)
初始化 ir tx调用
bflb_ir_link_txdma(irtx, true)
使能 ir tx dma 功能调用
bflb_ir_tx_enable(irtx, true)
开启 ir tx
1struct bflb_dma_channel_config_s dma_config = {
2 .direction = DMA_MEMORY_TO_PERIPH,
3 .src_req = DMA_REQUEST_NONE,
4 .dst_req = DMA_REQUEST_IR_TX,
5 .src_addr_inc = DMA_ADDR_INCREMENT_ENABLE,
6 .dst_addr_inc = DMA_ADDR_INCREMENT_DISABLE,
7 .src_burst_count = DMA_BURST_INCR1,
8 .dst_burst_count = DMA_BURST_INCR1,
9 .src_width = DMA_DATA_WIDTH_32BIT,
10 .dst_width = DMA_DATA_WIDTH_32BIT,
11};
12
13for (i = 0; i < 128; i++) {
14 tx_buffer[i] = i * 0x01010101;
15}
16
17dma0_ch0 = bflb_device_get_by_name("dma0_ch0");
18bflb_dma_channel_init(dma0_ch0, &dma_config);
19bflb_dma_channel_irq_attach(dma0_ch0, dma0_ch0_isr, NULL);
对于 TX, DMA 的配置如下:传输方向(direction)为内存到外设(MEMORY_TO_PERIPH),源请求(src_req)为内存,目标请求(dst_req)为 DMA_REQUEST_IR_TX
初始化 tx_buffer
调用
bflb_dma_channel_init(dma0_ch0, &dma_config)
初始化 DMA调用
bflb_dma_channel_irq_attach(dma0_ch0, dma0_ch0_isr, NULL)
注册 dma 通道 0 中断
1struct bflb_dma_channel_lli_pool_s tx_llipool[1];
2struct bflb_dma_channel_lli_transfer_s tx_transfers[1];
3
4tx_transfers[0].src_addr = (uint32_t)tx_buffer;
5tx_transfers[0].dst_addr = (uint32_t)DMA_ADDR_IR_TDR;
6tx_transfers[0].nbytes = 128 * 4;
7bflb_dma_channel_lli_reload(dma0_ch0, tx_llipool, 1, tx_transfers, 1);
8bflb_dma_channel_start(dma0_ch0);
分配一块 lli 内存池,最多可以传输 4064 * 1 字节
配置一块内存(tx_transfers)进行传输,源地址(src_addr)为存储发送数据的内存地址(tx_buffer),目标地址(dst_addr)为 IR TX FIFO地址(DMA_ADDR_IR_TDR)
调用
bflb_dma_channel_lli_reload(dma0_ch0, tx_llipool, 1, tx_transfers, 1)
初始化调用
bflb_dma_channel_start(dma0_ch0)
启动 DMA 传输
1while (dma_tc_flag0 != 1) {
2 bflb_mtimer_delay_ms(1);
3}
4printf("Check wave\r\n");
DMA 传输完成后,查看波形
编译和烧录
参考 环境搭建
实验现象
按下 RST 按键,数据传输完成后,查看波形是否正确。
各系列芯片对 IR 接收和发送的支持情况如下表:
信号 |
芯片系列 |
GPIO |
---|---|---|
IR TX |
BL702 |
支持 |
BL808 |
支持 |
|
BL616 |
不支持 |
|
IR RX |
BL702 |
支持 |
BL808 |
支持 |
|
BL616 |
支持 |
UART
UART - poll
本 demo 主要演示 UART 轮询模式收发功能。
硬件连接
芯片 UART TX 引脚连接 USB2TTL 模块 RX
芯片 UART RX 引脚连接 USB2TTL 模块 TX
本 demo 使用到的 gpio 如下表:
名称 |
芯片型号 |
GPIO |
---|---|---|
UART1_TX |
BL702 |
GPIO 18 |
BL616 |
GPIO 23 |
|
UART1_RX |
BL702 |
GPIO 19 |
BL616 |
GPIO 24 |
软件实现
具体软件代码见 examples/peripherals/uart/uart_poll
1board_init();
board_init
中会开启 UART IP 时钟,并选择 UART 时钟源和分频。
1board_uartx_gpio_init();
配置相关引脚为
UARTx TX
、UARTx RX
功能,默认 demo 使用 UART1 外设。
1uartx = bflb_device_get_by_name(DEFAULT_TEST_UART);
2
3struct bflb_uart_config_s cfg;
4
5cfg.baudrate = 2000000;
6cfg.data_bits = UART_DATA_BITS_8;
7cfg.stop_bits = UART_STOP_BITS_1;
8cfg.parity = UART_PARITY_NONE;
9cfg.flow_ctrl = 0;
10cfg.tx_fifo_threshold = 7;
11cfg.rx_fifo_threshold = 7;
12bflb_uart_init(uartx, &cfg);
获取 DEFAULT_TEST_UART 句柄,并初始化 UART
1int ch;
2while (1) {
3 ch = bflb_uart_getchar(uartx);
4 if (ch != -1) {
5 bflb_uart_putchar(uartx, ch);
6 }
7}
调用
bflb_uart_getchar
从 uart rx fifo 中读取数据,如果返回 -1,表示没有数据调用
bflb_uart_putchar
将数据 ch 填充到 uart tx fifo 中
编译和烧录
参考 环境搭建
实验现象
将 UART1 TX, RX, GND 引脚分别与 USB2TTL 模块 RX, TX, GND 相连,按下 reset 按键。
使用串口给 UART1 发送 0123456789
,USB2TTL 模块能接收到同样的数据。
UART - dma
本 demo 主要演示 UART dma 模式收发功能。
硬件连接
芯片 UART TX 引脚连接 USB2TTL 模块 RX
芯片 UART RX 引脚连接 USB2TTL 模块 TX
本 demo 使用到的 gpio 如下表:
名称 |
芯片型号 |
GPIO |
---|---|---|
UART1_TX |
BL702 |
GPIO 18 |
BL616 |
GPIO 23 |
|
UART1_RX |
BL702 |
GPIO 19 |
BL616 |
GPIO 24 |
软件实现
具体软件代码见 examples/peripherals/uart/uart_podma
1board_init();
board_init
中会开启 UART IP 时钟,并选择 UART 时钟源和分频。
1board_uartx_gpio_init();
配置相关引脚为
UARTx TX
、UARTx RX
功能,默认 demo 使用 UART1 外设。
1uartx = bflb_device_get_by_name(DEFAULT_TEST_UART);
2
3struct bflb_uart_config_s cfg;
4
5cfg.baudrate = 2000000;
6cfg.data_bits = UART_DATA_BITS_8;
7cfg.stop_bits = UART_STOP_BITS_1;
8cfg.parity = UART_PARITY_NONE;
9cfg.flow_ctrl = 0;
10cfg.tx_fifo_threshold = 7;
11cfg.rx_fifo_threshold = 7;
12bflb_uart_init(uartx, &cfg);
获取 DEFAULT_TEST_UART 句柄,并初始化 UART
1bflb_uart_link_txdma(uartx, true);
2bflb_uart_link_rxdma(uartx, true);
使能 uart tx、rx dma 功能
1struct bflb_dma_channel_config_s config;
2
3config.direction = DMA_MEMORY_TO_PERIPH;
4config.src_req = DMA_REQUEST_NONE;
5config.dst_req = DEFAULT_TEST_UART_DMA_TX_REQUEST;
6config.src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
7config.dst_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
8config.src_burst_count = DMA_BURST_INCR1;
9config.dst_burst_count = DMA_BURST_INCR1;
10config.src_width = DMA_DATA_WIDTH_8BIT;
11config.dst_width = DMA_DATA_WIDTH_8BIT;
12bflb_dma_channel_init(dma0_ch0, &config);
13
14struct bflb_dma_channel_config_s rxconfig;
15
16rxconfig.direction = DMA_PERIPH_TO_MEMORY;
17rxconfig.src_req = DEFAULT_TEST_UART_DMA_RX_REQUEST;
18rxconfig.dst_req = DMA_REQUEST_NONE;
19rxconfig.src_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
20rxconfig.dst_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
21rxconfig.src_burst_count = DMA_BURST_INCR1;
22rxconfig.dst_burst_count = DMA_BURST_INCR1;
23rxconfig.src_width = DMA_DATA_WIDTH_8BIT;
24rxconfig.dst_width = DMA_DATA_WIDTH_8BIT;
25bflb_dma_channel_init(dma0_ch1, &rxconfig);
26
27bflb_dma_channel_irq_attach(dma0_ch0, dma0_ch0_isr, NULL);
28bflb_dma_channel_irq_attach(dma0_ch1, dma0_ch1_isr, NULL);
配置 DMA CH0 为 UARTx TX , DMA CH1 为 UARTx RX .
注册 dma 通道中断
1struct bflb_dma_channel_lli_pool_s tx_llipool[20]; /* max trasnfer size 4064 * 20 */
2struct bflb_dma_channel_lli_transfer_s tx_transfers[3];
3
4tx_transfers[0].src_addr = (uint32_t)src_buffer;
5tx_transfers[0].dst_addr = (uint32_t)DEFAULT_TEST_UART_DMA_TDR;
6tx_transfers[0].nbytes = 4100;
7
8tx_transfers[1].src_addr = (uint32_t)src2_buffer;
9tx_transfers[1].dst_addr = (uint32_t)DEFAULT_TEST_UART_DMA_TDR;
10tx_transfers[1].nbytes = 4100;
11
12tx_transfers[2].src_addr = (uint32_t)src3_buffer;
13tx_transfers[2].dst_addr = (uint32_t)DEFAULT_TEST_UART_DMA_TDR;
14tx_transfers[2].nbytes = 4100;
15
16struct bflb_dma_channel_lli_pool_s rx_llipool[20];
17struct bflb_dma_channel_lli_transfer_s rx_transfers[1];
18rx_transfers[0].src_addr = (uint32_t)DEFAULT_TEST_UART_DMA_RDR;
19rx_transfers[0].dst_addr = (uint32_t)receive_buffer;
20rx_transfers[0].nbytes = 50;
21
22bflb_dma_channel_lli_reload(dma0_ch0, tx_llipool, 20, tx_transfers, 3);
23bflb_dma_channel_lli_reload(dma0_ch1, rx_llipool, 20, rx_transfers, 1);
24bflb_dma_channel_start(dma0_ch0);
25bflb_dma_channel_start(dma0_ch1);
分配一块 lli 内存池,个数为20,最多可以传输 4094 * 20 字节
配置三块不连续的内存进行传输
调用
bflb_dma_channel_lli_reload
初始化调用
bflb_dma_channel_start
启动传输等待传输完成并进入中断
编译和烧录
参考 环境搭建
实验现象
BTBLE
BTBLE 例程仅支持 BL616/BL618
BLE MESH
本节主要介绍 BLE mesh的使用,使用 ble mesh 组网并控制 led 开关。
硬件准备
手机app 安装 Bouffalo_Mesh_v1.0_20211118.apk
pc 终端工具:xshell 或者 mobaxterm
硬件连接
无
软件实现
更详细的代码请参考 examples/btble/blemesh_cli
编译和烧录
参考 环境搭建
实验现象
打开终端工具,选择好串口以后复位芯片,并敲回车,可以显示 shell 功能正常
输入
blemesh_init
和blemesh_pb 2 1

如果提示 Failed to enable PB-GATT (err -120),输入
blemesh_reset
命令复位

到此芯片端初始化完成
打开 Bouffalo_Mesh app, 点击 添加节点

扫描出芯片的 BLE 名称和 mac 地址, 单击名称进行连接

连接完成,点击 鉴定

鉴定完成,点击 规定,会弹出一个选项,点击 好


规定完成,会弹出一个选项,点击 好

配置完成后,网络菜单项会显示连接的 BLE 设备,此时点击 BLE 设备

点击分子菜单的下拉框,点击 Generic On Off Server


点击 绑定密钥,点击 Application Key 1


下拉到最底下,点击 上 或者 关闭 发送开关命令给 BLE 设备,BLE 设备的串口会打印出开关命令的值



在 APP 当前界面点击 订阅,如果当前没有组,则创建一个组,并点击 好,如果组已经存在,则使用存在的组,这样设备就在一个组网中

点击 组 的界面,并点击创建的组,会显示 LED 开关的界面,点击 上 或者 关闭 发送开关命令给 BLE 设备,如果存在多个 BLE 设备,则会发给所有的 BLE 设备


如果需要添加多个 BLE 设备进行组网,只需要按照上述步骤重复即可,并加入到一个 group 中
WIFI6
WIFI6 例程仅支持 BL616/BL618
WIFI HTTP
本节主要介绍 BL616/BL618 wifi 的使用,如何进行 http 请求。
开发前准备
硬件准备
准备一块BL616/BL618开发板,本文使用的是BL618 DVK开发板,如下图:

开发板带有一个USB接口,使用一根 Type-C 线连接到电脑后,USB在给开发板供电的同时可以枚举出一个串口和CK-Link调试器设备。 串口用于将程序烧录到芯片的flash,CK-Link用于JTAG调试。如果没有安装 ck-link 驱动,可能不能枚举出ck-link设备,在本评估过程中不需要使用 ck-link,只要串口成功枚举即可。
开发板同时带有一个Boot按键,用于选择芯片复位后运行模式,配合Reset按键可以实现芯片的Flash启动和下载模式的进入。
运行模式
直接按下RESET按键并松开,按键松开后芯片从Flash启动,进入运行模式。
下载模式
先按下BOOT按键,再按下RESET按键,然后松开RESET按键,最后松开BOOT按键,此时芯片进入烧录模式。
PC 准备
准备 pc 终端工具:xshell 或者 mobaxterm,linux 下可以选择 picocom 或者 putty
硬件连接
无
软件实现
更详细的代码请参考 examples/wifi/sta/wifi_http
编译和烧录
编译,Makefile 文件已经配置好了其他参数( CHIP 和 BOARD ),不需要用户再填写
1$ cd examples/wifi/sta/wifi_http
2$ make
烧录
参考 环境搭建 烧录章节
实验现象
打开串口终端软件,按下复位键,输出以下log,按回车键可以显示 bouffalolab /> 字样,类似于 linux 的终端

输入 wifi_sta_connect ssid pwd 连接网络,其中 ssid 为连接的 ap 名称,pwd 为连接的 ap 密码

wifi 连接成功后,会打印出分配的 ip 地址,并提示 CONNECTED

输入 ping url 可以 ping 一个网站,url 为网站 ip 地址或者域名

输入 wifi_http_test url 从网站中获取 html数据,url 为网站 ip 地址或者域名

其他命令的使用参考 WIFI6
UART 引脚映射详解
博流系列芯片每个 pin 都可以配置成 UART 任意一个功能,这种灵活的配置就会出现一些坑,比如多个 pin 配成了一个 uart 功能。
下面介绍下如何正确的理解 每个 pin 都可以配置成 UART 任意一个功能 并防止踩坑。最后讲下 bflb_gpio_uart_init
函数的实现。
UART SIG
在博流系列芯片中,有个 UART SIG 的概念, 每个 SIG 对应到 UART 所有功能,功能类似于 8选1 选择器或者 12 选1 选择器。并且每个 SIG 都有一个默认的 UART 功能,如图所示:

2 个 pin 选了一个 SIG
每个 GPIO 对应一个 SIG, 对应关系跟芯片相关,比如 BL602/BL702 是 mod 8 的关系,而 BL616/BL808 是 mod 12 的关系,这个时候就会产生一个问题, 存在重复的可能性, 假设现在是 mod 8 的关系,则 GPIO0 跟 GPIO8 都对应 SIG0,如果 GPIO0 配置了 UART 一个功能,则不能再使用 GPIO8,否则会出现两个 GPIO 共用一个功能的情况。
2 个 SIG 选了一个 UART 功能
为了方便软件,代码中将默认功能全部改成了 0xF,所有 SIG 都指向一个没有作用的功能。为什么这么做?
假设 GPIO2 默认使用 UART0 TX,这个时候我想使用 GPIO6 作为 UART0 TX,当我软件中配置 GPIO6 为 UART0 TX 以后,请问,GPIO2 是什么功能?没错,还是 UART0 TX,这个时候就是有问题的。
bflb_gpio_uart_init
针对以上两个问题,出现了 bflb_gpio_uart_init
。该函数的作用是避免了 2 个 SIG 选了一个 UART 功能 ,如果出现了,则后配置的覆盖前面的。
而对于 2 个 pin 选了一个 SIG 则是无法靠软件避免的,只能人为的避免。
DMA 链表模式(LLI)深度解析
博流系列芯片的 DMA 都支持链表模式(LLI)。
在进行一次 DMA 读或者写的时候,可以配置多个链表,从而当一个链表的数据传输完成时,会跳到下一个链表的起始地址,并继续传输数据,直到链表的下一个地址为 0。如果 DMA 使能了完成中断,则当 DMA 发送或者接收完成时,会进入完成中断。
那么有了这种 DMA 链表模式,我们就可以实现以下功能:
DMA 发送或者接收长度不限制
DMA 发送接收地址可以不连续
DMA 实现多种中断模式,半中断、3中断、4中断等等
DMA 实现循环功能
OK,那么当我们开始研究链表配置之前,我们需要了解一些前提: - 每个链表最多传输 4095 ,单位根据位宽决定 - 每个链表都可以触发中断
支持长度不限制
由于每个 dma 链表最多支持 4095,假设位宽用的是字节,那么一个链表最多传输 4095 字节,很显然这个不能满足我们需求,性能太低。那么如何提高传输长度呢?
我们可以使用多个链表,串接起来,这样就能够支持更大的传输长度了,并且传输的地址是连续的,dma 链表连接如图所示:

这个时候还有一个问题,当一个链表使用了 4095 字节,下一个链表是从 4095 的偏移开始,这个时候,就会产生非对齐的问题,如果是在 cache 场景下,是会有问题的。
因此,我们将 4095 减少到 4064,这样保证每个链表的首地址都是 32 字节对齐的。到这就实现了长度不限制的功能,具体实现参考 bflb_dma_lli_config
函数。
1void bflb_dma_lli_config(struct bflb_device_s *dev, struct bflb_dma_channel_lli_pool_s *lli_pool, uint32_t lli_count, uint32_t src_addr, uint32_t dst_addr, uint32_t transfer_offset, uint32_t last_transfer_len)
2{
3 uint32_t channel_base;
4 union bflb_dma_lli_control_s dma_ctrl_cfg;
5
6 channel_base = dev->reg_base;
7
8 dma_ctrl_cfg = (union bflb_dma_lli_control_s)getreg32(channel_base + DMA_CxCONTROL_OFFSET);
9
10 dma_ctrl_cfg.bits.TransferSize = 4064;
11 dma_ctrl_cfg.bits.I = 0;
12
13 /* nbytes will be integer multiple of 4064*n or 4064*2*n or 4064*4*n,(n>0) */
14 for (uint32_t i = 0; i < lli_count; i++) {
15 lli_pool[i].src_addr = src_addr;
16 lli_pool[i].dst_addr = dst_addr;
17 lli_pool[i].nextlli = 0;
18
19 if (dma_ctrl_cfg.bits.SI) {
20 src_addr += transfer_offset;
21 }
22
23 if (dma_ctrl_cfg.bits.DI) {
24 dst_addr += transfer_offset;
25 }
26
27 if (i == lli_count - 1) {
28 dma_ctrl_cfg.bits.TransferSize = last_transfer_len;
29 dma_ctrl_cfg.bits.I = 1;
30 }
31
32 if (i) {
33 lli_pool[i - 1].nextlli = (uint32_t)(uintptr_t)&lli_pool[i];
34 }
35
36 lli_pool[i].control = dma_ctrl_cfg;
37 }
38}
支持地址不连续
刚刚我们解决了长度限制问题,那么本身 dma 链表是支持地址不连续的,我们只需要把上面使用的多个链表当成一个大链表,然后两个大链表拼接,并且两个链表传输的首地址不连续,就可以实现地址不连续了。
具体实现参考 bflb_dma_channel_lli_reload
函数中的 bflb_dma_channel_lli_transfer_s
。dma 链表连接如图所示:

支持多中断
完成了上述两步以后,多中断也就完成了。
当我们支持完长度不限制后,最后一个链表会开启中断,当传输完成最后一个链表时,就会触发中断。
当我们支持完地址不连续后,多个大链表(也就是长度不限制的链表的最后一个链表)完成都会触发中断,假设设定了三个传输 bflb_dma_channel_lli_transfer_s
, 那么会触发三次 DMA 完成中断。