BL MCU SDK development guide

The BL702 series products are general-purpose microcontrollers based on “SiFive E24 Core” RISC-V , with network functions such as BLE 5.0, zigbee and Ethernet, as well as other rich peripherals. It can be used in a wide range of IoT and other low-power applications. Supports programming and debugging of the chip through JTAG , and also supports through UART programming. BL MCU SDK will provide users with comprehensive support for BL70X series MCU development.

Preparation

Hardware environment

  • At least one board for BL702 series MCU:

    • BL706_IOT

    • BL706_AVB

BL706_Iot as shown in the figure below

BL706_IoT

BL706_AVB as shown in the figure below

BL706_AVB

  • A debugger that supports standard JTAG, just choose one of the following debuggers:

    • CK-Link

    • Jlink V11

    • Sipeed RV-Debugger Plus

    • Bouffalo Lab Debugger

  • One PC host (Windows or Linux system)

Software Environment

In order to better use the BL702 series MCU, it is recommended that you have at least one of the following development environments:

  • Use CDK for Windows (Windows 7 or above is recommended)

  • Windows Use Eclipse (Windows 7 or above is recommended)

  • Linux (LTS version above Ubuntu 18 is recommended)

Sipeed RV-Debugger Plus driver installation settings

  • This section mainly introduces the driver installation settings of the Sipeed RV-Debugger Plus debugger. If you use **CK-Link** or **J-Link**, you don’t need to read this section.

Sipeed RV-Debugger plus

Windows

  • To use the Sipeed RV-Debugger Plus debugger on Windows we need to change the driver to the Win USB driver

      1. First, connect the Type-C USB interface of the debugger to the PC with a USB data cable, open the device manager of the PC, and you can see that the debugger is recognized as two serial ports in the port column (Note: not the Serial port of the board’s Boot interface), or in the Universal Serial Bus Controller, you see USB Serial Converter A and USB Serial Converter B

    _images/sipeed_rv_debugger_1.png
    _images/sipeed_rv_debugger_4.png

    重要

    1. The debugger port number must start with usb serial port, if you plug in more than one similar device, please leave only one, confirm the debugger port number

    重要

    1. If you see “USB Serial Device (COM*)” in the Device Manager, it means that the debugger has entered the “Boot” mode. Please power off the debugger and power it on again, and be careful not to connect the debugger to the target board; at this time, go to the device manager to see if it is normal

    _images/sipeed_rv_debugger_7.png

    重要

    1. If the serial port is not shown in the device manager, only other devices are shown, or if you only see USB Serial Converter A and USB Serial Converter B in the Universal Serial Bus Controller, please go to the FTDI official website Download the driver that matches your system

    _images/sipeed_rv_debugger_6.png
    _images/sipeed_rv_debugger_3.png
      1. Find JTAG Debugger (Interface 0), then select the replacement driver as WinUSB and click Replace Driver to replace

      1. Open the device manager again and see that one of the serial ports has been replaced with a universal serial bus device, indicating that the installation is successful

    _images/sipeed_rv_debugger_2.png
      1. At this point, the device driver of Sipeed RV-Debugger Plus has been replaced, and then you can play happily~

Possible problems:

小心

  1. There are no two serial ports when the debugger is connected, and an LED on the debugger is always on, then it should be in Boot mode. Please power off the debugger and power it on again. Be careful not to connect the debugger to the target board; after the debugger is powered on, the two LED lights will flicker and go out under normal circumstances; at this time, check whether the device in the task manager is correct.

小心

  1. If you still can’t use it normally after the above operations, and there is no correct phenomenon, it is recommended to download from the official Sipeed repository GitHub obtain the firmware and re-write; press and hold the Boot button on the debugger without releasing it, insert the debugger into the computer and power on, make the debugger enter the Boot mode, and flash the firmware again; power off and restart

Linux

  • First, connect the Type-C USB interface of the debugger to the PC host using the USB cable, open Terminal, and enter the command lsusb in the terminal to see the device with the following information

$ lsusb
...
Bus 001 Device 003: ID 0403:6010 Future Technology Devices International, Ltd FT2232C Dual USB-UART/FIFO IC
...
_images/sipeed_rv_debugger_8.png
  • If the above diagram does not show the FT2232C, you need to install the ftdi driver

$ sudo apt install libusb-dev libftdi-dev libhidapi-dev
  • Re-plug the debugger to make the changes take effect

  • Debugging code requires openocd to be installed, using openocd version 0.11

备注

In Linux system, /dev/ttyUSB1 is used for serial port, /dev/ttyUSB0 is used for debug port, if /dev/ttyACM0 is displayed, it means boot mode is entered.

Hardware connection

This document introduces how to connect the board of BL70x series MCU.

BL706_IOT

Use serial port to Programming

  • Before using the serial port to Programming, please make sure that Bouffalo Lab Dev Cube or the command programming tool is installed correctly

    • Use Type-C USB data cable or Mini USB data cable to connect to the Type-C interface or Mini interface on the board.

    • Press the Boot key on the board, don’t release it.

    • Press the RST key on the board, now you have entered Boot ROM, you can release the two keys.

    • At this time, you can see the corresponding serial port COM number from the Bouffalo Lab Dev Cube, if it does not appear, please click the Refresh key to refresh.

  • If you don’t have a suitable data cable, you can also use some common USB-TTL modules to connect to the UART0 port of the development board for programming. UART0 is on the HD1 group, the connection method is as follows:

USB-TTL      BL702_IoT
----------------------
  3V3   <-->   VDD
  TXD   <-->   RX0
  RXD   <-->   TX0
  GND   <-->   GND
  • The programming step is the same as above

BL706_AVB

重要

BL706_AVB has multiple multiplexed pins, please check carefully whether the required function pins are multiplexed; FUNC1: “Default: PIX; Connect: I2S/JTAG”, FUNC2: “Default: I2S; Connect: SPI” ; If you need to debug, please remember to connect the FUNC1 jumper

Use Sipeed RV-Debugger Plus to programming and debug

  • Powering the BL706_AVB

  • Connect the RV-Debugger Plus debugger to the USB port of the computer. If the driver is not installed correctly, please refer to Sipeed RV-Debugger Plus driver installation settings, set the driver, and proceed to the following steps

  • Connect the debugger and the BL706_AVB with a cable (as shown in the figure below)

重要

The FUNC1 jumper must be connected when debugging, otherwise the pins will be multiplexed with other functions and the JTAG function cannot be used; the serial port function can be used normally

RV-Debugger connect bl706_avb board

Use CK-Link to programming and debug

  • Connect the CK-Link USB interface to the PC with a suitable USB data cable

  • Connect the FUNC1 jump caps of the bl706_avb

  • Connect the pins of the HD8 group to the adapter board using a flat cable

  • Connect the JTAG pin of the adapter board with the corresponding JTAG pin of CK-Link using a Dupont wire

  • If you do not use CK-Link to power the board, you need to power the board separately

bl706-avb board         CK-Link
-------------------------------
    JTAG_TDI     <-->     TDI
    JTAG_TDO     <-->     TDO
    JTAG_TCK     <-->     TCK
    JTAG_TMS     <-->     TMS
    VDD33        <-->     VREF
    GND          <-->     GND

ck_link connect bl706_avb board

Use serial port to programming

  • Before using the serial port to programming, please make sure that Bouffalo Lab Dev Cube or the command programming tool is installed correctly

    • Use the Type-C USB or Mini USB data cable to connect to the corresponding Type-C port or Mini port on the board.

    • Press the Boot key on the board, don’t release it.

    • Press the RST key on the board, now you have entered Boot ROM, you can release the two keys.

    • At this time, you can see the corresponding serial port COM number from the Bouffalo Lab Dev Cube, if it does not appear, please click the Refresh button to refresh.

  • If you don’t have a suitable data cable, you can also use some common USB-TTL modules to connect to the UART0 port of the development board for programming. UART0 on the HD12 group, the connection method is as follows:

  • If you use Sipeed RV-Debugger Plus to connect BL706_AVB through a flat cable, you can also use the serial port of Sipeed RV Debugger Plus

USB-TTL      BL706_AVB
----------------------
  TXD   <-->   RX0
  RXD   <-->   TX0
  GND   <-->   GND

Connect BL706 AVB sub-modules

  • This section describes how to connect the BL706_AVB board with other modules, mainly including camera connection, Audio Codec module connection, and SPI screen connection.

BL706_AVB Connects to GC0308 Camera Module

    1. First, take the black locking part of the J5 drawer type FPC cable holder on the back of the BL706_AVB development board and pull it out from the edge

    1. When fully disconnected, as shown in the figure below.

    1. The FPC cable holder is a drawer down type, so next insert the camera with the side without metal contact points facing upwards into the FPC cable holder

  • After inserting the camera, press the black latch tightly

BL706_AVB Connecting Audio Codec Modules

  • Insert the HD19 group of pins of Audio Codec module into the HD11 row female socket of BL706_AVB development board; note that the module is extended outward.

  • The schematic diagram is as follows:

Development environment setup guide

Before the formal development, please set up a suitable development environment. The specific steps are as follows:

Guide to using CDK (like MDK Keil) under Windows

This document introduces the use of the CDK developed by T-HEAD Semiconductor under Windows to complete the related software development of BL702 series MCU.

Regarding T-HEAD CDK, this is an integrated development environment developed by T-HEAD. It has operations and settings that are very similar to the traditional MCU development environment, and aims to fully access cloud development resources without changing user development habits, combined with graphical debugging and analysis tools such as OSTracer and Profiling, to accelerate user product development.

Software and hardware environment

  • T-HEAD CDK software

  • One USB Type-A data cable, one Type-C data cable

  • A CK-Link emulator or a Sipeed RV-Debugger Plus debugger

  • A USB-TTL serial port module

  • Several Dupont lines

Download CDK

  • CDK software can be obtained from the official website of T-HEAD OCC

  • After the download is complete, unzip, double-click setup.exe, follow the prompts, and complete the software installation

Download bl_mcu_sdk

  • Download bl_mcu_sdk from the open source community.

    • You can use git clone or directly download to download the SDK

    • Before using git clone, please make sure that git has been installed correctly, open a terminal that supports git and enter the following command to get the latest SDK

    1$ git clone https://gitee.com/bouffalolab/bl_mcu_sdk.git  --recursive
    

Hardware connection

  • For specific connection methods, please refer to Hardware connection

  • Please make sure that the board is set up correctly before proceeding to the following steps

Test the Hello World project

When using Sipeed RV-Debugger Plus to debug a project, please follow the steps below:

Use CDK + Sipeed RV-Debugger Plus to compile and debug
Open Hello World
  • After obtaining the SDK, enter the examples/hellowd/helloworld/cdk directory and double-click helloworld.cdkproj to open the Helloworld project

Compile Hello World

helloworld.cdkproj

  • Select the OpenOCD_Debug project in the drop-down menu. Since Sipeed RV-Debugger Plus uses OpenOCD for debugging, this tutorial is based on the OpenOCD_Debug project;

  • If Sipeed RV-Debugger Plus does not install the driver correctly, please refer to Sipeed RV-Debugger Plus driver installation settings, set up the driver, and then proceed to the following steps

  • In the CDK toolbar, click the compile icon to compile the project

    • Click 1 Build Project to compile the currently selected project

    • Click 2 Clean Project to clear the results of the last compilation

    • Click 3 Flash Download to download the compiled code to the chip (Flash download function cannot be used with OpenOCD Debug)

    • Click 5 Start/Stop Debug whitout Download to debug directly without loading the current bin file

    • You can also right-click the project name in Project, and compile the project through the options in the right-click menu

Program Hello World
  • When using the OpenOCD mode debugging method in the CDK, it is not currently supported to directly use the CDK related flash tool to download the code, so please use the BL Dev Cube tool to program, please refer to BLDevCube start guide

  • Use CDK to debug after the code is programmed

Run Hello World
  • From the menu bar of the CDK View->Serial Pane, open the serial port panel, right-click in the opened Serial Pane, set the serial port, select your corresponding serial port number and baud rate

_images/cdk4.png

CDK Serial Pane setting

  • Press the RST key on the board, you can see the result of the code in the serial port

HelloWorld!

Debug Hello World
  • Click the Start/Stop Debugger button at the top of the toolbar to enter the debug interface, as shown in the figure below

Debug HelloWorld!

  • In the debug interface, the Register window can view the CPU internal register data; the Peripherals peripheral panel, you can view the corresponding peripheral register data, the top menu bar Peripherals-> System Viewer can select peripherals; click the relevant debugging button in the upper toolbar to perform operations such as breakpoint setting, single-step debugging, single-step instruction, and full-speed operation. Of course, these operations have corresponding shortcut keys and shortcut setting methods. For details, please refer to CDK Help.

  • We click the single step button to run the code, and we can see that the cursor moves to the next sentence of code, and we can see our output Hello World! displayed in the serial port panel.

When using CK-Link to debug the project, please follow the steps below:

Eclipse Development Guide under Windows

This document introduces the use of eclipse under Windows to build a software development environment for BL702 series MCU.

Software and hardware environment

  • Eclipse free installation package

  • Serial port assistant software

  • A USB Type-A data cable

  • A j-link emulator

  • A USB-TTL serial port module

  • Several Dupont lines

Download Eclipse

Download bl_mcu_sdk

  • Download from the open source community bl_mcu_sdk.

  • You can use git clone or directly download to download the SDK

  • Before using git clone, please make sure that git has been installed correctly. Open a terminal that supports git and enter the following command to get the latest SDK.

1$ git clone https://gitee.com/bouffalolab/bl_mcu_sdk.git  --recursive

Configure eclipse

  • Copy the eclipse compressed package to the working directory, unzip the eclipse compressed package

  • Enter the eclipse directory, double-click eclipse.exe to start eclipse

  • Select your Workspace directory, click Launch to enter the workspace

  • Click Window->preferences in the menu bar to open the environment configuration related page, ready to import the related configuration environment

  • Click the icon at “1” in the figure below to open the import configuration interface, follow the steps shown in the figure, and select the bflb_mcu_preferences.epf configuration file in the eclipse.exe directory.
  • After selecting the corresponding file, click Finish, select and click cancel in the dialog box without restarting.

Import bl_mcu_sdk

  • Click on the menu bar File->Import to open the configuration interface of the imported project

  • In the opened Import window, select General->Existing Projects into Workspace, and then click Next

  • After loading the project path of bl_mcu_sdk, click Finsh to complete the import

  • After the import is complete, close the Welcome window to see the imported project

  • Expand Build Target, you can see the three function buttons bl_clean, bl_make, and download.

    • Double-click the bl_clean button, it will clear the compilation cache in the build and out directories

    • Double-click the bl_make button, the set case will be compiled normally, if the default configuration is not modified, the helloworld project will be compiled

    • Double-click the download button, the code will be downloaded to the chip, if it is not compiled successfully, the default or last .bin file will be downloaded

Hardware connection

  • For specific board connection, please refer to Hardware connection; (The eclipse environment recommends using j-link for programming and debugging)

  • Please make sure that the development board is set up correctly before proceeding to the following steps

Test the Hello World project

Open Hello World
  • Open examples/hellowd/helloworld/main.c, you can edit and modify the code of the helloworld test demo. If you modify it, please save it and execute the compilation

Compile Hello World
  • Double click bl_make to compile the helloworld project

  • After successful compilation, you can see the log information as shown in the figure below in the Console window

_images/pic10.png
Program Hello World
  • Double-click download to program the helloworld project bin file to the chip

  • After the download is successful, you can see the log information as shown in the figure below in the Console window

_images/pic11.png
Run Hello World
  • Connect the TXD0, RXD0 and GND pins of the board to the USB-TTL serial port module with a DuPont cable, insert the serial port module into the PC, and use any serial port assistant software to open the serial port

  • After the programming is successful, press the rst button on the board. If the download is correct, you can see the log information as shown in the figure below in the serial port assistant software.

_images/eclipse_run.png
Debug Hello World
  • Click the Debug button in the eclipse toolbar to enter the debug configuration window

  • Select GDB SEGGER J-Link Debugging->Jlink_bl_mcu_sdk, select the .elf file that needs to be debugged in C/C++ Application:

  • Click Apply first, then click Debug to start Debug

  • After entering the Debug interface, you can see that the program stops at main, click the Step Over button in the upper toolbar to perform single-step debugging of the code project.

Eclipse Debugging

Compile and program different target projects

  • When you right-click the bl_make button and click Edit, the configuration interface for replacing the target project will pop up, as shown in the figure below

  • Where APP=xxx can be changed to the name of the target project that needs to be compiled and programmed. For example, if you want to compile and program the gpio/gpio_blink project, modify it to APP=gpio_blink.

  • make BOARD=bl706_iot in Build command will specify different Board types to adapt to different types of boards.

  • The Board type determines the corresponding borad header file when compiling. The default selection is make build BOARD=bl706_iot

Linux OR WSL environment development guide

This document introduces how to install and configure the software development tools needed for BL702 series MCUs in Linux. The installation and configuration method under WSL system is the same as under linux, please install WSL system by yourself. The difference is that one runs on a pure linux system and the other runs on windows. If you don’t want to install a virtual machine or a linux system, you can choose WSL.

Windows Subsystem for Linux (WSL) is a compatibility layer that can run native Linux binary executable files (ELF format) on Windows 10. It was developed by Microsoft and Canonical in cooperation. Its goal is to enable the pure Ubuntu image to be downloaded and decompressed to the user’s local computer, and the tools in the image can run on this subsystem. Therefore, the operation mode under WSL is exactly the same as the operation mode under linux.

Software and hardware environment

  • A mini USB data cable

  • A USB-TTL serial port module

  • Several Dupont lines

Configure RISC-V toolchain

1$ cd ~
2$ wget -c https://dev.bouffalolab.com/media/upload/download/riscv64-elf-x86_64-20210120.tar.gz
3$ mkdir -p riscv64-elf-20210120
4$ tar -zxvf riscv64-elf-x86_64-20210120.tar.gz -C riscv64-elf-20210120
5$ sudo cp -rf ~/riscv64-elf-20210120  /usr/bin
6$ echo "export PATH=\"$PATH:/usr/bin/riscv64-elf-20210120/bin\""  >> ~/.bashrc
7$ source ~/.bashrc

Configure cmake & make tools

1$ sudo apt update
2$ sudo apt install make
3$ cd ~
4$ wget -c https://cmake.org/files/v3.19/cmake-3.19.3-Linux-x86_64.tar.gz
5$ tar -zxvf cmake-3.19.3-Linux-x86_64.tar.gz
6$ sudo cp -rf ~/cmake-3.19.3-Linux-x86_64  /usr/bin
7$ echo "export PATH=\"$PATH:/usr/bin/cmake-3.19.3-Linux-x86_64/bin\""  >> ~/.bashrc
8$ source ~/.bashrc

Hardware connection

  • For the connection of the board, please refer to Hardware connection

  • Please make sure that the board is set correctly before proceeding to the following steps (Serial connection is recommended under Linux)

Get bl_mcu_sdk

  • Open the terminal and enter the following command to get bl_mcu_sdk

1 $ cd ~
2 $ git clone https://gitee.com/bouffalolab/bl_mcu_sdk.git  --recursive

Test Hello World project

Open Hello World
  • After obtaining the SDK, enter examples/hellowd/helloworld in the SDK, open main.c, and then edit the related code of helloworld.

1 $ cd ~/bl_mcu_sdk/examples/hellowd/helloworld
2 $ vi main.c
  • After editing, save the changes and close the file, and then compile

Compile Hello World
1 $ cd ~/bl_mcu_sdk
2 $ make build BOARD=bl706_iot APP=helloworld
Program Hello World
  • Please confirm the programming method first. If you use serial programming, please press and hold the boot key on the board and don’t release it. At this time, press the rst key, and then release the two keys. The board enters the boot_rom state.

  • At this time, enter the following command in the terminal to program

1 $ cd ~/bl_mcu_sdk
2 $ make download INTERFACE=uart COMx=/dev/ttyUSB1
  • If the download fails, please check:

      1. Whether the serial port is used for programming, whether the development board is powered, and whether the hardware connection is correct.

      1. Is the programming command executed in the bl_mcu_sdk directory

      1. Whether to enter boot_rom mode

      1. Whether the serial port is occupied, and whether your available serial port is selected correctly, if your serial port is not ttyUSB1, then please specify the correct serial port

Run Hello World
  • Open a new terminal, install and run the serial port tool

1 $ sudo apt install picocom   # Ignore if it is already installed
2 $ picocom -b 2000000 /dev/ttyUSB1 # Pay attention to your available serial port number (if you use the serial port of Sipeed RV-debugger Plus, it will be ``ttyUSB1``)
  • Press the rst key on the board, you can see hello world! in the serial terminal.

helloworld!

Debug Hello World

cdk-logo

eclipse-logo

linux-logo

Windows_CDK

Windows_eclipse

Linux

New Project Guide based on cmake framework

This document will introduce how to create a new project based on this SDK.

Examples directory structure

There are two levels of subdirectories under bl_mcu_sdk/examples, the first level is the folders of different peripherals. The second level is a specific test case of the peripheral, and the directory usually contains a CMakeList.txt and the source code related to the case.

Add a single source file project

备注

The source file must contain the c program entry, usually the main function, the source file may not be called main.c

  • Create a new my_case folder under examples to store your case

  • Create a new folder that needs to be tested in the my_case directory, such as gpio_case

  • Then add the main.c and CMakeLists.txt files in the gpio_case directory

The directory structure is as follows:

1bl_mcu_sdk
2├── examples
3    ├── my_case
4        ├── gpio_case
5        │   ├──CMakeLists.txt
6        │   └──main.c
7        └── xxxx_case

