BouffaloSDK 使用指南

欢迎来到 Bouffalo Lab BouffaloSDK 文档中心。

开发环境搭建

BouffaloSDK 提供了以下几种开发环境,包含环境搭建、编译、烧录和调试,选择一种适合你的即可。

Build and Debug with CDK

Build with Windows CMD

本节主要介绍如何在 Windows 下使用 命令行 进行开发。

环境搭建

  • 首先需要安装 git 工具,这里自行百度。

  • 使用 git 下载 RISC-V 工具链, 这里比如安装到 D 盘

1$ cd D:/
2$ git clone https://gitee.com/bouffalolab/toolchain_gcc_t-head_windows.git
  • 将 make、ninja、下载的 risc-v 工具链路径配置到系统环境变量中, 其中 make 和 ninja 工具位于 bouffalo_sdk/tools 目录下。

  • windows 11 配置完以后需要重启电脑才生效。

  • 完成上述步骤以后,需要检查是否真的安装成功。打开 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)

编译

打开 powershell 或者 cmd,进入 examples ,选择你想要编译的 case,且该 case 下必须有 Makefile 文件。使用下面命令编译,生成的 bin 文件会输出到 build/build_out 目录下。

  • 使用 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

烧录

1 $ cd examples/helloworld
2 $ make flash CHIP=chip_name COMX=port_name ## port_name 为串口号名称,比如 COM5

备注

如果使用 BL808 或者 BL606P,需要在上面基础上添加 CPU_ID=id ,id 可以为 m0 或者 d0

调试

推荐使用 eclipse + cklink 调试,参考 Build and Debug with Eclipse,也可以使用命令行调试,这里不做介绍

Build with Linux or WSL

本节主要介绍如何在 Linux 或者 WSL 下使用 命令行 进行开发。Windows 下如何安装 WSL 以及 WSL1/WSL2 下如何使用串口请自行百度。

环境搭建

  • 首先打开 linux 终端,并安装依赖工具

1$ cd ~
2$ sudo apt install git make ninja-build -y
  • 其次使用 git 下载 RISC-V 工具链

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
  • 完成上述步骤以后,需要检查是否真的安装成功,输入下列命令:

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)

编译

打开 linux 终端,进入 examples ,选择你想要编译的 case,且该 case 下必须有 Makefile 文件。使用下面命令编译,生成的 bin 文件会输出到 build/build_out 目录下。

  • 使用 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

烧录

1 $ cd examples/helloworld
2 $ make flash CHIP=chip_name COMX=port_name ## port_name 为串口号名称,比如 linux 中/dev/ttyACMx

备注

如果使用 BL808 或者 BL606P,需要在上面基础上添加 CPU_ID=id ,id 可以为 m0 或者 d0

调试

推荐使用 eclipse + cklink 调试,参考 Build and Debug with Eclipse,也可以使用命令行调试,这里不做介绍

Build and Debug with Eclipse

本节主要介绍如何使用 Eclipse 进行开发。

环境搭建

  • 首先下载 Eclipse , 选择 Eclipse IDE for C/C++ Developers , 并根据你的电脑版本进行下载。

  • 选择工作路径并点击 Launch

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

  • 导入 bl_mcu_sdk

编译

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

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

调试

完成上述步骤以后,如果是在 flash 上调试,需要先将 bin 文件烧录到芯片中 ,然后才能够进行下面的调试环节。

  • 在 Eclipse 中 点击 debug 图标,并点击 Debug Configurations

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

  • 最终进入调试界面

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

使用调试器调试代码

本节主要介绍如何使用调试器进行代码的调试。

芯片默认 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

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 结尾的函数, 使能方式同上

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 即可同步代码。

Peripherals

简介

BouffaloSDK 中外设驱动分为两类: LHALSOC ,前者对通用外设进行了统一的封装,不同芯片使用同一套接口,方便用户使用和移植到其他平台。后者则是每个芯片独有且特殊的部分,比如 GLB、HBN、PDS、AON 等等。

下面主要列出 LHAL 相关 API 的使用。

LHAL 中各个外设 API 声明均在 drivers/include 目录下可以查看。在学习 API 使用之前,我们需要了解 struct bflb_device_s 结构体的含义和作用。

1struct bflb_device_s {
2    const char *name;
3    uint32_t reg_base;
4    uint8_t irq_num;
5    uint8_t idx;
6    uint8_t sub_idx;
7    uint8_t dev_type;
8    void *user_data;
9};

parameter

description

name

外设名称

reg_base

外设寄存器基地址

irq_num

外设中断号

idx

外设 id

idx

外设 id,例如 UART0、UART1

sub_idx

外设子 id,例如 DMA0_CH0、DMA0_CH1

dev_type

外设类型

user_data

用户变量

从上我们可以知道 struct bflb_device_s 结构体成员的具体信息,其中比较重要的就是 reg_baseirq_num ,有了这两个,我们才能操作外设寄存器和外设中断。那么当我们进行外设配置之前,就需要先获取该结构体句柄,从中获取我们需要的信息,否则不能对外设进行操作。获取结构体句柄有以下两种方式:

  • bflb_device_get_by_name 通过 name 获取

  • bflb_device_get_by_id 通过 dev_typeidx 获取

那么还有一个问题,结构体句柄保存在哪?

对于每个系列芯片支持的外设,我们将结构体句柄保存在一个 table 表中。详见 lhal/config/xxx/device_table.c 文件。

API 支持情况

了解了上面的基本概念以后,接下来就可以查阅相关 LHAL API 并使用了。当前已经支持的 LHAL API 列表如下:

备注

✔️ 表示已支持; 表示未支持; 表示已支持但未测试; 表示没有该外设。

peripheral

BL602/BL604

BL702/BL704/BL706

BL616/BL618

BL808

ADC

✔️

✔️

CAM

CKS

✔️

✔️

DAC

✔️

✔️

DMA

✔️

✔️

✔️

EFUSE

✔️

✔️

✔️

EMAC

✔️

✔️

✔️

FLASH

✔️

✔️

✔️

✔️

GPIO

✔️

✔️

✔️

I2C

✔️

✔️

IR

✔️

✔️

✔️

MJPEG

✔️

✔️

PWM_v1

✔️

PWM_v2

✔️

✔️

RTC

✔️

✔️

✔️

SEC_AES

✔️

✔️

✔️

SEC_SHA

✔️

✔️

✔️

SEC_TRNG

✔️

✔️

✔️

SEC_PKA

✔️

✔️

✔️

SPI

✔️

✔️

TIMER

✔️

✔️

✔️

UART

✔️

✔️

✔️

USB_v1

✔️

USB_v2

✔️

✔️

WDG

✔️

✔️

✔️

API 列表

除了使用网页版查看 API 以外, 还提供了 LHAL Doxygen 版本,离线生成文档。 Doxyfile 文件在 drivers/lhal 目录下。

使用 Doxywizard 导入并运行即可。

ADC

Macros
adc channel

参考 bflb_adc.h 文件,不详细列出。

adc clock div

对 adc 时钟再一次进行分频。分频后的时钟必须小于等于 500K。ADC CLK = CLK_SOURCE/CLK_DIV/adc_clk_div。

1 #define ADC_CLK_DIV_4  1
2 #define ADC_CLK_DIV_8  2
3 #define ADC_CLK_DIV_12 3
4 #define ADC_CLK_DIV_16 4
5 #define ADC_CLK_DIV_20 5
6 #define ADC_CLK_DIV_24 6
7 #define ADC_CLK_DIV_32 7
adc resolution

adc 位数,可以选择 12B、14B、16B。其中 14B 和 16B 自带过采样处理。

1 #define ADC_RESOLUTION_12B 0
2 #define ADC_RESOLUTION_14B 2
3 #define ADC_RESOLUTION_16B 4
adc vref

adc 内置参考电压选择,可以选择 2.0 V 或者 3.2V。

1 #define ADC_VREF_3P2V 0
2 #define ADC_VREF_2P0V 1
Structs
struct bflb_adc_config_s

adc 初始化配置结构体。

1 struct bflb_adc_config_s {
2     uint8_t clk_div;
3     uint8_t scan_conv_mode;
4     uint8_t continuous_conv_mode;
5     uint8_t differential_mode;
6     uint8_t resolution;
7     uint8_t vref;
8 };

parameter

description

clk_div

分频值

scan_conv_mode

是否开启扫描模式

continuous_conv_mode

是否开启连续转换模式

differential_mode

是否开启差分模式

resolution

采样位数

vref

参考电压选择

struct bflb_adc_channel_s

配置 adc 通道时使用。

1 struct bflb_adc_channel_s {
2     uint8_t pos_chan;
3     uint8_t neg_chan;
4 };

parameter

description

pos_chan

正向通道

neg_chan

反向通道(单端模式下无用)

struct bflb_adc_result_s

adc 标准转换结果

1 struct bflb_adc_result_s {
2   int8_t pos_chan;
3   int8_t neg_chan;
4   int32_t value;
5   int32_t millivolt;
6 };

parameter

description

pos_chan

正向通道

neg_chan

反向通道(单端模式下无用)

value

adc 转换结果

millivolt

转换结果转 mv

Functions
bflb_adc_init

说明: 初始化 adc。adc 使用之前需要开启 adc ip 时钟、设置 adc 时钟源和分频值、选择使用的 gpio 为 analog 模式。

1 void bflb_adc_init(struct bflb_device_s *dev, const struct bflb_adc_config_s *config);

parameter

description

dev

设备句柄

config

配置项

bflb_adc_deinit

说明: 反初始化 adc。

