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.txtproject(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.txtproject(xxx) 中名称一致。

    • addresspartition_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 文件并拷贝

    • addresspartition_xxx.toml 指定

  • mfg 要烧录的 mfg 固件,必须使用 mfg 名称。 mfg 可选,可以不烧录

    • filedir 表示 mfg 固件所在相对路径,正常来说是编译完后放在 build/build_out 目录。 自动从 bsp/board/board_name/config 目录拷贝

    • addresspartition_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标签过滤功能

  • 可对不需要的等级、功能、标签进行裁剪,缩小代码体积

_images/bflog_log.png

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协议栈的架构:
    _images/ble1.png
    • 总共有3个主要层,共同构成了一个完整的蓝牙低能耗协议栈
      • Host:这一层位于应用程序之下,由多个(非实时)网络和传输协议组成,使应用程序能够以标准和互操作的方式与对等设备通信。

      • Controller:控制器实现了链路层(LE LL),这是一种低层次的实时协议,它与无线电硬件一起提供了空中通信的标准互操作。LL处理包的接收和传输,保证数据的传递,并处理所有LL控制程序。

      • Radio Hardware:实现所需的模拟和数字基带功能块,允许链路层固件在频谱的2.4GHz波段发送和接收。

  • 主控Host:
    _images/ble2.png
    • 蓝牙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 CH0ADC 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 口

名称

芯片系列

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 CH0DAC

  • 注册 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 口

名称

芯片系列

精度

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 口

开发板

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 口

信号

芯片系列

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 口

信号

芯片系列

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 口

名称

芯片型号

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 TXUARTx 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 口

名称

芯片型号

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 TXUARTx 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 CH0UARTx TXDMA CH1UARTx 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_initblemesh_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 文件已经配置好了其他参数( CHIPBOARD ),不需要用户再填写

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 完成中断。

支持循环模式