CMakeLists.txt:

1set(mains main.c)
2generate_bin()
  • After writing the code in main.c, compile it in the bl_mcu_sdk directory. The compilation command is as follows:

1make BOARD=bl706_iot APP=gpio_case

Add multiple source file projects

备注

One of the source files must contain the c program entry, usually the main function, the source file does not need to be called main.c. This source file depends on other source files.

  • The steps for adding a multi-source file project are the same as the basic steps for adding a single source file project

  • Add test1.c, test1.h, test2.c, test2.h and other source files and header files that need to be relied on in the gpio_case directory

The directory structure is as follows:

 1bl_mcu_sdk
 2├── examples
 3    ├── my_case
 4        ├── gpio_case
 5        │   ├──CMakeLists.txt
 6        │   ├──test1.c
 7        │   ├──test1.h
 8        │   ├──test2.c
 9        │   ├──test2.h
10        │   └──main.c
11        └── xxxx_case
  • At this time, the CMakeLists.txt file needs to add the corresponding dependent source file, the content is as follows:

CMakeLists.txt:

1set(mains main.c)
2set(TARGET_REQUIRED_SRCS test1.c test2.c)
3generate_bin()
  • After writing the code, compile it in the bl_mcu_sdk directory. The compilation command is as follows:

1make BOARD=bl706_iot APP=gpio_case

Add a new project with dependent libraries

  • The steps for adding a new project with dependent libraries are the same as the basic steps for adding a single source file project

  • If the dependent library used already exists in this SDK, you only need to modify CMakeLists.txt

    • If the dependent library does not exist, you need to add it yourself, please refer to the follow-up instructions for details

If it already exists, the directory structure is as follows:

1bl_mcu_sdk
2├── examples
3    ├── my_case
4        ├── gpio_case
5        │   ├──CMakeLists.txt
6        │   └──main.c
7        └── xxxx_case
  • At this time, the CMakeLists.txt file needs to add the corresponding dependent library files. For example, we add the FreeRTOS component library, the content is as follows:

CMakeLists.txt:

1set(TARGET_REQUIRED_LIBS freertos)
2set(mains main.c)
3generate_bin()
  • After writing the code, compile it in the bl_mcu_sdk directory. The compilation command is as follows:

1make BOARD=bl706_iot APP=gpio_case  SUPPORT_FREERTOS=y

Add a new project and set the private compilation option (gcc option)

  • The steps of adding a new project are basically the same as adding a single source file project

  • Mainly modify the CMakeLists.txt file and add private compilation options

CMakeLists.txt:

1set(mains main.c)
2set(TARGET_REQUIRED_PRIVATE_OPTIONS -Ofast)
3generate_bin()
  • After writing the code, compile it in the bl_mcu_sdk directory. The compilation command is as follows:

1make BOARD=bl706_iot APP=gpio_case

Add a new project and its dependent source files and library files

New Project Guide based on CDK

This document will briefly explain how to create a new CDK project based on this SDK, please make sure that the CDK IDE is properly installed before following this tutorial.

Examples directory structure

There are two levels of subdirectories under bl_mcu_sdk/examples, the first level is the folders of different peripherals. The second level is a specific test case of the peripheral, The second level directory usually also contains a directory named cdk and the source code associated with the case. The cdk directory usually contains a xxx.cdkproj file, which is a CDK project file. If the CDK IDE is properly installed, double-click the project to open it. The newly created project should be at the same level as the case level in the current examples directory.

备注

The source file must contain the c program entry, usually the main function, the source file may not be called main.c

  • Create a new my_case folder under examples to store your case

  • Create a new folder that needs to be tested in the my_case directory, such as gpio_case

  • Then add the main.c file and cdk directory in the gpio_case directory

The directory structure is as follows:

1bl_mcu_sdk
2├── examples
3    ├── my_case
4        ├── gpio_case
5        │   ├── cdk
6        │   │   ├──gpio_case.cdkproj
7        │   ├── CMakeLists.txt
8        │   └── main.c
9        └── xxxx_case

BLDevCube start guide

This document mainly introduces the use of Bouffalo Lab Dev Cube for code programming. For more details, please refer to BLDevCube user manual

Download Bouffalo Lab Dev Cube

  • Download the version suitable for your operating system from the developer community, download address:https://dev.bouffalolab.com/download

    • For users who do not have a registered account, click on the guest portal

  • After the download is complete, you can use it after decompression

Configure tool download method

  • Double-click BLDevCube.exe, in the Chip Type drop-down box, select the corresponding chip model, click Finish to enter the Dev Cube interface

  • Enter the interface, select MCU under View in the menu bar to enter the MCU program download interface

select mcu

  • Select the corresponding download method in the Interface column under Image, and choose according to your actual physical connection

  • Image file configure the absolute path of the downloaded image, click Browse to select the Bin file

  • When you click click here to show advanced options, the advanced mirroring configuration will be expanded, usually keep the default configuration; it should be noted that Flash Clock will affect the clock frequency of Flash and PSRAM at the same time , If you need to use PSRAM, you can increase the clock frequency to get better performance

Download with UART

  • COM Port is used for UART download, select the COM number connected to the chip, and click Refresh to refresh the COM number

  • When Uart Speed is used for UART download, configure the appropriate baud rate, the default is 2M

  • Please make sure the hardware configuration is correct before downloading:

    • Use Type-C USB or Mini USB to connect to the corresponding USB Type-C port or Mini port on the board.

    • Press the Boot key on the board, don’t release it

    • Press the RST key on the board, now you have entered Boot ROM model, you can release the two keys

    • At this time, you can see the corresponding serial port COM number from the Bouffalo Lab Dev Cube, if it does not appear, please click the Refresh button to refresh

  • After completing the above configuration correctly, click the Create&Program button to download

  • After the download is successful, you will see the status bar turn green and display Success

download success!

Download with Openocd

  • Download using openocd is basically the same as using serial port download option configuration, just switch Interface to Openocd

  • The hardware connection needs to be changed to a debugger connection that supports Openocd (this tutorial takes Sipeed RV Debugger as an example):

      1. Connect the RV debugger to the USB port of the computer, open the device manager, you will find that the debugger is recognized as two serial ports (note: not the serial port on the board)

    _images/sipeed_rv_debugger_1.png
      1. Download the zadig-2.4 replacement driver from sipeed. Download link: http://dl.sipeed.com/MAIX/tools/sipeed-rv-debugger/zadig-2.4.exe

      1. After downloading, double-click to open zadig-2.4.exe, and check List All Devices in Options.

      1. Find JTAG Debugger (Interface 0), then select the replacement driver as WinUSB and click Replace Driver to replace

      1. Open the device manager again and see that one of the serial ports has been replaced with a universal serial bus device, indicating that the installation is successful

    _images/sipeed_rv_debugger_2.png
      1. Connect the JTAG pins of the debugger with the JTAG pins of the board

  • After completing the above configuration correctly, click the Create&Program button to download

  • After the download is successful, you will see the status bar turn green and display Success

Board Configuration System User Guide

In order to implement the idea of everything is a file, we propose a Board configuration system for embedded applications with different hardware configuration requirements. The Board configuration system is mainly used for initializing the three basic elements of clock, GPIO, and peripheral default configuration in embedded applications.

Board Configuration System contains three configuration files, and a bl_config_wizard graphical configuration software

  • clock_config.h Clock configuration Include file

  • peripheral_config.h Peripheral configuration Include file

  • pinmux_config.h Pin Function Configuration Include file

  • bl_config_wizard The graphical interface configures the above three types of files

The user only needs to modify three configuration files and the system will be initialized automatically, thus eliminating the need to call a series of complex and lengthy initialization functions in the user program. Boufflao Lab provides bl_config_wizard configuration software for users to quickly and easily generate configuration files for their projects.

bl_config_wizard supports PC-side online configuration, but currently does not support mobile terminal online configuration.

bl_config_wizard Preview

The features of each file in the Board configuration system

The board system is mainly used for different boards, different boards create different board files and put them in bsp/board directory, and a board file, in the case of pins not conflicting, can be shared to different demos, no need to create multiple projects and reduce the project file size.

错误

If there is a pin conflict and you have to use the same Board file, please modify the pins yourself

board.c

board.c Main initialization of clock and pins

blxxx_config.h

blxxx_config.h Mainly contains some header files for the HAL layer driver.

提示

The above two files do not need to be changed by the user, and the same MCU can be copied and pasted directly into your own board directory for use

clock_config.h

clock_config.h Mainly configures the clock sources for the system and peripherals as well as the frequency division system.

peripheral_config.h

peripheral_config.h It mainly contains the enablement of peripherals and the configuration of parameters.

警告

Macros starting with #define BSP_USING_XXX are used to enable the configuration of the peripheral, if the macro is not enabled, all functions of the peripheral cannot be used

警告

Macro starting with XXX_CONFIG, used to initialize the configuration of the peripheral, which is later used by calling device_open

pinmux_config.h

pinmux_config.h Mainly configures the GPIO pin function of the peripheral.

警告

In mcu sdk, all demos share this file, so some demos are not usable and require frequent changes to the pin function configuration in this file. If the user has already set the pin assignments, there is no need to modify them frequently.

Use of the Board configuration tool bl_config_wizard

Generate a new pinmux_config.h file

  1. Select Pin & Peripheral Configuration in the window bar.

  2. Select MCU model, currently supports BL706 pin configuration, BL704 pin configuration, BL702 pin configuration.

  3. Select the function of the pin, take BL706 pin configuration as an example, click on the drop-down box of PAD_GPIO_XX and select the desired function, as shown in the figure.

Select pin function

  1. After configuring all the pin functions, click Export Configuration File and then you can select the path and modify the file name in the pop-up box, as shown in the figure.

Exporting configuration files

Modify the original pinmux_config.h file

Often in use, instead of generating a new pinmux_config.h file, we make changes to the original pinmux_config.h file, and bl_config_wizard supports such a need.

  1. Select Pin & Peripheral Configuration in the window bar.

  2. Select MCU model, currently supports BL706 pin configuration, BL704 pin configuration, BL702 pin configuration.

  3. Click on import configuration file and select the pinmux_config.h file in the pop-up box.

  4. Select the pin to be modified and click on its drop-down box to change the pin function

  5. When you are done, click Export Profile and then you can select the path and modify the file name in the pop-up box.

Modify the pinmux_config.h file in the CDK tool

  • pinmux_config.h Also supports the use of graphical configuration wizards in the CDK for adjusting the corresponding pin functions

  • Drag the pinmux_config.h file directly into the CDK text editor interface, and you will see the Configuration Wizard tab at the bottom of the text editor

    Configuration Wizard

  • Click on the Configuration Wizard tab to open the graphical configuration wizard interface

  • The functions supported by the pin can be selected by selecting the drop-down box

    Graphical configuration wizard to set pin functions

  • Please refer to the Graphical Configuration Wizard section of CDK Help for more information on the specific functions and code rules of the Graphical Configuration Wizard.

Differences with STM32CUBEMX Configuration Tool

STM32CUBEMX is also a tool to configure the clock, peripherals and GPIO initialization, eventually generating a complete project with initialization at the very beginning of main.c, and the GPIO and peripheral initialization base will be called in stm32xxx_hal_msp.c.

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_QUADSPI_Init();
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if(huart->Instance==UART5)
    {
    /* USER CODE BEGIN UART5_MspInit 0 */

    /* USER CODE END UART5_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_UART5_CLK_ENABLE();

        __HAL_RCC_GPIOB_CLK_ENABLE();
        /**UART5 GPIO Configuration
        PB12     ------> UART5_RX
        PB13     ------> UART5_TX
        */
        GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF14_UART5;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        /* UART5 interrupt Init */
        HAL_NVIC_SetPriority(UART5_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(UART5_IRQn);
    /* USER CODE BEGIN UART5_MspInit 1 */

    /* USER CODE END UART5_MspInit 1 */
    }

}

提示

The projects generated by stm32 all work for one project and cannot be compiled for more than one project at the same time. If you use more than one project, you have to generate more than one of these two files. When using multiple projects, it will indirectly increase the file size and add duplicate files.

API Overview

Introduction

bl_mcu_sdk code hierarchy is divided into the following main layers.

  • The application layer: codes written by users.

  • The component layer: some opensource components,the interface calls the HAL layer, while the wireless layer is used for wireless functions.

  • HAL layer and wireless layer for adaptation to different MCUs, where the HAL layer is divided into two layers
    • Device driver management: provides a standard set of interfaces, which are implemented by the peripheral driver adaptation layer

    • Peripheral driver adaptation layer: implements the standard interface of the device driver management and extends its own unique interface

  • Standard driver layer based on register packaging

  • Hardware layer, also known as the register layer

code structure

Device driver management layer

The realization of the device driver management layer adopts the object-oriented idea. First of all, we regard the peripheral as a device or a file, adhering to the concept of everything is a file, and files have standard calling interfaces: open, close, ctrl, write, read, callback. Different file types are different (such as serial device, ADC device, SPI device), and the way of opening is also different (such as polling, interrupt, DMA), from this, we can construct a base class (parent class) of an object.

Base class

struct device
{
    char name[NAME_MAX];            /*name of device */
    dlist_t list;                   /*list node of device */
    enum device_status_type status; /*status of device */
    enum device_class_type type;    /*type of device */
    uint16_t oflag;                 /*oflag of device */

    int (*open)(struct device *dev, uint16_t oflag);
    int (*close)(struct device *dev);
    int (*control)(struct device *dev, int cmd, void *args);
    int (*write)(struct device *dev, uint32_t pos, const void *buffer, uint32_t size);
    int (*read)(struct device *dev, uint32_t pos, void *buffer, uint32_t size);
    void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event);
    void *handle;
};

Base class member: name

Name the device and use device_find to find the device.

Base class member: type

type records the category of the current device, and the type that can be selected are as follows.

enum device_class_type
{
    DEVICE_CLASS_NONE = 0,
    DEVICE_CLASS_GPIO,
    DEVICE_CLASS_UART,
    DEVICE_CLASS_SPI,
    DEVICE_CLASS_I2C,
    DEVICE_CLASS_ADC,
    DEVICE_CLASS_DMA,
    DEVICE_CLASS_TIMER,
    DEVICE_CLASS_PWM,
    DEVICE_CLASS_SDIO,
    DEVICE_CLASS_USB,
    DEVICE_CLASS_I2S,
    DEVICE_CLASS_CAMERA,
    DEVICE_CLASS_SEC_HASH,
} ;

Base class member: status

status is used to record the current status of the device and provides 4 statuses.

enum device_status_type
{
    DEVICE_UNREGISTER = 0,
    DEVICE_REGISTERED,
    DEVICE_OPENED,
    DEVICE_CLOSED
} ;

Base class member: oflag

oflag records the flag information filled in during registration and the oflag information filled in when using device_open.

Base class members: list

The addition and deletion of equipment is stored in a doubly linked list, which saves memory.

Base class members: standard function pointers

Provides a standard function interface for different peripherals. When the peripheral implements this type of interface and assigns it to the member, the function of rewriting can be achieved.

Device driver management layer standard interface

device_register

device_register is used to register the device and register the device information in the linked list.

int device_register(struct device *dev, const char *name);
  • dev: device handle.

  • name: the name of the device.

  • return: return error code, 0 means registration is successful, others mean errors.

device_unregister

device_unregister is used to delete the device and delete the device information from the linked list.

int device_unregister(const char *name);
  • dev: device handle

  • name: the name of the device to be deleted

  • return: error code, 0 means delete, others mean error

device_find

device_find is used to find the device from the linked list according to name, and return the first address of the device handle.

struct device *device_find(const char *name);
  • dev: device handle

  • name: the name of the device to be searched

  • return: error code,! 0 means the device handle was found, NULL means the device was not found.

device_open

device_open is used to open the device, and oflag represents the opening method. Currently, there are 6 opening methods available. The bottom layer calls the open member in the dev handle.

int device_open(struct device *dev, uint16_t oflag);
  • dev: device handle

  • oflag: open method

  • return: error code, 0 means opening is successful, others mean errors

oflag can write the following parameters:

#define DEVICE_OFLAG_STREAM_TX  0x001 /* The device is turned on in polling sending mode */
#define DEVICE_OFLAG_STREAM_RX  0x002 /* The device is turned on in polling receiving mode */
#define DEVICE_OFLAG_INT_TX     0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX     0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX     0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX     0x020 /* The device is turned on in DMA receiving mode */

device_close

device_close is used to close the device. The bottom layer calls the close member in the dev handle.

int device_close(struct device *dev);

-dev: device handle -return: error code, 0 means closing is successful, others mean error

device_control

device_control is used to control the device and modify parameters according to commands. The bottom layer calls the control member in the dev handle.

int device_control(struct device *dev, int cmd, void *args);
  • dev: device handle

  • cmd: device control command

  • args: control parameters

  • return: Different control commands return different meanings.

cmd provides the following standard commands. In addition, different peripherals also have their own commands

#define DEVICE_CTRL_SET_INT             0x01    /* set interrupt */
#define DEVICE_CTRL_CLR_INT             0x02    /* clear interrupt */
#define DEVICE_CTRL_GET_INT             0x03    /* get interrupt status*/
#define DEVICE_CTRL_RESUME              0x04    /* resume device */
#define DEVICE_CTRL_SUSPEND             0x05    /* suspend device */
#define DEVICE_CTRL_CONFIG              0x06    /* config device */
#define DEVICE_CTRL_GET_CONFIG          0x07    /* get device configuration */
#define DEVICE_CTRL_ATTACH_TX_DMA       0x08
#define DEVICE_CTRL_ATTACH_RX_DMA       0x09
#define DEVICE_CTRL_TX_DMA_SUSPEND      0x0a
#define DEVICE_CTRL_RX_DMA_SUSPEND      0x0b
#define DEVICE_CTRL_TX_DMA_RESUME       0x0c
#define DEVICE_CTRL_RX_DMA_RESUME       0x0d
#define DEVICE_CTRL_RESVD1              0x0E
#define DEVICE_CTRL_RESVD2              0x0F

device_write

device_write is used to send data, and the sending mode can be polling, interrupt, dma. The bottom layer calls the write member in the dev handle.

int device_write(struct device *dev, uint32_t pos, const void *buffer, uint32_t size);
  • dev: device handle

  • pos: different devices have different meanings for pos

  • buffer: the buffer to be written

  • size: the length to be written

  • return: error code, 0 means writing is successful, others mean errors

device_read

device_read is used to receive data, and the receiving mode can be polling, interrupt, dma. The bottom layer calls the read member in the dev handle.

int device_read(struct device *dev, uint32_t pos, void *buffer, uint32_t size);
  • dev: device handle

  • pos: different devices have different meanings for pos

  • buffer: the buffer to be read

  • size: the length to be read

  • return: error code, 0 means successful reading, others mean errors

device_set_callback

device_set_callback is used to register interrupt callback function. The bottom layer calls the callback member in the dev handle.

int device_set_callback(struct device *dev, void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event));
  • dev: device handle

  • callback: the interrupt callback function to be registered

    • dev: device handle

    • args: Different peripherals have different meanings

    • size: transmission length

    • event: interrupt event type

Peripheral driver adaptation layer

Subclass inherits from parent class

The first member of different peripherals is struct device, which is equivalent to the inheritance of the parent class, so that the subclass can be used to access the parent class member. When the subclass is used to modify the members of the parent class, it has its own functions. The realization principle is that the first address of different structures is the address of the first member in the structure.

typedef struct xxx_device
{
    struct device parent;

} xxx_device_t;

Rewrite standard interface

Each peripheral has a xxx_register function, which is used to rewrite the standard interface.

dev->open = xxx_open;
dev->close = xxx_close;
dev->control = xxx_control;
dev->write = xxx_write;
dev->read = xxx_read;

Peripheral

Clock tree

Introduction

The BL series chips have a lot of clock source selection and provide a clock tree configuration table to facilitate user configuration. Users do not need to call the clock setting interface. Users only need to care about the final system clock and peripheral clock frequency. The clock configuration table is located in the clock_config.h file under the bsp/board/xxx_board directory.

Clock Frequency Acquisition Interface

system_clock_get

system_clock_get is used to get the system clock frequency.

uint32_t system_clock_get(enum system_clock_type type);
  • type the type of system clock frequency

type provide the following types

enum system_clock_type
{
    SYSTEM_CLOCK_ROOT_CLOCK = 0,
    SYSTEM_CLOCK_FCLK,
    SYSTEM_CLOCK_BCLK,
    SYSTEM_CLOCK_XCLK,
    SYSTEM_CLOCK_32K_CLK,
    SYSTEM_CLOCK_AUPLL,
};
peripheral_clock_get

peripheral_clock_get is used to get the peripheral clock frequency.

uint32_t peripheral_clock_get(enum peripheral_clock_type type);
  • type peripheral clock frequency type

type provide the following types

enum peripheral_clock_type
{
    PERIPHERAL_CLOCK_UART = 0,
    PERIPHERAL_CLOCK_SPI,
    PERIPHERAL_CLOCK_I2C,
    PERIPHERAL_CLOCK_ADC,
    PERIPHERAL_CLOCK_DAC,
    PERIPHERAL_CLOCK_I2S,
    PERIPHERAL_CLOCK_PWM,
    PERIPHERAL_CLOCK_CAM,
};

GPIO

Introduction

The full name of GPIO is General Purpose Input Output. The GPIO peripherals of BL series chips mainly have the following functions.

  • General input and output pull-up and pull-down

  • Multiplex function pull-up and pull-down

  • Simulation function

  • External interrupt (rising edge, falling edge, high level, low level)

  • Hardware eliminate jitter

  • Drive capacity control