1 void bflb_adc_deinit(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_adc_channel_config

说明: 配置 adc 通道。

1 int bflb_adc_channel_config(struct bflb_device_s *dev, struct bflb_adc_channel_s *chan, uint8_t channels);

parameter

description

dev

设备句柄

chan

通道(一对)

channels

通道对数(单次扫描模式下只能为 1)

bflb_adc_start_conversion

说明: 启动 adc 转换。连续转换模式下只需要调用一次。

1 void bflb_adc_start_conversion(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_adc_stop_conversion

说明: 停止 adc 转换。

1 void bflb_adc_stop_conversion(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_adc_get_count

说明: 获取 adc 转换个数。

1 uint8_t bflb_adc_get_count(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

转换个数(最大为32)

bflb_adc_read_raw

说明: 读取一次 adc 转换值。

1 uint32_t bflb_adc_read_raw(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

转换值(注意不是最终采样值)

bflb_adc_rxint_mask

说明: adc 转换完成中断开关。

1 void bflb_adc_rxint_mask(struct bflb_device_s *dev, bool mask);

parameter

description

dev

设备句柄

mask

是否屏蔽中断

bflb_adc_errint_mask

说明: adc 错误中断开关。

1 void bflb_adc_errint_mask(struct bflb_device_s *dev, bool mask);

parameter

description

dev

设备句柄

mask

是否屏蔽中断

bflb_adc_get_intstatus

说明: adc 中断标志。

1uint32_t bflb_adc_get_intstatus(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

中断标志

返回值如下:

1 ADC_INTSTS_NEG_SATURATION
2 ADC_INTSTS_NEG_SATURATION
3 ADC_INTSTS_FIFO_UNDERRUN
4 ADC_INTSTS_FIFO_OVERRUN
5 ADC_INTSTS_ADC_READY
bflb_adc_int_clear

说明: 清除 adc 中断标志。

1 void bflb_adc_int_clear(struct bflb_device_s *dev, uint32_t int_clear);

parameter

description

dev

设备句柄

int_clear

清除值

int_clear 可以填入以下参数:

1 ADC_INTCLR_NEG_SATURATION
2 ADC_INTCLR_POS_SATURATION
3 ADC_INTCLR_FIFO_UNDERRUN
4 ADC_INTCLR_FIFO_OVERRUN
5 ADC_INTCLR_ADC_READY
bflb_adc_parse_result

说明: 对 adc 转换结果进行解析。

1 void bflb_adc_parse_result(struct bflb_device_s *dev, uint32_t *buffer, struct bflb_adc_result_s *result, uint16_t count);

parameter

description

dev

设备句柄

buffer

转换值

result

输出结果

count

转换个数

bflb_adc_tsen_init

说明: 初始化 adc tsen 模块。

1void bflb_adc_tsen_init(struct bflb_device_s *dev, uint8_t tsen_mod);

parameter

description

dev

设备句柄

tsen_mod

模式选择

bflb_adc_tsen_get_temp

说明: 初始化 adc tsen 模块。

1float bflb_adc_tsen_get_temp(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

温度

bflb_adc_vbat_enable

说明: 开启 vbat 。

1void bflb_adc_vbat_enable(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_adc_vbat_disable

说明: 关闭 vbat。

1void bflb_adc_vbat_disable(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

CKS

Macros

Structs

Functions

CLOCK

Macros

Structs

Functions
bflb_clk_get_system_clock

说明: 获取系统时钟频率。

1 uint32_t bflb_clk_get_system_clock(uint8_t type);

parameter

description

dev

设备句柄

type

外设类型

type 可以填入以下参数:

1#define BFLB_SYSTEM_ROOT_CLOCK 0
2#define BFLB_SYSTEM_CPU_CLK    1
3#define BFLB_SYSTEM_PBCLK      2
4#define BFLB_SYSTEM_XCLK       3
5#define BFLB_SYSTEM_32K_CLK    4
bflb_clk_get_peripheral_clock

说明: 获取外设时钟频率。

1 uint32_t bflb_clk_get_peripheral_clock(uint8_t type, uint8_t idx);

parameter

description

dev

设备句柄

type

外设类型

idx

外设 id

type 可以填入以下参数:

 1#define BFLB_DEVICE_TYPE_ADC      0
 2#define BFLB_DEVICE_TYPE_DAC      1
 3#define BFLB_DEVICE_TYPE_AUDIOADC 2
 4#define BFLB_DEVICE_TYPE_AUDIODAC 3
 5#define BFLB_DEVICE_TYPE_GPIO     4
 6#define BFLB_DEVICE_TYPE_UART     5
 7#define BFLB_DEVICE_TYPE_SPI      6
 8#define BFLB_DEVICE_TYPE_I2C      7
 9#define BFLB_DEVICE_TYPE_DMA      8
10#define BFLB_DEVICE_TYPE_I2S      9
11#define BFLB_DEVICE_TYPE_IR       10
12#define BFLB_DEVICE_TYPE_TIMER    11
13#define BFLB_DEVICE_TYPE_PWM      12
14#define BFLB_DEVICE_TYPE_CAMERA   14
15#define BFLB_DEVICE_TYPE_FLASH    15
16#define BFLB_DEVICE_TYPE_QSPI     16
17#define BFLB_DEVICE_TYPE_SDH      17
18#define BFLB_DEVICE_TYPE_SDU      18
19#define BFLB_DEVICE_TYPE_ETH      19
20#define BFLB_DEVICE_TYPE_RTC      20
21#define BFLB_DEVICE_TYPE_CRC      21
22#define BFLB_DEVICE_TYPE_RNG      22
23#define BFLB_DEVICE_TYPE_MIPI     23
24#define BFLB_DEVICE_TYPE_DPI      24
25#define BFLB_DEVICE_TYPE_DSI      25
26#define BFLB_DEVICE_TYPE_CSI      26
27#define BFLB_DEVICE_TYPE_USB      27
28#define BFLB_DEVICE_TYPE_AES      28
29#define BFLB_DEVICE_TYPE_SHA      29
30#define BFLB_DEVICE_TYPE_MD5      30
31#define BFLB_DEVICE_TYPE_TRNG     31
32#define BFLB_DEVICE_TYPE_PKA      32
33#define BFLB_DEVICE_TYPE_CKS      33
34#define BFLB_DEVICE_TYPE_MJPEG    34

DAC

Macros
dac clock div

dac 分频,只有 DMA 模式下可以使用 1 分频。

1#define DAC_CLK_DIV_16 0
2#define DAC_CLK_DIV_32 1
3#define DAC_CLK_DIV_64 3
4#define DAC_CLK_DIV_1  4
dac channel

博流系列芯片 dac 通道共两组:A 通道和 B 通道。

1 #define DAC_CHANNEL_A (1 << 0)
2 #define DAC_CHANNEL_B (1 << 1)
Structs

Functions
bflb_dac_init

说明: 初始化 dac。dac 使用之前需要开启 dac ip 时钟、设置 dac 时钟源和分频值、选择使用的 gpio 为 analog 模式。

1 void bflb_dac_init(struct bflb_device_s *dev, uint8_t clk_div);

parameter

description

dev

设备句柄

clk_div

分频值

bflb_dac_deinit

说明: 反初始化 dac。

1 void bflb_dac_deinit(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_dac_channel_enable

说明: dac 通道使能。

1 void bflb_dac_channel_enable(struct bflb_device_s *dev, uint8_t ch);

parameter

description

dev

设备句柄

ch

通道,多个通道之间使用 | 连接

bflb_dac_channel_disable

说明: dac 通道关闭。

1 void bflb_dac_channel_disable(struct bflb_device_s *dev, uint8_t ch);

parameter

description

dev

设备句柄

ch

通道,多个通道之间使用 | 连接

bflb_dac_set_value

说明: 更改 dac 通道值。

1 void bflb_dac_set_value(struct bflb_device_s *dev, uint8_t ch, uint16_t value);

parameter

description

dev

设备句柄

ch

通道,多个通道之间使用 | 连接

value

通道值

DMA

Macros
dma direction

dma 传输方向支持 4 种。

1 #define DMA_MEMORY_TO_MEMORY 0
2 #define DMA_MEMORY_TO_PERIPH 1
3 #define DMA_PERIPH_TO_MEMORY 2
4 #define DMA_PERIPH_TO_PERIPH 3
dma addr increment

dma 地址自增使能。

1 #define DMA_ADDR_INCREMENT_DISABLE 0
2 #define DMA_ADDR_INCREMENT_ENABLE  1
dma data width

dma 传输位宽支持 8BIT 、16BIT、32BIT。

1 #define DMA_TRANSFER_WIDTH_8BIT  0
2 #define DMA_TRANSFER_WIDTH_16BIT 1
3 #define DMA_TRANSFER_WIDTH_32BIT 2
dma burst size

dma 突发传输个数,最多 16 字节突发。

1 #define DMA_BURST_INCR1  0
2 #define DMA_BURST_INCR4  1
3 #define DMA_BURST_INCR8  2
4 #define DMA_BURST_INCR16 3

警告

位宽 * 突发个数 需要小于等于 16 字节

dma addr

参考 bflb_dma.h 文件,不详细列出。

dma request

参考 bflb_dma.h 文件,不详细列出。

Structs
struct bflb_dma_channel_lli_pool_s

由于 dma 支持 lli 模式,所以需要在传输之前分配一块内存池给 lli 使用, 调用 bflb_dma_lli_reload 后会自动消耗内存池和配置相关 lli 信息,无需用户手动配置。

1 struct bflb_dma_channel_lli_pool_s {
2     uint32_t src_addr;
3     uint32_t dst_addr;
4     uint32_t nextlli;
5     union bflb_dma_lli_control_s lli_control;
6 };
struct bflb_dma_channel_lli_transfer_s

用户使用 lli 传输时,需要填充的传输内容:源地址、目标地址和长度,并且如果地址不连续,可以增加多个 transfer。

1 struct bflb_dma_channel_lli_transfer_s {
2     uint32_t src_addr;
3     uint32_t dst_addr;
4     uint32_t nbytes;
5 };
struct bflb_dma_channel_config_s

初始化 dma 通道时需要填充的信息。

 1 struct bflb_dma_channel_config_s {
 2     uint8_t direction;
 3     uint32_t src_req;
 4     uint32_t dst_req;
 5     uint8_t src_addr_inc;
 6     uint8_t dst_addr_inc;
 7     uint8_t src_burst_count;
 8     uint8_t dst_burst_count;
 9     uint8_t src_width;
10     uint8_t dst_width;
11 };

parameter

description

direction

传输方向

src_req

源 dma 请求

dst_req

目标 dma 请求

src_addr_inc

源地址是否自增

dst_addr_inc

目标地址是否自增

src_burst_count

源地址突发个数

dst_burst_count

目标地址突发个数

src_width

源地址位宽

dst_width

目标地址位宽

Functions
bflb_dma_channel_init

说明: 初始化 dma 通道。

1 void bflb_dma_channel_init(struct bflb_device_s *dev, const struct bflb_dma_channel_config_s *config);

parameter

description

dev

设备句柄

config

配置参数

bflb_dma_channel_deinit

说明: 反初始化 dma 通道。

1 void bflb_dma_channel_deinit(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_dma_channel_start

说明: 启动 dma 通道传输。

1 void bflb_dma_channel_start(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_dma_channel_stop

说明: 关闭 dma 通道传输。

1 void bflb_dma_channel_stop(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_dma_channel_isbusy

说明: 检查 dma 通道是否处于 busy 状态。

1bool bflb_dma_channel_isbusy(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

是否 busy

bflb_dma_channel_irq_attach

说明: 注册 dma 通道中断完成回调函数并开启通道完成中断。

1void bflb_dma_channel_irq_attach(struct bflb_device_s *dev, void (*callback)(void *arg), void *arg);

parameter

description

dev

设备句柄

callback

回调函数

arg

用户参数

bflb_dma_channel_irq_detach

说明: 关闭 dma 通道完成中断。

1void bflb_dma_channel_irq_detach(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_dma_channel_lli_reload

说明: 配置 dma 通道的 lli 信息。

1 int bflb_dma_channel_lli_reload(struct bflb_device_s *dev,
2                                 struct bflb_dma_channel_lli_pool_s *lli_pool, uint32_t max_lli_count,
3                                 struct bflb_dma_channel_lli_transfer_s *transfer, uint32_t count);

parameter

description

dev

设备句柄

lli_pool

lli 内存池

max_lli_count

lli 内存池大小

transfer

传输句柄

count

传输次数

return

返回使用的 lli count个数,小于0表示错误

bflb_dma_feature_control

说明: dma 其他特性相关控制,一般不常用。

1void bflb_dma_feature_control(struct bflb_device_s *dev, int cmd, size_t arg);

parameter

description

dev

设备句柄

cmd

控制字

arg

控制参数

cmd 可以填入以下参数:

1#define DMA_CMD_SET_SRCADDR_INCREMENT (0x01)
2#define DMA_CMD_SET_DSTADDR_INCREMENT (0x02)
3#define DMA_CMD_SET_ADD_MODE          (0x03)
4#define DMA_CMD_SET_REDUCE_MODE       (0x04)

FLASH

Macros
flash iomode
1#define FLASH_IOMODE_NIO 0
2#define FLASH_IOMODE_DO  1
3#define FLASH_IOMODE_QO  2
4#define FLASH_IOMODE_DIO 3
5#define FLASH_IOMODE_QIO 4
Structs

Functions
bflb_flash_init

说明: flash 初始化,自动识别支持的 flash 并重新配置 flash 的参数。如果返回错误,必须停止运行代码。

1int bflb_flash_init(void);

parameter

description

return

返回 0 表示成功,其他表示错误,必须停止运行代码

bflb_flash_get_jedec_id

说明: 获取 flash jedec id。

1uint32_t bflb_flash_get_jedec_id(void);

parameter

description

return

返回 flash jedec id

bflb_flash_get_cfg

说明: 获取 flash 配置。

1void bflb_flash_get_cfg(uint8_t **cfg_addr, uint32_t *len);

parameter

description

cfg_addr

保存 flash 配置的地址

len

flash 配置的长度

bflb_flash_set_iomode

说明: 设置 flash IO 工作模式。

1void bflb_flash_set_iomode(uint8_t iomode);

parameter

description

iomode

flash IO 工作模式

bflb_flash_get_image_offset

说明: 获取代码 xip 虚拟地址实际所在的 flash 物理地址。

1uint32_t bflb_flash_get_image_offset(void);

parameter

description

return

返回 flash xip 物理地址

bflb_flash_erase

说明: flash 扇区擦除。 len 为擦除的长度,需要为 4096 倍数,假设 addr 为0 , len 为 4096,则擦除范围为 0 ~ 4095。

1int bflb_flash_erase(uint32_t addr, uint32_t len);

parameter

description

addr

擦除的物理地址

len

擦除长度,需要是 4096 的倍数

return

返回 0 表示成功,其他表示错误

bflb_flash_write

说明: 获取代码 xip 虚拟地址实际所在的 flash 物理地址。

1int bflb_flash_write(uint32_t addr, uint8_t *data, uint32_t len);

parameter

description

addr

写入的物理地址

data

写入的数据缓冲区

len

写入长度

return

返回 0 表示成功,其他表示错误

bflb_flash_read

说明: 获取代码 xip 虚拟地址实际所在的 flash 物理地址。

1int bflb_flash_read(uint32_t addr, uint8_t *data, uint32_t len);

parameter

description

addr

读取的物理地址

data

读取的数据缓冲区

len

读取长度

return

返回 0 表示成功,其他表示错误

bflb_flash_aes_init

说明: 配置某一段 flash 区域进行硬件 aes 解密,能够通过 xip 直接读取解密后的内容。

1void bflb_flash_aes_init(struct bflb_flash_aes_config_s *config);

parameter

description

config

flash aes 配置

bflb_flash_aes_enable

说明: 使能 flash aes 解密。

1void bflb_flash_aes_enable(void);
bflb_flash_aes_disable

说明: 关闭 flash aes 解密。

1void bflb_flash_aes_disable(void);

GPIO

Macros
gpio pin

参考 bflb_gpio.h 文件,不详细列出。

警告

相同系列芯片引脚数会有区别,注意区分

gpio mode

gpio 模式可以配置成输入、输出、模拟、复用。

1 #define GPIO_INPUT      (0 << GPIO_MODE_SHIFT) /* Input Enable */
2 #define GPIO_OUTPUT     (1 << GPIO_MODE_SHIFT) /* Output Enable */
3 #define GPIO_ANALOG     (2 << GPIO_MODE_SHIFT) /* Analog Enable */
4 #define GPIO_ALTERNATE  (3 << GPIO_MODE_SHIFT) /* Alternate Enable */
gpio function

参考 bflb_gpio.h 文件,不详细列出。

gpio pupd

gpio 上下拉可以选择上拉、下拉、浮空。

1 #define GPIO_FLOAT      (0 << GPIO_PUPD_SHIFT) /* No pull-up, pull-down */
2 #define GPIO_PULLUP     (1 << GPIO_PUPD_SHIFT) /* Pull-up */
3 #define GPIO_PULLDOWN   (2 << GPIO_PUPD_SHIFT) /* Pull-down */
gpio smt

gpio 滤波开关。

1 #define GPIO_SMT_DIS   (0 << GPIO_SMT_SHIFT)
2 #define GPIO_SMT_EN    (1 << GPIO_SMT_SHIFT)
gpio drive

gpio 输出能力选择。

1 #define GPIO_DRV_0     (0 << GPIO_DRV_SHIFT)
2 #define GPIO_DRV_1     (1 << GPIO_DRV_SHIFT)
3 #define GPIO_DRV_2     (2 << GPIO_DRV_SHIFT)
4 #define GPIO_DRV_3     (3 << GPIO_DRV_SHIFT)
gpio int trig mode

gpio 外部中断触发模式。

 1 #define GPIO_INT_TRIG_MODE_SYNC_FALLING_EDGE 0
 2 #define GPIO_INT_TRIG_MODE_SYNC_RISING_EDGE  1
 3 #define GPIO_INT_TRIG_MODE_SYNC_LOW_LEVEL    2
 4 #define GPIO_INT_TRIG_MODE_SYNC_HIGH_LEVEL   3
 5 #if defined(BL702)
 6 #define GPIO_INT_TRIG_MODE_ASYNC_FALLING_EDGE 4
 7 #define GPIO_INT_TRIG_MODE_ASYNC_RISING_EDGE  5
 8 #define GPIO_INT_TRIG_MODE_ASYNC_LOW_LEVEL    6
 9 #define GPIO_INT_TRIG_MODE_ASYNC_HIGH_LEVEL   7
10 #else
11 #define GPIO_INT_TRIG_MODE_SYNC_FALLING_RISING_EDGE 4
12 #define GPIO_INT_TRIG_MODE_ASYNC_FALLING_EDGE       8
13 #define GPIO_INT_TRIG_MODE_ASYNC_RISING_EDGE        9
14 #define GPIO_INT_TRIG_MODE_ASYNC_LOW_LEVEL          10
15 #define GPIO_INT_TRIG_MODE_ASYNC_HIGH_LEVEL         11
16 #endif

警告

602、702 不支持双边沿

gpio uart function

博流系列芯片,每个 gpio 可以选择到 UART 的任意一个功能。

 1 #define GPIO_UART_FUNC_UART0_RTS 0
 2 #define GPIO_UART_FUNC_UART0_CTS 1
 3 #define GPIO_UART_FUNC_UART0_TX  2
 4 #define GPIO_UART_FUNC_UART0_RX  3
 5 #define GPIO_UART_FUNC_UART1_RTS 4
 6 #define GPIO_UART_FUNC_UART1_CTS 5
 7 #define GPIO_UART_FUNC_UART1_TX  6
 8 #define GPIO_UART_FUNC_UART1_RX  7
 9 #if defined(BL808) || defined(BL606P)
10 #define GPIO_UART_FUNC_UART2_RTS 8
11 #define GPIO_UART_FUNC_UART2_CTS 9
12 #define GPIO_UART_FUNC_UART2_TX  10
13 #define GPIO_UART_FUNC_UART2_RX  11
14 #endif
Structs

Functions
bflb_gpio_init

说明: 初始化 gpio。如果配置为 UART 功能,请使用 bflb_gpio_uart_init

1 void bflb_gpio_init(struct bflb_device_s *dev, uint8_t pin, uint32_t cfgset);

parameter

description

dev

设备句柄

pin

gpio pin

cfgset

gpio 配置项:gpio mode、gpio function、gpio pupd、gpio smt、gpio drive,多个配置需要使用 | 连接

bflb_gpio_deinit

说明: 反初始化 gpio,默认为输入浮空状态

1 void bflb_gpio_deinit(struct bflb_device_s *dev, uint8_t pin);

parameter

description

dev

设备句柄

pin

gpio pin

bflb_gpio_set

说明: gpio 输出高电平

1 void bflb_gpio_set(struct bflb_device_s *dev, uint8_t pin);

parameter

description

dev

设备句柄

pin

gpio pin

bflb_gpio_reset

说明: gpio 输出低电平

1 void bflb_gpio_reset(struct bflb_device_s *dev, uint8_t pin);

parameter

description

dev

设备句柄

pin

gpio pin

bflb_gpio_read

说明: 读取 gpio 电平

1 bool bflb_gpio_read(struct bflb_device_s *dev, uint8_t pin);

parameter

description

dev

设备句柄

pin

gpio pin

return

true 为 高电平,false 为低电平

bflb_gpio_int_init

说明: gpio 外部中断初始化

1 void bflb_gpio_int_init(struct bflb_device_s *dev, uint8_t pin, uint8_t trig_mode);

parameter

description

dev

设备句柄

pin

gpio pin

trig_mode

中断触发模式

bflb_gpio_int_mask

说明: gpio 外部中断屏蔽开关

1 void bflb_gpio_int_mask(struct bflb_device_s *dev, uint8_t pin, bool mask);

parameter

description

dev

设备句柄

pin

gpio pin

mask

是否屏蔽中断

bflb_gpio_get_intstatus

说明: 获取 gpio 外部中断是否触发的标志

1 bool bflb_gpio_get_intstatus(struct bflb_device_s *dev, uint8_t pin);

parameter

description

dev

设备句柄

pin

gpio pin

return

为 true 表示触发

bflb_gpio_int_clear

说明: 清除 gpio 中断标志

1 void bflb_gpio_int_clear(struct bflb_device_s *dev, uint8_t pin);

parameter

description

dev

设备句柄

pin

gpio pin

bflb_gpio_uart_init

说明: gpio 配置成 uart 的某一个功能。

1 void bflb_gpio_uart_init(struct bflb_device_s *dev, uint8_t pin, uint8_t uart_func);

parameter

description

dev

设备句柄

pin

gpio pin

uart_func

uart 具体某一个功能

I2C

Macros

Structs
struct bflb_i2c_msg_s

i2c 传输时需要填充的消息。

1 struct bflb_i2c_msg_s {
2     uint16_t addr;
3     uint16_t flags;
4     uint8_t *buffer;
5     uint16_t length;
6 };

parameter

description

addr

设备地址(7 位或者 10 位)

flags

传输时附带标志

buffer

数据区

length

数据长度

flag 可以为以下参数:

1#define I2C_M_READ    0x0001
2#define I2C_M_TEN     0x0002
3#define I2C_M_DMA     0x0004
4#define I2C_M_NOSTOP  0x0040
5#define I2C_M_NOSTART 0x0080

备注

I2C_M_NOSTOP 表示 i2c 设备需要操作寄存器地址

Functions
bflb_i2c_init

说明: 初始化 i2c 并配置频率。

1 void bflb_i2c_init(struct bflb_device_s *dev, uint32_t frequency);

parameter

description

dev

设备句柄

frequency

配置频率(范围 305 HZ ~ 400KHZ)

bflb_i2c_deinit

说明: 反初始化 i2c。

1 void bflb_i2c_deinit(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_i2c_transfer

说明: i2c 消息传输。

1 int bflb_i2c_transfer(struct bflb_device_s *dev, struct bflb_i2c_msg_s *msgs, int count);

parameter

description

dev

设备句柄

msgs

消息指针

count

消息个数

IRQ

Macros

Structs

Functions
bflb_irq_initialize

说明: 中断控制器初始化,清除所有中断和中断 pending。

1 void bflb_irq_initialize(void);
bflb_irq_save

说明: 关闭全局中断并保存之前的状态。

1uintptr_t bflb_irq_save(void);

parameter

description

return

返回关闭之前的状态

bflb_irq_restore

说明: 恢复关闭全局中断之前的状态。

1void bflb_irq_restore(uintptr_t flags);

parameter

description

flags

关闭之前的状态

bflb_irq_attach

说明: 注册中断入口函数。

1 int bflb_irq_attach(int irq, irq_callback isr, void *arg);

parameter

description

flags

关闭之前的状态

bflb_irq_detach

说明: 取消中断函数注册。

1 int bflb_irq_detach(int irq);

parameter

description

flags

关闭之前的状态

bflb_irq_enable

说明: 开启中断。

1 void bflb_irq_enable(int irq);

parameter

description

irq

中断号

bflb_irq_disable

说明: 关闭中断。

1 void bflb_irq_disable(int irq);

parameter

description

irq

中断号

bflb_irq_set_pending

说明: 设置中断 pending 位。

1 void bflb_irq_set_pending(int irq);

parameter

description

irq

中断号

bflb_irq_clear_pending

说明: 清除中断 pending 位。

1 void bflb_irq_clear_pending(int irq);

parameter

description

irq

中断号

bflb_irq_set_nlbits

说明: 设置中断分组。

1 void bflb_irq_set_nlbits(uint8_t nlbits);

parameter

description

irq

中断号

bflb_irq_set_priority

说明: 设置中断优先级。

1 void bflb_irq_set_priority(int irq, uint8_t preemptprio, uint8_t subprio);

parameter

description

irq

中断号

preemptprio

抢占优先级

subprio

子优先级

L1C

Macros

Structs

Functions
bflb_l1c_icache_enable

说明: 开启 icache。

1 void bflb_l1c_icache_enable(void);
bflb_l1c_icache_disable

说明: 关闭 icache。

1 void bflb_l1c_icache_disable(void);
bflb_l1c_dcache_enable

说明: 开启 dcache。

1 void bflb_l1c_dcache_enable(void);
bflb_l1c_dcache_disable

说明: 关闭 dcache。

1 void bflb_l1c_dcache_disable(void);
bflb_l1c_dcache_clean_range

说明: clean 一段数据到内存中。

1 void bflb_l1c_dcache_clean_range(unsigned long addr, uint32_t len);

parameter

description

addr

首地址(必须 32 字节对齐)

len

长度

bflb_l1c_dcache_invalidate_range

说明: 将 cache 中的数据置为 dity。

1 void bflb_l1c_dcache_invalidate_range(unsigned long addr, uint32_t len);

parameter

description

addr

首地址(必须 32 字节对齐)

len

长度

bflb_l1c_dcache_clean_invalidate_range

说明: clean 一段数据到内存中,并使 cache 中的数据失效。

1 void bflb_l1c_dcache_clean_invalidate_range(unsigned long addr, uint32_t len);

parameter

description

addr

首地址(必须 32 字节对齐)

len

长度

MTIMER

Macros

Structs

Functions
bflb_mtimer_config

说明: mtimer 定时配置。

1void bflb_mtimer_config(uint64_t ticks, void (*interruptfun)(void));

parameter

description

ticks

定时 tick 数

interruptfun

中断回调

bflb_mtimer_get_freq

说明: 获取 mtimer 频率。默认 mtimer 经过分频后设置成了 1M,所以频率就是 1M。

1uint32_t bflb_mtimer_get_freq(void);

parameter

description

return

频率

bflb_mtimer_delay_ms

说明: mtimer 毫秒延时。

1void bflb_mtimer_delay_ms(uint32_t time);

parameter

description

time

延时时间

bflb_mtimer_delay_us

说明: mtimer 微妙延时。

1void bflb_mtimer_delay_us(uint32_t time);

parameter

description

time

延时时间

bflb_mtimer_get_time_us

说明: 获取 mtimer 当前时间,us 为单位。

1uint64_t bflb_mtimer_get_time_us();

parameter

description

return

当前时间

bflb_mtimer_get_time_ms

说明: 获取 mtimer 当前时间,ms 为单位。

1uint64_t bflb_mtimer_get_time_ms();

parameter

description

return

当前时间

PWM_v1

备注

PWM V1 版本每个 PWM 的所有通道频率可以单独设置。

Macros
pwm channel

PWM V1 版本共 5 个 pwm 通道。(BL702L 只有通道 0)。

Structs
struct bflb_pwm_v1_channel_config_s

pwm v1 初始化配置结构体。

1 struct bflb_pwm_v1_channel_config_s {
2     uint8_t clk_source;
3     uint16_t clk_div;
4     uint16_t period;
5 };

parameter

description

clk_source

时钟源选择:PBCLK or XCLK or 32K_CLK

clk_div

分频值

period

周期值

备注

PWM 最终产生的频率 = clk_source/clk_div/period

Functions
bflb_pwm_v1_channel_init

说明: 初始化 pwm 通道。使用之前需要选择 gpio 为 pwm 功能。

1void bflb_pwm_v1_channel_init(struct bflb_device_s *dev, uint8_t ch, const struct bflb_pwm_v1_channel_config_s *config);

parameter

description

dev

设备句柄

ch

通道号

config

配置项

bflb_pwm_v1_channel_deinit

说明: 复位 pwm 通道。

1void bflb_pwm_v1_channel_deinit(struct bflb_device_s *dev, uint8_t ch);

parameter

description

dev

设备句柄

ch

通道号

bflb_pwm_v1_start

说明: 启动 pwm 通道输出。

1void bflb_pwm_v1_start(struct bflb_device_s *dev, uint8_t ch);

parameter

description

dev

设备句柄

ch

通道号

bflb_pwm_v1_stop

说明: 关闭 pwm 通道输出。

1void bflb_pwm_v1_stop(struct bflb_device_s *dev, uint8_t ch);

parameter

description

dev

设备句柄

ch

通道号

bflb_pwm_v1_set_period

说明: 修改 pwm 通道周期值,从而更改 pwm 通道输出的频率。

1void bflb_pwm_v1_set_period(struct bflb_device_s *dev, uint8_t ch, uint16_t period);

parameter

description

dev

设备句柄

ch

通道号

period

周期值

bflb_pwm_v1_channel_set_threshold

说明: 设置 pwm 占空比。

1void bflb_pwm_v1_channel_set_threshold(struct bflb_device_s *dev, uint8_t ch, uint16_t low_threhold, uint16_t high_threhold);

parameter

description

dev

设备句柄

ch

通道号

low_threhold

低阈值

high_threhold

高阈值,需要大于 low_threhold,并且小于等于 period

备注

PWM 占空比 = (high_threhold - low_threhold)/period

PWM_v2

备注

PWM V2 版本每个 PWM 的所有通道共用一个频率。

Macros
pwm channel

PWM V2 版本每个 PWM 共 4 个 pwm 通道。

pwm polarity

PWM V2 版本输出有效极性。当正向通道阈值位于设置的低阈值和高阈值之间,为有效极性,如果设置有效极性为高,则输出高电平,反之输出低电平。反向通道相反,阈值位于设置的低阈值和高阈值之外,为有效极性,如果设置有效极性为高,则输出高电平,反之输出低电平。

1#define PWM_POLARITY_ACTIVE_LOW  0
2#define PWM_POLARITY_ACTIVE_HIGH 1
Structs
struct bflb_pwm_v2_config_s

pwm v2 初始化配置结构体。

1 struct bflb_pwm_v2_config_s {
2     uint8_t clk_source;
3     uint16_t clk_div;
4     uint16_t period;
5 };

parameter

description

clk_source

时钟源选择:PBCLK or XCLK or 32K_CLK

clk_div

分频值

period

周期值

备注

PWM 最终产生的频率 = clk_source/clk_div/period

struct bflb_pwm_v2_channel_config_s

pwm v2 初始化配置结构体。

1 struct bflb_pwm_v2_channel_config_s {
2     uint8_t positive_polarity;
3     uint8_t negative_polarity;
4     uint8_t positive_stop_state;
5     uint8_t negative_stop_state;
6     uint8_t positive_brake_state;
7     uint8_t negative_brake_state;
8     uint8_t dead_time;
9 };

parameter

description

positive_polarity

正向输出极性

negative_polarity

反向输出极性

positive_stop_state

正向输出空闲状态

negative_stop_state

反向输出空闲状态

positive_brake_state

正向输出刹车状态

negative_brake_state

反向输出刹车状态

dead_time

死区时间

Functions
bflb_pwm_v2_init

说明: 初始化 pwm 。使用之前需要选择 gpio 为 pwm 功能。

1void bflb_pwm_v2_init(struct bflb_device_s *dev, const struct bflb_pwm_v2_config_s *config);

parameter

description

dev

设备句柄

config

配置项

bflb_pwm_v2_deinit

说明: 复位 pwm 。

1void bflb_pwm_v2_deinit(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_pwm_v2_start

说明: 启动 pwm 输出。

1void bflb_pwm_v2_start(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_pwm_v2_stop

说明: 关闭 pwm 输出。

1void bflb_pwm_v2_stop(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_pwm_v2_set_period

说明: 修改 pwm 周期值,从而更改 pwm 输出的频率。

1void bflb_pwm_v2_set_period(struct bflb_device_s *dev, uint16_t period);

parameter

description

dev

设备句柄

period

周期值

bflb_pwm_v2_channel_init

说明: PWM 通道初始化。

1void bflb_pwm_v2_channel_init(struct bflb_device_s *dev, uint8_t ch, struct bflb_pwm_v2_channel_config_s *config);

parameter

description

dev

设备句柄

ch

通道号

config

通道配置

bflb_pwm_v2_channel_set_threshold

说明: 设置 PWM 占空比。

1void bflb_pwm_v2_channel_set_threshold(struct bflb_device_s *dev, uint8_t ch, uint16_t low_threhold, uint16_t high_threhold);

parameter

description

dev

设备句柄

ch

通道号

low_threhold

低阈值

high_threhold

高阈值,需要大于 low_threhold,并且小于等于 period

备注

PWM 占空比 = (high_threhold - low_threhold)/period

bflb_pwm_v2_channel_positive_start

说明: PWM 正向通道使能输出。

1void bflb_pwm_v2_channel_positive_start(struct bflb_device_s *dev, uint8_t ch);

parameter

description

dev

设备句柄

ch

通道号

bflb_pwm_v2_channel_negative_start

说明: PWM 反向通道使能输出。

1void bflb_pwm_v2_channel_negative_start(struct bflb_device_s *dev, uint8_t ch);

parameter

description

dev

设备句柄

ch

通道号

bflb_pwm_v2_channel_positive_stop

说明: PWM 正向通道停止输出。

1void bflb_pwm_v2_channel_positive_stop(struct bflb_device_s *dev, uint8_t ch);

parameter

description

dev

设备句柄

ch

通道号

bflb_pwm_v2_channel_negative_stop

说明: PWM 反向通道停止输出。

1void bflb_pwm_v2_channel_negative_stop(struct bflb_device_s *dev, uint8_t ch);

parameter

description

dev

设备句柄

ch

通道号

bflb_pwm_v2_int_enable

说明: PWM 中断使能和关闭。

1void bflb_pwm_v2_int_enable(struct bflb_device_s *dev, uint32_t int_en, bool enable);

parameter

description

dev

设备句柄

int_en

中断使能位

enable

是否开启中断

int_en 可以填入以下值,多个中断可以使用 | 连接:

 1#define PWM_INTEN_CH0_L  (1 << 0)
 2#define PWM_INTEN_CH0_H  (1 << 1)
 3#define PWM_INTEN_CH1_L  (1 << 2)
 4#define PWM_INTEN_CH1_H  (1 << 3)
 5#define PWM_INTEN_CH2_L  (1 << 4)
 6#define PWM_INTEN_CH2_H  (1 << 5)
 7#define PWM_INTEN_CH3_L  (1 << 6)
 8#define PWM_INTEN_CH3_H  (1 << 7)
 9#define PWM_INTEN_PERIOD (1 << 8)
10#define PWM_INTEN_BRAKE  (1 << 9)
11#define PWM_INTEN_REPT   (1 << 10)
bflb_pwm_v2_get_intstatus

说明: 获取 PWM 中断标志。

1uint32_t bflb_pwm_v2_get_intstatus(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

返回中断标志

返回值如下:

 1#define PWM_INTSTS_CH0_L  (1 << 0)
 2#define PWM_INTSTS_CH0_H  (1 << 1)
 3#define PWM_INTSTS_CH1_L  (1 << 2)
 4#define PWM_INTSTS_CH1_H  (1 << 3)
 5#define PWM_INTSTS_CH2_L  (1 << 4)
 6#define PWM_INTSTS_CH2_H  (1 << 5)
 7#define PWM_INTSTS_CH3_L  (1 << 6)
 8#define PWM_INTSTS_CH3_H  (1 << 7)
 9#define PWM_INTSTS_PERIOD (1 << 8)
10#define PWM_INTSTS_BRAKE  (1 << 9)
11#define PWM_INTSTS_REPT   (1 << 10)
bflb_pwm_v2_int_clear

说明: 清除 PWM 中断标志。

1void bflb_pwm_v2_int_clear(struct bflb_device_s *dev, uint32_t int_clear);

parameter

description

dev

设备句柄

int_clear

清除值

int_clear 可以填入以下参数:

 1#define PWM_INTCLR_CH0_L  (1 << 0)
 2#define PWM_INTCLR_CH0_H  (1 << 1)
 3#define PWM_INTCLR_CH1_L  (1 << 2)
 4#define PWM_INTCLR_CH1_H  (1 << 3)
 5#define PWM_INTCLR_CH2_L  (1 << 4)
 6#define PWM_INTCLR_CH2_H  (1 << 5)
 7#define PWM_INTCLR_CH3_L  (1 << 6)
 8#define PWM_INTCLR_CH3_H  (1 << 7)
 9#define PWM_INTCLR_PERIOD (1 << 8)
10#define PWM_INTCLR_BRAKE  (1 << 9)
11#define PWM_INTCLR_REPT   (1 << 10)
bflb_pwm_v2_feature_control

说明: PWM 其他特性相关控制,一般不常用。

1 int bflb_pwm_v2_feature_control(struct bflb_device_s *dev, int cmd, size_t arg);

parameter

description

dev

设备句柄

cmd

控制字

arg

控制参数

return

负值表示不支持此命令

cmd 可以填入以下参数:

1#define PWM_CMD_SET_TRIG_ADC_SRC       (0x01)
2#define PWM_CMD_SET_EXT_BRAKE_POLARITY (0x02)
3#define PWM_CMD_SET_EXT_BRAKE_ENABLE   (0x03)
4#define PWM_CMD_SET_SW_BRAKE_ENABLE    (0x04)
5#define PWM_CMD_SET_STOP_ON_REPT       (0x05)
6#define PWM_CMD_SET_REPT_COUNT         (0x06)

RTC

Macros
BFLB_RTC_SEC2TIME

将秒转换成 RTC 时间

BFLB_RTC_TIME2SEC

将 RTC 时间转换成秒

Structs

Functions
bflb_rtc_set_time

说明: 设置 RTC 时间。

1 void bflb_rtc_set_time(struct bflb_device_s *dev, uint64_t time);

parameter

description

dev

设备句柄

time

RTC 时间(单位: 1/32768 s)

bflb_rtc_get_time

说明: 获取 RTC 时间。

1 uint64_t bflb_rtc_get_time(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

RTC 时间(单位: 1/32768 s)

SEC_AES

Macros

Structs

Functions

SEC_SHA

Macros

Structs

Functions

SEC_PKA

Macros

Structs

Functions

SPI

Macros
spi role

spi 支持主从角色。

1 #define SPI_ROLE_MASTER 0
2 #define SPI_ROLE_SLAVE  1
spi mode

spi 支持 4 种工作模式。

1 #define SPI_DEV_MODE0 0 /* CPOL=0 CHPHA=0 */
2 #define SPI_DEV_MODE1 1 /* CPOL=0 CHPHA=1 */
3 #define SPI_DEV_MODE2 2 /* CPOL=1 CHPHA=0 */
4 #define SPI_DEV_MODE3 3 /* CPOL=1 CHPHA=1 */
spi data_width

spi 支持 4 种位宽。

1 #define SPI_DATA_WIDTH_8BIT  0
2 #define SPI_DATA_WIDTH_16BIT 1
3 #define SPI_DATA_WIDTH_24BIT 2
4 #define SPI_DATA_WIDTH_32BIT 3
spi bit_order

spi bit 先行方式。

1 #define SPI_BIT_LSB 1
2 #define SPI_BIT_MSB 0
spi byte_order

spi 字节先行方式。

1 #define SPI_BYTE_LSB 0
2 #define SPI_BYTE_MSB 1
Structs
struct bflb_spi_config_s

spi 初始化配置结构体。

 1 struct bflb_spi_config_s {
 2     uint32_t freq;
 3     uint8_t role;
 4     uint8_t mode;
 5     uint8_t data_width;
 6     uint8_t bit_order;
 7     uint8_t byte_order;
 8     uint8_t tx_fifo_threshold;
 9     uint8_t rx_fifo_threshold;
10 };

parameter

description

freq

频率(BL702 不得超过32M,其他芯片不得超过 40M)

role

主从选择

mode

工作模式

data_width

数据宽度

bit_order

bit 先行方式

byte_order

字节先行方式

tx_fifo_threshold

发送 fifo 中断触发阈值(大于阈值触发中断)

rx_fifo_threshold

接收 fifo 中断触发阈值(大于阈值触发中断)

备注

SPI 阈值最大为 16

Functions
bflb_spi_init

说明: 初始化 spi。使用之前需要开启 spi ip 时钟、设置 spi 时钟源和分频值、选择 gpio 为 spi 功能。

1 void bflb_spi_init(struct bflb_device_s *dev, const struct bflb_spi_config_s *config);

parameter

description

dev

设备句柄

config

配置项

bflb_spi_deinit

说明: 反初始化 spi。

1 void bflb_spi_deinit(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_spi_poll_send

说明: 通过 spi 阻塞式发送一个数据并接收一个数据。

1 uint32_t bflb_spi_poll_send(struct bflb_device_s *dev, uint32_t data);

parameter

description

dev

设备句柄

data

发送的数据(可以为字节、字、双字)

return

接收的数据(可以为字节、字、双字)

bflb_spi_poll_exchange

说明: 通过 spi 阻塞式发送一段数据并接收一段数据。

1 int bflb_spi_poll_exchange(struct bflb_device_s *dev, const void *txbuffer, void *rxbuffer, size_t nbyte);

parameter

description

dev

设备句柄

txbuffer

发送缓冲区

rxbuffer

接收缓冲区

nbyte

数据长度

bflb_spi_txint_mask

说明: spi tx fifo 阈值中断屏蔽开关,开启后超过设定阈值则触发中断。

1 void bflb_spi_txint_mask(struct bflb_device_s *dev, bool mask);

parameter

description

dev

设备句柄

mask

是否屏蔽中断

bflb_spi_rxint_mask

说明: spi rx fifo 阈值中断和超时屏蔽开关,开启后超过设定阈值则或者超时则触发中断。

1 void bflb_spi_rxint_mask(struct bflb_device_s *dev, bool mask);

parameter

description

dev

设备句柄

mask

是否屏蔽中断

bflb_spi_errint_mask

说明: spi 错误中断屏蔽开关。

1 void bflb_spi_errint_mask(struct bflb_device_s *dev, bool mask);

parameter

description

dev

设备句柄

mask

是否屏蔽中断

bflb_spi_get_intstatus

说明: 获取 spi 中断标志。

1 uint32_t bflb_spi_get_intstatus(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

中断标志

中断标志有以下选项:

1#define SPI_INTSTS_TC                (1 << 0)
2#define SPI_INTSTS_TX_FIFO           (1 << 1)
3#define SPI_INTSTS_RX_FIFO           (1 << 2)
4#define SPI_INTSTS_SLAVE_TIMEOUT     (1 << 3)
5#define SPI_INTSTS_SLAVE_TX_UNDERRUN (1 << 4)
6#define SPI_INTSTS_FIFO_ERR          (1 << 5)
bflb_spi_int_clear

说明: 清除 spi 中断标志。

1 void bflb_spi_int_clear(struct bflb_device_s *dev, uint32_t int_clear);

parameter

description

dev

设备句柄

int_clear

清除值

int_clear 可以填入以下参数:

1#define SPI_INTCLR_TC                (1 << 16)
2#define SPI_INTCLR_SLAVE_TIMEOUT     (1 << 19)
3#define SPI_INTCLR_SLAVE_TX_UNDERRUN (1 << 20)
bflb_spi_feature_control

说明: spi 其他特性相关控制,一般不常用。

1 int bflb_spi_feature_control(struct bflb_device_s *dev, int cmd, size_t arg);

parameter

description

dev

设备句柄

cmd

控制字

arg

控制参数

return

负值表示不支持此命令

cmd 可以填入以下参数:

1#define SPI_CMD_SET_DATA_WIDTH  (0x01)
2#define SPI_CMD_GET_DATA_WIDTH  (0x02)
3#define SPI_CMD_CLEAR_TX_FIFO   (0x03)
4#define SPI_CMD_CLEAR_RX_FIFO   (0x04)
5#define SPI_CMD_SET_CS_INTERVAL (0x05)

TIMER

Macros
timer clock source

定时器输入时钟源可以选择以下类型,注意: TIMER_CLKSRC_GPIO BL602/BL702 系列没有此功能。

1#define TIMER_CLKSRC_BCLK 0
2#define TIMER_CLKSRC_32K  1
3#define TIMER_CLKSRC_1K   2
4#define TIMER_CLKSRC_XTAL 3
5#define TIMER_CLKSRC_GPIO 4
6#define TIMER_CLKSRC_NO   5
timer counter mode

定时器计数模式分为两种: freerun(向上计数模式)、preload(重装载模式)。

1 #define TIMER_COUNTER_MODE_PROLOAD 0
2 #define TIMER_COUNTER_MODE_UP      1
timer compare id

定时器一共三个 compare id, 用于设置不同的定时时间,可以当三个定时器使用。

1 #define TIMER_COMP_ID_0 0
2 #define TIMER_COMP_ID_1 1
3 #define TIMER_COMP_ID_2 2
Structs
struct bflb_timer_config_s

timer 初始化配置结构体。

 1 struct bflb_timer_config_s {
 2     uint8_t counter_mode;
 3     uint8_t clock_source;
 4     uint8_t clock_div;
 5     uint8_t trigger_comp_id;
 6     uint32_t comp0_val;
 7     uint32_t comp1_val;
 8     uint32_t comp2_val;
 9     uint32_t preload_val;
10 };

parameter

description

counter_mode

计数模式

clock_source

时钟源选择

clock_div

分频值

trigger_comp_id

选择触发的最高 comp id(选择越高,则能够定时的个数越多)

comp0_val

comp0 比较值

comp1_val

comp1 比较值(需要大于 comp0_val)

comp2_val

comp2 比较值(需要大于 comp1_val)

preload_val

重装载值

Functions
bflb_timer_init

说明: 初始化 timer。使用之前需要开启 timer ip 时钟。

1 void bflb_timer_init(struct bflb_device_s *dev, const struct bflb_timer_config_s *config);

parameter

description

dev

设备句柄

config

配置项

bflb_timer_deinit

说明: 反初始化 timer。

1 void bflb_timer_deinit(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_timer_start

说明: 启动 timer 。

1 void bflb_timer_start(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_timer_stop

说明: 停止 timer。

1 void bflb_timer_stop(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_timer_set_compvalue

说明: 设置 timer comp id 比较值。

1 void bflb_timer_set_compvalue(struct bflb_device_s *dev, uint8_t cmp_no, uint32_t val);

parameter

description

dev

设备句柄

cmp_no

comp id

val

比较值

bflb_timer_get_compvalue

说明: 获取 comp id 比较值。

1 uint32_t bflb_timer_get_compvalue(struct bflb_device_s *dev, uint8_t cmp_no);

parameter

description

dev

设备句柄

cmp_no

comp id

return

比较值

bflb_timer_get_countervalue

说明: 获取 timer 计数值。

1 uint32_t bflb_timer_get_countervalue(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

计数值

bflb_timer_compint_mask

说明: timer comp 中断屏蔽开关。

1 void bflb_timer_compint_mask(struct bflb_device_s *dev, uint8_t cmp_no, bool mask);

parameter

description

dev

设备句柄

cmp_no

comp id

mask

是否屏蔽中断

bflb_timer_get_compint_status

说明: 获取 timer comp id 中断匹配标志。

1 bool bflb_timer_get_compint_status(struct bflb_device_s *dev, uint8_t cmp_no);

parameter

description

dev

设备句柄

cmp_no

comp id

return

为 true 表示匹配

bflb_timer_compint_clear

说明: 清除 timer comp id 中断标志。

1 void bflb_timer_compint_clear(struct bflb_device_s *dev, uint8_t cmp_no);

parameter

description

dev

设备句柄

cmp_no

comp id

UART

Macros
uart direction

UART 方向可以设置为 TX、RX、TXRX

uart databits
1 #define UART_DATA_BITS_5 0
2 #define UART_DATA_BITS_6 1
3 #define UART_DATA_BITS_7 2
4 #define UART_DATA_BITS_8 3
5 #define UART_DATA_BITS_9 4
uart stopbits
1 #define UART_STOP_BITS_0_5 0
2 #define UART_STOP_BITS_1   1
3 #define UART_STOP_BITS_1_5 2
4 #define UART_STOP_BITS_2   3
uart parity
1 #define UART_PARITY_NONE  0
2 #define UART_PARITY_ODD   1
3 #define UART_PARITY_EVEN  2
4 #define UART_PARITY_MARK  3
5 #define UART_PARITY_SPACE 4
uart bit order
1 #define UART_LSB_FIRST 0
2 #define UART_MSB_FIRST 1
Structs
struct bflb_uart_config_s

uart 初始化配置结构体。

 1 struct bflb_uart_config_s {
 2     uint32_t baudrate;
 3     uint8_t direction;
 4     uint8_t data_bits;
 5     uint8_t stop_bits;
 6     uint8_t parity;
 7     uint8_t bit_order;
 8     uint8_t flow_ctrl;
 9     uint8_t tx_fifo_threshold;
10     uint8_t rx_fifo_threshold;
11 };

parameter

description

baudrate

波特率

direction

方向

data_bits

数据位

stop_bits

停止位

parity

校验位

bit_order

bit 先行方式

flow_ctrl

流控

tx_fifo_threshold

发送 fifo 中断触发阈值(大于阈值触发中断)

rx_fifo_threshold

接收 fifo 中断触发阈值(大于阈值触发中断)

备注

BL702 阈值为 128, 其他芯片为 32

Functions
bflb_uart_init

说明: 初始化 uart。使用之前需要开启 uart ip 时钟、设置 uart 时钟源和分频值、选择 gpio 为 uart 中的一个功能。

1 void bflb_uart_init(struct bflb_device_s *dev, const struct bflb_uart_config_s *config);

parameter

description

dev

设备句柄

config

配置项

bflb_uart_deinit

说明: 反初始化 uart。

1 void bflb_uart_deinit(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

bflb_uart_putchar

说明: 通过 uart 阻塞式发送一个字符。

1 void bflb_uart_putchar(struct bflb_device_s *dev, int ch);

parameter

description

dev

设备句柄

ch

字符

bflb_uart_getchar

说明: 通过 uart 异步接收一个字符。

1 int bflb_uart_getchar(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

返回 -1 表示没有数据,返回其他表示接收的字符

bflb_uart_txready

说明: 查询 uart tx fifo 是否准备就绪,准备好才可以填充字符。

1 bool bflb_uart_txready(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

为 true 表示就绪

bflb_uart_txempty

说明: 查询 uart tx fifo 是否为空。

1 bool bflb_uart_txempty(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

为 true 表示 fifo 已空,无法填充数据

bflb_uart_rxavailable

说明: 查询 uart rx 是否有数据。

1 bool bflb_uart_rxavailable(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

为 true 表示有数据,可以进行读取

bflb_uart_txint_mask

说明: uart tx fifo 阈值中断屏蔽开关,开启后超过设定阈值则触发中断。

1 void bflb_uart_txint_mask(struct bflb_device_s *dev, bool mask);

parameter

description

dev

设备句柄

mask

是否屏蔽中断

bflb_uart_rxint_mask

说明: uart rx fifo 阈值中断和超时屏蔽开关,开启后超过设定阈值则或者超时则触发中断。

1 void bflb_uart_rxint_mask(struct bflb_device_s *dev, bool mask);

parameter

description

dev

设备句柄

mask

是否屏蔽中断

bflb_uart_errint_mask

说明: uart 错误中断屏蔽开关。

1 void bflb_uart_errint_mask(struct bflb_device_s *dev, bool mask);

parameter

description

dev

设备句柄

mask

是否屏蔽中断

bflb_uart_get_intstatus

说明: 获取 uart 中断标志。

1 uint32_t bflb_uart_get_intstatus(struct bflb_device_s *dev);

parameter

description

dev

设备句柄

return

中断标志

中断标志有以下选项:

 1#define UART_INTSTS_TX_END  (1 << 0)
 2#define UART_INTSTS_RX_END  (1 << 1)
 3#define UART_INTSTS_TX_FIFO (1 << 2)
 4#define UART_INTSTS_RX_FIFO (1 << 3)
 5#define UART_INTSTS_RTO     (1 << 4)
 6#define UART_INTSTS_PCE     (1 << 5)
 7#define UART_INTSTS_TX_FER  (1 << 6)
 8#define UART_INTSTS_RX_FER  (1 << 7)
 9#if !defined(BL602)
10#define UART_INTSTS_RX_LSE (1 << 8)
11#endif
12#if !defined(BL602) && !defined(BL702)
13#define UART_INTSTS_RX_BCR (1 << 9)
14#define UART_INTSTS_RX_ADS (1 << 10)
15#define UART_INTSTS_RX_AD5 (1 << 11)
16#endif
bflb_uart_int_clear

说明: 清除 uart 中断标志。

1 void bflb_uart_int_clear(struct bflb_device_s *dev, uint32_t int_clear);

parameter

description

dev

设备句柄

int_clear

清除值

int_clear 可以填入以下参数:

 1#define UART_INTCLR_TX_END (1 << 0)
 2#define UART_INTCLR_RX_END (1 << 1)
 3#define UART_INTCLR_RTO    (1 << 4)
 4#define UART_INTCLR_PCE    (1 << 5)
 5#if !defined(BL602)
 6#define UART_INTCLR_RX_LSE (1 << 8)
 7#endif
 8#if !defined(BL602) && !defined(BL702)
 9#define UART_INTCLR_RX_BCR (1 << 9)
10#define UART_INTCLR_RX_ADS (1 << 10)
11#define UART_INTCLR_RX_AD5 (1 << 11)
12#endif
bflb_uart_feature_control

说明: uart 其他特性相关控制,一般不常用。

1 int bflb_uart_feature_control(struct bflb_device_s *dev, int cmd, size_t arg);

parameter

description

dev

设备句柄

cmd

控制字

arg

控制参数

return

负值表示不支持此命令

cmd 可以填入以下参数:

 1#define UART_CMD_SET_BAUD_RATE           (0x01)
 2#define UART_CMD_SET_DATA_BITS           (0x02)
 3#define UART_CMD_SET_STOP_BITS           (0x03)
 4#define UART_CMD_SET_PARITY_BITS         (0x04)
 5#define UART_CMD_CLR_TX_FIFO             (0x05)
 6#define UART_CMD_CLR_RX_FIFO             (0x06)
 7#define UART_CMD_SET_RTO_VALUE           (0x07)
 8#define UART_CMD_SET_RTS_VALUE           (0x08)
 9#define UART_CMD_GET_TX_FIFO_CNT         (0x09)
10#define UART_CMD_GET_RX_FIFO_CNT         (0x0a)
11#define UART_CMD_SET_AUTO_BAUD           (0x0b)
12#define UART_CMD_GET_AUTO_BAUD           (0x0c)
13#define UART_CMD_SET_BREAK_VALUE         (0x0d)
14#define UART_CMD_SET_TX_LIN_VALUE        (0x0e)
15#define UART_CMD_SET_RX_LIN_VALUE        (0x0f)
16#define UART_CMD_SET_TX_RX_EN            (0x10)
17#define UART_CMD_SET_TX_RS485_EN         (0x11)
18#define UART_CMD_SET_TX_RS485_POLARITY   (0x12)
19#define UART_CMD_SET_ABR_ALLOWABLE_ERROR (0x13)
20#define UART_CMD_SET_SW_RTS_CONTROL      (0x14)
21#define UART_CMD_IR_CONFIG               (0x15)
22#define UART_CMD_SET_TX_FREERUN          (0x16)
23#define UART_CMD_SET_TX_END_INTERRUPT    (0x17)
24#define UART_CMD_SET_RX_END_INTERRUPT    (0x18)
25#define UART_CMD_SET_TX_TRANSFER_LEN     (0x19)
26#define UART_CMD_SET_RX_TRANSFER_LEN     (0x20)
27#define UART_CMD_SET_TX_EN               (0x21)
28#define UART_CMD_SET_BCR_END_INTERRUPT   (0x22)
29#define UART_CMD_GET_BCR_COUNT           (0x23)

WDG

Macros

Structs

Functions

Components

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

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

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

  • 主控Host:
    _images/image2.png
    • 蓝牙Host层实现了所有高级协议和配置文件,最重要的是它为应用程序提供了高级API
      • HCI:Host与controller接口

      • L2CAP:逻辑链路控制和适应协议

      • GATT:通用属性配置层(Generic Attribute Profile)

      • GAP:通用访问配置层(Generic Access Profile)

      • SMP:安全管理器配置层(Security Manager Specification)

  • 应用Application
    • 应用层是用户开发实际蓝牙应用的地方,包含必要的协议栈参数设置,以及各种功能函数的调用。我们分别从蓝牙从机和蓝牙主机两种设备来分析。
      • 蓝牙从机
        • 相关硬件和基础服务初始化

        • 设置广播参数:广播数据,广播间隔,扫描回应等参数或者数据

        • 设置Profile:添加从机服务、特征值,还有设置回调函数用于接收主机数据等

        • 设置配对参数(可选)

        • 启动广播,开始运行

        • 等待相关事件,及事件处理,例如收到主机发来的数据,被链接等等

      • 蓝牙主机
        • 相关硬件和基础服务初始化

        • 设置扫描参数

        • 设置连接参数

        • 设置配对参数(可选)

        • 启动协议栈,开始运行

        • 等待相关事件,及事件处理,例如扫描事件,从机的Notify事件等等。

API参考

  • API介绍

void ble_controller_init(uint8_t task_priority)

/**
* function      controller层初始化
* @param[in]    task_priority:  任务优先级
* @return       空
*/

int hci_driver_init(void)

/**
* function      HCI接口驱动初始化
* @param[in]    空
* @return       0:成功,!=0:失败
*/

int bt_enable(bt_ready_cb_t cb)

/**
* function      Ble使能
* @param[in]    cb:如果成功调用回调函数
* @return       0:成功,!=0:失败
*/
int bt_le_adv_start(const struct bt_le_adv_param *param,const struct bt_data *ad, size_t ad_len,

const struct bt_data *sd, size_t sd_len)

/**
* function      开启BLE广播
*
* @param[in]    param:  指向广播配置参数指针
* @param[in]    ad:     指向广播包中数据指针
* @param[in]    ad_len: 广播包中数据的长度
* @param[in]    sd:     指向扫描响应包数据指针
* @param[in]    sd_len: 扫描响应包数据的长度
* @return       0:成功,!=0:失败
*/

int bt_le_adv_update_data(const struct bt_data *ad, size_t ad_len,const struct bt_data *sd, size_t sd_len)

/**
* function      更新BLE广播数据
* @param[in]    ad:     指向广播包中数据指针
* @param[in]    ad_len: 广播包中数据的长度
* @param[in]    sd:     指向扫描响应包数据指针
* @param[in]    sd_len: 扫描响应包数据的长度
* @return       0:成功,!=0:失败
*/

int bt_le_adv_stop(void)

/**
* function      停止BLE广播
* @param[in]    空
* @return       0:成功,!=0:失败
*/

int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb)

/**
* function      开启BLE扫描
* @param[in]    param:  指向扫描参数的指针
* @param[in]    cb:     扫描回调函数
* @return       0:成功,!=0:失败
*/

int bt_le_scan_stop(void)

/**
* function      停止BLE扫描
* @param[in]    空
* @return       0:成功,!=0:失败
*/

int bt_le_whitelist_add(const bt_addr_le_t *addr)

/**
* function      通过地址添加设备到白名单列表中
* @param[in]    addr:指向需要添加设备地址的指针
* @return       0:成功,!=0:失败
*/

int bt_le_whitelist_rem(const bt_addr_le_t *addr)

/**
* function      从白名单列表中移除设备
* @param[in]    addr:指向需要移除设备地址的指针
* @return       0:成功,!=0:失败
*/

int bt_le_whitelist_clear(void)

/**
* function      清除白名单列表
* @param[in]    空
* @return       0:成功,!=0:失败
*/

int bt_le_set_chan_map(u8_t chan_map[5])

/**
* function      设置(LE)通道映射
* @param[in]    chan_map:通道数组
* @return       0:成功,!=0:失败
*/

int bt_unpair(u8_t id, const bt_addr_le_t *addr)

/**
* function      清除配对信息
* @param[in]    id:    本地标识(大多只是默认的BT ID)
* @param[in]    addr:  远端设备地址,NULL或者BT_ADDR_LE_ANY清除所有远端设备
* @return       0:成功,!=0:失败
*/

int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info)

/**
* function      获取当前连接设备的信息
* @param[in]    conn:  指向当前连接的指针
* @param[in]    info:  指向当前连接设备信息的指针
* @return       0:成功,!=0:失败
*/

int bt_conn_get_remote_dev_info(struct bt_conn_info *info)

/**
* function      获取已连接设备的信息
* @param[in]    info:  指向当前连接设备信息的指针
* @return       已连接设备的个数
*/

int bt_conn_le_param_update(struct bt_conn *conn,const struct bt_le_conn_param *param)

/**
* function      更新连接参数
* @param[in]    conn:  指向当前连接的指针
* @param[in]    param: 指向连接参数的指针
* @return       0:成功,!=0:失败
*/

int bt_conn_disconnect(struct bt_conn *conn, u8_t reason)

/**
* function      断开当前连接
* @param[in]    conn:  指向当前连接的指针
* @param[in]    reason:断开当前连接的原因
* @return       0:成功,!=0:失败
*/

struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer,const struct bt_le_conn_param *param)

/**
* function      创建连接
* @param[in]    peer:  需要连接设备地址的指针
* @param[in]    param: 指向连接参数指针
* @return       成功:有效的连接对象,否则失败
*/

int bt_conn_create_auto_le(const struct bt_le_conn_param *param)

/**
* function      自动创建连接白名单列表中的设备
* @param[in]    param: 指向连接参数指针
* @return       0:成功,!=0:失败
*/

int bt_conn_create_auto_stop(void)

/**
* function      停止自动创建连接白名单列表中的设备
* @param[in]    空
* @return       0:成功,!=0:失败
*/

int bt_le_set_auto_conn(const bt_addr_le_t *addr,const struct bt_le_conn_param *param)

/**
* function      自动创建连接远端设备
* @param[in]    addr:  远端设备地址指针
* @param[in]    param: 指向连接参数指针
* @return       0:成功,!=0:失败
*/

struct bt_conn *bt_conn_create_slave_le(const bt_addr_le_t *peer,const struct bt_le_adv_param *param)

/**
* function      发起定向的广播包给远端设备
* @param[in]    peer:  远端设备地址指针
* @param[in]    param: 指向广播参数的指针
* @return       成功:有效的连接对象,否则失败
*/

int bt_conn_set_security(struct bt_conn *conn, bt_security_t sec)

/**
* function      设置连接安全等级
* @param[in]    conn:  指向连接对象的指针
* @param[in]    sec:   安全等级
* @return       0:成功,!=0:失败
*/

bt_security_t bt_conn_get_security(struct bt_conn *conn)

/**
* function      获取当前连接的安全等级
* @param[in]    conn:  指向连接对象的指针
* @return       安全级别
*/

u8_t bt_conn_enc_key_size(struct bt_conn *conn)

/**
* function      获取当前连接的加密key的大小
* @param[in]    conn:  指向连接对象的指针
* @return       加密key的大小
*/

void bt_conn_cb_register(struct bt_conn_cb *cb)

/**
* function      注册连接回调函数
* @param[in]    cb:  连接回调函数
* @return       空
*/

void bt_set_bondable(bool enable)

/**
* function      设置/清除SMP配对请求/响应数据认证需求中的绑定标志
* @param[in]    enable:  1,使能,0:不使能
* @return       空
*/

int bt_conn_auth_cb_register(const struct bt_conn_auth_cb *cb)

/**
* function      注册认证回调函数
* @param[in]    cb: 回调函数指针
* @return       0:成功,!=0:失败
*/

int bt_conn_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)

/**
* function      用密钥回复对方
* @param[in]    conn:    连接对象指针
* @param[in]    passkey: 输入的密钥
* @return       0:成功,!=0:失败
*/

int bt_conn_auth_cancel(struct bt_conn *conn)

/**
* function      取消认证过程
* @param[in]    conn:    连接对象指针
* @return       0:成功,!=0:失败
*/

int bt_conn_auth_passkey_confirm(struct bt_conn *conn)

/**
* function      如果密码匹配,回复对方
* @param[in]    conn:    连接对象的指针
* @return       0:成功,!=0:失败
*/

int bt_conn_auth_pincode_entry(struct bt_conn *conn, const char *pin)

/**
* function      用PIN码进行回复对方
* @param[in]    conn:  连接对象的指针
* @param[in]    pin:   PIN码的指针
* @return       0:成功,!=0:失败
*/

int bt_le_read_rssi(u16_t handle,int8_t *rssi)

/**
* function      读取对方RSSI值
* @param[in]    handle:连接的句柄值
* @param[in]    rssi:  rssi的指针
* @return       0:成功,!=0:失败
*/

int bt_get_local_address(bt_addr_le_t *adv_addr)

/**
* function      读取本机的地址
* @param[in]    adv_addr:  指向地址的指针
* @return       0:成功,!=0:失败
*/

int bt_set_tx_pwr(int8_t power)

/**
* function      设置本机发射功率
* @param[in]    power:  功率值
* @return       0:成功,!=0:失败
*/

数据结构参考

bt_le_adv_param数据结构:

 1 /** LE Advertising Parameters. */
 2 struct bt_le_adv_param {
 3     /** Local identity */
 4     u8_t  id;
 5
 6     /** Bit-field of advertising options */
 7     u8_t  options;
 8
 9     /** Minimum Advertising Interval (N * 0.625) */
10     u16_t interval_min;
11
12     /** Maximum Advertising Interval (N * 0.625) */
13     u16_t interval_max;
14
15     #if defined(CONFIG_BT_STACK_PTS)
16     u8_t  addr_type;
17     #endif
18 };

此数据结构用来配置广播参数,包括本地识别id、广播选项位域、广播间隙等,其中广播选项位域有如下枚举类型参数可选:

 1 enum {
 2     /** Convenience value when no options are specified. */
 3     BT_LE_ADV_OPT_NONE = 0,
 4
 5     /** Advertise as connectable. Type of advertising is determined by
 6         * providing SCAN_RSP data and/or enabling local privacy support.
 7         */
 8     BT_LE_ADV_OPT_CONNECTABLE = BIT(0),
 9
10     /** Don't try to resume connectable advertising after a connection.
11         *  This option is only meaningful when used together with
12         *  BT_LE_ADV_OPT_CONNECTABLE. If set the advertising will be stopped
13         *  when bt_le_adv_stop() is called or when an incoming (slave)
14         *  connection happens. If this option is not set the stack will
15         *  take care of keeping advertising enabled even as connections
16         *  occur.
17         */
18     BT_LE_ADV_OPT_ONE_TIME = BIT(1),
19
20     /** Advertise using the identity address as the own address.
21         *  @warning This will compromise the privacy of the device, so care
22         *           must be taken when using this option.
23         */
24     BT_LE_ADV_OPT_USE_IDENTITY = BIT(2),
25
26     /** Advertise using GAP device name */
27     BT_LE_ADV_OPT_USE_NAME = BIT(3),
28
29     /** Use low duty directed advertising mode, otherwise high duty mode
30         *  will be used. This option is only effective when used with
31         *  bt_conn_create_slave_le().
32         */
33     BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY = BIT(4),
34
35     /** Enable use of Resolvable Private Address (RPA) as the target address
36         *  in directed advertisements when CONFIG_BT_PRIVACY is not enabled.
37         *  This is required if the remote device is privacy-enabled and
38         *  supports address resolution of the target address in directed
39         *  advertisement.
40         *  It is the responsibility of the application to check that the remote
41         *  device supports address resolution of directed advertisements by
42         *  reading its Central Address Resolution characteristic.
43         */
44     BT_LE_ADV_OPT_DIR_ADDR_RPA = BIT(5),
45
46     /** Use whitelist to filter devices that can request scan response
47         *  data.
48         */
49     BT_LE_ADV_OPT_FILTER_SCAN_REQ = BIT(6),
50
51     /** Use whitelist to filter devices that can connect. */
52     BT_LE_ADV_OPT_FILTER_CONN = BIT(7),
53 };

如果需要发送一个广播包,配置可以如下:

1 param.id = 0;
2 param.options = (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME | BT_LE_ADV_OPT_ONE_TIME);
3 param.interval_min = 0x00a0;
4 param.interval_max = 0x00f0;

bt_data数据结构:

1 struct bt_data {
2     u8_t type;
3     u8_t data_len;
4     const u8_t *data;
5 };

此数据结构用来填充广播包中的数据,具体的数据包类型可以参考如下:

 1 Service UUID
 2 Local Name
 3 Flags
 4 Manufacturer Specific Data
 5 TX Power Level
 6 Secure Simple Pairing OOB
 7 Security Manager OOB
 8 Security Manager TK Value
 9 Slave Connection Interval Range
10 Service Solicitation
11 Service Data
12 Appearance
13 Public Target Address
14 Random Target Address
15 Advertising Interval
16 LE Bluetooth Device Address
17 LE Role
18 Uniform Resource Identifier
19 LE Supported Features
20 Channel Map Update Indication

用该数据结构配置一个广播包数据,如下所示:

1 struct bt_data ad_discov[] = {
2     BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
3     BT_DATA(BT_DATA_NAME_COMPLETE, "BL602-BLE-DEV", 13),
4 };

bt_le_scan_param数据结构:

 1 /** LE scan parameters */
 2 struct bt_le_scan_param {
 3     /** Scan type (BT_LE_SCAN_TYPE_ACTIVE or BT_LE_SCAN_TYPE_PASSIVE) */
 4     u8_t  type;
 5
 6     /** Bit-field of scanning filter options. */
 7     u8_t  filter_dup;
 8
 9     /** Scan interval (N * 0.625 ms) */
10     u16_t interval;
11
12     /** Scan window (N * 0.625 ms) */
13     u16_t window;
14 };

此数据结构用来填充扫描参数, type:为扫描类型有2种类型BT_LE_SCAN_TYPE_ACTIVE(0x01)、BT_LE_SCAN_TYPE_PASSIVE(0x00)。 filter_dup:0x00,除定向广告外,接受所有广播和扫描响应,0x01,只接收白名单列表中设备的广播和扫描响应。 interval:扫描间隙。 window:扫描窗口。

如果开启扫描请求,可以配置如下:

1 scan_param.type = BT_LE_SCAN_TYPE_PASSIVE
2 scan_param.filter_dup = 0x00
3 interval=BT_GAP_SCAN_SLOW_INTERVAL_1
4 window=BT_GAP_SCAN_SLOW_WINDOW_1

bt_le_conn_param数据结构:

 1 /** Connection parameters for LE connections */
 2 struct bt_le_conn_param {
 3     u16_t interval_min;
 4     u16_t interval_max;
 5     u16_t latency;
 6     u16_t timeout;
 7
 8     #if defined(CONFIG_BT_STACK_PTS)
 9     u8_t  own_address_type;
10     #endif
11 };

此数据结构用来填充连接参数,interval_min:连接间隙最少值(0x0018),interval_max:连接间隙最大值(0x0028), latency:指定为连接事件数的连接允许的最大从延迟。 timeout:连接超时时间。

配置该数据结构,如下:

1 interval_min=BT_GAP_INIT_CONN_INT_MIN(0x0018)
2 interval_max=BT_GAP_INIT_CONN_INT_MAX(0x0028)
3 latency=0
4 timeout=400

bt_conn 数据结构:

 1 struct bt_conn {
 2     u16_t                   handle;
 3     u8_t                    type;
 4     u8_t                    role;
 5
 6     ATOMIC_DEFINE(flags, BT_CONN_NUM_FLAGS);
 7
 8     /* Which local identity address this connection uses */
 9     u8_t                    id;
10
11 #if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
12     bt_security_t           sec_level;
13     bt_security_t           required_sec_level;
14     u8_t                    encrypt;
15 #endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
16
17     /* Connection error or reason for disconnect */
18     u8_t                    err;
19
20     bt_conn_state_t         state;
21
22     u16_t                   rx_len;
23     struct net_buf          *rx;
24
25     /* Sent but not acknowledged TX packets with a callback */
26     sys_slist_t             tx_pending;
27     /* Sent but not acknowledged TX packets without a callback before
28     * the next packet (if any) in tx_pending.
29     */
30     u32_t                   pending_no_cb;
31
32     /* Completed TX for which we need to call the callback */
33     sys_slist_t             tx_complete;
34     struct k_work           tx_complete_work;
35
36
37     /* Queue for outgoing ACL data */
38     struct k_fifo           tx_queue;
39
40     /* Active L2CAP channels */
41     sys_slist_t             channels;
42
43     atomic_t                ref;
44
45     /* Delayed work for connection update and other deferred tasks */
46     struct k_delayed_work   update_work;
47
48     union {
49         struct bt_conn_le   le;
50 #if defined(CONFIG_BT_BREDR)
51         struct bt_conn_br   br;
52         struct bt_conn_sco  sco;
53 #endif
54     };
55
56 #if defined(CONFIG_BT_REMOTE_VERSION)
57     struct bt_conn_rv {
58         u8_t  version;
59         u16_t manufacturer;
60         u16_t subversion;
61     } rv;
62 #endif
63 };

此数据结构为当前连接数据结构,其中包括BLE蓝牙连接相关的参数,连接成功后该数据结构可以被用户调用。

WIFI

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 转换

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

打印 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 结构体中

  • 打印通道号和电压值

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

打印 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 转换

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

打印 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 转换

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

打印 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 计算得到的环境温度值。

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

打印计算得到的环境温度。

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 转换

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

打印芯片 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 输出

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

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 启动传输

  • 等待传输完成并进入中断

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

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

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

打印 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 中断

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

将 GPIO 0 引脚电平拉低,进入中断并打印中断次数。

各开发板支持的 GPIO 引脚如下表:

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 传输

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

通过串口(波特率大于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}
  • 检查发送和读取的数据是否一致

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

按下 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}
  • 检查发送和读取的数据是否一致

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

按下 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}
  • 检查发送和读取的数据是否一致

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

按下 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}
  • 检查发送和接收的数据是否一致

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

按下开发板中 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}
  • 检查发送和接收的数据是否一致

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

按下开发板中 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 中

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

按下开发板中 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 传输完成后,查看波形

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

按下 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 中

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

将 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 启动传输

  • 等待传输完成并进入中断

编译和烧录

参考 Build with Linux or WSL 或者 Build with Windows CMD

实验现象

Components

FreeRTOS

本 demo 主要演示 FreeRTOS 基本功能:任务创建和任务切换、信号量、内存管理。更详细的代码请参考 examples/freertos

内存管理

FreeRTOS 内存管理默认使用 heap3。

中断管理

FreeRTOS 为 RISC-V 提供了统一的中断和异常入口,名为 freertos_risc_v_trap_handler,该函数主要作用如下:

  • 压栈

  • 根据 mcause 查找 mtimer 中断,并执行 xTaskIncrementTick

  • 根据 mcause 查找 ecall 异常,并执行 vTaskSwitchContext

  • 根据 mcause,如果是非 ecall 异常,则执行 exception_entry;如果是非 mtimer 中断,则执行 portasmHANDLE_INTERRUPT, portasmHANDLE_INTERRUPT 实际调用 interrupt_entry

  • 出栈

那么问题来了,是如何统一中断和异常的呢?

通常 RISC-V SOC 中断支持 vector 模式和 direct 模式,博流系列芯片都使用了 vector 模式,在 startup/start.S 文件中可以看到

1/* mtvec: for all exceptions and non-vector mode IRQs */
2la      a0, default_trap_handler
3ori     a0, a0, 3
4csrw    mtvec, a0
5
6/* mtvt: for all vector mode IRQs */
7la      a0, __Vectors
8csrw    mtvt, a0

startup/interrupt 中可以找到 __Vectors:

1const pFunc __Vectors[] __attribute__((section(".init"), aligned(64))) = {
2    default_interrupt_handler, /*         */
3    default_interrupt_handler, /*         */
4    default_interrupt_handler, /*         */
5    ....
6};

此时还没有跟 freertos_risc_v_trap_handler 扯上关系,在 freertos/CMakelist.txt 中配置了 freertos_risc_v_trap_handlerdefault_interrupt_handler 之间的关系。那么当中断触发时, 调用 default_interrupt_handler 其实就是调用 freertos_risc_v_trap_handler 了。

1sdk_add_compile_definitions(-DportasmHANDLE_INTERRUPT=interrupt_entry -Ddefault_interrupt_handler=freertos_risc_v_trap_handler)

portASM.S 文件中 xPortStartFirstTask 函数又重新配置了 mtvecfreertos_risc_v_trap_handler,这个时候,中断和异常就统一使用 freertos_risc_v_trap_handler 函数了。

编译和烧录

  • 命令行编译

参考 Build with Linux or WSL 或者 Build with Windows CMD

  • 烧录

参考 bl_dev_cube

实验现象

BLE CLI

TODO

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

支持循环模式