The pin configuration of bl mcu sdk is divided into two kinds.

  • GPIO multiplexing function is through a special pinmux table, users only need to modify the functions of related pins in the table, and the program will automatically configure these pins. pinmux table is located in the pinmux_config.h file under the bsp/board/xxx_board directory.

  • Configure the pins through the standard GPIO device interface. The disadvantage is that only common input and output and interrupt functions can be configured. It is recommended to use the table to configure the multiplexing functions.

GPIO Device Interface

gpio_set_mode

gpio_set_mode is used to configure the mode of gpio.

void gpio_set_mode(uint32_t pin, uint32_t mode);
  • pin the pin to be configured

  • mode pin function to be configured

mode provides the following types

#define GPIO_OUTPUT_MODE                        0
#define GPIO_OUTPUT_PP_MODE                     1
#define GPIO_OUTPUT_PD_MODE                     2
#define GPIO_INPUT_MODE                         3
#define GPIO_INPUT_PP_MODE                      4
#define GPIO_INPUT_PD_MODE                      5
#define GPIO_ASYNC_RISING_TRIGER_INT_MODE       6
#define GPIO_ASYNC_FALLING_TRIGER_INT_MODE      7
#define GPIO_ASYNC_HIGH_LEVEL_INT_MODE          8
#define GPIO_ASYNC_LOW_LEVEL_INT_MODE           9
#define GPIO_SYNC_RISING_TRIGER_INT_MODE        10
#define GPIO_SYNC_FALLING_TRIGER_INT_MODE       11
#define GPIO_SYNC_HIGH_LEVEL_INT_MODE           12
#define GPIO_SYNC_LOW_LEVEL_INT_MODE            13
gpio_write

gpio_write is used to set pin level

void gpio_write(uint32_t pin, uint32_t value);
  • pin the pin to be set

  • value the level to be set

gpio_toggle

gpio_toggle: is used to toggle pin level

void gpio_toggle(uint32_t pin);
  • pin: the pin to be toggled

gpio_read

gpio_read is used to read pin level

int  gpio_read(uint32_t pin);
  • pin the pin to read the level

  • return 0 is low level, 1 is high level

gpio_attach_irq

gpio_attach_irq is used to attache an interrupt callback function to the interrupt pin

void gpio_attach_irq(uint32_t pin, void (*cbfun)(uint32_t pin));
  • pin the pin to which the interrupt callback is attached

  • cbfun register interrupt callback

gpio_irq_enable

gpio_irq_enable is used to enable gpio interrupt

void gpio_irq_enable(uint32_t pin,uint8_t enabled);
  • pin the pin to turn on or off the interrupt

  • enabled 0 is to close the interrupt, 1 is to open the interrupt

UART

Introduction

UART is a universal asynchronous transmitter-receiver, which provides a flexible way for full-duplex data exchange with external devices. The UART device in BL series MCU has the following characteristics:

  • Data bit length can choose 5/6/7/8 bits

  • Stop bit length can be selected 0.5/1/1.5/2 bits

  • Support odd/even/no parity bit

  • Support hardware flow control (RTS/CTS)

  • Automatic baud rate detection

  • Support LIN protocol (transceive BREAK/SYNC)

  • Send/receive FIFO

  • Support polling, interrupt, DMA transfer

  • Unique rto interrupt

UART Device Structure Definition

typedef struct uart_device
{
    struct device parent;
    uint8_t id;
    uint32_t baudrate;
    uart_databits_t databits;
    uart_stopbits_t stopbits;
    uart_parity_t parity;
    uint8_t fifo_threshold;
    void* tx_dma;
    void* rx_dma;
} uart_device_t;
  • parent inherit the properties of the parent class

  • id serial port id, if serial port 0 is enabled, id is 0, if serial port 1 is enabled, id is 1, and so on

  • baudrate baud rate

  • databits data bits

  • stopbits stop bits

  • parity parity

  • fifo_threshold fifo threshold, the maximum value of different mcu is different

  • tx_dma additional send dma handle

  • rx_dma additional receive dma handle

databits provides the following types

typedef enum
{
    UART_DATA_LEN_5 = 0,  /*!< Data length is 5 bits */
    UART_DATA_LEN_6 = 1,  /*!< Data length is 6 bits */
    UART_DATA_LEN_7 = 2,  /*!< Data length is 7 bits */
    UART_DATA_LEN_8 = 3   /*!< Data length is 8 bits */
} uart_databits_t;

stopbits provides the following types

typedef enum
{
    UART_STOP_ONE = 0,  /*!< One stop bit */
    UART_STOP_ONE_D_FIVE = 1,  /*!< 1.5 stop bit */
    UART_STOP_TWO = 2   /*!< Two stop bits */
} uart_stopbits_t;

parity provides the following types

typedef enum
{
    UART_PAR_NONE = 0,  /*!< No parity */
    UART_PAR_ODD  = 1,  /*!< Parity bit is odd */
    UART_PAR_EVEN = 2,  /*!< Parity bit is even */
} uart_parity_t;

UART Device Parameter Configuration Table

Each UART device has a parameter configuration macro, the macro definition is located in the peripheral_config.h file under the bsp/board/xxx directory, and the variable definition is located in hal_uart.c, so the user does not need to define variable. When the user opens the macro of the corresponding device, the configuration of the device will take effect. For example, open the macro BSP_USING_UART0, UART0_CONFIG will take effect, and the UART0 device can be registered and used.

/*Parameter configuration macro*/
#if defined(BSP_USING_UART0)
#ifndef UART0_CONFIG
#define UART0_CONFIG \
{   \
.id = 0, \
.baudrate = 2000000,\
.databits = UART_DATA_LEN_8, \
.stopbits = UART_STOP_ONE, \
.parity = UART_PAR_NONE, \
.fifo_threshold = 1, \
}
#endif
#endif

/*Variable definitions*/
static uart_device_t uartx_device[UART_MAX_INDEX] =
{
#ifdef BSP_USING_UART0
        UART0_CONFIG,
#endif
#ifdef BSP_USING_UART1
        UART1_CONFIG,
#endif
};

备注

The above configuration can be modified through UART_DEV(dev)->xxx and can only be used before calling device_open.

UART Device Interface

UART device interface follows which provided by the standard device driver management layer.

uart_register

uart_register is used to register a UART device standard driver interface. Before registering, you need to open the macro definition of the corresponding UART device. For example, define the macro BSP_USING_UART0 to use the UART0 device. After the registration is completed, other interfaces can be used. If the macro is not defined, the UART0 device cannot be used.

int uart_register(enum uart_index_type index, const char *name);
  • index the index of the device to be registered

  • name device name

index is used to select UART device configuration, one index corresponds to a UART device configuration, such as UART0_INDEX corresponds to UART0_CONFIG configuration, index has the following optional types

enum uart_index_type
{
#ifdef BSP_USING_UART0
    UART0_INDEX,
#endif
#ifdef BSP_USING_UART1
    UART1_INDEX,
#endif
    UART_MAX_INDEX
};
device_open

device_open is used to open a UART device, this funtion calls uart_open actually.

int device_open(struct device *dev, uint16_t oflag);
  • dev device handle

  • oflag open mode

  • return error code, 0 means opening is successful, others mean errors

oflag provides the following types

#define DEVICE_OFLAG_STREAM_TX  0x001 /* The device is turned on in polling sending mode */
#define DEVICE_OFLAG_STREAM_RX  0x002 /* The device is turned on in rotation receiving mode */
#define DEVICE_OFLAG_INT_TX     0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX     0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX     0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX     0x020 /* The device is turned on in DMA receiving mode */
device_close

device_close is used to close a UART device,this funtion calls uart_close actually.

int device_close(struct device *dev);
  • dev device handle

  • return error code, 0 means closing is successful, others mean error

device_control

device_control is used to control and modify the parameters of the UART device according to commands.This funtion calls uart_control actually.

int device_control(struct device *dev, int cmd, void *args);
  • dev device handle

  • cmd device control command

  • args control parameters

  • return different control commands return different meanings.

In addition to standard control commands, UART device also has its own special control commands.

#define DEVICE_CTRL_UART_GET_TX_FIFO        0x10
#define DEVICE_CTRL_UART_GET_RX_FIFO        0x11

args input is different depending on cmd, the list is as follows:

table1

cmd

args

description

DEVICE_CTRL_SET_INT

uart_it_type

Enable uart device interrupt

DEVICE_CTRL_CLR_INT

uart_it_type

Disable uart device interrupt

DEVICE_CTRL_CONFIG

uart_param_cfg_t*

Modify the serial port configuration

DEVICE_CTRL_ATTACH_TX_DMA

NULL

Link to tx dma device

DEVICE_CTRL_ATTACH_RX_DMA

NULL

Link to rx dma device

DEVICE_CTRL_TX_DMA_SUSPEND

NULL

Suspend uart tx dma mode

DEVICE_CTRL_RX_DMA_SUSPEND

NULL

Suspend uart rx dma mode

DEVICE_CTRL_TX_DMA_RESUME

NULL

Resume uart tx dma mode

DEVICE_CTRL_RX_DMA_RESUME

NULL

Resume uart rx dma mode

DEVICE_CTRL_UART_GET_TX_FIFO

uint32_t*

Get the number of uart tx fifo

DEVICE_CTRL_UART_GET_RX_FIFO

uint32_t*

Get the number of uart rx fifo

device_write

device_write is used to send data. The sending mode can be polling, interrupt, dma according to the open mode.This funtion calls uart_write actually.

int device_write(struct device *dev, uint32_t pos, const void *buffer, uint32_t size);
  • dev device handle

  • pos useless

  • buffer the buffer to be written

  • size the length to be written

  • return error code, 0 means writing is successful, others mean errors

device_read

device_read is used to receive data. The receiving mode can be polling, interrupt, dma according to the open mode.This funtion calls uart_read actually.

int device_read(struct device *dev, uint32_t pos, void *buffer, uint32_t size);
  • dev device handle

  • pos useless

  • buffer the buffer to be read

  • size the length to be read

  • return error code, 0 means successful reading, others mean errors

device_set_callback

device_set_callback is used to register a uart interrupt callback function.

int device_set_callback(struct device *dev, void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event));
  • dev device handle

  • callback the interrupt callback function to be registered

    • dev device handle

    • args receive and send buffer, the data type is uint8_t*

    • size transmission length

    • event interrupt event type

event type definition is as follows:

enum uart_event_type
{
    UART_EVENT_TX_END,
    UART_EVENT_TX_FIFO,
    UART_EVENT_RX_END,
    UART_EVENT_RX_FIFO,
    UART_EVENT_RTO,
    UART_EVENT_UNKNOWN
};

PWM

Introduction

PWM is a technology that implements analog voltage control in a digital way. It modulates the width of a series of pulses, equivalent to the required waveform (including shape and amplitude), and digitally encodes the analog signal level. That is to say, by adjusting the change of the duty cycle to adjust the change of the signal, energy, etc. The duty cycle refers to the percentage of the entire signal period when the signal is at a high level. For example, the duty cycle of a square wave is 50%. The DMA device in BL series MCU has the following characteristics:

  • Support 5-channel PWM

  • Three clock sources can be selected (bus clock <bclk>, crystal oscillator clock <xtal_ck>, slow clock <32k>), with 16-bit clock divider

  • Double threshold domain setting, increase pulse flexibility

PWM Device Structure Definition

typedef struct pwm_device {
    struct device parent;
    uint8_t ch;
    uint8_t polarity_invert_mode;
    uint16_t period;
    uint16_t threshold_low;
    uint16_t threshold_high;
    uint16_t it_pulse_count;

} pwm_device_t;
  • parent inherit the properties of the parent class

  • ch channel number, ch is 0 if PWM channel 0 is enabled, ch is 1 if PWM channel 1 is enabled, and so on

  • polarity_invert_mode polarity invert enable

  • period PWM period value

  • threshold_low PWM low threshold

  • threshold_high PWM high threshold

  • it_pulse_count The cycle count value that triggered the interrupt condition

备注

PWM actual frequency = pwm_clock_source/frequency_division/period, period is not the actual PWM period,it just has the same name.

备注

PWM Duty cycle = (threshold_high-threshold_low)/period * 100%

PWM Device Parameter Configuration Table

Each PWM device has a parameter configuration macro, the macro definition is located in the peripheral_config.h file under the bsp/board/xxx directory, and the variable definition is located in hal_pwm.c, so the user does not need to define variable. When the user opens the macro of the corresponding device, the configuration of the device will take effect. For example, open the macro BSP_USING_PWM_CH2, and PWM_CH2_CONFIG will take effect, and at the same time, channel 2 of the PWM device can be registered and used.

/*Parameter configuration macro*/
#if defined(BSP_USING_PWM_CH2)
#ifndef PWM_CH2_CONFIG
#define PWM_CH2_CONFIG                   \
    {                                    \
        .ch = 2,                         \
        .polarity_invert_mode = DISABLE, \
        .period = 0,                     \
        .threshold_low = 0,              \
        .threshold_high = 0,             \
        .it_pulse_count = 0,             \
    }
#endif
#endif


/*Variable definitions*/
static pwm_device_t pwmx_device[PWM_MAX_INDEX] = {
#ifdef BSP_USING_PWM_CH0
    PWM_CH0_CONFIG,
#endif
#ifdef BSP_USING_PWM_CH1
    PWM_CH1_CONFIG,
#endif
#ifdef BSP_USING_PWM_CH2
    PWM_CH2_CONFIG,
#endif
#ifdef BSP_USING_PWM_CH3
    PWM_CH3_CONFIG,
#endif
#ifdef BSP_USING_PWM_CH4
    PWM_CH4_CONFIG,
#endif
};

备注

The above configuration can be modified through PWM_DEV(dev)->xxx, and can only be used before calling device_open.

PWM device interface

The PWM device interfaces all follow the interfaces provided by the standard device driver management layer. And in order to facilitate the user to call, some standard interfaces are redefined using macros.

pwm_register

pwm_register is used to register a channel of the PWM device standard driver interface. Before registering, you need to open the macro definition of a certain channel of the corresponding PWM device. For example, define BSP_USING_PWM_CH0 before you can use the PWM channel 0 device. After the registration is completed, other interfaces can be used. If no macro is defined, the PWM device cannot be used.

int pwm_register(enum pwm_index_type index, const char *name);
  • index the index of the device to be registered

  • name name the registered device

index is used to select the configuration of a certain channel of the PWM device. An index corresponds to a channel configuration of a PWM device. For example, PWM_CH0_INDEX corresponds to the configuration of PWM channel 0, and index has the following optional types:

enum pwm_index_type
{
#ifdef BSP_USING_PWM_CH0
    PWM_CH0_INDEX,
#endif
#ifdef BSP_USING_PWM_CH1
    PWM_CH1_INDEX,
#endif
#ifdef BSP_USING_PWM_CH2
    PWM_CH2_INDEX,
#endif
#ifdef BSP_USING_PWM_CH3
    PWM_CH3_INDEX,
#endif
#ifdef BSP_USING_PWM_CH4
    PWM_CH4_INDEX,
#endif
    PWM_MAX_INDEX
};
device_open

device_open is used to open a channel of a pwm device,this funtion calls pwm_open actually.

int device_open(struct device *dev, uint16_t oflag);
  • dev device handle

  • oflag open mode

  • return error code, 0 means opening is successful, others mean errors

oflag provides the following types

#define DEVICE_OFLAG_STREAM_TX  0x001 /* The device is turned on in polling sending mode */
#define DEVICE_OFLAG_STREAM_RX  0x002 /* The device is turned on in polling receiving mode */
#define DEVICE_OFLAG_INT_TX     0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX     0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX     0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX     0x020 /* The device is turned on in DMA receiving mode */
device_close

device_close is used to close a channel of a pwm device.this funtion calls pwm_close actually.

int device_close(struct device *dev);
  • dev device handle

  • return error code, 0 means closing is successful, others mean error

device_control

device_control is used to control and modify the parameters of the PWM device according to commands.This funtion calls pwm_control actually.

int device_control(struct device *dev, int cmd, void *args);
  • dev device handle

  • cmd device control command

  • args control parameters

  • return different control commands return different meanings

In addition to standard control commands, PWM devices also have their own special control commands.

#define DEIVCE_CTRL_PWM_IT_PULSE_COUNT_CONFIG 0x10

args input is different depending on cmd, the list is as follows:

table1

cmd

args

description

DEVICE_CTRL_RESUME

NULL

Enable the current PWM channel

DEVICE_CTRL_SUSPEND

NULL

Disable the current PWM channel

DEVICE_CTRL_PWM_FREQUENCE_CONFIG

uint32_t

Configure the current PWM channel period

DEVICE_CTRL_PWM_DUTYCYCLE_CONFIG

pwm_dutycycle_config_t

Configure the current PWM channel duty cycle

DEVICE_CTRL_PWM_IT_PULSE_COUNT_CONFIG

uint32_t

Configure the trigger PWM interrupt period value

device_set_callback

device_set_callback is used to register a PWM channel interrupt callback function.

int device_set_callback(struct device *dev, void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event));
  • dev device handle

  • callback the interrupt callback function to be registered

    • dev device handle

    • args unused

    • size unused

    • event interrupt event type

event type definition is as follows:

enum pwm_event_type
{
    PWM_EVENT_COMPLETE,
};
pwm_channel_start

pwm_channel_start is used to start the PWM channel. It actually calls device_control, where cmd is DEVICE_CTRL_RESUME.

pwm_channel_start(dev)
  • dev pwm channel handle that needs to be opened

pwm_channel_stop

pwm_channel_stop is used to close the PWM channel. It actually calls device_control, where cmd is DEVICE_CTRL_SUSPEND.

pwm_channel_stop(dev)
  • dev pwm channel handle that needs to be closed

pwm_channel_update

pwm_channel_update is used to update the frequency and duty cycle of the PWM channel. The actual call is device_control, where cmd is DEVICE_CTRL_CONFIG.

pwm_channel_update(dev,cfg)
  • dev pwm channel handle that needs to be updated

  • cfg pwm_config_t handle

pwm_it_pulse_count_update

pwm_it_pulse_count_update is used to update the count value of the PWM channel. The PWM interrupt needs to be enabled before it takes effect. When the PWM count reaches the set period count value, an interrupt will be generated. The actual call is device_control, where cmd is DEIVCE_CTRL_PWM_IT_PULSE_COUNT_CONFIG.

pwm_it_pulse_count_update(dev,count)
  • dev pwm channel handle that needs to update the cycle count value

  • count cycle count value

DMA

Introduction

DMA is a memory access technology that can directly read and write system memory independently without processor intervention. Under the same degree of processor burden, DMA is a fast data transfer method. The DMA device in BL series MCU has the following characteristics:

  • 8 independent dedicated channels

  • Four transmission directions: memory to memory, memory to peripheral, peripheral to memory, peripheral to peripheral

  • LLI linked list

DMA Device Structure Definition

typedef struct dma_device
{
    struct device parent;
    uint8_t id;
    uint8_t ch;
    uint8_t direction;
    uint8_t transfer_mode;
    uint32_t src_req;
    uint32_t dst_req;
    uint8_t src_burst_size;
    uint8_t dst_burst_size;
    uint8_t src_width;
    uint8_t dst_width;
    dma_lli_ctrl_t *lli_cfg;
} dma_device_t;
  • parent inherits the properties of the parent class

  • id DMA id number, default 0, currently there is only one DMA

  • ch channel number

  • direction transmission direction

  • transfer_mode transfer mode

  • src_req source request

  • dst_req destination request

  • src_burst_size source burst bytes

  • dst_burst_size destination number of burst bytes

  • src_width source transmission bit width

  • dst_width destination transmission bit width

  • lli_cfg used to store some information of the dma channel, the user does not need to worry about it

direction provides the following types

typedef enum {
    DMA_MEMORY_TO_MEMORY = 0,                        /*!< DMA transfer tyep:memory to memory */
    DMA_MEMORY_TO_PERIPH,                            /*!< DMA transfer tyep:memory to peripheral */
    DMA_PERIPH_TO_MEMORY,                            /*!< DMA transfer tyep:peripheral to memory */
    DMA_PERIPH_TO_PERIPH,                            /*!< DMA transfer tyep:peripheral to peripheral */
}dma_transfer_dir_type;

transfer_mode provides the following types

#define DMA_LLI_ONCE_MODE     0
#define DMA_LLI_CYCLE_MODE    1

src_req provides the following types

#define DMA_REQUEST_NONE        0x00000000 /*!< DMA request peripheral:None */
#define DMA_REQUEST_UART0_RX    0x00000000 /*!< DMA request peripheral:UART0 RX */
#define DMA_REQUEST_UART0_TX    0x00000001 /*!< DMA request peripheral:UART0 TX */
#define DMA_REQUEST_UART1_RX    0x00000002 /*!< DMA request peripheral:UART1 RX */
#define DMA_REQUEST_UART1_TX    0x00000003 /*!< DMA request peripheral:UART1 TX */
#define DMA_REQUEST_I2C0_RX     0x00000006 /*!< DMA request peripheral:I2C RX */
#define DMA_REQUEST_I2C0_TX     0x00000007 /*!< DMA request peripheral:I2C TX */
#define DMA_REQUEST_SPI0_RX     0x0000000A /*!< DMA request peripheral:SPI RX */
#define DMA_REQUEST_SPI0_TX     0x0000000B /*!< DMA request peripheral:SPI TX */
#define DMA_REQUEST_I2S_RX      0x00000014 /*!< DMA request peripheral:I2S RX */
#define DMA_REQUEST_I2S_TX      0x00000015 /*!< DMA request peripheral:I2S TX */
#define DMA_REQUEST_ADC0        0x00000016 /*!< DMA request peripheral:ADC0 */
#define DMA_REQUEST_DAC0        0x00000017 /*!< DMA request peripheral:DAC0 */
#define DMA_REQUEST_USB_EP0     0x00000018 /*!< DMA request peripheral:USB EP0*/
#define DMA_REQUEST_USB_EP1     0x00000019 /*!< DMA request peripheral:USB EP1*/
#define DMA_REQUEST_USB_EP2     0x0000001A /*!< DMA request peripheral:USB EP2*/
#define DMA_REQUEST_USB_EP3     0x0000001B /*!< DMA request peripheral:USB EP3*/
#define DMA_REQUEST_USB_EP4     0x0000001C /*!< DMA request peripheral:USB EP4*/
#define DMA_REQUEST_USB_EP5     0x0000001D /*!< DMA request peripheral:USB EP5*/
#define DMA_REQUEST_USB_EP6     0x0000001E /*!< DMA request peripheral:USB EP6*/
#define DMA_REQUEST_USB_EP7     0x0000001F /*!< DMA request peripheral:USB EP7 */

dst_req provides the following types

#define DMA_REQUEST_NONE        0x00000000 /*!< DMA request peripheral:None */
#define DMA_REQUEST_UART0_RX    0x00000000 /*!< DMA request peripheral:UART0 RX */
#define DMA_REQUEST_UART0_TX    0x00000001 /*!< DMA request peripheral:UART0 TX */
#define DMA_REQUEST_UART1_RX    0x00000002 /*!< DMA request peripheral:UART1 RX */
#define DMA_REQUEST_UART1_TX    0x00000003 /*!< DMA request peripheral:UART1 TX */
#define DMA_REQUEST_I2C0_RX     0x00000006 /*!< DMA request peripheral:I2C RX */
#define DMA_REQUEST_I2C0_TX     0x00000007 /*!< DMA request peripheral:I2C TX */
#define DMA_REQUEST_SPI0_RX     0x0000000A /*!< DMA request peripheral:SPI RX */
#define DMA_REQUEST_SPI0_TX     0x0000000B /*!< DMA request peripheral:SPI TX */
#define DMA_REQUEST_I2S_RX      0x00000014 /*!< DMA request peripheral:I2S RX */
#define DMA_REQUEST_I2S_TX      0x00000015 /*!< DMA request peripheral:I2S TX */
#define DMA_REQUEST_ADC0        0x00000016 /*!< DMA request peripheral:ADC0 */
#define DMA_REQUEST_DAC0        0x00000017 /*!< DMA request peripheral:DAC0 */
#define DMA_REQUEST_USB_EP0     0x00000018 /*!< DMA request peripheral:USB EP0*/
#define DMA_REQUEST_USB_EP1     0x00000019 /*!< DMA request peripheral:USB EP1*/
#define DMA_REQUEST_USB_EP2     0x0000001A /*!< DMA request peripheral:USB EP2*/
#define DMA_REQUEST_USB_EP3     0x0000001B /*!< DMA request peripheral:USB EP3*/
#define DMA_REQUEST_USB_EP4     0x0000001C /*!< DMA request peripheral:USB EP4*/
#define DMA_REQUEST_USB_EP5     0x0000001D /*!< DMA request peripheral:USB EP5*/
#define DMA_REQUEST_USB_EP6     0x0000001E /*!< DMA request peripheral:USB EP6*/
#define DMA_REQUEST_USB_EP7     0x0000001F /*!< DMA request peripheral:USB EP7 */

src_burst_size provides the following types

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

dst_burst_size provides the following types

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

src_width provides the following types

#define DMA_TRANSFER_WIDTH_8BIT  0
#define DMA_TRANSFER_WIDTH_16BIT 1
#define DMA_TRANSFER_WIDTH_32BIT 2

dst_width: provide the following types

#define DMA_TRANSFER_WIDTH_8BIT  0
#define DMA_TRANSFER_WIDTH_16BIT 1
#define DMA_TRANSFER_WIDTH_32BIT 2

DMA Device Parameter Configuration Table

Each DMA device has a parameter configuration macro, the macro definition is located in the peripheral_config.h file under the bsp/board/xxx directory, and the variable definition is located in hal_dma.c, so there is no need for the user himself Define variables. When the user opens the macro of the corresponding device, the configuration of the device will take effect. For example, open the macro BSP_USING_DMA0_CH0, DMA0_CH0_CONFIG will take effect, and the DMA channel 0 device can be registered and used.

/*Parameter configuration macro*/
#if defined(BSP_USING_DMA0_CH0)
#ifndef DMA0_CH0_CONFIG
#define DMA0_CH0_CONFIG \
{   \
 .id = 0, \
 .ch = 0,\
 .direction = DMA_MEMORY_TO_MEMORY,\
 .transfer_mode = DMA_LLI_ONCE_MODE, \
 .src_req = DMA_REQUEST_NONE, \
 .dst_req = DMA_REQUEST_NONE, \
 .src_width = DMA_TRANSFER_WIDTH_32BIT , \
 .dst_width = DMA_TRANSFER_WIDTH_32BIT , \
}
#endif
#endif


/*Variable definitions*/
static dma_device_t dmax_device[DMA_MAX_INDEX] =
{
#ifdef BSP_USING_DMA0_CH0
    DMA0_CH0_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH1
    DMA0_CH1_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH2
    DMA0_CH2_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH3
    DMA0_CH3_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH4
    DMA0_CH4_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH5
    DMA0_CH5_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH6
    DMA0_CH6_CONFIG,
#endif
#ifdef BSP_USING_DMA0_CH7
    DMA0_CH7_CONFIG,
#endif
};

备注

The above configuration can be modified through DMA_DEV(dev)->xxx and can only be used before calling device_open.

DMA Device Interface

The DMA device interface follows which provided by the standard device driver management layer. In order to facilitate the user to call, some standard interfaces are redefined using macros.

dma_register

dma_register is used to register a DMA device standard driver interface. Before registering, you need to open the channel macro definition of the corresponding DMA device. For example, after defining the macro BSP_USING_DMA_CH0, the 0 channel of the DMA device can be used. After the registration is completed, other interfaces can be used. If the macro is not defined, the 0 channel of the DMA device cannot be used.

int dma_register(enum dma_index_type index, const char *name);
  • index device index to be registered

  • name device name to be registered

index is used to select the configuration of a certain channel of DMA, an index corresponds to a channel configuration of a DMA, for example, DMA_CH0_INDEX corresponds to the configuration of DMA channel 0, and index has the following optional types

enum dma_index_type
{
#ifdef BSP_USING_DMA0_CH0
    DMA0_CH0_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH1
    DMA0_CH1_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH2
    DMA0_CH2_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH3
    DMA0_CH3_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH4
    DMA0_CH4_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH5
    DMA0_CH5_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH6
    DMA0_CH6_INDEX,
#endif
#ifdef BSP_USING_DMA0_CH7
    DMA0_CH7_INDEX,
#endif
    DMA_MAX_INDEX
};
device_open

device_open is used to open a channel of a dma device,this funtion calls dma_open actually.

int device_open(struct device *dev, uint16_t oflag);
  • dev device handle

  • oflag open mode

  • return Error code, 0 means opening is successful, other means error

oflag provides the following types

#define DEVICE_OFLAG_STREAM_TX  0x001 /* The device is turned on in polling sending mode */
#define DEVICE_OFLAG_STREAM_RX  0x002 /* The device is turned on in polling receiving mode */
#define DEVICE_OFLAG_INT_TX     0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX     0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX     0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX     0x020 /* The device is turned on in DMA receiving mode */
device_close

device_close is used to close a channel of a dma device,this funtion calls dma_close actually.

int device_close(struct device *dev);
  • dev device handle

  • return error code, 0 means closing is successful, others mean error

device_control

device_control is used to control and modify the parameters of the dma device according to commands.This funtion calls dma_control actually.

int device_control(struct device *dev, int cmd, void *args);
  • dev device handle

  • cmd device control command

  • args control parameters

  • return different control commands return different meanings

In addition to standard control commands, DMA devices also have their own special control commands.

#define DMA_CHANNEL_GET_STATUS  0x10
#define DMA_CHANNEL_START       0x11
#define DMA_CHANNEL_STOP        0x12
#define DMA_CHANNEL_UPDATE      0x13

args input is different depending on cmd, the list is as follows:

table1

cmd

args

description

DEVICE_CTRL_SET_INT

NULL

Enable DMA transfer completion interrupt

DEVICE_CTRL_CLR_INT

NULL

Disable DMA transfer completion interrupt

DMA_CHANNEL_GET_STATUS

NULL

Get DMA channel completion status

DMA_CHANNEL_START

NULL

Open dma channel

DMA_CHANNEL_STOP

NULL

Close dma channel

DMA_CHANNEL_UPDATE

NULL

Update dma transmission configuration

device_set_callback

device_set_callback is used to register a DMA channel interrupt callback function.

int device_set_callback(struct device *dev, void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event));
  • dev Device handle

  • callback The interrupt callback function to be registered

    • dev device handle

    • args unused

    • size unused

    • event interrupt event type

event type definition is as follows:

enum dma_event_type
{
    DMA_EVENT_COMPLETE,
};
dma_channel_start

dma_channel_start is used to open the DMA channel. It actually calls device_control, where cmd is DMA_CHANNEL_START.

dma_channel_start(dev)
  • dev dma channel handle that needs to be opened

dma_channel_stop

dma_channel_stop is used to close the DMA channel. It actually calls device_control, where cmd is DMA_CHANNEL_STOP.

dma_channel_stop(dev)
  • dev dma channel handle that needs to be closed

dma_channel_update

dma_channel_update is used to update the DMA configuration. The actual call is device_control, where cmd is DMA_CHANNEL_UPDATE.

dma_channel_update(dev,list)
  • dev dma channel handle that needs to be updated

  • list dma_lli_ctrl_t handle

dma_channel_check_busy

dma_channel_check_busy is used to query whether the currently used DMA channel has completed the transfer. It actually calls device_control, where cmd is DMA_CHANNEL_GET_STATUS.

dma_channel_check_busy(dev)
  • dev DMA channel handle to be queried

  • return return the current DMA status, 0 means the transfer is complete, 1 means the transfer is not complete

dma_reload

dma_reload is used to update the configuration of a certain channel of DMA. Compared with dma_channel_update, this function does not require the user to pass many parameters, but only needs to fill in the source address, destination address, and length. After this function is called, the DMA channel is not turned on. You need to manually call the dma_channel_start function.

int dma_reload(struct device *dev, uint32_t src_addr, uint32_t dst_addr, uint32_t transfer_size);
  • dev DMA channel handle to be queried

  • src_addr transmission source address

  • dst_addr transmission destination address

  • transfer_size the total length of transferred bytes. If the number of bits transferred is 16 bits or 32 bits, it needs to be converted into byte length here.

I2C

Introduction

I2C (Inter-Intergrated Circuit) is a serial communication bus that uses a multi-master-slave architecture to connect low-speed peripheral devices. Each device has a unique address identification, and can be used as a transmitter or receiver. Each device connected to the bus can set the address with software through a unique address and the always-existing relationship between master and slave, and the host can be used as a host transmitter or host receiver. If two or more hosts are initialized at the same time, data transmission can prevent data from being destroyed through conflict detection and arbitration. The I2C devices in the BL series MCU have the following characteristics:

  • Flexible configuration of slave address slaveAddr, register address subAddr

  • Clock frequency that can be flexibly adjusted

  • Support polling, interrupt, DMA transfer

I2C Device Structure Definition

typedef struct i2c_device
{
    struct device parent;
    uint8_t id;
    uint8_t mode;
    uint32_t phase;
} i2c_device_t;
  • parent inherit the properties of the parent class

  • ch i2c id, 0 means i2c0, 1 means i2c1

  • mode i2c transmission mode, 0 means using hardware i2c, 1 means using software i2c, current software i2c is temporarily invalid

  • phase i2c clock phase div, i2c_clk = i2c_source_clk/(4*(phase+1))

  • TODO

I2C Device Parameter Configuration Table

Each I2C device has a parameter configuration macro, the macro definition is located in the peripheral_config.h file under the bsp/board/xxx directory, and the variable definition is located in hal_i2c.c, so the user does not need to define variable. When the user opens the macro of the corresponding device, the configuration of the device will take effect. For example, open the macro BSP_USING_I2C0, I2C0_CONFIG will take effect, and the I2C device can be registered and used.

/*Parameter configuration macro*/
#if defined(BSP_USING_I2C0)
#ifndef I2C0_CONFIG
#define I2C0_CONFIG \
{   \
.id = 0, \
.mode = I2C_HW_MODE,\
.phase = 15, \
}
#endif
#endif

/*Variable definition*/
static i2c_device_t i2cx_device[I2C_MAX_INDEX] =
{
#ifdef BSP_USING_I2C0
    I2C0_CONFIG,
#endif
};

备注

The above configuration can be modified through I2C_DEV(dev)->xxx and can only be used before calling device_open.

I2C Device Interface

The I2C device standard interface currently only uses device_open, and provides a standard data transceiver interface.

i2c_register

i2c_register is used to register an I2C device standard driver interface. The macro definition of the corresponding I2C device needs to be opened before registration. For example, define the macro BSP_USING_I2C0 to use the I2C0 device. After the registration is completed, other interfaces can be used. If the macro is not defined, the I2C0 device cannot be used.

int i2c_register(enum i2c_index_type index, const char *name);
  • index device index to be registered

  • name device name to be registered

index is used to select I2C device, one index corresponds to one I2C device configuration, for example, I2C0_INDEX corresponds to I2C0_CONFIG configuration, index has the following optional types

enum i2c_index_type
{
#ifdef BSP_USING_I2C0
    I2C0_INDEX,
#endif
    I2C_MAX_INDEX
};
device_open

device_open is used to open an i2c device, this funtion calls i2c_open actually.

int device_open(struct device *dev, uint16_t oflag);
  • dev device handle

  • oflag open mode

  • return error code, 0 means opening is successful, others mean errors

oflag provides the following types

#define DEVICE_OFLAG_STREAM_TX  0x001 /* The device is turned on in polling sending mode */
#define DEVICE_OFLAG_STREAM_RX  0x002 /* The device is turned on in polling receiving mode */
#define DEVICE_OFLAG_INT_TX     0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX     0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX     0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX     0x020 /* The device is turned on in DMA receiving mode */
i2c_transfer

i2c_transfer is used to transfer i2c msg. The member flags in i2c_msg_t structure indicates whether the direction of the transfer is writing or reading, and the length of the specified register address is 0, 1, 2.

int i2c_transfer(struct device *dev, i2c_msg_t msgs[], uint32_t num);
  • dev device handle

  • msgs message to be transmitted

  • num the number of messages

  • return error code, 0 means opening is successful, others mean error

i2c_msg_t structure is defined as follows:

typedef struct i2c_msg
{
    uint8_t slaveaddr;
    uint32_t subaddr;
    uint16_t flags;
    uint16_t len;
    uint8_t *buf;
} i2c_msg_t;
  • slaveaddr i2c slave device 7-bit slave address

  • subaddr i2c slave device register address

  • flags read and write mode and register address length

  • len transmission data length

  • buf data buffer

flags definition is as follows:

/*Read and write mode*/
#define I2C_WR 0x0000
#define I2C_RD 0x0001

/*Register address length*/
#define SUB_ADDR_0BYTE 0x0010
#define SUB_ADDR_1BYTE 0x0020
#define SUB_ADDR_2BYTE 0x0040

SPI

Introduction

Serial Peripheral Interface Bus (SPI) is a synchronous serial communication interface specification for short-range communication. The full-duplex communication mode is used between devices, which is a master-slave mode of one master and one or more slaves, and requires at least 4 wires. In fact, 3 wires are also available (when transmitting in one direction), including SDI (data input), SDO (data output), SCLK (clock), CS (chip select). The SPI devices in the BL series MCU have the following characteristics:

  • It can be used as both SPI master and SPI slave.

  • The transmit and receive channels each have a FIFO with a depth of 4 words

  • Both master and slave devices support 4 clock formats (CPOL, CPHA)

  • Both master and slave devices support 1/2/3/4 byte transmission mode

  • Flexible clock configuration, up to 40M clock

  • Configurable MSB/LSB priority transmission

  • Receive filter function

  • Timeout mechanism under slave device

  • Support polling, interrupt, DMA transfer

SPI Device Structure Definition

typedef struct spi_device
{
    struct device parent;
    uint8_t id;
    uint32_t clk;
    uint8_t mode;
    uint8_t direction;
    uint8_t clk_polaraity;
    uint8_t clk_phase;
    uint8_t datasize;
    uint8_t fifo_threshold;
    void* tx_dma;
    void* rx_dma;
} spi_device_t;
  • parent inherit the properties of the parent class

  • id SPI id, 0 means SPI0

  • clk SPI clock frequency

  • mode master mode or slave mode

  • direction transmission first mode

  • clk_polaraity clock polarity

  • clk_phase clock phase

  • datasize data transmission bit width

  • fifo_threshold fifo threshold, the maximum is 4

  • tx_dma additional send dma handle

  • rx_dma dditional receive dma handle

mode provides the following types

#define SPI_SLVAE_MODE                                0
#define SPI_MASTER_MODE                               1

direction provides the following types

#define SPI_LSB_BYTE0_DIRECTION_FIRST 0
#define SPI_LSB_BYTE3_DIRECTION_FIRST 1
#define SPI_MSB_BYTE0_DIRECTION_FIRST 2
#define SPI_MSB_BYTE3_DIRECTION_FIRST 3

clk_polaraity provides the following types

#define SPI_POLARITY_LOW                              0
#define SPI_POLARITY_HIGH                             1

clk_phase provides the following types

#define SPI_PHASE_1EDGE                               0
#define SPI_PHASE_2EDGE                               1

datasize provides the following types

#define SPI_DATASIZE_8BIT                            0
#define SPI_DATASIZE_16BIT                           1
#define SPI_DATASIZE_24BIT                           2
#define SPI_DATASIZE_32BIT                           3

SPI Device Parameter Configuration Table

Each SPI device has a parameter configuration macro, the macro definition is located in the peripheral_config.h file under the bsp/board/xxx directory, and the variable definition is located in hal_spi.c, so the user does not need to define variable. When the user opens the macro of the corresponding device, the configuration of the device will take effect. For example, open the macro BSP_USING_SPI0, SPI0_CONFIG will take effect, and the SPI0 device can be registered and used.

/*Parameter configuration macro*/
#if defined(BSP_USING_SPI0)
#ifndef SPI0_CONFIG
#define SPI0_CONFIG \
{   \
.id = 0, \
.clk = 18000000,\
.mode = SPI_MASTER_MODE, \
.direction = SPI_MSB_BYTE0_DIRECTION_FIRST, \
.clk_polaraity = SPI_POLARITY_LOW, \
.clk_phase = SPI_PHASE_1EDGE, \
.datasize = SPI_DATASIZE_8BIT, \
.fifo_threshold = 1, \
}
#endif
#endif

/*Variable definition*/
static spi_device_t spix_device[SPI_MAX_INDEX] =
{
#ifdef BSP_USING_SPI0
    SPI0_CONFIG,
#endif
};

备注

The above configuration can be modified through SPI_DEV(dev)->xxx and can only be used before calling device_open.

SPI Device Interface

SPI device interface follows which provided by the standard device driver management layer.

spi_register

spi_register is used to register an SPI device standard driver interface. Before registering, you need to open the macro definition of the corresponding SPI device. For example, define the macro BSP_USING_SPI0 before you can use the SPI0 device. After the registration is completed, other interfaces can be used. If no macro is defined, the SPI device cannot be used.

int spi_register(enum spi_index_type index, const char *name);
  • index device index to be registered

  • name device name to be registered

index is used to select SPI device configuration, one index corresponds to one SPI device configuration, for example, SPI0_INDEX corresponds to SPI0_CONFIG configuration, and index has the following optional types

enum spi_index_type
{
#ifdef BSP_USING_SPI0
    SPI0_INDEX,
#endif
    SPI_MAX_INDEX
};
device_open

device_open is used to open the device,this funtion calls spi_open actually.

int device_open(struct device *dev, uint16_t oflag);
  • dev device handle

  • oflag open mode

  • return error code, 0 means opening is successful, others mean errors

oflag provides the following types

#define DEVICE_OFLAG_STREAM_TX  0x001 /* The device is turned on in polling sending mode */
#define DEVICE_OFLAG_STREAM_RX  0x002 /* The device is turned on in polling receiving mode */
#define DEVICE_OFLAG_INT_TX     0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX     0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX     0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX     0x020 /* The device is turned on in DMA receiving mode */
device_close

device_close is used to close the device,this funtion calls spi_close actually.

int device_close(struct device *dev);
  • dev device handle

  • return error code, 0 means closing is successful, others means error

device_control

device_control is used to control the device and modify parameters according to commands.This funtion calls spi_control actually.

int device_control(struct device *dev, int cmd, void *args);
  • dev device handle

  • cmd device control command

  • args control parameters

  • return Different control commands return different meanings.

In addition to standard control commands, SPI devices also have their own special control commands.

#define DEVICE_CTRL_SPI_CONFIG_CLOCK       0x10

args input is different depending on cmd, the list is as follows:

table1

cmd

args

description

DEVICE_CTRL_SET_INT

NULL

Enable spi device interrupt

DEVICE_CTRL_CLR_INT

NULL

Disable spi device interrupt

DEVICE_CTRL_RESUME

NULL

Resume spi device

DEVICE_CTRL_SUSPEND

NULL

Suspend spi device

DEVICE_CTRL_ATTACH_TX_DMA

NULL

Link to tx dma device

DEVICE_CTRL_ATTACH_RX_DMA

NULL

Link to rx dma device

DEVICE_CTRL_SPI_CONFIG_CLOCK

NULL

Modify SPI device clock

DEVICE_CTRL_TX_DMA_SUSPEND

NULL

Suspend spi tx dma mode

DEVICE_CTRL_RX_DMA_SUSPEND

NULL

Suspend spi rx dma mode

DEVICE_CTRL_TX_DMA_RESUME

NULL

Resume spi tx dma mode

DEVICE_CTRL_RX_DMA_RESUME

NULL

Resume spi rx dma mode

device_write

device_write is used to send data. The sending mode can be polling, interrupt, dma according to the open mode.This funtion calls spi_write actually.

int device_write(struct device *dev, uint32_t pos, const void *buffer, uint32_t size);
  • dev device handle

  • pos useless

  • buffer the buffer to be written

  • size the length to be written

  • return error code, 0 means writing is successful, others mean errors

device_read

device_read is used to receive data, and the receiving mode can be polling, interrupt, dma according to the open mode.This funtion calls spi_read actually.

int device_read(struct device *dev, uint32_t pos, void *buffer, uint32_t size);
  • dev device handle

  • pos useless

  • buffer the buffer to be read

  • size the length to be read

  • return error code, 0 means successful reading, others mean errors

device_set_callback

device_set_callback is used to register an SPI device interrupt callback function.

int device_set_callback(struct device *dev, void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event));
  • dev device handle

  • callback the interrupt callback function to be registered

    • dev device handle

    • args receive and send buffer, the data type is uint8_t*

    • size transmission length

    • event interrupt event type

event type definition is as follows:

enum spi_event_type
{
    SPI_EVENT_TX_FIFO,
    SPI_EVENT_RX_FIFO,
    SPI_EVENT_UNKNOWN
};
spi_transmit

spi_transmit is used to send data from SPI devices.

int spi_transmit(struct device *dev, void *buffer, uint32_t size, uint8_t type);
  • dev device handle

  • buffer send data buffer

  • size send length

  • type send bit width type

type provides the following types

#define SPI_TRANSFER_TYPE_8BIT    0
#define SPI_TRANSFER_TYPE_16BIT   1
#define SPI_TRANSFER_TPYE_24BIT   2
#define SPI_TRANSFER_TYPE_32BIT   3
spi_receive

spi_receive is used to receive data from SPI devices.

int spi_receive(struct device *dev, void *buffer, uint32_t size, uint8_t type);
  • dev device handle

  • buffer receive data buffer

  • size receiving length

  • type bit width type

spi_transmit_receive

spi_transmit_receive is used to send and receive data from SPI devices.

int spi_transmit_receive(struct device *dev, const void *send_buf, void *recv_buf, uint32_t length, uint8_t type);
  • dev device handle

  • send_buf send data buffer

  • recv_buf receive data buffer

  • length send and receive length

  • type bit width type

ADC

Introduction

ADC (Analog-to-digital Converter) can convert continuous analog signals into discrete digital signals.

The ADC module in BL MCU series has the following characteristics:

  • Support selecting 12/14/16 bits conversion result output

  • ADC maximum working clock is 2MHZ

  • Support 2.0V, 3.2V optional internal reference voltage

  • DMA support

  • Support four modes: single channel single conversion, continuous single channel conversion, single multi-channel conversion and continuous multi-channel conversion mode

  • Support both single-ended and differential input modes

  • 12 external analog channels

  • 2 DAC internal channels

  • 1 VBAT /2 channel

ADC Device Structure Definition

typedef struct adc_device {
    struct device parent;
    adc_clk_div_t clk_div;
    adc_vref_t vref;
    bool continuous_conv_mode;
    bool differential_mode;
    adc_data_width_t data_width;
    adc_fifo_threshold_t fifo_threshold;
    adc_pga_gain_t gain;
} adc_device_t;
  • parent inherits the properties of the parent class

  • clk_div Partial frequency clock in ADC module

  • vref 2.0/3.2V reference voltage optional

  • continuous_conv_mode Whether to select continuous mode. If it is in continuous mode, once adc_start operation, ADC will continue to work until adc_stop. If it is not in continuous mode, adc will only convert the result once every adc_start.

  • differential_mode Whether it is in differential mode, if it is in differential mode, negative voltage can be measured.

  • data_width Measurement width selection, the actual accuracy of ADC is 12 bits, but the accuracy of 14bits / 16bits can be achieved by averaging multiple times through OSR. Note that when a higher data width is selected, the frequency will decrease due to averaging. For details, please refer to the enumeration information.

  • fifo_threshold This parameter affects the DMA handling threshold and ADC FIFO interrupt threshold

  • gain ADC gain selection for input signal

  • Others to be added

ADC Device Parameter Configuration Table

Each ADC has a parameter configuration macro, the macro definition is located in the peripheral_config.h file under the bsp/board/xxx directory, and the variable definition is located in hal_adc.c, so there is no need for the user to define it by himself . When the user opens the macro of the corresponding device, the configuration of the device will take effect. For example, open the macro BSP_USING_ADC0 to take effect, and at the same time, the ADC device can be registered and used.

/*Parameter configuration macro*/
#if defined(BSP_USING_ADC0)
#ifndef ADC0_CONFIG
#define ADC0_CONFIG                                        \
    {                                                      \
        .clk_div = ADC_CLOCK_DIV_32,                       \
        .vref = ADC_VREF_3P2V,                             \
        .continuous_conv_mode = DISABLE,                   \
        .differential_mode = DISABLE,                      \
        .data_width = ADC_DATA_WIDTH_16B_WITH_256_AVERAGE, \
        .fifo_threshold = ADC_FIFO_THRESHOLD_1BYTE,        \
        .gain = ADC_GAIN_1                                 \
    }
#endif
#endif

/*Variable definitions*/
static adc_device_t adcx_device[ADC_MAX_INDEX] = {
#ifdef BSP_USING_ADC0
    ADC0_CONFIG,
#endif
};

备注

The above configuration can be modified through ADC_DEV(dev)->xxx and can only be used before device_open.

ADC Device Interface

ADC device interfaces all follow the interfaces provided by the standard device driver management layer.

adc_register

adc_register is used to register an ADC device standard driver interface.Before registering, you need to open the macro definition of the corresponding ADC device. For example, define the macro BSP_USING_ADC0 to use the ADC0 device. After the registration is completed, other interfaces can be used. If no macro is defined, the ADC0 device cannot be used.

int adc_register(enum adc_index_type index, const char *name);
  • index device index to be registered

  • name device name to be registered

index is used to select ADC device configuration, one index corresponds to one ADC device configuration, for example, ADC0_INDEX corresponds to ADC0_CONFIG configuration. index has the following optional types:

enum adc_index_type
{
#ifdef BSP_USING_ADC0
    ADC0_INDEX,
#endif
    ADC_MAX_INDEX
};
device_open

device_open is used to open an ADC device,this funtion calls adc_open actually.

int device_open(struct device *dev, uint16_t oflag);
  • dev device handle

  • oflag open mode

  • return Error code, 0: open successfully, others: error

oflag provides the following types

#define DEVICE_OFLAG_STREAM_TX  0x001 /* The device is turned on in rotation sending mode */
#define DEVICE_OFLAG_STREAM_RX  0x002 /* The device is turned on in rotation receiving mode */
#define DEVICE_OFLAG_INT_TX     0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX     0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX     0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX     0x020 /* The device is turned on in DMA receiving mode */
device_close

device_close is used to close an ADC device,this funtion calls adc_close actually.

int device_close(struct device *dev);
  • dev device handle

  • return Error code, 0: open successfully, others: error

device_control

device_control is used to control and modify the parameters of the adc device according to commands.This funtion calls adc_control actually.

int device_control(struct device *dev, int cmd, void *args);
  • dev Device handle

  • cmd Device control commands

  • args Control parameter

  • return Different control commands return different meanings.

In addition to standard control commands, serial devices also have their own special control commands.

#define DEVICE_CTRL_ADC_CHANNEL_START  0x10
#define DEVICE_CTRL_ADC_CHANNEL_STOP   0x11
#define DEVICE_CTRL_ADC_CHANNEL_CONFIG 0x12
#define DEVICE_CTRL_ADC_VBAT_ON        0x13
#define DEVICE_CTRL_ADC_VBAT_OFF       0x14
#define DEVICE_CTRL_ADC_TSEN_ON        0x15
#define DEVICE_CTRL_ADC_TSEN_OFF       0x16

args input is different depending on cmd, the list is as follows:

table1

cmd

args

description

DEVICE_CTRL_SET_INT

adc_it_type

Enable ADC device interrupt

DEVICE_CTRL_CLR_INT

adc_it_type

Disable ADC device interrupt

DEVICE_CTRL_CONFIG

ADC_param_cfg_t

Modify ADC configuration

DEVICE_CTRL_ADC_CHANNEL_CONFIG

adc_channel_cfg_t

Modify ADC channel configuration

DEVICE_CTRL_ATTACH_RX_DMA

struct device*

Link receiving DMA device

DEVICE_CTRL_ADC_CHANNEL_START

NULL

Start/continue ADC conversion

DEVICE_CTRL_ADC_CHANNEL_STOP

NULL

Stop ADC conversion

DEVICE_CTRL_ADC_VBAT_ON

NULL

Turn on the internal VDD measurement circuit

DEVICE_CTRL_ADC_VBAT_OFF

NULL

Turn off the internal VDD measurement circuit

DEVICE_CTRL_ADC_TSEN_ON

NULL

Turn on the internal temperature measurement circuit (requires hardware support)

DEVICE_CTRL_ADC_TSEN_OFF

NULL

Turn off the internal temperature measurement circuit (requires hardware support)

device_read

device_read is used to receive the data of ADC device, the receiving mode can be polling, interrupt, dma.

int device_read(struct device *dev, uint32_t pos, void *buffer, uint32_t size);
  • dev Dvice handle

  • pos No effect

  • buffer Buffer to read

  • size Length to read

  • return Error code, 0: open successfully, others: error

device_set_callback

device_set_callback is used to register an ADC threshold interrupt callback function.

int device_set_callback(struct device *dev, void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event));
  • dev Device handle

  • callback The interrupt callback function to be registered

    • dev Device handle

    • args Receive and send buffer, the data type is uint8_t*

    • size Transmission length

    • event Type of interrupt event

event type is as follows:

enum ADC_event_type
{
    ADC_EVENT_FIFO_READY,
    ADC_EVENT_OVERRUN,
    ADC_EVENT_UNDERRUN,
};

DAC

Introduction

DAC Structure Definition

DAC Parameter Configuration Table

DAC Device Interface

TIMER

Introduction

The TIMER device in BL series MCU has the following characteristics:

  • Multiple clock source options

  • 8-bit clock divider, the division factor is 1-256

  • Two 32-bit timers

  • Each timer can independently set three groups of alarm values

  • Support FreeRun mode and PreLoad mode

  • 16-bit watchdog

  • Support write protection to prevent system abnormalities caused by incorrect settings

  • Support two watchdog overflow modes, interrupt or reset

TIMER Device Structure Definition

typedef struct timer_device {
    struct device parent;
    uint8_t id;
    enum timer_cnt_mode_type cnt_mode;
    enum timer_preload_trigger_type trigger;
    uint32_t reload;
    uint32_t timeout1;
    uint32_t timeout2;
    uint32_t timeout3;
} timer_device_t;
  • parent inherit the properties of the parent class

  • id timer id

  • cnt_mode counting mode:FreeRun and preload

  • trigger source of preload comparator

  • reload reload value in preload mode

  • timeout1 compare source 0 timout value ,unit is us

  • timeout2 compare source 1 timout value ,unit is us

  • timeout3 compare source 2 timout value ,unit is us

ch provides the following types

enum timer_index_type {
    TIMER0_INDEX,
    TIMER1_INDEX,
    TIMER_MAX_INDEX
};

cnt_mode provides the following types

enum timer_cnt_mode_type {
    TIMER_CNT_PRELOAD,
    TIMER_CNT_FREERUN,
};

pl_trig_src provides the following types

enum timer_preload_trigger_type {
    TIMER_PRELOAD_TRIGGER_NONE,
    TIMER_PRELOAD_TRIGGER_COMP0,
    TIMER_PRELOAD_TRIGGER_COMP1,
    TIMER_PRELOAD_TRIGGER_COMP2,
};

TIMER Device Parameter Configuration Table

Each TIMER has a parameter configuration macro, the macro definition is located in the peripheral_config.h file under the bsp/board/xxx directory, and the variable definition is located in hal_timer.c, so the user does not need to define the variable . When the user opens the macro of the corresponding device, the configuration of the device will take effect. For example, open the macro BSP_USING_TIMER_CH0, TIMER_CH0_CONFIG will take effect, and the TIMER_CH0_INDEX device can be registered and used.

/*Parameter configuration macro*/
#if defined(BSP_USING_TIMER0)
#ifndef TIMER0_CONFIG
#define TIMER0_CONFIG                           \
    {                                           \
        .id = 0,                                \
        .cnt_mode = TIMER_CNT_PRELOAD,          \
        .trigger = TIMER_PRELOAD_TRIGGER_COMP2, \
        .reload = 0,                            \
        .timeout1 = 1000000,                    \
        .timeout2 = 2000000,                    \
        .timeout3 = 3000000,                    \
    }
#endif
#endif

#if defined(BSP_USING_TIMER1)
#ifndef TIMER1_CONFIG
#define TIMER1_CONFIG                           \
    {                                           \
        .id = 1,                                \
        .cnt_mode = TIMER_CNT_PRELOAD,          \
        .trigger = TIMER_PRELOAD_TRIGGER_COMP0, \
        .reload = 0,                            \
        .timeout1 = 1000000,                    \
        .timeout2 = 2000000,                    \
        .timeout3 = 3000000,                    \
    }
#endif
#endif

/*Variable definitions*/
static timer_device_t timerx_device[TIMER_MAX_INDEX] = {
#ifdef BSP_USING_TIMER0
    TIMER0_CONFIG,
#endif
#ifdef BSP_USING_TIMER1
    TIMER1_CONFIG,
#endif
};

备注

The above configuration can be modified through TIMER_DEV(dev)->xxx and can only be used before calling device_open.

TIMER Device Interface

TIMER device interface follows which provided by the standard device driver management layer.

timer_register

timer_register is used to register a TIMER device standard driver interface. Before registering, you need to open the macro definition of the corresponding TIMER device. For example, define the macro BSP_USING_TIMER_CH0 to use the TIMER_CH0_INDEX device. After the registration is completed, other interfaces can be used. If the macro is not defined, the TIMER_CH0_INDEX device cannot be used.

int timer_register(enum timer_index_type index, const char *name);
  • index the index of the device to be registered

  • name Name the device

index is used to select TIMER device configuration, one index corresponds to a TIMER device configuration, for example, TIMER_CH0_INDEX corresponds to TIMER_CH0_CONFIG configuration, and index has the following optional types

enum timer_index_type {
#ifdef BSP_USING_TIMER0
    TIMER0_INDEX,
#endif
#ifdef BSP_USING_TIMER1
    TIMER1_INDEX,
#endif
    TIMER_MAX_INDEX
};
device_open

device_open is used to open a TIMER device, this funtion calls timer_open actually.

int device_open(struct device *dev, uint16_t oflag);
  • dev device handle

  • oflag open method

  • return error code, 0 means opening is successful, others mean error

oflag provides the following types

#define DEVICE_OFLAG_STREAM_TX  0x001 /* The device is turned on in polling sending mode */
#define DEVICE_OFLAG_STREAM_RX  0x002 /* The device is turned on in polling receiving mode */
#define DEVICE_OFLAG_INT_TX     0x004 /* The device is turned on in interrupt sending mode */
#define DEVICE_OFLAG_INT_RX     0x008 /* The device is turned on in interrupt receiving mode */
#define DEVICE_OFLAG_DMA_TX     0x010 /* The device is turned on in DMA transmission mode */
#define DEVICE_OFLAG_DMA_RX     0x020 /* The device is turned on in DMA receiving mode */
device_close

device_close is used to close a TIMER device,this funtion calls timer_open actually.

int device_close(struct device *dev);
  • dev device handle

  • return error code, 0 means closing is successful, others mean error

device_control

device_control is used to control and modify the parameters of the TIMER device according to commands.This funtion calls timer_control actually.

int device_control(struct device *dev, int cmd, void *args);
  • dev device handle

  • cmd device control command

  • args control parameters

  • return different control commands return different meanings

In addition to standard control commands, TIMER device also has its own special control commands.

#define DEVICE_CTRL_TIMER_CH_START   0x80
#define DEVICE_CTRL_TIMER_CH_STOP    0x81
#define DEVICE_CTRL_GET_MATCH_STATUS 0x82

args input is different depending on cmd, the list is as follows:

table1

cmd

args

description

DEVICE_CTRL_SET_INT

timer_it_type

Enable TIMER interrupt

DEVICE_CTRL_CLR_INT

timer_it_type

Disable TIMER interrupt

DEVICE_CTRL_GET_INT

NULL

Get TIMER interrupt status

DEVICE_CTRL_RESUME

NULL

Enable TIMER

DEVICE_CTRL_SUSPEND

NULL

Disable TIMER

DEVICE_CTRL_GET_CONFIG

NULL

Get TIMER current count

device_write

device_write is used to config timer device timeout value.This funtion calls timer_write actually.

int device_write(struct device *dev, uint32_t pos, const void *buffer, uint32_t size);
  • dev device handle

  • pos unused

  • buffer timer_timeout_cfg_t handle

  • size the length of timer_timeout_cfg_t

  • return error code, 0 means writing is successful, others mean errors

device_set_callback

device_set_callback is used to register a timer compare interrupt callback function.

int device_set_callback(struct device *dev, void (*callback)(struct device *dev, void *args, uint32_t size, uint32_t event));
  • dev device handle

  • callback the interrupt callback function to be registered

    • dev device handle

    • args unused

    • size unused

    • event interrupt event type

event type definition is as follows:

enum timer_event_type {
    TIMER_EVENT_COMP0,
    TIMER_EVENT_COMP1,
    TIMER_EVENT_COMP2,
    TIMER_EVENT_UNKNOWN
};

BLE

Introduction

  • Features
    • HOST
      • GAP support peripheral and Central, Observer and Broadcaster

      • GATT support server and Client

      • Support pairing with the secure connection feature in Bluetooth 4.2

      • Support permanent storage of Bluetooth specific settings and data

    • mesh
      • TODO

  • The architecture of the BLE protocol stack:
    _images/image1.png
    • There are 3 main layers, which together form a complete Bluetooth low power protocol stack
      • Host: Under the application program, it is composed of multiple (non-real-time) networks and transmission protocols, enabling the application program to communicate with peer devices in a standard and interoperable manner

      • Controller:The controller implements the link layer (LE LL), which is a low-level real-time protocol that provides standard interoperability for over-the-air communication with the radio. LL handles the reception and transmission of packets, guarantees the transfer of data, and handles all LL control procedures

      • Radio Hardware:Implement analog and digital baseband functions, allowing link layer firmware to transmit and receive in the 2.4GHz band of the spectrum

  • Master Host:
    _images/image2.png
    • The Bluetooth Host layer implements all high-level protocols and configuration files, the most important thing is that it provides high-level APIs for applications
      • HCI: Host and controller interface

      • L2CAP: Logical Link Control and Adaptation Protocol

      • GATT: Generic Attribute Profile

      • GAP: Generic Access Profile

      • SMP: Security Manager Specification

  • Application
    • The application layer contains the necessary protocol stack parameter settings and api reference. We analyze the two devices separately from the Bluetooth slave and the Bluetooth master
      • Bluetooth slave
        • Hardware and basic service initialization

        • Set broadcast parameters: broadcast data, broadcast interval, scan response, etc

        • Profile settings: add slave services, feature values, and set callback functions to receive host data, etc

        • Set pairing parameters (optional)

        • Start the broadcast, start running

        • Waiting for related events, and event processing, such as receiving data from the host, being linked, etc

      • Bluetooth host
        • Related hardware and basic service initialization

        • Set scan parameters

        • Set connection parameters

        • Set pairing parameters (optional)

        • Start the protocol stack and start running

        • Wait for related events, and event processing, such as scan events, Notify events from slaves, etc

API

  • API introduction

void ble_controller_init(uint8_t task_priority)

/**
* function      Controller layer initialization
* @param[in]    task_priority: task priority
* @return       None
*/

int hci_driver_init(void)

/**
* function      HCI interface driver initialization
* @param[in]    None
* @return       0: success, !=0: fail
*/

int bt_enable(bt_ready_cb_t cb)

/**
* function      BLE enable
* @param[in]    cb: Call the callback function if successful
* @return       0: success, !=0: fail
*/
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      Turn on BLE broadcast
*
* @param[in]    param:  Pointer to broadcast configuration parameter
* @param[in]    ad:     Pointer to the data in the broadcast packet
* @param[in]    ad_len: The length of the data in the broadcast packet
* @param[in]    sd:     Pointer to scan response packet data
* @param[in]    sd_len: Scan response packet data length
* @return               0: success, !=0: fail
*/

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      Update BLE broadcast data
* @param[in]    ad:     Pointer to the data in the broadcast packet
* @param[in]    ad_len: The length of the data in the broadcast packet
* @param[in]    sd:     Pointer to scan response packet data
* @param[in]    sd_len: Scan response packet data length
* @return               0: success, !=0: fail
*/

int bt_le_adv_stop(void)

/**
* function      Stop BLE broadcast
* @param[in]    None
* @return       0: success, !=0: fail
*/

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

/**
* function      Turn on BLE scanning
* @param[in]    param: Pointer to scan parameter
* @param[in]    cb:    Scan callback function
* @return              0: success, !=0: fail
*/

int bt_le_scan_stop(void)

/**
* function      Stop BLE scanning
* @param[in]    None
* @return       0: success, !=0: fail
*/

int bt_le_whitelist_add(const bt_addr_le_t *addr)

/**
* function      Add devices to the whitelist by address
* @param[in]    addr: Pointer to the address of the device that needs to be added
* @return       0: success, !=0: fail
*/

int bt_le_whitelist_rem(const bt_addr_le_t *addr)

/**
* function      Remove the device from the whitelist
* @param[in]    addr: Pointer to the address of the device that needs to be removed
* @return       0: success, !=0: fail
*/

int bt_le_whitelist_clear(void)

/**
* function      Clear the whitelist list
* @param[in]    None
* @return       0: success, !=0: fail
*/

int bt_le_set_chan_map(u8_t chan_map[5])

/**
* function      Set (LE) channel mapping
* @param[in]    chan_map: channel array
* @return       0: success, !=0: fail
*/

int bt_unpair(u8_t id, const bt_addr_le_t *addr)

/**
* function      Clear pairing information
* @param[in]    id: Local ID (mostly just the default BT ID)
* @param[in]    addr: Remote device address, NULL or BT_ADDR_LE_ANY to clear all remote devices
* @return       0: success, !=0: fail
*/

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

/**
* function      Get the information of the currently connected device
* @param[in]    conn: Pointer to the current connection
* @param[in]    info: Pointer to the information of the currently connected device
* @return       0: success, !=0: fail
*/

int bt_conn_get_remote_dev_info(struct bt_conn_info *info)

/**
* function      Get information about connected devices
* @param[in]    info: Pointer to the information of the currently connected device
* @return       Number of connected devices
*/

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

/**
* function      Update connection parameters
* @param[in]    conn: Pointer to the current connection
* @param[in]    param: Pointer to connection parameter
* @return       0: success, !=0: fail
*/

int bt_conn_disconnect(struct bt_conn *conn, u8_t reason)

/**
* function      Disconnect current connection
* @param[in]    conn: pointer to the current connection
* @param[in]    reason: the reason for disconnecting the current connection
* @return       0: success, !=0: fail
*/

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

/**
* function      Create connection
* @param[in]    peer: The pointer of the device address that needs to be connected
* @param[in]    param: Pointer to connection parameter
* @return       Success: a valid connection object, otherwise it fails
*/

int bt_conn_create_auto_le(const struct bt_le_conn_param *param)

/**
* function      Automatically create and connect to devices in the whitelist
* @param[in]    param: pointer to connection parameter
* @return       0: success, !=0: fail
*/

int bt_conn_create_auto_stop(void)

/**
* function      Stop automatic creation and connect to devices in the whitelist
* @param[in]    None
* @return       0: success, !=0: fail
*/

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

/**
* function      Automatically create a connection to the remote device
* @param[in]    addr: Remote device address pointer
* @param[in]    param: Pointer to connection parameter
* @return       0: success, !=0: fail
*/

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

/**
* function      Initiate a directed broadcast packet to the remote device
* @param[in]    peer: Remote device address pointer
* @param[in]    param: Pointer to broadcast parameters
* @return       Success: a valid connection object, otherwise it fails
*/

int bt_conn_set_security(struct bt_conn *conn, bt_security_t sec)

/**
* function      Set the connection security level
* @param[in]    conn: Pointer to the connection object
* @param[in]    sec: Security level
* @return       0: success, !=0: fail
*/

bt_security_t bt_conn_get_security(struct bt_conn *conn)

/**
* function      Get the security level of the current connection
* @param[in]    conn: Pointer to the connection object
* @return       Security Level
*/

u8_t bt_conn_enc_key_size(struct bt_conn *conn)

/**
* function      Get the size of the encryption key of the current connection
* @param[in]    conn: Pointer to the connection object
* @return       The size of the encryption key
*/

void bt_conn_cb_register(struct bt_conn_cb *cb)

/**
* function      Register connection callback function
* @param[in]    cb: Connection callback function
* @return       None
*/

void bt_set_bondable(bool enable)

/**
* function      Set/clear the binding flag in the SMP pairing request/response to the data authentication request
* @param[in]    enable: 1: enable, 0: disable
* @return       None
*/

int bt_conn_auth_cb_register(const struct bt_conn_auth_cb *cb)

/**
* function      Register authentication callback function
* @param[in]    cb: Callback function pointer
* @return       0: success, !=0: failure
*/

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

/**
* function      Reply with the key
* @param[in]    conn: Connect object pointer
* @param[in]    passkey: the key entered
* @return       0: success, !=0: failure
*/

int bt_conn_auth_cancel(struct bt_conn *conn)

/**
* function      Cancel the authentication process
* @param[in]    conn: Connection object pointer
* @return       0: success, !=0: failure
*/

int bt_conn_auth_passkey_confirm(struct bt_conn *conn)

/**
* function      If the password matches, reply to the other side
* @param[in]    conn: Pointer to connection object
* @return       0: success, !=0: failure
*/

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

/**
* function      Reply with PIN code
* @param[in]    conn: Pointer to connection object
* @param[in]    pin: PIN code pointer
* @return       0: success, !=0: failure
*/

int bt_le_read_rssi(u16_t handle,int8_t *rssi)

/**
* function      Read the RSSI value of the opposite device
* @param[in]    handle: The handle value of the connection
* @param[in]    rssi: rssi pointer
* @return       0: success, !=0: failure
*/

int bt_get_local_address(bt_addr_le_t *adv_addr)

/**
* function      Read the address of the machine
* @param[in]    adv_addr: Pointer to address
* @return       0: success, !=0: failure
*/

int bt_set_tx_pwr(int8_t power)

/**
* function      Set the transmit power of this device
* @param[in]    power: power value
* @return       0: success, !=0: failure
*/

Data structure reference

bt_le_adv_paramdata structure:

/** LE Advertising Parameters. */
struct bt_le_adv_param {
    /** Local identity */
    u8_t  id;

    /** Bit-field of advertising options */
    u8_t  options;

    /** Minimum Advertising Interval (N * 0.625) */
    u16_t interval_min;

    /** Maximum Advertising Interval (N * 0.625) */
    u16_t interval_max;

    #if defined(CONFIG_BT_STACK_PTS)
    u8_t  addr_type;
    #endif
};

This data structure is used to configure broadcast parameters, including local identification, broadcast option bit fields, broadcast gaps, etc. The broadcast option bit fields have the following enumerated type parameters to choose from:

enum {
    /** Convenience value when no options are specified. */
    BT_LE_ADV_OPT_NONE = 0,

    /** Advertise as connectable. Type of advertising is determined by
        * providing SCAN_RSP data and/or enabling local privacy support.
        */
    BT_LE_ADV_OPT_CONNECTABLE = BIT(0),

    /** Don't try to resume connectable advertising after a connection.
        *  This option is only meaningful when used together with
        *  BT_LE_ADV_OPT_CONNECTABLE. If set the advertising will be stopped
        *  when bt_le_adv_stop() is called or when an incoming (slave)
        *  connection happens. If this option is not set the stack will
        *  take care of keeping advertising enabled even as connections
        *  occur.
        */
    BT_LE_ADV_OPT_ONE_TIME = BIT(1),

    /** Advertise using the identity address as the own address.
        *  @warning This will compromise the privacy of the device, so care
        *           must be taken when using this option.
        */
    BT_LE_ADV_OPT_USE_IDENTITY = BIT(2),

    /** Advertise using GAP device name */
    BT_LE_ADV_OPT_USE_NAME = BIT(3),

    /** Use low duty directed advertising mode, otherwise high duty mode
        *  will be used. This option is only effective when used with
        *  bt_conn_create_slave_le().
        */
    BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY = BIT(4),

    /** Enable use of Resolvable Private Address (RPA) as the target address
        *  in directed advertisements when CONFIG_BT_PRIVACY is not enabled.
        *  This is required if the remote device is privacy-enabled and
        *  supports address resolution of the target address in directed
        *  advertisement.
        *  It is the responsibility of the application to check that the remote
        *  device supports address resolution of directed advertisements by
        *  reading its Central Address Resolution characteristic.
        */
    BT_LE_ADV_OPT_DIR_ADDR_RPA = BIT(5),

    /** Use whitelist to filter devices that can request scan response
        *  data.
        */
    BT_LE_ADV_OPT_FILTER_SCAN_REQ = BIT(6),

    /** Use whitelist to filter devices that can connect. */
    BT_LE_ADV_OPT_FILTER_CONN = BIT(7),
};

If you need to send a broadcast packet, the configuration can be as follows:

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

bt_datadata structure:

struct bt_data {
    u8_t type;
    u8_t data_len;
    const u8_t *data;
};

This data structure is used to fill the data in the broadcast packet, the specific data packet type can refer to the following:

Service UUID
Local Name
Flags
Manufacturer Specific Data
TX Power Level
Secure Simple Pairing OOB
Security Manager OOB
Security Manager TK Value
Slave Connection Interval Range
Service Solicitation
Service Data
Appearance
Public Target Address
Random Target Address
Advertising Interval
LE Bluetooth Device Address
LE Role
Uniform Resource Identifier
LE Supported Features
Channel Map Update Indication

Use this data structure to configure a broadcast packet data, as shown below:

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

bt_le_scan_paramdata structure:

/** LE scan parameters */
struct bt_le_scan_param {
    /** Scan type (BT_LE_SCAN_TYPE_ACTIVE or BT_LE_SCAN_TYPE_PASSIVE) */
    u8_t  type;

    /** Bit-field of scanning filter options. */
    u8_t  filter_dup;

    /** Scan interval (N * 0.625 ms) */
    u16_t interval;

    /** Scan window (N * 0.625 ms) */
    u16_t window;
};

This data structure is used to fill scan parameters, type: There are two types of scan types: BT_LE_SCAN_TYPE_ACTIVE (0x01) and BT_LE_SCAN_TYPE_PASSIVE (0x00) filter_dup: 0x00, except for targeted advertisements, accept all broadcast and scan responses, 0x01, only receive broadcast and scan responses from devices in the whitelist interval: Scan interval window: Scan window

If the scan request is enabled, it can be configured as follows:

scan_param.type = BT_LE_SCAN_TYPE_PASSIVE
scan_param.filter_dup = 0x00
interval=BT_GAP_SCAN_SLOW_INTERVAL_1
window=BT_GAP_SCAN_SLOW_WINDOW_1

bt_le_conn_paramdata structure:

/** Connection parameters for LE connections */
struct bt_le_conn_param {
    u16_t interval_min;
    u16_t interval_max;
    u16_t latency;
    u16_t timeout;

    #if defined(CONFIG_BT_STACK_PTS)
    u8_t  own_address_type;
    #endif
};

This data structure is used to fill in the connection parameters, interval_min: the minimum value of the connection interval (0x0018), interval_max: the maximum value of the connection interval (0x0028), latency: The maximum slave latency allowed for connection events timeout: The time for the connection to time out

Configure the data structure as follows:

interval_min=BT_GAP_INIT_CONN_INT_MIN(0x0018)
interval_max=BT_GAP_INIT_CONN_INT_MAX(0x0028)
latency=0
timeout=400

bt_conndata structure:

struct bt_conn {
    u16_t                   handle;
    u8_t                    type;
    u8_t                    role;

    ATOMIC_DEFINE(flags, BT_CONN_NUM_FLAGS);

    /* Which local identity address this connection uses */
    u8_t                    id;

#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
    bt_security_t           sec_level;
    bt_security_t           required_sec_level;
    u8_t                    encrypt;
#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */

    /* Connection error or reason for disconnect */
    u8_t                    err;

    bt_conn_state_t         state;

    u16_t                   rx_len;
    struct net_buf          *rx;

    /* Sent but not acknowledged TX packets with a callback */
    sys_slist_t             tx_pending;
    /* Sent but not acknowledged TX packets without a callback before
    * the next packet (if any) in tx_pending.
    */
    u32_t                   pending_no_cb;

    /* Completed TX for which we need to call the callback */
    sys_slist_t             tx_complete;
    struct k_work           tx_complete_work;


    /* Queue for outgoing ACL data */
    struct k_fifo           tx_queue;

    /* Active L2CAP channels */
    sys_slist_t             channels;

    atomic_t                ref;

    /* Delayed work for connection update and other deferred tasks */
    struct k_delayed_work   update_work;

    union {
        struct bt_conn_le   le;
#if defined(CONFIG_BT_BREDR)
        struct bt_conn_br   br;
        struct bt_conn_sco  sco;
#endif
    };

#if defined(CONFIG_BT_REMOTE_VERSION)
    struct bt_conn_rv {
        u8_t  version;
        u16_t manufacturer;
        u16_t subversion;
    } rv;
#endif
};

This data structure is the current connection data structure, which includes the parameters related to the BLE connection. After the connection is successful, the data structure can be called by the user.

GPIO

GPIO output - Lamp LED

This demo is based on the output mode of GPIO.

Hardware Connection

This demo is based on BL706_IOT and the connection method is as follows

   GPIO function         GPIO pin
----------------------------------
    D0      <-->     GPIO22
    D1      <-->     GPIO29
    D2      <-->     GPIO30
    D3      <-->     GPIO31

Software Implementation

  • See examples/gpio/gpio_blink for the software code

1gpio_set_mode(GPIO_PIN_22, GPIO_OUTPUT_PP_MODE);
2gpio_set_mode(GPIO_PIN_29, GPIO_OUTPUT_PP_MODE);
3gpio_set_mode(GPIO_PIN_30, GPIO_OUTPUT_PP_MODE);
4gpio_set_mode(GPIO_PIN_31, GPIO_OUTPUT_PP_MODE);
  • Use the above code to configure GPIO22 GPIO29 GPIO30 GPIO31 to output pull-up mode.

1gpio_write(GPIO_PIN_22, 0);
  • Use the above code to modify the output level value.

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=gpio_blink

Experimental Phenomena

Video display:

GPIO interrupt - Button detection

This demo is based on GPIO external interrupt mode.

Hardware Connection

This demo is based on BL706_IOT, add the button circuit by yourself, the connection method is as follows

   GPIO function         GPIO pin
----------------------------------
    SW1      <-->     GPIO11

Software Implementation

  • See examples/gpio/gpio_int for the software code

1static void gpio11_int_callback(uint32_t pin)
2{
3    MSG("gpio rising trigger !\r\n");
4}
5
6gpio_set_mode(GPIO_PIN_11,GPIO_SYNC_RISING_TRIGER_INT_MODE);
7gpio_attach_irq(GPIO_PIN_11,gpio11_int_callback);
8gpio_irq_enable(GPIO_PIN_11,ENABLE);
  • Use the above code to configure GPIO11 as the GPIO rising edge interrupt trigger mode, and register the interrupt callback function.

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=gpio_int

Experimental Phenomena

When the button is pressed, the serial port will print "gpio rising trigger !"

UART

UART - Loopback

This demo is based on UART polling sending and receiving FIFO interrupt mode.

Hardware Connection

This demo is based on BL706_IOT and the connection method is as follows

   GPIO function         GPIO pin
----------------------------------
    UART0_TX      <-->     GPIO14
    UART0_RX      <-->     GPIO15

Software Implementation

  • See examples/uart/uart_echo for the software code

1#define BSP_UART_CLOCK_SOURCE  ROOT_CLOCK_SOURCE_PLL_96M
2#define BSP_UART_CLOCK_DIV  0

-Configure the UART device clock source, see bsp/board/bl706_iot/clock_config.h

1#define CONFIG_GPIO14_FUNC GPIO_FUN_UART0_TX
2#define CONFIG_GPIO15_FUNC GPIO_FUN_UART0_RX
  • Configure UART device multiplexing pins, see bsp/board/bl706_iot/pinmux_config.h

 1#define BSP_USING_UART0
 2
 3#if defined(BSP_USING_UART0)
 4#ifndef UART0_CONFIG
 5#define UART0_CONFIG \
 6{   \
 7.id = 0, \
 8.baudrate = 2000000,\
 9.databits = UART_DATA_LEN_8, \
10.stopbits = UART_STOP_ONE, \
11.parity = UART_PAR_NONE, \
12.fifo_threshold = 1, \
13}
14#endif
15#endif
  • Enable BSP_USING_UART0 and configure the UART device, see bsp/board/bl706_iot/peripheral_config.h

1bflb_platform_init();
  • In the bflb_platform_init function, we have registered and opened a serial port device for debugging, to provide users with a basic function of MSG for printing out messages. The specific implementation is as follows

1    uart_register(board_get_debug_uart_index(), "debug_log", DEVICE_OFLAG_RDWR);
2    struct device *uart = device_find("debug_log");
3
4    if (uart)
5    {
6        device_open(uart, DEVICE_OFLAG_STREAM_TX | DEVICE_OFLAG_INT_RX);
7        device_set_callback(uart, NULL);
8        device_control(uart, DEVICE_CTRL_CLR_INT, (void *)(UART_RX_FIFO_IT));
9    }
  • First call the uart_register function to register the UART device, currently register UART0

  • Then use the find function to find the handle corresponding to the device and save it in the uart handle

  • Finally use device_open to open the uart device with polling sending and interrupt receiving, the interrupt is closed by default and the receiving interrupt callback function is not registered

1if (uart)
2{
3    device_set_callback(uart, uart_irq_callback);
4    device_control(uart, DEVICE_CTRL_SET_INT, (void *)(UART_RX_FIFO_IT|UART_RTO_IT));
5}
  • Register the user-specified UART0 receiving interrupt service function through the device_set_callback function. Open the RX_FIFO and RTO interrupts through the device_control function

 1void uart_irq_callback(struct device *dev, void *args, uint32_t size, uint32_t state)
 2{
 3    if (state == UART_EVENT_RX_FIFO)
 4    {
 5        device_write(dev,0,(uint8_t *)args,size);
 6    }
 7    else if (state == UART_EVENT_RTO)
 8    {
 9        device_write(dev,0,(uint8_t *)args,size);
10    }
11}
  • This function is the interrupt service function of the example, and its function is to send out the received data.

    • state will return the interrupt type of the UART device

    • args contains the return data pointer

    • size contains the length of the returned data

    • dev is the handle of the interrupted uart device

  • When an interrupt occurs, the device_write function will be called to send the received data back.

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=uart_echo

Experimental Phenomena

Video display:

PWM

PWM - Breathing LED

This demo is based on PWM polling mode.

Hardware Connection

This demo is based on BL706_IOT, and the connection method is as follows

   GPIO function         GPIO pin
----------------------------------
    PWM_CH2      <-->     GPIO22

Software Implementation

-See examples/pwm/pwm_breath_led for the software code

1#define BSP_PWM_CLOCK_SOURCE  ROOT_CLOCK_SOURCE_XCLK
2#define BSP_PWM_CLOCK_DIV  1
  • Configure the PWM device clock source, see bsp/board/bl706_iot/clock_config.h

1#define CONFIG_GPIO22_FUNC GPIO_FUN_PWM
  • Configure PWM device multiplexing pins, see bsp/board/bl706_iot/pinmux_config.h

 1#define BSP_USING_PWM_CH2
 2
 3#if defined(BSP_USING_PWM_CH2)
 4#ifndef PWM_CH2_CONFIG
 5#define PWM_CH2_CONFIG \
 6{   \
 7    .ch = 2, \
 8    .frequency = 1000000, \
 9    .dutycycle = 0, \
10    .it_pulse_count = 0,\
11}
12#endif
13#endif
  • Enable BSP_USING_PWM_CH2 and configure PWM device configuration, see bsp/board/bl706_iot/peripheral_config.h

 1pwm_register(PWM_CH2_INDEX, "led_breath", DEVICE_OFLAG_RDWR);
 2
 3struct device *led_breath = device_find("led_breath");
 4
 5if (led_breath) {
 6    PWM_DEV(led_breath)->period = 32; //frequence = 32M/1/32 = 1Mhz
 7    PWM_DEV(led_breath)->threshold_low = 16;
 8    PWM_DEV(led_breath)->threshold_high = 32;
 9    device_open(led_breath, DEVICE_OFLAG_STREAM_TX);
10    pwm_channel_start(led_breath);
11}
  • First call the pwm_register function to register a channel of the PWM device, currently register PWM_CH2

  • Then use the find function to find the handle corresponding to the device and save it in the led_breath handle

  • Set the frequency of PWM_CH2 to 1Mhz, and the duty cycle to 50%

  • Use device_open to open the led_breath device in polling mode

1    for (pwm_cfg.threshold_high = 0; pwm_cfg.threshold_high <= 32; pwm_cfg.threshold_high++) {
2        device_control(led_breath, DEIVCE_CTRL_PWM_DUTYCYCLE_CONFIG, &pwm_cfg);
3        bflb_platform_delay_ms(50);
4    }
5
6    for (pwm_cfg.threshold_high = 32; 0 <= pwm_cfg.threshold_high && pwm_cfg.threshold_high <= 32; pwm_cfg.threshold_high--) {
7        device_control(led_breath, DEIVCE_CTRL_PWM_DUTYCYCLE_CONFIG, &pwm_cfg);
8        bflb_platform_delay_ms(50);
9    }
  • Use the device_contorl function with the DEIVCE_CTRL_PWM_DUTYCYCLE_CONFIG instruction to modify the duty cycle of the current PWM channel.

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=pwm_breath_led

Experimental Phenomena

pwm breath led!

Video display:

PWM - 驱动步进电机

步进电机是一种将电脉冲转化为角位移的执行机构。当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动一个固定的角度(及步进角)。可以通过控制脉冲个来控制角位移量,从而达到准确定位的目的;同时可以通过控制脉冲频率来控制电机转动的速度和加速度,从而达到调速的目的。

本 demo 采用步进电机 28BYJ48 型四相八拍电机,使用 ULN2003 芯片驱动,电压为 DC5V—DC12V。当对步进电机施加一系列连续不断的控制脉冲时,它可以连续不断地转动。每一个脉冲信号对应步进电机的某一相或两相绕组的通电状态改变一次,也就对应转子转过一定的角度(一个步距角)。当通电状态的改变完成一个循环时,转子转过一个齿距。

28BYJ48

ULN2003

这个步进电机内部有个真正的步进马达转子,每一个脉冲能使这个真正的转子转动5.625°,看下图的数据表格中的减速比是1:64,意思是这个真正的步进马达转子转动64周才能让输出轴转动1周,因此下图的表格中步距角度才写的是5.625°/64,表明的意思是一个脉冲可以让输出轴转动5.625°/64的角度。所以要让马达转一周(360°), 则需要360/5.625*64=4096个脉冲。 脉冲(或拍)的数量决定转动的角度,单位时间内脉冲(或拍)的数量决定转动的速度

四相步进电机可以在不同的通电方式下运行,常见的通电方式有如下三种:

  • 一相励磁:单(单相绕组通电)四拍(A+,B+,A-,B-……)

  • 二相励磁:双(双相绕组通电)四拍(A+B+,B+A-,A-B-,B-A+……)

  • 一二相励磁:八拍(A+B+,B+,B+A-,A-,A-B-,B-,B-A+,A+……)

硬件连接

本 demo 基于BL706_IOT开发板,连接方式如下

   GPIO function         GPIO pin
----------------------------------
    PWM_CH0      <-->     GPIO10
    PWM_CH1      <-->     GPIO11
    PWM_CH2      <-->     GPIO12
    PWM_CH3      <-->     GPIO3

参考电路

软件实现

  • 软件代码见 examples/pwm/pwm_step_motor

1#define BSP_PWM_CLOCK_SOURCE  ROOT_CLOCK_SOURCE_RC_32K
2#define BSP_PWM_CLOCK_DIV  32
  • 配置 PWM 设备时钟源,见 bsp/board/bl706_iot/clock_config.h

1#define CONFIG_GPIO3_FUNC GPIO_FUN_PWM
2#define CONFIG_GPIO10_FUNC GPIO_FUN_PWM
3#define CONFIG_GPIO11_FUNC GPIO_FUN_PWM
4#define CONFIG_GPIO12_FUNC GPIO_FUN_PWM
  • 配置 PWM 设备复用引脚,见 bsp/board/bl706_iot/pinmux_config.h

 1#define BSP_USING_PWM_CH0
 2#define BSP_USING_PWM_CH1
 3#define BSP_USING_PWM_CH2
 4#define BSP_USING_PWM_CH3
 5
 6#if defined(BSP_USING_PWM_CH0)
 7#ifndef PWM_CH0_CONFIG
 8#define PWM_CH0_CONFIG                   \
 9    {                                    \
10        .ch = 0,                         \
11        .polarity_invert_mode = DISABLE, \
12        .period = 0,                     \
13        .threshold_low = 0,              \
14        .threshold_high = 0,             \
15        .it_pulse_count = 0,             \
16    }
17#endif
18#endif
19
20#if defined(BSP_USING_PWM_CH1)
21#ifndef PWM_CH1_CONFIG
22#define PWM_CH1_CONFIG                   \
23    {                                    \
24        .ch = 1,                         \
25        .polarity_invert_mode = DISABLE, \
26        .period = 0,                     \
27        .threshold_low = 0,              \
28        .threshold_high = 0,             \
29        .it_pulse_count = 0,             \
30    }
31#endif
32#endif
33
34#if defined(BSP_USING_PWM_CH2)
35#ifndef PWM_CH2_CONFIG
36#define PWM_CH2_CONFIG                   \
37    {                                    \
38        .ch = 2,                         \
39        .polarity_invert_mode = DISABLE, \
40        .period = 0,                     \
41        .threshold_low = 0,              \
42        .threshold_high = 0,             \
43        .it_pulse_count = 0,             \
44    }
45#endif
46#endif
47
48#if defined(BSP_USING_PWM_CH3)
49#ifndef PWM_CH3_CONFIG
50#define PWM_CH3_CONFIG                   \
51    {                                    \
52        .ch = 3,                         \
53        .polarity_invert_mode = DISABLE, \
54        .period = 0,                     \
55        .threshold_low = 0,              \
56        .threshold_high = 0,             \
57        .it_pulse_count = 0,             \
58    }
59#endif
60#endif
  • 使能 BSP_USING_PWM_CH0, BSP_USING_PWM_CH1 , BSP_USING_PWM_CH2, BSP_USING_PWM_CH3 并配置 PWM 设备配置,见 bsp/board/bl706_iot/peripheral_config.h

 1pwm_register(PWM_CH0_INDEX, "motor_ch0", DEVICE_OFLAG_RDWR);
 2pwm_register(PWM_CH1_INDEX, "motor_ch1", DEVICE_OFLAG_RDWR);
 3pwm_register(PWM_CH2_INDEX, "motor_ch2", DEVICE_OFLAG_RDWR);
 4pwm_register(PWM_CH3_INDEX, "motor_ch3", DEVICE_OFLAG_RDWR);
 5
 6motor_ch0 = device_find("motor_ch0");
 7motor_ch1 = device_find("motor_ch1");
 8motor_ch2 = device_find("motor_ch2");
 9motor_ch3 = device_find("motor_ch3");
10
11if (motor_ch0) {
12    PWM_DEV(motor_ch0)->period = 8; //frequence = 32K/160/8 = 25hz
13    PWM_DEV(motor_ch0)->threshold_low = 2;
14    PWM_DEV(motor_ch0)->threshold_high = 7;
15    PWM_DEV(motor_ch0)->polarity_invert_mode = ENABLE;
16    device_open(motor_ch0, DEVICE_OFLAG_STREAM_TX);
17}
18if (motor_ch1) {
19    PWM_DEV(motor_ch1)->period = 8; //frequence = 32K/160/8 = 25hz
20    PWM_DEV(motor_ch1)->threshold_low = 1;
21    PWM_DEV(motor_ch1)->threshold_high = 4;
22    device_open(motor_ch1, DEVICE_OFLAG_STREAM_TX);
23}
24if (motor_ch2) {
25    PWM_DEV(motor_ch2)->period = 8; //frequence = 32K/160/8 = 25hz
26    PWM_DEV(motor_ch2)->threshold_low = 3;
27    PWM_DEV(motor_ch2)->threshold_high = 6;
28    device_open(motor_ch2, DEVICE_OFLAG_STREAM_TX);
29}
30if (motor_ch3) {
31    PWM_DEV(motor_ch3)->period = 8; //frequence = 32K/160/8 = 25hz
32    PWM_DEV(motor_ch3)->threshold_low = 5;
33    PWM_DEV(motor_ch3)->threshold_high = 8;
34    device_open(motor_ch3, DEVICE_OFLAG_STREAM_TX);
35}
36pwm_channel_start(motor_ch0);
37pwm_channel_start(motor_ch1);
38pwm_channel_start(motor_ch2);
39pwm_channel_start(motor_ch3);
  • 首先调用 pwm_register 函数注册 PWM 设备的一个通道,当前注册 PWM 通道0/1/2/3

  • 然后通过 find 函数找到设备对应的句柄,保存于4个句柄中

  • 设置 4个通道 的频率为 125hz,占空比为37.5%

  • 使用 device_open 以轮询模式来打开 4个通道

 1enum motor_dir_type {
 2    CW,
 3    CCW,
 4    STOP
 5};
 6
 7void motor_set_dir(enum motor_dir_type dir)
 8{
 9    pwm_dutycycle_config_t pwm_cfg[4];
10
11    if (dir == CW) {
12        pwm_cfg[0].threshold_low = 2;
13        pwm_cfg[0].threshold_high = 7;
14        pwm_cfg[1].threshold_low = 1;
15        pwm_cfg[1].threshold_high = 4;
16        pwm_cfg[2].threshold_low = 3;
17        pwm_cfg[2].threshold_high = 6;
18        pwm_cfg[3].threshold_low = 5;
19        pwm_cfg[3].threshold_high = 8;
20    }
21
22    else if (dir == CCW) {
23        pwm_cfg[0].threshold_low = 2;
24        pwm_cfg[0].threshold_high = 7;
25        pwm_cfg[1].threshold_low = 5;
26        pwm_cfg[1].threshold_high = 8;
27        pwm_cfg[2].threshold_low = 3;
28        pwm_cfg[2].threshold_high = 6;
29        pwm_cfg[3].threshold_low = 1;
30        pwm_cfg[3].threshold_high = 4;
31    } else if (dir == STOP) {
32        pwm_cfg[0].threshold_low = 0;
33        pwm_cfg[0].threshold_high = 0;
34        pwm_cfg[1].threshold_low = 0;
35        pwm_cfg[1].threshold_high = 0;
36        pwm_cfg[2].threshold_low = 0;
37        pwm_cfg[2].threshold_high = 0;
38        pwm_cfg[3].threshold_low = 0;
39        pwm_cfg[3].threshold_high = 0;
40    }
41    device_control(motor_ch0, DEIVCE_CTRL_PWM_DUTYCYCLE_CONFIG, &pwm_cfg[0]);
42    device_control(motor_ch1, DEIVCE_CTRL_PWM_DUTYCYCLE_CONFIG, &pwm_cfg[1]);
43    device_control(motor_ch2, DEIVCE_CTRL_PWM_DUTYCYCLE_CONFIG, &pwm_cfg[2]);
44    device_control(motor_ch3, DEIVCE_CTRL_PWM_DUTYCYCLE_CONFIG, &pwm_cfg[3]);
45}
  • 使用 device_contorl 函数,配合 DEIVCE_CTRL_PWM_DUTYCYCLE_CONFIG 指令,修改4个 PWM 通道的的高低阈值。

备注

该函数的功能主要用于切换步进电机的方向

编译和烧录

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=pwm_step_motor

实验现象

MTIMER

MTIMER - System Timer

This demo is based on a 64-bit timer (MTIMER) that comes with the risc-v kernel. This demo can provide reference for os tick.

Hardware Connection

None

Software Implementation

  • See examples/systick for the software code

备注

The mtimer clock defaults to 1M after frequency division, which is convenient for later use and reduces calculation time.

1void systick_isr()
2{
3    static uint32_t tick=0;
4    tick++;
5    MSG("tick:%d\r\n",tick);
6}
7
8bflb_platform_set_alarm_time(1000000,systick_isr);
  • Use the above code to set the mtimer timing time to 1s, and register the interrupt callback function.

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=systick

Experimental Phenomena

The tick value is incremented by 1 per second and printed through the serial port.

DMA

DMA - Data Transfer Between RAM

This demo is based on the memory to memory mode of DMA for data transfer.

Hardware Connection

None

Software Implementation

  • For the code see examples/dma/dma_m2m

 1#define BSP_USING_DMA0_CH0
 2
 3#if defined(BSP_USING_DMA0_CH0)
 4#ifndef DMA0_CH0_CONFIG
 5#define DMA0_CH0_CONFIG \
 6{   \
 7.id = 0, \
 8.ch = 0,\
 9.direction = DMA_MEMORY_TO_MEMORY,\
10.transfer_mode = DMA_LLI_ONCE_MODE, \
11.src_req = DMA_REQUEST_NONE, \
12.dst_req = DMA_REQUEST_NONE, \
13.src_width = DMA_TRANSFER_WIDTH_32BIT , \
14.dst_width = DMA_TRANSFER_WIDTH_32BIT , \
15}
16#endif
17#endif
  • Enable BSP_USING_DMA0_CH0 and configure the DMA device, see bsp/board/bl706_iot/peripheral_config.h

 1dma_register(DMA0_CH0_INDEX, "DMA", DEVICE_OFLAG_RDWR);
 2
 3struct device *dma = device_find("DMA");
 4
 5if (dma)
 6{
 7    device_open(dma, 0);
 8    device_set_callback(dma, dma_transfer_done);
 9    device_control(dma, DEVICE_CTRL_SET_INT, NULL);
10}
  • First call the dma_register function to register a channel of the DMA device, currently register DMA_CH0

  • Then use the find function to find the handle corresponding to the device and save it in the dma handle

  • Finally use device_open to open the dma device in the default mode, call device_set_callback to register a dma channel 0 interrupt callback function, and call device_control to open the dma transmission completion interrupt

1dma_reload(dma,(uint32_t)dma_src_buffer,(uint32_t)dma_dst_buffer,8000);
2dma_channel_start(dma);
  • Call the dma_reload function to supplement the configuration of dma channel 0. A part of the configuration has been supplemented in DMA0_CH0_CONFIG. Here we mainly supplement the source data address, destination data address and total transmission length

  • Call dma_channel_start to start dma transmission

 1void dma_transfer_done(struct device *dev, void *args, uint32_t size, uint32_t state)
 2{
 3    uint32_t index=0;
 4
 5    if(!state)
 6    {
 7        MSG("dma transfer task done\r\n");
 8
 9        for(index=0;index<8000;index++){
10            if(dma_dst_buffer[index]!=0xff){
11                MSG("dma transfer error\r\n");
12            }
13        }
14
15        MSG("dma transfer success\r\n");
16    }
17
18}
  • Check whether the data transmission is correct in the interrupt function

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=dma_m2m

Experimental Phenomena

The data in the dma_src_buffer array is transferred to the dma_dst_buffer array through DMA channel 0 with a source 32-bit width and a target 32-bit width. After the data transfer is completed, the serial port prints dma transfer success.

SPI

SPI - TFT LCD Display

Hardware Connection

This demo is based on BL706_AVB, and the connection method is as follows

   GPIO function         GPIO pin
----------------------------------
    LCD_CS      <-->     GPIO10
    LCD_DC      <-->     GPIO22
    SPI_SCK     <-->     GPIO19
    SPI_MISO    <-->     GPIO20
    SPI_MOSI    <-->     GPIO21

Software Implementation

  • See examples/spi/spi_lcd for the software code

1#define BSP_SPI_CLOCK_SOURCE  ROOT_CLOCK_SOURCE_BCLK
2#define BSP_SPI_CLOCK_DIV  0
  • Configure the SPI device clock source, see bsp/board/bl706_avb/clock_config.h

1#define CONFIG_GPIO19_FUNC GPIO_FUN_SPI
2#define CONFIG_GPIO20_FUNC GPIO_FUN_SPI
3#define CONFIG_GPIO21_FUNC GPIO_FUN_SPI
  • Configure SPI device multiplexing pins, see bsp/board/bl706_avb/pinmux_config.h

备注

bsp/board/bl706_avb/pinmux_config.h is currently used by all demo demos, so you need to select PINMUX_SELECT as PINMUX_LVGL, and open one of the demos

备注

In order to adapt to the bl702_avb hardware, the MOSI and MISO of SPI have been swapped by default. If you want to restore the default, modify SPI_SWAP_ENABLE in drivers/bl702_driver/hal_drv/default_config/spi_config.h to 0

 1#define BSP_USING_SPI0
 2
 3#if defined(BSP_USING_SPI0)
 4#ifndef SPI0_CONFIG
 5#define SPI0_CONFIG \
 6{   \
 7.id = 0, \
 8.clk = 36000000,\
 9.mode = SPI_MASTER_MODE, \
10.direction = SPI_MSB_BYTE0_DIRECTION_FIRST, \
11.clk_polaraity = SPI_POLARITY_LOW, \
12.clk_phase = SPI_PHASE_1EDGE, \
13.datasize = SPI_DATASIZE_8BIT, \
14.fifo_threshold = 4, \
15}
16#endif
17#endif
  • Enable BSP_USING_SPI0 and configure SPI device, see bsp/board/bl706_avb/peripheral_config.h

 1gpio_set_mode(LCD_CS_PIN,GPIO_OUTPUT_MODE);
 2gpio_set_mode(LCD_DC_PIN,GPIO_OUTPUT_MODE);
 3gpio_write(LCD_CS_PIN,1); //CS1
 4gpio_write(LCD_DC_PIN,1); //DC
 5
 6spi0 = device_find("spi0");
 7if(spi0)
 8{
 9    device_close(spi0);
10}
11else{
12    spi_register(SPI0_INDEX,"spi0",DEVICE_OFLAG_RDWR);
13    spi0 = device_find("spi0");
14}
15if(spi0)
16{
17    device_open(spi0,DEVICE_OFLAG_STREAM_TX|DEVICE_OFLAG_STREAM_RX);
18}
  • Configure the LCD_CS and LCD_DC pins as output mode and pull up

  • Call spi_register function to register SPI device, currently register SPI0

  • Then use the find function to find the handle corresponding to the device and save it in the spi0 handle

  • Finally use device_open to open the spi0 device in polling sending mode

 1void LCD_WR_Byte(uint8_t data)
 2{
 3    CS1_LOW;
 4    DC_HIGH;
 5    spi_transmit(spi0,&data,1,SPI_TRANSFER_TYPE_8BIT);
 6    CS1_HIGH;
 7}
 8
 9void LCD_WR_HalfWord(uint16_t data)
10{
11    CS1_LOW;
12    DC_HIGH;
13    spi_transmit(spi0,&data,1,SPI_TRANSFER_TYPE_16BIT);
14    CS1_HIGH;
15}
16
17void LCD_WR_Word(uint32_t data)
18{
19    CS1_LOW;
20    DC_HIGH;
21    spi_transmit(spi0,&data,1,SPI_TRANSFER_TYPE_32BIT);
22    CS1_HIGH;
23}
  • Provide interface for LCD display driver

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_avb APP=spi_lcd

Experimental Phenomena

spi display!

I2C

I2C - AT24CXX read and write

Hardware Connection

This demo is based on BL706_IOT, add AT24CXX circuit by yourself, the connection method is as follows

   GPIO function         GPIO pin
----------------------------------
    I2C_SCL      <-->     GPIO11
    I2C_SDA      <-->     GPIO16

Software Implementation

  • See examples/i2c/i2c_at24cxx for the software code

1#define BSP_I2C_CLOCK_SOURCE  ROOT_CLOCK_SOURCE_BCLK
2#define BSP_I2C_CLOCK_DIV  0
  • Configure the I2C device clock source, see bsp/board/bl706_iot/clock_config.h

1#define CONFIG_GPIO11_FUNC GPIO_FUN_I2C
2#define CONFIG_GPIO16_FUNC GPIO_FUN_I2C
  • Configure I2C device multiplexing pins, see bsp/board/bl706_iot/peripheral_config.h

 1#define BSP_USING_I2C0
 2
 3#if defined(BSP_USING_I2C0)
 4#ifndef I2C0_CONFIG
 5#define I2C0_CONFIG \
 6{   \
 7.id = 0, \
 8.mode = I2C_HW_MODE,\
 9.phase = 15, \
10}
11#endif
12#endif
  • Enable BSP_USING_I2C0 and configure I2C device, see bsp/board/bl706_iot/peripheral_config.h

1i2c_register(I2C0_INDEX, "i2c", DEVICE_OFLAG_RDWR);
2struct device *i2c0 = device_find("i2c");
3
4if (i2c0)
5{
6    MSG("device find success\r\n");
7    device_open(i2c0, 0);
8}
  • First call the i2c_register function to register the I2C device, currently register I2C0

  • Then use the find function to find the handle corresponding to the device and save it in the i2c0 handle

  • Finally use device_open to open the I2C0 device in the default mode

 1i2c_msg_t msg[2];
 2uint8_t buf[8] = {0};
 3
 4msg[0].buf = buf;
 5msg[0].flags = SUB_ADDR_1BYTE | I2C_WR;
 6msg[0].len = 8;
 7msg[0].slaveaddr = 0x50;
 8msg[0].subaddr = 0x00;
 9
10msg[1].buf = buf;
11msg[1].flags = SUB_ADDR_1BYTE | I2C_RD;
12msg[1].len = 8;
13msg[1].slaveaddr = 0x50;
14msg[1].subaddr = 0x00;
15if (i2c_transfer(i2c0, &msg[0], 2) == 0)
16    MSG("\r\n read:%0x\r\n", msg[1].buf[0] << 8 | msg[1].buf[1]);
  • Call i2c_transfer to transfer two msg, one msg represents writing 8-byte data to eeprom, and one msg represents reading 8-byte data from eeprom

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=i2c_at24cxx

Experimental Phenomena

ADC

ADC - Key Detection Voltage

This demo mainly introduces the key functions of the ADC. The ADC is used to detect the voltage value of the key input pin and judge whether the corresponding key is pressed according to different voltage divisions.

Hardware Connection

This demo is based on BL706_AVB:

   GPIO function         GPIO pin
----------------------------------
    ADC CH8      <-->     GPIO18

Voltage divider circuit:

adc key

Software Implementation

  • For the code see examples/adc/adc_key

1#define BSP_ADC_CLOCK_SOURCE  ROOT_CLOCK_SOURCE_XCLK
2#define BSP_ADC_CLOCK_DIV  0
  • Configure the ADC device clock source, see bsp/board/bl706_avb/clock_config.h

1#define CONFIG_GPIO18_FUNC GPIO_FUN_ADC
  • Configure ADC device multiplexing pins, see bsp/board/bl706_avb/pinmux_config.h

 1#define BSP_USING_ADC0
 2
 3#if defined(BSP_USING_ADC0)
 4#ifndef ADC0_CONFIG
 5#define ADC0_CONFIG \
 6{   \
 7    .clk_div = ADC_CLOCK_DIV_32,\
 8    .vref = ADC_VREF_3P2V,\
 9    .continuous_conv_mode = DISABLE,\
10    .differential_mode = DISABLE,\
11    .data_width = ADC_DATA_WIDTH_16B_WITH_256_AVERAGE,\
12    .fifo_threshold = ADC_FIFO_THRESHOLD_1BYTE,\
13    .gain = ADC_GAIN_1\
14}
15#endif
16#endif
  • Enable BSP_USING_ADC0 and configure the ADC device, see bsp/board/bl706_iot/peripheral_config.h

 1adc_channel_cfg_t adc_channel_cfg;
 2adc_channel_cfg.pos_channel = posChList;
 3adc_channel_cfg.neg_channel = negChList;
 4
 5adc_register(ADC0_INDEX, "adc_key", DEVICE_OFLAG_STREAM_RX);
 6
 7adc_key = device_find("adc_key");
 8
 9if(adc_key)
10{
11    ADC_DEV(adc_key)->continuous_conv_mode = ENABLE;
12    device_open(adc_key, DEVICE_OFLAG_STREAM_RX);
13    device_control(adc_key,DEVICE_CTRL_ADC_CHANNEL_CONFIG,&adc_channel_cfg);
14
15}else{
16    MSG("device open failed\r\n");
17}
18
19adc_channel_start(adc_key);
  • First call the adc_register function to register the adc_key device, which is currently registered as ADC0

  • Then use the find function to find the handle corresponding to the device and save it in the adc_key handle

  • Then use device_open to open the adc_key device in polling mode, and call device_control to complete the ADC related configuration

  • Finally call adc_channel_start to enable ADC conversion

1device_read(adc_key,0,(void *)&result_val,1);
2keyValue = get_adc_key_value(result_val.volt * 1000);
3if( keyValue!=KEY_NO_VALUE){
4
5    MSG("key %d pressed\r\n",keyValue);
6    MSG("result_val.volt: %0.2f mv\n", (result_val.volt * 1000));
7}
  • Call device_read to read the adc_key device information and save it to the result_val structure

  • Call the get_adc_key_value function to get the current key value and voltage value

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_avb APP=adc_key

Experimental Phenomena

In this experiment, pressing SW1 ~ SW5 on the board in turn will get different voltage values:

  • key 0: ~0V

  • key 1: ~0.1V

  • key 2: ~0.2V

  • key 3: ~0.3V

  • key 4: ~0.43V

operation result:

_images/adc_key_result.png

Video display:

TIMER

TIMER - Second Timing Interrupt

This demo is based on TIMER interrupt mode with second timing.

Hardware Connection

None

Software Implementation

  • See examples/timer/timer_int for the software code

1#define BSP_TIMER0_CLOCK_SOURCE ROOT_CLOCK_SOURCE_FCLK
2#define BSP_TIMER0_CLOCK_DIV    0
  • Configure TIMER device clock source,see bsp/board/bl706_iot/clock_config.h

 1#define BSP_USING_TIMER0
 2
 3#if defined(BSP_USING_TIMER0)
 4#ifndef TIMER0_CONFIG
 5#define TIMER0_CONFIG                           \
 6    {                                           \
 7        .id = 0,                                \
 8        .cnt_mode = TIMER_CNT_PRELOAD,          \
 9        .trigger = TIMER_PRELOAD_TRIGGER_COMP2, \
10        .reload = 0,                            \
11        .timeout1 = 1000000,                    \
12        .timeout2 = 2000000,                    \
13        .timeout3 = 3000000,                    \
14    }
15#endif
16#endif
  • Enable BSP_USING_TIMER0 and configure TIMER0 device,see bsp/board/bl706_iot/peripheral_config.h

 1timer_register(TIMER0_INDEX, "timer0");
 2
 3timer0 = device_find("timer0");
 4
 5if (timer0) {
 6    device_open(timer0, DEVICE_OFLAG_INT_TX); /* 1s,2s,3s timing*/
 7    device_set_callback(timer0, timer0_irq_callback);
 8    device_control(timer0, DEVICE_CTRL_SET_INT, (void *)(TIMER_COMP0_IT | TIMER_COMP1_IT | TIMER_COMP2_IT));
 9} else {
10    MSG("timer device open failed! \n");
11}
  • Call timer_register function to register TIMER device, currently register TIMER0

  • Then use the find function to find the handle corresponding to the device and save it in the timer0 handle

  • Finally use device_open to open the timer0 device in interrupt mode

  • Call device_set_callback to register irq callback named timer0_irq_callback. Call device_control to enable irq and configure timing period.

 1void timer0_irq_callback(struct device *dev, void *args, uint32_t size, uint32_t state)
 2{
 3    if (state == TIMER_EVENT_COMP0) {
 4        MSG("timer event comp0! \r\n");
 5    } else if (state == TIMER_EVENT_COMP1) {
 6        MSG("timer event comp1! \r\n");
 7    } else if (state == TIMER_EVENT_COMP2) {
 8        BL_CASE_SUCCESS;
 9        timer_timeout_cfg_t cfg = { 2, 12000000 }; /*modify compare id 2 timeout 12s*/
10        device_write(dev, 0, &cfg, sizeof(timer_timeout_cfg_t));
11        MSG("timer event comp2! \r\n");
12    }
13}
  • In irq callback,try to determine whether compare id flag is coming.

  • Call device_write to modify compare id 2 timeout with 12s.

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=timer_int

Experimental Phenomena

SHELL Command Debugging

In order to facilitate the user to use the pc or other controllers to debug the functions of the development board (non-emulator debugging), we provide users with a shell command component, which is similar to the command operation under linux. The user sends commands on the PC or other control terminals, and sends the data to the shell of the development board through serial port, usb, Ethernet, Bluetooth, wifi, etc. The shell will read the received commands for analysis and scan the registered internal functions. After scanning the matching function, execute the matching function, and return the incoming key value and the result of the function execution to the pc or control terminal in real time . It should be noted that the controller side needs to send the key value of the standard keyboard. This demo will demonstrate how to use shell to debug commands through the serial port.

This shell component has the following functions:

  • Support standard keyboard character control

  • Support command auto completion

  • Support up and down keys to view historical commands

  • Support left and right keys to modify commands

  • Support file system and network system debugging

Prepare

  • PC control terminal uses serial terminal software: xshell or mobaxterm

  • Connection medium: usb to serial port or network or usb

Hardware Connection

This demo is based on BL706_IOT and the connection method is as follows

   GPIO function         GPIO pin
----------------------------------
    UART0_TX      <-->     GPIO14
    UART0_RX      <-->     GPIO15

Software Implementation

Shell porting to serial port

  • See examples/shell for the software code

1#define BSP_UART_CLOCK_SOURCE  ROOT_CLOCK_SOURCE_PLL_96M
2#define BSP_UART_CLOCK_DIV  0
  • Configure the UART device clock source, see bsp/board/bl706_iot/clock_config.h

1#define CONFIG_GPIO14_FUNC GPIO_FUN_UART0_TX
2#define CONFIG_GPIO15_FUNC GPIO_FUN_UART0_RX
  • Configure UART device multiplexing pins, see bsp/board/bl706_iot/pinmux_config.h

 1#define BSP_USING_UART0
 2
 3#if defined(BSP_USING_UART0)
 4#ifndef UART0_CONFIG
 5#define UART0_CONFIG \
 6{   \
 7.id = 0, \
 8.baudrate = 2000000,\
 9.databits = UART_DATA_LEN_8, \
10.stopbits = UART_STOP_ONE, \
11.parity = UART_PAR_NONE, \
12.fifo_threshold = 1, \
13}
14#endif
15#endif
  • Enable BSP_USING_UART0 and configure UART device configuration, see bsp/board/bl706_iot/peripheral_config.h

1bflb_platform_init();
  • In the bflb_platform_init function, we have registered and opened a serial port device for debugging, to provide users with a basic function of MSG for printing out messages. The specific implementation is as follows

1    uart_register(board_get_debug_uart_index(), "debug_log", DEVICE_OFLAG_RDWR);
2    struct device *uart = device_find("debug_log");
3
4    if (uart)
5    {
6        device_open(uart, DEVICE_OFLAG_STREAM_TX | DEVICE_OFLAG_INT_RX);
7        device_set_callback(uart, NULL);
8        device_control(uart, DEVICE_CTRL_CLR_INT, (void *)(UART_RX_FIFO_IT));
9    }
  • First call the uart_register function to register the UART device, currently register UART0

  • Then use the find function to find the handle corresponding to the device and save it in the uart handle

  • Finally use device_open to open the uart device with polling sending and interrupt receiving, the interrupt is closed by default and the receiving interrupt callback function is not registered

1struct device *uart = device_find("debug_log");
2if (uart) {
3    device_set_callback(uart, shell_irq_callback);
4    device_control(uart, DEVICE_CTRL_SET_INT, (void *)(UART_RX_FIFO_IT));
5}
  • Register the receive interrupt service function for UART0 through the device_set_callback function. Open the UART_RX_FIFO_IT interrupt via the device_control function

1void shell_irq_callback(struct device *dev, void *args, uint32_t size, uint32_t state)
2{
3    uint8_t data;
4    if (state == UART_EVENT_RX_FIFO) {
5        data = *(uint8_t *)args;
6        shell_handler(data);
7    }
8}

-In the interrupt callback function, judge whether the state is UART_EVENT_RX_FIFO, and if it is, pass the received byte to the shell_handler function.

1shell_init();
  • Call shell_init to initialize the shell components.

SHELL Command Registration

Shell command registration uses the following two macros

 1void hellowd()
 2{
 3    MSG("hello World\r\n");
 4}
 5
 6int echo(int argc, char *argv[])
 7{
 8    MSG("%dparameter(s)\r\n", argc);
 9
10    for (uint8_t i = 1; i < argc; i++) {
11        MSG("%s\r\n", argv[i]);
12    }
13
14    return 0;
15}
16
17SHELL_CMD_EXPORT(hellowd, hellowd test)
18SHELL_CMD_EXPORT(echo, echo test)
 1void hellowd()
 2{
 3    MSG("hello World\r\n");
 4}
 5
 6int cmd_echo(int argc, char *argv[])
 7{
 8    MSG("%dparameter(s)\r\n", argc);
 9
10    for (uint8_t i = 1; i < argc; i++) {
11        MSG("%s\r\n", argv[i]);
12    }
13
14    return 0;
15}
16
17SHELL_CMD_EXPORT_ALIAS(hellowd, hellwd,hellowd test)
18SHELL_CMD_EXPORT_ALIAS(cmd_echo, echo,echo test)

Compile and Program

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=shell SUPPORT_SHELL=y

Experimental Phenomena

shell test

FATFS Read And Write

LowPower Evaluation

Introduction

BL series chips have rich low-power features to adapt to different low-power applications. In order to facilitate users to quickly evaluate and use the low-power performance of bl series MCUs, bl_mcu_sdk provides a set of low-power interfaces. The low-power levels are divided into four levels.

  1. Running: The power consumption when the CPU is running normally, and the power consumption is determined by the function executed by the customer application code.

  2. WFI: WFI mode, the clock of the CPU is in the Gating state, the CPU stops running, and the program will continue to run when the user exits the WFI mode.

  3. PDS: In PDS mode, most power domains on the chip are turned off, and the CPU is turned off at the same time. RAMs such as ITCM and DTCM in the same power domain as the CPU cannot be used. Only 64K OCTAM can save data. The internal RTC can be used to wake up, or use GPIO pins (when the GPIO power domain is not turned off) to wake up.

  4. HBN: HBN mode, shuts down most of the power domains on the chip, shuts down the CPU and 64K OCRAM, only 4K RAM in the AON domain can save data. The internal RTC can be used to wake up, or a specific wake-up pin (pin located in the AON domain) can be used to wake up.

bl_mcu_sdk provides a simple low-power reference example (bl_mcu_sdk examples/power/lowpower_test/) to help users quickly evaluate low-power features. If you need to further adapt to your own low-power scenarios and adopt different low-power methods, please refer to the relevant datasheet or seek technical support from Boufflao Lab. In this example, the clock selection of peripherals and CPU are both crystal oscillator 32M. The power consumption measurement results based on this example are shown in the following table:

Mode

Reference current

Basic mode

Wake-up source

Remark

Running

5.68 mA

Run

All peripheral clocks are turned on

WFI

3.14 mA

WFI

Any interruption

Except the serial port, other peripheral clocks are closed

PDS

10 uA

PDS 31

Internal RTC/pin interrupt

64K OCRAM to save data

HBN

1 uA

HBN 1

Internal RTC/pin interrupt

4K AON RAM to save data

The reference current in the above table is obtained through the sample firmware test. The definition of four levels of “run” ” wfi” “pds” “hbn” simplifies the original setting of hbn level and pds level.

See bl702_bl704_bl706_DS_EN_Combo_1.9.pdf page 28

_images/powerTable.png

Low power consumption example test method

Compile low-power sample code

Write make APP=lowpower_test SUPPORT_SHELL=y BOARD=bl706_lp in the project directory to complete the compilation of the low power consumption example bl706, or use the CDK project directly to complete the compilation and download. You can refer to this document “Quick Development Guide” to get more information about compiling and programming.

When the compilation and programming are successful, connect the serial port to the computer and reset the chip, Xshell will display the page as shown in the figure below.

_images/xShell_lowpower.png

Prepare the hardware environment required for low-power testing

  • It is possible to connect the ammeter and the circuit board of the power supply side in series

  • Ammeter

  • PC (running Windows or Linux system)

  • TTL to USB

As shown in the figure below, connect the ammeter in series into the power supply circuit of the bl706 module, and issue different low-power commands through the serial debugging assistant software on the PC side, so that the bl706 enters the corresponding low-power mode. Observe the indicated current value and complete the evaluation.

_images/lowpower_arch.png

Use Xshell to start evaluating low-power performance

The user can enter the corresponding low power consumption mode by entering the following commands in Xshell.

run

  • After resetting the chip, it enters the run mode by default without entering any low power consumption mode. The chip is actually running the while(1); statement.

wfi

  • Enter wfi mode without adding any parameters. After entering, the CPU is in clock gating state to reduce power consumption

  • After entering wfi mode, any interrupt will wake up, such as uart interrupt. Pressing Enter in Xshell will trigger the BL706 UART RX interrupt, so the wfi low power consumption mode can be awakened by this method.

pds sleeptime

  • pds can choose to take the parameter “sleeptime” to determine its internal RTC wake-up time. If the command does not carry this parameter, the RTC internal wake-up is not used by default. The current firmware only supports power-on reset wake-up.

  • If the instruction contains the sleeptime parameter, pds will be awakened at the moment of sleeptime * clock_period, which will behave as resetting the chip and reprinting the initial message.

  • After entering the low-power mode, the RTC clock is 32K, so when the sleeptime is 32768, it appears to wake up after a second of sleep.

hbn sleeptime

  • hbn can choose to take the parameter “sleeptime” to determine its internal RTC wake-up time. If the command does not carry this parameter, the RTC internal wake-up is not used by default. The current firmware only supports power-on reset wake-up.

  • If the instruction contains the sleeptime parameter, hbn will be awakened at the moment of sleeptime * clock_period, which will behave as resetting the chip and printing the start message again.

  • After entering the low-power mode, the RTC clock is 32K, so when the sleeptime is 32768, it appears to wake up after a second of sleep.

BOOT2 IAP

IAP (In Application Programming) is to program some areas of User Flash during the running of the user program. The purpose is to update the firmware program in the product through the reserved communication port after the product is released.

If you need to realize the IAP function, that is, it will be automatically updated when the user program is running. Need to write two project codes when designing the firmware program. The first project program does not perform normal functional operations, but only receives programs or data through some communication channel (such as USB, USART) to update the second part of the code, and the second project code is the real function Code.

Bouffalo Lab provides the boot2_iap.bin file and releases it simultaneously with the Dev Cube software package. Users can use Dev Cube to program boot2_iap.bin into the target board. After programming once, the user code can be updated online through the IAP function.

bl_mcu_sdk contains the source code of boot2_iap, users can check the code in examples/boot2_iap, and complete the compilation and programming. For the compilation and programming process, please refer to the introduction of “Quick Development Guide”.

Prepare

  • The latest version of Dev Cube

  • Bl706

  • TTL-USB

Experimental phenomena

For the specific steps of using Dev Cube to complete the IAP function, please refer to DevCube User Manual “IAP Program Download”.

BLE Client And Server Interconnection

This demo is based on bl702 to demonstrate the connection and data sending and receiving of ble server and ble client.

Prepare

  • Two bl702 boards or one bl702 board + mobile app

Software Implementation

BLE client software Implementation

  • For the software code, see examples/ble/ble_central

 1static struct bt_conn_cb ble_tp_conn_callbacks = {
 2 .connected = ble_tp_connected,
 3 .disconnected = ble_tp_disconnected,
 4}
 5
 6void ble_tp_init()
 7{
 8    if( !isRegister )
 9    {
10        isRegister = 1;
11        bt_conn_cb_register(&ble_tp_conn_callbacks);
12    }
13}
  • In the bt_conn_cb_register function, register the callback function for successful connection and disconnection

  • In the ble_start_scan function, the device will start scanning

  • In the device_found function, the device uploads the scanned Bluetooth device, the code uses adv_name to find the Bluetooth device that needs to be connected, and initiate the connection

 1static void ble_write_data_task(void *pvParameters)
 2{
 3    int error;
 4    uint8_t buf[20] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
 5    while(1)
 6   {
 7        k_sem_take(&write_data_poll_sem, K_FOREVER);
 8        BT_WARN("ble_write_data\r\n");
 9        // Send data to server
10        error =  bt_gatt_write_without_response(ble_tp_conn,char_hdl.tp_wr_hdl,buf,20,0);
11        BT_WARN("Write Complete (err %d)\r\n", error);
12   }
13}
  • After the connection is successful, in the ble_write_data_task function, the client sends the data in buf to the server

1static u8_t notify_func(struct bt_conn *conn,struct bt_gatt_subscribe_params *params,const void *data, u16_t length);
  • After the connection is successful, in the notify_func function, the client receives the data from the server, data is the data content, length is the data length

BLE server software implementation

  • See examples/ble/ble_peripheral for the software code

 1int ble_start_adv(void)
 2{
 3    struct bt_le_adv_param adv_param = {
 4        //options:3, connectable undirected, adv one time
 5        .options = 3, \
 6        .interval_min = BT_GAP_ADV_FAST_INT_MIN_3, \
 7        .interval_max = BT_GAP_ADV_FAST_INT_MAX_3, \
 8    };
 9
10
11    char *adv_name = "BL_TEST_01"; // This name must be the same as adv_name in ble_central
12    uint8_t data[1] = {(BT_LE_AD_LIMITED | BT_LE_AD_NO_BREDR)};
13    uint8_t data_uuid[2] = {0x12, 0x18};//0x1812
14    uint8_t data_appearance[2] = {0x80, 0x01};//0x0180
15    uint8_t data_manu[4] = {0x71, 0x01, 0x04, 0x13};
16    struct bt_data adv_data[] = {
17            BT_DATA(BT_DATA_FLAGS, data, 1),
18            BT_DATA(BT_DATA_UUID16_ALL, data_uuid, sizeof(data_uuid)),
19            BT_DATA(BT_DATA_GAP_APPEARANCE, data_appearance, sizeof(data_appearance)),
20            BT_DATA(BT_DATA_NAME_COMPLETE, adv_name, strlen(adv_name)),
21            BT_DATA(BT_DATA_MANUFACTURER_DATA, data_manu, sizeof(data_manu))
22        };
23
24
25    return bt_le_adv_start(&adv_param, adv_data, ARRAY_SIZE(adv_data), NULL, 0);
26}
  • In the ble_start_adv function, adv_name sets the name of the broadcast device, and the device starts to broadcast

1static int ble_tp_recv_wr(struct bt_conn *conn, const struct bt_gatt_attr *attr,const void *buf, u16_t len, u16_t offset, u8_t flags);

-After the connection is successful, in ble_tp_recv_wr, the server receives the data from the client, buf is the data content, len is the data length

 1static void ble_tp_notify_task(void *pvParameters)
 2{
 3    int err = -1;
 4    char data[244] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
 5    k_sem_give(&notify_poll_sem);
 6    while(1)
 7    {
 8        k_sem_take(&notify_poll_sem, K_FOREVER);
 9        //send data to client
10        err = bt_gatt_notify(ble_tp_conn, get_attr(BT_CHAR_BLE_TP_NOT_ATTR_VAL_INDEX), data, (tx_mtu_size - 3));
11        BT_WARN("ble tp send notify : %d\n", err);
12
13    }
14}
  • After the connection is successful, in the ble_tp_notify_task function, the server sends the data in data to the client.

Compile and program

  • CDK tool compilation

    Not currently supported

  • Command compilation

1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=ble_peripheral SUPPORT_FREERTOS=y SUPPORT_FLOAT=y SUPPORT_BLE=y
1 $ cd <sdk_path>/bl_mcu_sdk
2 $ make BOARD=bl706_iot APP=ble_central SUPPORT_FREERTOS=y SUPPORT_FLOAT=y SUPPORT_BLE=y

Experimental phenomena

  • Two bl702 connections

  • Mobile phone connect bl702

  • The connection is successful, as shown in the figure below

  • Steps to send and receive data

    • Click 1 Unknow Service to display specific service properties

    • Click 2 to turn on Notification, allowing the server to send data to the client

    • Click 3 where the client sends data to the server, fill in the data you want to send, and click the SEND button