Skip to content

Telink tc_ble_sdk Developer Handbook


SDK Overview

The tc_ble_sdk (Telink TC Series Bluetooth Low Energy Multi-Connection SDK) provides reference code for BLE multi-connection applications based on the TC series MCUs. Users can develop their own applications on this basis.

“Multiple” means the coexistence of multiple (greater than or equal to one) Master and/or Slave roles. For example, a device can simultaneously operate as 4 Masters and 3 Slaves (abbreviated as M4S3).

Except for the BLE stack section, all other functional modules in tc_ble_sdk (such as Flash, Clock, GPIO, IR, etc.) are the same as those in the TC BLE Single Connection SDK Handbook. To avoid repeating descriptions of these modules, please prepare the corresponding TC BLE Single Connection SDK Handbook.

In the following sections, each module description includes a detail on whether the current module is fully consistent with the TC BLE Single Connection SDK Handbook. If there are any differences, they will also be described in detail.

The link to the required TC BLE Single Connection SDK Handbook is as follows:

Applicable ICs

The tc_ble_sdk applies to the following models:

  • B85 series: TLSR8251/TLSR8253/TLSR8258 hardware modules are essentially identical, with only slight differences in SRAM and flash sizes.
  • B87 series: TLSR8271/TLSR8273/TLSR8278 hardware modules are essentially identical, with only slight differences in SRAM and flash sizes.
  • TC321x series: A newly added chip series that supports functionality similar to the B85 and B87 series, while differing in hardware features and optimizations.

Please refer to the corresponding chip datasheets for details.

Note

  • The B85m series includes the B85 and B87 series chips.

Software Organization Structure

The tc_ble_sdk software architecture consists of two parts: the application layer and the BLE stack.

File Structure

After importing tc_ble_sdk into the IDE, the file structure displayed is shown in the figure below. There are eight top-level folders: algorithm, application, boot, common, drivers, proj_lib, stack, and vendor.

Note

  • Officially Recommended IDE: Telink IOT Studio for the TC series

SDK File Structure

algorithm: This folder contains some general algorithms, such as aes_ccm. The C files corresponding to most algorithms are supplied in the form of library files, leaving only the corresponding header files.

application: This folder contains general application programs, e.g., print, keyboard, etc.

boot: This folder contains the MCU software bootloader, which is the assembly startup routine executed when the MCU powers on or wakes up from deep sleep, preparing the system for subsequent C program execution.

common: This folder contains generic functions across platforms, such as memory handling functions and string handling functions.

drivers: This folder contains MCU peripheral drivers, e.g., Clock, Flash, I2C, USB, GPIO, UART.

proj_lib: This folder contains library files necessary for the SDK running. BLE stack, RF driver, PM driver, etc., are supplied in the form of library files.

stack: This folder contains header files for the BLE stack. Source files supplied in the form of library files are not open to users.

vendor: This folder contains example code or the user's code.

The tc_ble_sdk provides six demonstrations: acl_connection_demo, acl_central_demo, acl_peripheral_demo, ble_controller, acl_c1p1_demo, and ble_remote. Note that only the TC321x supports the eslp_esl_demo. These demonstrations are located in the vendor folder, as shown in the figure below.

Note

  • The b85m_m1s1 and b85m_slave demonstrations support both Suspend mode and Deep Sleep Retention mode, while the other demonstrations support Suspend mode only and do not support Deep Sleep Retention mode.

TC BLE Multi-Connection SDK Demonstrations

Take the acl_connection_demo as an example to explain the structure of the demonstration files. The file structure is shown in the figure below:

acl_connection_demo File Structure

main.c

The main.c file contains the main function and the interrupt handler functions. The main function serves as the entry point for program execution and includes the configuration required for the system to operate normally; it is recommended that users not modify it. The interrupt handler functions serve as the entry points when the system triggers an interrupt.

_attribute_ram_code_ int main(void)
{
    #if (BLE_APP_PM_ENABLE)
        blc_pm_select_internal_32k_crystal();
    #endif

    //Basic MCU Hardware Initialization
    #if(MCU_CORE_TYPE == MCU_CORE_825x)
        cpu_wakeup_init();
    #elif(MCU_CORE_TYPE == MCU_CORE_827x || MCU_CORE_TYPE == MCU_CORE_TC321X)
        cpu_wakeup_init(DCDC_LDO_MODE, INTERNAL_CAP_XTAL24M);
    #endif

    /* detect if MCU is wake_up from deep retention mode */
    int deepRetWakeUp = pm_is_MCU_deepRetentionWakeup();  //MCU deep retention wakeUp

    clock_init(SYS_CLK_TYPE); //Clock initialization

    rf_drv_init(RF_MODE_BLE_1M); //RF initialization

    gpio_init(!deepRetWakeUp); //GPIO initialization

    if( deepRetWakeUp ){ //MCU wake_up from deepSleep retention mode
        user_init_deepRetn ();
    }
    else{ //MCU power_on or wake_up from deepSleep mode
        user_init_normal ();
    }

    irq_enable();

    while(1)
    {
        main_loop ();//Includes BLE packet transmission and reception handling, as well as user tasks
    }
    return 0;
}

app_config.h

The user configuration file "app_config.h" serves to configure parameters of the whole system (e.g., BLE parameters, GPIO configuration, low power enable/disable, encryption enable/disable, etc.).

Each parameter in app_config.h will be explained in detail when each module is introduced in the following sections.

Application Files

app.c: User main file for BLE system initialization, data processing, and low power management.

app_att.c: Provides GATT service table and profile file. The GATT service table has provided the standard GATT service, standard GAP service, standard HID service, and some private services. Users can refer to these to add their own service and profile.

app_ui.c: Primarily provides key functions.

app_buffer.c: This file is used to define the buffers used by each layer of the stack, such as: LinkLayer TX & RX buffer, L2CAP layer MTU TX & RX buffer, HCI TX & RX buffer, etc.

Common Files

These files are located in the vendor/common directory.

blt_soft_timer.c: This file provides the implementation for the software timer.

custom_pair.c: This file provides Telink's custom pairing solution.

device_manage.c: This file is primarily used to manage device connection information (e.g., connection handle, attribute handle, BLE device address, address type, etc.). These details are required by users when developing applications.

simple_sdp.c: This file provides a simple SDP (Service Discovery Protocol) implementation for the Master role.

BLE Stack Entry

The BLE interrupt handler entry function is blc_sdk_irq_handler().

The blc_sdk_main_loop() is the main entry function for BLE logic and data processing, and is responsible for handling data and events related to the BLE stack.

Software Bootloader Description

In the tc_ble_sdk, different MCU models correspond to different bootloader files. The bootloader files are located in the boot folder. Telink’s bootloader files consist of two parts: the link file and the cstartup.s assembly file.

TC Series Bootloader Files

The bootloader files in the tc_ble_sdk are shown in the figure below.

The software bootloader files are located in the SDK/boot/ directory. Starting with version V4.0.2.0, the software bootloader files and link files are streamlined, and automated configuration is added. Users of older versions should carefully read the following introduction in the handbook.

Software Bootloader for Versions Prior to SDK V4.0.2.0

Bootloader File for TC BLE Multi-Connection SDK

There are two link files in tc_ble_sdk: boot.link (supports suspend mode) and boot_32k_retn.link (supports suspend mode and deep sleep retention mode).

Note

  • When compiling the project, the link file that is actually used is tc_ble_sdk/boot.link.
  • Only b85m_slave and b85m_m1s1 support deep sleep retention mode. When using these two demonstrations, it is necessary to copy the contents of tc_ble_sdk/boot/boot_32k_retn.link to tc_ble_sdk/boot.link.
  • For all other demonstrations, copy the contents of tc_ble_sdk/boot/boot.link to tc_ble_sdk/boot.link.

The cstartup.s files in tc_ble_sdk are different, depending on the size of the chip’s SRAM resources. Each MCU has two corresponding cstartup.s files; ‘RET_32K’ indicates support for Deep Sleep Retention 32K mode. Since the SRAM of both the TLSR8273 and TLSR8278 is 64 KB, their cstartup.s files are identical, and the TLSR8278 configuration is used for both.

Taking the acl_connection_demo project with the TLSR8258 MCU as an example, the procedure for configuring the corresponding cstartup.s file is as follows:

Locate the “cstartup_8258.s” file in the boot/8258 folder. Find the macro MCU_STARTUP_8258 within this file, then configure it in the project properties window as shown in the figure below.

Select cstartup

The relationship between bootloader file selection and different MCUs in the B85m series is shown in the table below.

MCU acl_connection_demo, b85m_controller, b85m_feature, b85m_master_dongle b85m_m1s1, b85m_slave
TLSR8253 boot.link, cstartup_8253.S boot_32k_retn.link, cstartup_8253_RET_32K.S
TLSR8258 boot.link, cstartup_8258.S boot_32k_retn.link, cstartup_8258_RET_32K.S
TLSR8273/8278 boot.link, cstartup_8278.S boot_32k_retn.link, cstartup_8278_RET_32K.S

Software Bootloader for SDK Version 4.0.2.0 and Later

Bootloaders and boot.link Paths for Different ICs in the New Version

The TLSR825x, TLSR827x, and TC321x series ICs correspond to different software bootloader files: cstartup_825x.s, cstartup_827x.s, and cstartup_TC321X.s, respectively. Users can configure the SRAM size in the .s file by selecting the appropriate chip. The TLSR825x, TLSR827x, and TC321x series ICs use the same set of link files in the new version.

Taking cstartup_825x.s as an example:

#define SRAM_BASE_ADDR      (0x840000)
#define SRAM_32K            (0x8000)    //32KSRAM
#define SRAM_48K            (0xc000)    //48KSRAM
#define SRAM_64K            (0x10000)   //64KSRAM

#if (MCU_STARTUP_8258)
    #define SRAM_SIZE       (SRAM_BASE_ADDR + SRAM_64K)
#elif (MCU_STARTUP_8253)
    #define SRAM_SIZE       (SRAM_BASE_ADDR + SRAM_48K)
#elif (MCU_STARTUP_8251)
    #define SRAM_SIZE       (SRAM_BASE_ADDR + SRAM_32K)
#endif

#ifndef SRAM_SIZE
#define SRAM_SIZE           (SRAM_BASE_ADDR + SRAM_64K)
#endif

The default configuration for projects in the tc_ble_sdk uses a TLSR8258 with 64 KB of SRAM. Users must manually modify this configuration based on the type of chip they are using.

Take the acl_connection_demo as an example to illustrate how to modify the SRAM size defined in the bootloader.

As shown in the figure below, replace -DMCU_STARTUP_8258 with -DMCU_STARTUP_8253 to switch between chips with different SRAM sizes.

Software Bootloader Settings in the New Version

In the new version, the retention size can be automatically set via the API blc_app_setDeepsleepRetentionSramSize(). This works by using _retention_size_ to determine the size of RAM occupied by the end of the current retention_data segment. If it is less than 16KB, the program is set to Retention 16KB SRAM mode; if it exceeds 16KB but is less than 32KB, the program automatically switches to Retention 32KB SRAM mode; if it exceeds 32KB, the program reports an error during the compilation phase.

The code for this check in blc_app_setDeepsleepRetentionSRAMSize() is as follows.

if (((u32)&_retention_size_) < 0x4000){
    blc_pm_setDeepsleepRetentionType(DEEPSLEEP_MODE_RET_SRAM_LOW16K); //retention size < 16k, use 16k deep retention
}
else if (((u32)&_retention_size_) < 0x8000){
    blc_pm_setDeepsleepRetentionType(DEEPSLEEP_MODE_RET_SRAM_LOW32K); //retention size < 32k and >16k, use 32k deep retention
}
else{
    /* retention size > 32k, overflow. deep retention size setting err*/
    #if (UART_PRINT_DEBUG_ENABLE)
        tlkapi_printf(APP_LOG_EN, "[APP][INI] deep retention size setting err");
    #endif
}

When the retention size exceeds 32KB, the following error message appears:

Error Message When Deep Sleep Retention Size Exceeds 32KB

Library

tc_ble_sdk Library

The tc_ble_sdk library configuration for the project is shown in the figure below.

tc_ble_sdk Library Configuration

The figure below shows the libraries currently available in the proj_lib folder of the tc_ble_sdk.

tc_ble_sdk Library

The table below shows the compatibility between different MCUs, demonstrations, and libraries in the tc_ble_sdk.

MCU acl_connection_demo acl_central_demo acl_peripheral_demo acl_c1p1_demo ble_remote eslp_esl_demo
B85 liblt_8258.a liblt_8258_c0p4.a liblt_8258_c1p1.a \ \
B87 liblt_8278.a liblt_8278_c0p4.a liblt_8278_c1p1.a \ \
TC321x liblt_tc321x.a liblt_tc321x_c0p4.a liblt_tc321x_c1p1.a liblt_tc321x_c1p1.a liblt_tc321x_esl.a

MCU Basic Modules

MCU Address Space

MCU Address Space Allocation

Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

SRAM Memory Allocation

Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

In tc_ble_sdk, only acl_c1p1_demo, acl_peripheral_demo, and eslp_esl_demo support the deepsleep retention feature. For the SRAM allocation of acl_c1p1_demo and acl_peripheral_demo, please refer to the TC BLE Single Connection SDK Handbook. The other demos only use suspend and deepsleep modes and do not support deepsleep retention. Their SRAM and firmware memory allocation are shown in the figure below.

SRAM allocation and firmware memory allocation for demos without deepsleep retention support.

SRAM and Firmware Space Allocation without Deepsleep Retention Support

The firmware in Flash includes the vector, ramcode, text, rodata, and data initial value.

SRAM includes the vector, ramcode, cache, data, bss, unused SRAM area, and stack.

The vector and ramcode in SRAM are copies of the corresponding vector and ramcode in Flash.

MCU Address Space Allocation

Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

SDK Flash Space Allocation

Due to its relatively complex functionality, multi-connection uses more Flash storage area. Therefore, it does not adopt the method of automatically detecting the Flash size for configuration; instead, users are required to manually configure the Flash size based on their actual product requirements.

In all demos, app_config.h only provides default configurations as placeholders. Users must check, verify, and modify the definition of "FLASH_SIZE_CONFIG" as needed.

#define FLASH_SIZE_512K                          0x80000
#define FLASH_SIZE_1M                            0x100000
#define FLASH_SIZE_2M                            0x200000

#define FLASH_SIZE_CONFIG                        FLASH_SIZE_1M

Flash storage operates on a basic unit of one sector (4K bytes), because Flash erasure is performed on a sector basis (using the flash_erase_sector function). In theory, information of the same type should be stored in the same sector, while different types of information should be stored in different sectors to prevent accidentally erasing other types of information during an erase operation. Therefore, it is recommended that users follow the principle of "storing different types of information in different sectors" when using Flash to store custom information.

In the Telink BLE Multiple Connection SDK, four types of information need to be stored in Flash: MAC, calibration information, encryption pairing information, and SDP information. These parameters are each allocated different Flash spaces by default in the SDK.

The Flash space where MAC and calibration information are stored varies depending on the size of the chip’s Flash. By default, for TC series chips with 512 KB Flash, the MAC is stored in the 4 KB Flash space starting at 0x76000, and calibration information is stored in the 4 KB Flash space starting at 0x77000. For chips with 1 MB Flash, the MAC is stored in the 4 KB Flash space starting at 0xFF000, and calibration information is stored in the 4 KB Flash space starting at 0xFE000.

Flash Space for MAC and Calibration Information Storage

The encryption pairing information and SDP information are also stored in independent Flash spaces. The SDK can automatically configure the corresponding default locations based on the Flash size set by the user. By default, for chips with 512 KB Flash, the encryption pairing information is stored in the 16 KB Flash space starting at 0x78000, custom bonding information is stored in the 4 KB Flash space starting at 0x7C000, and SDP information is stored in the 8 KB Flash space starting at 0x7D000. For chips with 1 MB Flash, the encryption pairing information is stored in the 16 KB Flash space starting at 0xFA000, custom bonding information is stored in the 4 KB Flash space starting at 0xF8000, and SDP information is stored in the 8 KB Flash space starting at 0xF6000. For chips with 2 MB Flash, the encryption pairing information is stored in the 16 KB Flash space starting at 0x1FA000, custom bonding information is stored in the 4 KB Flash space starting at 0x1F8000, and SDP information is stored in the 8 KB Flash space starting at 0x1F6000.

The SDK can automatically configure the corresponding storage spaces for MAC and calibration values based on the Flash size set by the user. Users can also modify the corresponding macros in vendor/common/blt_common.h to change the default storage spaces for MAC and calibration values according to their own needs. In this case, attention should be paid to modifying the programming addresses on the Telink production tool accordingly.

Default Flash storage addresses for 512 KB Flash

Default Flash storage addresses for 1 MB Flash

Default Flash storage addresses for 1 MB Flash

Clock Module

Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

The difference between the multi-connection SDK and the single-connection SDK is that the multi-connection SDK requires at least a 32 MHz system clock (CLK_32M). Lower clock speeds are too slow to support its operation.

GPIO Module

Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

System Interrupt

Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

Note that tc_ble_sdk does not support interrupt nesting. All interrupts share the same priority and are serviced on a first-come, first-served basis. In addition, due to the strict BLE timing requirements, it is recommended that the execution time of user-defined interrupt service routines be less than 50 us. Therefore, time-consuming operations such as Flash access should be avoided within interrupt handlers. Similarly, any operation that disables interrupts should also be limited to within 50 us.

BLE Module

This handbook refers to Bluetooth Core Specification version 5.3.

BLE SDK Software Architecture

Standard BLE SDK Architecture

The figure below shows the standard BLE SDK software architecture compliant with the Bluetooth core specification.

BLE SDK Software Architecture

As shown above, the BLE protocol stack includes the Host and Controller.

  • As a BLE bottom-layer protocol, the “Controller” contains Physical Layer (PHY) and Link Layer (LL). Host Controller Interface (HCI) is the sole communication interface for all data transfer between the Controller and Host.

  • As a BLE upper-layer protocol, the “Host” contains protocols including Logical Link Control and Adaptation Protocol (L2CAP), Attribute Protocol (ATT), Security Manager Protocol (SMP), as well as Profiles including Generic Access Profile (GAP) and Generic Attribute Profile (GATT).

  • The “Application” (APP) layer contains user application codes and Profiles corresponding to various Services. User controls and accesses Host via “GAP”, while Host transfers data with Controller via “HCI”, as shown below.

HCI Data Transfer between Host and Controller

(1) BLE Host uses HCI cmd to operate and set the Controller. Controller API corresponding to each HCI cmd will be introduced in this chapter.

(2) The controller will report various HCI Events to the Host via HCI, which is detailed in this chapter.

(3) Host will send target data to Controller via HCI, while Controller will directly load data to the Physical Layer for transfer.

(4) When the Controller receives RF data in the Physical Layer, it will first check whether the data belongs to the Link Layer or the Host, and then process correspondingly: If the data belongs to LL, the data will be processed directly; if the data belongs to Host, the data will be sent to Host via HCI.

(1) Telink BLE Multiple Connection Controller

The tc_ble_sdk supports standard BLE Controllers, including HCI, PHY (Physical Layer), and LL (Link Layer).

The tc_ble_sdk includes five standard states of Link Layer (standby, advertising, scanning, initiating, connection). In the connection state, it supports up to 4 Master roles and 4 Slave roles simultaneously.

The Controller architecture diagram is shown below:

Telink HCI Architecture

(2) Telink BLE Multiple Connection Whole Stack (Controller + Host)

The tc_ble_sdk provides a reference design for the BLE multiple connection full stack (Controller + Host); however, it does not fully support the Master SDP (Service Discovery). This is detailed in subsequent chapters.

The Telink BLE stack architecture simplifies the standard structure described above to minimize the SDK’s system resource consumption (including SRAM, runtime, and power consumption). The architecture is shown in the figure below. The acl_connection_demo and acl_c1p1_demo in the SDK are both based on this architecture.

Telink BLE Multiple Connection Whole Stack Architecture

In the figure above, solid arrows indicate data transfer controllable via user APIs, while hollow arrows indicate data transfer within the protocol stack not involved in user APIs.

Controller can still communicate with Host (L2CAP layer) via HCI; however, the HCI is no longer the only interface, and the APP layer can directly exchange data with the Link Layer of the Controller. The Power Management (PM) Module is embedded in the Link Layer, and the application layer can invoke related PM interfaces to set power management.

Considering efficiency, data transfer between the APP layer and the Host is not controlled via GAP; the ATT, SMP, and L2CAP can directly communicate with the APP layer via the corresponding interface. However, the event of the Host should be communicated with the APP layer via the GAP layer.

Generic Attribute Profile (GATT) is implemented in the Host layer based on Attribute Protocol. Various Profiles and Services can be defined in the APP layer based on GATT. Basic Profiles, including HIDS, BAS, and OTA, are provided in this BLE SDK.

The following sections explain each layer of the BLE multi-connection protocol stack according to the structure above, as well as user APIs for each layer.

Physical Layer is totally controlled by Link Layer, since it does not involve the application layer, and it is not covered in this document.

Though HCI still implements part of the data transfer between Host and Controller, it is basically implemented by the protocol stack of Host and Controller, with little involvement of the APP layer. The user only needs to register the HCI data callback handling function in the L2CAP layer.

Controller

Connection Number Configuration

(1) supportedMaxMasterNum & supportedMaxSlaveNum

The tc_ble_sdk refers to the maximum number of connection Master roles as supportedMaxMasterNum and the maximum number of connection Slave roles as supportedMaxSlaveNum. These values are determined by the library, as shown in the table below:

IC library supportedMaxMaster-Num supportedMaxSlaveNum
825x liblt_8258 4 4
liblt_8258_c0p4 0 4
liblt_8258_c1p1 1 1
827x liblt_8278 4 4
liblt_8278_c0p4 0 4
liblt_8278_c1p1 1 1
TC321x liblt_tc321x 4 4
liblt_tc321x_c0p4 0 4
liblt_tc321x_c1p1 1 1
liblt_tc321x_esl 0 1

The SDK can use the following API to query the number of masters and slaves currently supported by the stack.

int blc_ll_getSupportedMaxConnNumber(void);

(2) appMaxMasterNum & appMaxSlaveNum

After determining the supportedMaxMasterNum and supportedMaxSlaveNum, users can use the following API to set the maximum number of masters and slaves desired for their application, referred to as appMaxMasterNum and appMaxSlaveNum, respectively.

ble_sts_t   blc_ll_setMaxConnectionNumber(int max_master_num, int max_slave_num);

This API can only be called during initialization; that is, the relevant connection counts are required to be determined before the Link Layer begins running and cannot be modified later.

The user’s appMaxMasterNum and appMaxSlaveNum must be less than or equal to supportedMaxMasterNum and supportedMaxSlaveNum.

In the sample code, this API is called during the initialization phase:

blc_ll_setMaxConnectionNumber( ACL_CENTRAL_MAX_NUM, ACL_PERIPHR_MAX_NUM);

Users need to define their own appMaxMasterNum and appMaxSlaveNum in app_config.h, which correspond to ACL_CENTRAL_MAX_NUM and ACL_PERIPHR_MAX_NUM in the SDK.

#define ACL_CENTRAL_MAX_NUM                             4
#define ACL_PERIPHR_MAX_NUM                             4

The appMaxMasterNum and appMaxSlaveNum parameters can conserve various MCU resources. For example, in the M4S4 library, if users only need to use M3S2, they can set ACL_CENTRAL_MAX_NUM and ACL_PERIPHR_MAX_NUM to 3 and 2, respectively:

a. Save SRAM resources

The Link Layer TX Master FIFO and TX Slave FIFO, as well as the L2CAP Master MTU buffer and L2CAP Slave MTU buffer, are all allocated based on appMaxMasterNum and appMaxSlaveNum, thereby saving some SRAM resources. For details, please refer to the subsequent descriptions related to TX FIFOs in this document.

b. Save time and power

For the M4S4, the stack stops scanning only when currentMasterNum reaches 4 and stops advertising only when currentSlaveNum reaches 4. However, for the M3S2, the stack stops scanning when currentMasterNum reaches 3 and stops advertising when currentSlaveNum reaches 2. This eliminates unnecessary scanning and advertising, thereby saving PHY layer bandwidth and reducing MCU power consumption.

(3) currentMaxMasterNum & currentMaxSlaveNum

By defining appMaxMasterNum and appMaxSlaveNum, users can determine the maximum number of masters and slaves created by the Link Layer at runtime. However, the actual number of masters and slaves at any given moment remains uncertain. For example, if appMaxMasterNum is set to 4, the number of masters at any given time could be 0, 1, 2, 3, or 4.

The SDK provides the following three APIs, allowing users to query the current number of Masters and Slaves on the Link Layer in real time.

int blc_ll_getCurrentConnectionNumber(void);//Master + Slave connection number
int blc_ll_getCurrentMasterRoleNumber(void);//Master role number
int blc_ll_getCurrentSlaveRoleNumber(void);//Slave  role number

Users can first refer to the description of the Link Layer state machine in the TC BLE Single-Connection SDK. All five basic states of the Link Layer are supported. If the Connection state is further divided into Connection Slave role and Connection Master role, the Link Layer must be, and can only be, one of the following six states at any given time: Standby, Advertising, Scanning, Initiating, Connection Slave role, or Connection Master role.

For the tc_ble_sdk, since it needs to support multiple masters and slaves simultaneously, the Link Layer cannot be in a single state at any given time; instead, it is required to maintain a combination of several states.

The state machine of the tc_ble_sdk’s Link Layer is relatively complex; therefore, this document provides only a general overview to help users gain a basic understanding of the underlying mechanisms and use the corresponding APIs.

(1) Link Layer State Machine Initialization

The tc_ble_sdk is designed with each basic state as a modular component; modules that need to be used should be initialized in advance.

The MCU initialization is mandatory, and the API is as follows:

void        blc_ll_initBasicMCU (void);

The API for adding a standby module is as follows. This is mandatory; all BLE applications require initialization.

void        blc_ll_initStandby_module (u8 *public_adr);

The real parameter public_adr is a pointer to the BLE public MAC address.

The initialization APIs of the corresponding modules for several other states (Advertising, Scanning, ACL Master, ACL Slave) are as follows.

void        blc_ll_initLegacyAdvertising_module(void);
void        blc_ll_initLegacyScanning_module(void);
void        blc_ll_initAclConnection_module(void);
void        blc_ll_initAclMasterRole_module (void)
void        blc_ll_initAclSlaveRole_module(void);

(2) Link Layer State Combinations

The Initiating state is relatively simple. When the Scanning state requires initiating a connection with a specific advertising device, the Link Layer enters the Initiating state. Within a certain period (known as the “create connection timeout”), the connection is either successfully established—resulting in the creation of a Connection Master role—or it fails, in which case the Link Layer returns to the Scanning state. To simplify the description of the Link Layer state machine and make it easier for users to understand, the temporary Initiating state is omitted from the following introduction.

The tc_ble_sdk Link Layer state machine can be described from two perspectives: the switching between Advertising and Slave modes, and the switching between Scanning and Master modes. These two perspectives do not affect each other.

Taking M1S1 as an example, assume that the user’s appMaxMasterNum and appMaxSlaveNum are both set to 1. The state machine for M1S1’s switching between Advertising and Slave is as follows:

M1S1 Advertising and Slave Transition

In the figure, adv_enable and adv_disable refer to the states set by the user’s last call to blc_ll_setAdvEnable(adv_enable) when the respective conditions occur.

The state machine for M1S1 Scanning and Master switching is as follows:

M1S1 Scanning and Master Switching

In the figure, scan_enable and scan_disable refer to the state set by the user’s last call to blc_ll_setScanEnable(scan_enable, filter_duplicate) when the condition occurs.

Both Advertising and Slave, as well as Scanning and Master, each have three states. Since these two sets are logically independent and do not affect each other, there are ultimately 3 × 3 = 9 possible Link Layer combination states, as shown in the table below:

2A 2B 2C
1A Standby Scanning Master
1B Advertising Advertising + Scanning Advertising + Master
1C Slave Slave + Scanning Slave + Master

Taking a more complex M4S4 as an example, assuming the user's appMaxMasterNum and appMaxSlaveNum are 4 and 4, respectively, the state machine for M4S4 advertising and slave switching is as follows:

M4S4 Advertising and Slave Switching

The state machine for M4S4 Scanning and Master switching is as follows:

M4S4 Scanning and Master Switching

There are 9 possible states for Advertising and Slave, and 9 possible states for Scanning and Master. Since the logic between these two sets is completely independent and they do not affect each other, the total number of possible Link Layer state combinations is 9 × 9 = 81, as shown in the table below:

/ 2A 2B 2C 2D 2E 2F 2G 2H 2I
1A Standby Scanning Scanning Master*1 Scanning Master*2 Scanning Master*3 Master*4 Master*1 Master*2 Master*3
1B Adv Adv Scanning Adv Scanning Master*1 Adv Scanning Master*2 Adv Scanning Master*3 Adv Master*4 Adv Master*1 Adv Master*2 Adv Master*3
1C Adv Slave*1 Adv Slave*1 Scanning Adv Slave*1 Scanning Master*1 Adv Slave*1 Scanning Master*2 Adv Slave*1 Scanning Master*3 Adv Slave*1 Master*4 Adv Slave*1 Master*1 Adv Slave*1 Master*2 Adv Slave*1 Master*3
1D Adv Slave*2 Adv Slave*2 Scanning Adv Slave*2 Scanning Master*1 Adv Slave*2 Scanning Master*2 Adv Slave*2 Scanning Master*3 Adv Slave*2 Master*4 Adv Slave*2 Master*1 Adv Slave*2 Master*2 Adv Slave*2 Master*3
1E Adv Slave*3 Adv Slave*3 Scanning Adv Slave*3 Scanning Master*1 Adv Slave*3 Scanning Master*2 Adv Slave*3 Scanning Master*3 Adv Slave*3 Master*4 Adv Slave*3 Master*1 Adv Slave*3 Master*2 Adv Slave*3 Master*3
1F Slave*4 Slave*4 Scanning Slave*4 Scanning Master*1 Slave*4 Scanning Master*2 Slave*4 Scanning Master*3 Slave*4 Master*4 Slave*4 Master*1 Slave*4 Master*2 Slave*4 Master*3
1G Slave*1 Slave*1 Scanning Slave*1 Scanning Master*1 Slave*1 Scanning Master*2 Slave*1 Scanning Master*3 Slave*1 Master*4 Slave*1 Master*1 Slave*1 Master*2 Slave*1 Master*3
1H Slave*2 Slave*2 Scanning Slave*2 Scanning Master*1 Slave*2 Scanning Master*2 Slave*2 Scanning Master*3 Slave*2 Master*4 Slave*2 Master*1 Slave*2 Master*2 Slave*2 Master*3
1I Slave*3 Slave*3 Scanning Slave*3 Scanning Master*1 Slave*3 Scanning Master*2 Slave*3 Scanning Master*3 Slave*3 Master*4 Slave*3 Master*1 Slave*3 Master*2 Slave*3 Master*3

If the user's appMaxMasterNum/appMaxSlaveNum is not M1S1 or M4S4, please analyze it using the method described above.

The concepts of supportedMaxMasterNum / supportedMaxSlaveNum and appMaxMasterNum / appMaxSlaveNum are introduced above, corresponding to the number of Masters and Slaves in the state machine combination table. In addition, the concepts of currentMasterNum and currentSlaveNum are defined, representing the actual number of Masters and Slaves in the Link Layer at the current moment. For example, in the “1D2E” combination state in the table above, currentMasterNum is 3 and currentSlaveNum is 2.

The Link Layer timing is relatively complex. This section introduces only the most basic concepts, which are sufficient for users to understand and properly use the related APIs.

The Link Layer defines five basic states: Standby, Advertising, Scanning, Initiating, and Connection. Since Initiating is only used briefly when the Master creates a connection, it is omitted here. The timing of the remaining four states is briefly described below.

In this section, the M4S4 state is used as an example, where appMaxMasterNum and appMaxSlaveNum are assumed to be 4 and 4, respectively.

The timing of each sub-state (Advertising, Master0–Master3, Slave0–Slave3, Scanning, and UI task) is illustrated in the following figure:

Illustration of Sub-States

(1) Standby State Timing

This corresponds to the 1A2A state for M4S4 in the table above.

When the Link Layer is in Idle state, the Link Layer and Physical Layer do not have any tasks to process, and the blc_sdk_main_loop function does not work at all and does not produce any interrupts. It can be considered that UI entry (UI task) occupies the whole main_loop time.

Standby State Timing

(2) Scanning only, no Advertising, no Connection Timing

This corresponds to the 1A2B state for M4S4 in the table above.

At this time, only the Scanning state needs to be processed, and scan is the most efficient. The Link Layer switches channel 37/38/39 according to the scan interval, and the timing diagram is as follows:

Scanning State Timing Allocation

According to the size of the scan window, to determine the true scan time, if the scan window is equal to the scan interval, all the time in the scan; if the scan window is less than the scan interval, select the time equal to the scan window from the front in the scan interval to scan.

The Scan window shown in the figure is about 60% of the Scan interval. In the first 60% of the time, the Link Layer is in the Scanning state, and the PHY layer is receiving packets. At the same time, the user can use this time to execute their UI task in the main_loop. The last 40% of the time is not in the Scanning state, the PHY layer stops working, and the user can use this time to execute their UI task in the main_loop. For the design of the low power management to be described later, this time also allows the MCU to enter suspend to reduce the power consumption of the whole machine.

(3) Advertising only, no Scanning, no Connection Timing

This corresponds to the 1B2A state for M4S4 in the table above.

The Advertising Event is allocated to the timeline according to the ADV interval, and the timing diagram is as follows:

Advertising State Timing Allocation

For all the details of the ADV Event, just refer to the details of the ADV Event in the Telink TC BLE Single Connection SDK; both of them are the same.

Users can use non-ADV time to execute their own UI task in the main_loop.

(4) Advertising, Scanning, no Connection Timing

This corresponds to the 1B2B state for M4S4 in the table above.

First, according to the ADV interval, the Advertising Event is first allocated to the timeline, and then the Scanning is allocated. The timing diagram is as follows:

Advertising + Scanning State Timing Allocation

Since the application requires higher timing accuracy for advertising than scan, ADV Event has higher priority at this time, first allocate the timing of ADV Event, then use the remaining time between ADV Event for Scan, while the users can use this remaining time to execute their own UI task in main_loop. When the scan window set by the user is equal to the scan interval, the scan duration in the figure will fill the remaining time; when the scan window set by the user is less than the scan interval, the Link Layer will automatically calculate and get a scan duration that satisfies the following condition: Scan duration/(ADV interval + rand_dly) should be equal to Scan window/Scan interval as much as possible.

(5) Connection, Advertising, Scanning Timing

The number of connections has not yet reached the set maximum, and the advertising and scanning states still exist at this time.

This corresponds to the 1C2C state for M4S4 in the table above.

M4S4 1C2C State Timing Allocation

The allocation of connection tasks (whether Master or Slave) is done first and will be allocated according to the timing of the respective connections. If multiple tasks occupy the same time slot and a conflict occurs, they will be allocated according to priority and preempted with high priority. Abandoned tasks are automatically increased in priority to ensure that they are not always discarded.

Then the ADV task is allocated, the principle is:

a. The time interval from the last ADV event should be greater than the set minimum ADV interval time.

b. The time between the current task and the next task is greater than a certain value (3.75 ms), because ADV takes a certain amount of time to complete.

c. The allocated time period is not occupied by other connection tasks.

Finally the allocation of scan tasks, the principle is: as long as there is more than enough time between the two tasks, this time is allocated to the scan tasks, and the percentage of the scan is also confirmed according to the scan window/scan interval.

(6) Connection, no Advertising, no Scanning Timing

The number of connections has reached the set maximum, and there are no advertising and scanning states at this time.

The following figure corresponds to the 1G2H state of M4S4 in the table above.

M4S4 1G2H State Timing Allocation

The following figure corresponds to the 1E2F state of M4S4 in the table above.

M4S4 1E2F State Timing Allocation

At this time, only connected tasks are assigned according to the respective connected timings. If a conflict occurs, it will be allocated according to priority; the high priority tasks will preempt, and abandoned tasks are automatically given increased priority, increasing the chances of grabbing them in the next conflict.

ACL TX FIFO & ACL RX FIFO

(1) ACL TX FIFO Definition and Setting

Application layer and BLE Host all the data eventually need to complete the RF data sent through the Controller's Link Layer, in the Link Layer according to the number of connections set by the user, the corresponding TX FIFO is defined.

The ACL TX FIFO is defined as follows:

u8  app_acl_mstTxfifo[ACL_MASTER_TX_FIFO_SIZE * ACL_MASTER_TX_FIFO_NUM * ACL_CENTRAL_MAX_NUM] = {0};
u8  app_acl_slvTxfifo[ACL_SLAVE_TX_FIFO_SIZE * ACL_SLAVE_TX_FIFO_NUM * ACL_PERIPHR_MAX_NUM] = {0};

The TX FIFO of ACL Master and ACL Slave are defined separately. Take ACL Slave as an example, ACL Master principle is the same, and so on.

The size of the array app_acl_slvTxfifo is related to three macros:

a. ACL_PERIPHR_MAX_NUM is the maximum number of connections, i.e., appMaxSlaveNum. Users can modify this value in app_config.h as needed.

b. ACL_SLAVE_TX_FIFO_SIZE is the size of each sub_buffer, which is related to the possible maximum value of data sent by the ACL Slave. Use the following macro to define the implementation in the SDK.

#define ACL_SLAVE_TX_FIFO_SIZE   CAL_LL_ACL_TX_FIFO_SIZE(ACL_SLAVE_MAX_TX_OCTETS)

Among them, CAL_LL_ACL_TX_FIFO_SIZE is a formula that is related to the implementation of the MCU. Different MCU calculation methods may be different. Please refer to the notes in app_buffer.h for details.

ACL_SLAVE_MAX_TX_OCTETS is the user-defined slaveMaxTxOctets. If the client uses DLE (Data Length Extension), this value needs to be modified accordingly; the default value is 27, which corresponds to the minimum value when DLE is not used, which is used to save SRAM. For details, please refer to the comments in app_buffer.h and the detailed description in the Bluetooth Core Specification.

c. ACL_SLAVE_TX_FIFO_NUM refers to the number of sub_buffers. Please refer to the comments in app_buffer.h for the selection of this value. This value has a certain relationship with the amount of data sent in the client application. If the amount of data sent is large and the real-time requirement is high, you can consider a larger number.

The user defines the TX FIFO according to the actual situation, and the Master TX FIFO and the Slave TX FIFO are defined separately, so that one is for each connection's data are cached in their own TX FIFO, the TX data between each connection will not interfere with each other; the second is also according to the actual situation, flexible definition TX FIFO size, corresponding to reduce the consumption of RAM. For example:

  • The Slave needs the DLE function, while the Master does not need DLE, so that the FIFOs can be defined separately to save RAM space. For the explanation of DLE, please refer to "MTU and DLE".
  • For example, if the customer is actually using 3 Master and 2 Slave, the customer can define only 3 Master TX FIFO and 2 Slave TX FIFO, thus reducing the use of RAM and saving RAM space:
#define ACL_CENTRAL_MAX_NUM                     3
#define ACL_PERIPHR_MAX_NUM                     2

Below, we describe the settings of the TX FIFO in various states with a diagram, so that you can have a more intuitive understanding.

a. M4S4, ACL Master, ACL Slave do not use DLE

Assume that the relevant definitions are as follows:

#define ACL_CENTRAL_MAX_NUM                 4
#define ACL_PERIPHR_MAX_NUM                 4
#define ACL_MASTER_MAX_TX_OCTETS        27
#define ACL_SLAVE_MAX_TX_OCTETS         27

#define ACL_MASTER_TX_FIFO_SIZE      CAL_LL_ACL_TX_FIFO_SIZE(ACL_MASTER_MAX_TX_OCTETS)
#define ACL_MASTER_TX_FIFO_NUM       8
#define ACL_SLAVE_TX_FIFO_SIZE       CAL_LL_ACL_TX_FIFO_SIZE(ACL_SLAVE_MAX_TX_OCTETS)
#define ACL_SLAVE_TX_FIFO_NUM        8

Then the TX FIFO is defined as the following value:

u8  app_acl_mstTxfifo[40 * 8 * 4] = {0};
u8  app_acl_slvTxfifo[40 * 8 * 4] = {0};

The figure is as follows:

Each connection corresponds to a TX FIFO, and the number of each connection FIFO is 8 (0 \~ 7), and the size of 0 \~ 7 is the same (40 bytes):

TX FIFO Default Setting

b. M4S4 and ACL Master use DLE with MasterMaxTxOctets set to the maximum value of 251, while ACL Slave does not use DLE.

Assume that the relevant definitions are as follows:

#define ACL_CENTRAL_MAX_NUM                 4
#define ACL_PERIPHR_MAX_NUM                 4
#define ACL_MASTER_MAX_TX_OCTETS        251
#define ACL_SLAVE_MAX_TX_OCTETS         27

#define ACL_MASTER_TX_FIFO_SIZE      CAL_LL_ACL_TX_FIFO_SIZE(ACL_MASTER_MAX_TX_OCTETS)
#define ACL_MASTER_TX_FIFO_NUM       8
#define ACL_SLAVE_TX_FIFO_SIZE       CAL_LL_ACL_TX_FIFO_SIZE(ACL_SLAVE_MAX_TX_OCTETS)
#define ACL_SLAVE_TX_FIFO_NUM        8

Then the TX FIFO is defined as the following value:

u8  app_acl_mstTxfifo[264 * 8 * 4] = {0};
u8  app_acl_slvTxfifo[40 * 8 * 4] = {0};

As can be seen from the figure, the number of FIFOs for each connection of Master and Slave is the same, which is 8 (0 ~ 7). However, each FIFO size of the Master is 264 bytes, while the FIFO size of the Slave is 40 bytes.

Master Uses DLE and Slave does not Use DLE's Buffer

c. M3S2, ACL Master, ACL Slave do not use DLE

3 Master and 2 Slave Buffer Situation

(2) ACL RX FIFO Definition and Setting

The ACL RX FIFO is defined as follows:

u8  app_acl_rxfifo[ACL_RX_FIFO_SIZE * ACL_RX_FIFO_NUM] = {0};

The ACL Master and ACL Slave share the ACL RX FIFO. Take ACL Slave as an example, ACL Master principle is the same, and so on.

The size of the array app_acl_rxfifo is related to two macros:

a. ACL_RX_FIFO_SIZE is the buffer size, which is related to the possible maximum value of data received by the ACL Slave. Use the following macro definitions in the SDK to implement.

#define ACL_RX_FIFO_SIZE         CAL_LL_ACL_RX_FIFO_SIZE(ACL_CONN_MAX_RX_OCTETS)

Among them, CAL_LL_ACL_RX_FIFO_SIZE is a formula, which is related to the MCU implementation, different MCU calculation methods may be different, you can refer to the notes in app_buffer.h for details.

ACL_CONN_MAX_RX_OCTETS is the user-defined slaveMaxRxOctets. If the client uses DLE (Data Length Extension), this value needs to be modified accordingly; the default value is 27, which corresponds to the minimum value when DLE is not used, which is used to save SRAM. For details, please refer to the comments in app_buffer.h and the detailed description in the Bluetooth Core Specification.

b. ACL_RX_FIFO_NUM refers to the number of buffers. Please refer to the comments in app_buffer.h for the selection of this value. This value is related to the amount of data received in the client applications, if the amount of data received is large and the real-time requirement is high, you can consider a larger number.

Assume that the M4S4 ACL RX FIFO is defined as follows:

#define ACL_CENTRAL_MAX_NUM                 4
#define ACL_PERIPHR_MAX_NUM                 4

#define ACL_CONN_MAX_RX_OCTETS          27
#define ACL_RX_FIFO_SIZE                CAL_LL_ACL_RX_FIFO_SIZE(ACL_CONN_MAX_RX_OCTETS)
#define ACL_RX_FIFO_NUM                 16

The corresponding ACL RX FIFO allocation is shown below:

ACL RX Buffer

(3) RX Overflow Analysis

Refer to the introduction in Telink TC BLE Single Connection Handbook, the principle is the same.

MTU and DLE Concepts and Usage

(1) MTU and DLE Concepts Description

Starting from version 4.2, the Bluetooth Core Specification supports Data Length Extension (DLE).

The Link Layer in the tc_ble_sdk supports data length extension and rf_len length supports the maximum length of 251 bytes in the Bluetooth Core Specification. For details, please refer to Bluetooth Core Specification V5.3, Vol 6, Part B, 2.4.2.21 LL_LENGTH_REQ and LL_LENGTH_RSP.

Before the specific explanation, it's necessary to understand the concepts of MTU and DLE. First look at a picture:

Concepts of MTU and DLE

The following figure shows the specific contents of MTU and DLE:

Contents of MTU and DLE

  • MTU stands for maximum transmission unit and is used in computer networking to define the maximum size of a Protocol Data Unit (PDU) that can be sent by a specific protocol.

  • The Attribute MTU (ATT_MTU as defined by the specification) is the largest size of an ATT payload that can be sent between a client and a server. The Bluetooth Core Specification specifies that the minimum value of MTU is 23 bytes.

The DLE is a data length extension. The BLE spec specifies that the minimum value of DLE is 27 bytes.

To carry more data in a packet requires negotiation between Master and Slave, interacting the DLE size through LL_LENGTH_REQ and LL_LENGTH_RSP.

The Bluetooth Core Specification defines the minimum DLE length as 27 bytes and the maximum as 251 bytes.

The maximum value of 251 bytes is determined by the RF length field, which is one byte wide and can represent a maximum value of 255. For encrypted links, an additional 4-byte MIC field is required. Therefore: 251 + 4 = 255.

(2) MTU and DLE Automatic Interaction Method

If the user needs to use the data length extension function, set it as follows. The SDK also provides the corresponding MTU&DLE usage demo, refer to feature_dle in the vendor/feature_test/feature_test project.

In vendor/feature_test/feature_config.h:

#define FEATURE_TEST_MODE   TEST_LL_DLE

a. MTU size exchange

First, MTU interaction is required, just modify ATT_MTU_MASTER_RX_MAX_SIZE and ATT_MTU_SLAVE_RX_MAX_SIZE, which represent the RX MTU size of ACL Master and ACL Slave respectively. The default is the minimum value of 23, just change it to the desired value. CAL_MTU_BUFF_SIZE is a fixed calculation formula and can not be modified.

The process of MTU size exchange ensures that the minimum MTU size of both parties takes effect, preventing the peer device from being unable to process long packets at the BLE L2cap layer and greater than or equal to 23.

#define ATT_MTU_MASTER_RX_MAX_SIZE   23
#define  MTU_M_BUFF_SIZE_MAX         CAL_MTU_BUFF_SIZE(ATT_MTU_MASTER_RX_MAX_SIZE)

#define ATT_MTU_SLAVE_RX_MAX_SIZE    23
#define MTU_S_BUFF_SIZE_MAX          CAL_MTU_BUFF_SIZE(ATT_MTU_SLAVE_RX_MAX_SIZE)

Then the following APIs are called inside the initialization to set the RX MTU size of ACL Master and ACL Slave respectively.

Note

  • These two APIs need to be placed in blc_gap_init() to take effect.
blc_att_setMasterRxMTUSize(ATT_MTU_MASTER_RX_MAX_SIZE); 
blc_att_setSlaveRxMTUSize(ATT_MTU_SLAVE_RX_MAX_SIZE); 

For the implementation of MTU size exchange, please refer to the detailed description in the "ATT & GATT" section of this document --- Exchange MTU Request, Exchange MTU Response, and also refer to the writing method of feature_dle in the vendor/feature_test/b85_feature project.

b. Set connMaxTxOctets and connMaxRxOctets

Secondly, you need to set the size of the DLE of the Master and Slave, refer to the introduction of ACL TX FIFO and ACL RX FIFO, you only need to modify the following macros, and change 27 to the desired value. If these values are not set to the default 27, the stack performs DLE interactions automatically after connection (this feature can be disabled via the API, allowing DLE interactions to be performed only when needed).

#define ACL_CONN_MAX_RX_OCTETS          27
#define ACL_MASTER_MAX_TX_OCTETS        27
#define ACL_SLAVE_MAX_TX_OCTETS         27

Then the following API is called in the initialization to set the RX DLE size and TX DLE size of ACL Master and ACL Slave respectively.

ble_sts_t   blc_ll_setAclConnMaxOctetsNumber(u8 maxRxOct, u8 maxTxOct_master, u8 maxTxOct_slave)

c. Operation of sending and receiving long packets

Please refer to some instructions in the "ATT & GATT" section of this document, including Handle Value Notification and Handle Value Indication, Write request and Write Command, etc.

On the basis that all the above 3 steps are completed correctly, you can start sending and receiving long packets.

To send a long packet, call the APIs corresponding to Handle Value Notification and Handle Value Indication of the ATT layer, as shown below, and bring the address and length of the data to be sent into the following formal parameters "*p" and "len".

ble_sts_t    blc_gatt_pushHandleValueNotify (u16 connHandle, u16 attHandle, u8 *p, int len);
ble_sts_t    blc_gatt_pushHandleValueIndicate (u16 connHandle, u16 attHandle, u8 *p, int len);

To receive long packets, just needs to process the callback function "w" corresponding to Write request and Write Command, and in the callback function, reference the data pointed to by the formal parameter pointer.

(3) MTU and DLE Manual Interaction Method

If the user does not want the stack to automatically interact with MTU/DLE due to some special circumstances, the SDK also provides the corresponding API, and the user decides when to interact with MTU/DLE according to the specific situation. Most of the settings for manual interaction are the same as for automatic interaction, with the differences handled as described below.

For MTU, call API API blc_att_setMasterRxMTUSize(23) and blc_att_setSlaveRxMTUSize(23) when initialization, set the initial MTU to the minimum value of 23, and no automatic interaction is performed after stack comparison. When the user needs to interact with the MTU, call these two APIs (blc_att_setMasterRxMTUSize and blc_att_setSlaveRxMTUSize) to set the MTU to the actual value, and then just call the API blc_att_requestMtuSizeExchange() to trigger the MTU interaction.

For DLE, you can use API blc_ll_setAutoExchangeDataLengthEnable(0) to disable automatic interaction during initialization, and then call API blc_ll_sendDateLengthExtendReq() to trigger DLE interaction when you need to interact. Refer to the Controller API Section for specific descriptions of these two APIs.

Coded PHY/2M PHY

Coded PHY and 2M PHY are new features added in Core_v5.0, which largely extend the application scenarios of BLE, Coded PHY includes S2 (500 kbps) and S8 (125 kbps) to adapt to longer distance applications, and 2M PHY (2 Mbps) greatly improves the BLE bandwidth. 2M PHY/Coded PHY can be used in the advertising channel or the data channel in the connected state.

(1) Coded PHY/2M PHY Demonstration Introduction

In the tc_ble_sdk, to conserve SRAM, the Coded PHY/2M PHY is disabled by default. Users can manually enable this feature if they choose to use it.

  • For the local device, refer to vendor/feature_test/feature_2M_coded_phy.

Define the following macro in feature_config.h:

#define FEATURE_TEST_MODE                   TEST_2M_CODED_PHY_CONNECTION
  • For the peer device, users can use a mobile phone or other standard BLE Master device; alternatively, they can use any Telink BLE SDK that supports Coded PHY/2M PHY to compile and run a BLE Master device, such as the xxx_kma_master_dongle compiled using the TC BLE Single-Connection SDK or the xxx_master_dongle compiled using the tc_ble_sdk.

(2) Code PHY/2M PHY API Introduction

a. API blc_ll_init2MPhyCodedPhy_feature()

void blc_ll_init2MPhyCodedPhy_feature(void);

This SPI is used to enable the coded PHY/2M PHY.

b. API blc_ll_setPhy()

ble_sts_t   blc_ll_setPhy(  u16 connHandle,          le_phy_prefer_mask_t all_phys, le_phy_prefer_type_t tx_phys, le_phy_prefer_type_t rx_phys, le_ci_prefer_t phy_options);

This is the Bluetooth Core Specification standard interface. For details, please refer to Bluetooth Core Specification V5.3, Vol. 4, Part E, 7.8.49 LE Set PHY command. It is used to trigger the local device to actively request a PHY exchange. If the PHY exchange determines that a new PHY can be used, the master device triggers a PHY update, and the new PHY takes effect in a short time.

connHandle: Master/Slave connHandle is filled according to the actual situation, refer to "Connection Handle".

For other parameters, please refer to the Bluetooth Core Specification definition, combined with the enumeration type definition on the SDK and the demo usage to understand.

Channel Selection Algorithm #2

Channel Selection Algorithm #2 is a new Feature added in Bluetooth Core Specification V5.0, which has stronger anti-interference capability. For the specific instructions of the channel selection algorithm, please refer to Bluetooth Core Specification V5.3, Vol 6, Part B, 4.5.8.3 Channel Selection algorithm #2.

In the tc_ble_sdk, CSA #2 is disabled by default and can be enabled manually if the user chooses to use this feature, which requires calling the following API in user_init().

void blc_ll_initChannelSelectionAlgorithm_2_feature(void);

CSA #2 can only be used to establish a connection when both the local device and the peer master/slave device support CSA #2 (i.e., the ChSel field is set to 1 on both devices).

Controller API

In the standard BLE protocol stack architecture shown, the application layer cannot interact directly with the Link Layer of Controller, the data must be sent down through the Host, and finally the Host transmits the control commands to the Link Layer through the HCI. All Controller control commands issued by the Host through the HCI interface are strictly defined in the Bluetooth Core Specification. For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E: Host Controller Interface Functional Specification.

The tc_ble_sdk adopts the Whole Stack architecture, and the application layer directly operates and sets the Link Layer across the Host, but the APIs used are strictly in accordance with the HCI part of the Bluetooth Core Specification standard.

The declaration of the Controller API is in the header file in the stack/ble/controller directory and is classified according to the Link Layer state machine functions as ll.h, leg_adv.h, leg_scan.h, leg_init.h, acl_slave.h, acl_master.h.h, acl_conn.h, etc. Users can search according to the function of Link Layer, for example, APIs related to legacy advertising should be declared in leg_adv.h.

The enumeration type ble_sts_t is defined in stack/ble/ble_common.h, this type is used as the return value type for most of the APIs in the SDK, BLE_SUCCESS (value 0) is returned only when the setup parameters of the API call are all correct and accepted by the protocol stack; all other non-zero values returned indicate setting errors, and each different value corresponds to an error type. In the following API specific description, all possible return values of each API will be listed, and the specific error reasons of each error return value will be explained.

This return value type ble_sts_t is not limited to the API of Link Layer, but also applies to some APIs of Host layer.

(1) BLE MAC Address Initialization

The most basic types of BLE MAC addresses in this document include public addresses and random static addresses.

Call the following API to obtain public address and random static address:

void blc_initMacAddress(int flash_addr, u8 *mac_public,  u8 *mac_random_static);

The flash_addr can fill in the address of the MAC address stored on the flash, refer to the document in front of the introduction "SDK Flash Space Allocation". If you do not need random static address, the mac_random_static obtained above can be ignored.

After the BLE public MAC address is successfully obtained, the API for Link Layer initialization is called and the MAC address is passed into the BLE protocol stack:

blc_ll_initStandby_module (mac_public); //mandatory

(2) blc_ll_setAdvData

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.8.7 LE Set Advertising Data command.

Protocol Stack Advertising Package Format

In the BLE stack, the format of the advertising packet is shown above, the first two bytes are the Header, followed by the Advertising PDU, up to 37 bytes, including AdvA (6 bytes) and AdvData (up to 31 bytes).

The following API is used to set the data in the AdvData section:

ble_sts_t   blc_ll_setAdvData (u8 *data, u8 len);

The data pointer points to the first address of the PDU, and len is the length of the data.

The possible results returned by the return type ble_sts_t are shown in the table below:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success

The return value ble_sts_t is only BLE_SUCCESS, the API will not carry out parameter reasonableness check, the user needs to pay attention to the reasonableness of setting parameters.

The user can call this API to set the advertising data during initialization, and can also call this API at any time in the main_loop to modify the advertising data when the program is running.

(3) blc_ll_setScanRspData

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.8.8 LE Set Scan Response Data command.

Similar to the advertising packet PDU settings above, the scan response PDU settings use the API:

ble_sts_t   blc_ll_setScanRspData (u8 *data, u8 len);

The data pointer points to the first address of the PDU, and len is the length of the data.

The return type ble_sts_t may return the results shown in the table below:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success

The return value ble_sts_t is only BLE_SUCCESS, the API will not carry out parameter reasonableness check, the user needs to pay attention to the reasonableness of setting parameters.

The user can call this API to set the scan response data during initialization, and can also call this API at any time in the main_loop to modify the scan response data when the program is running.

(4) blc_ll_setAdvParam

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.8.5 LE Set Advertising Parameters command.

Advertising Event in BLE Stack

The Advertising Event (ADV Event) in the BLE stack is shown in the figure above, which means that at each T_advEvent, the Slave performs an advertising round, sending a packet on each of the three advertising channels (channel 37, channel 38, channel 39).

The following API sets the parameters related to ADV Event.

ble_sts_t   blc_ll_setAdvParam( adv_inter_t intervalMin, adv_inter_t intervalMax, 
adv_type_t advType, own_addr_type_t ownAddrType,  
u8 peerAddrType, u8 *peerAddr, adv_chn_map_t adv_channelMap, adv_fp_type_t advFilterPolicy); 

a. intervalMin & intervalMax

Set the range of advertising interval (ADV interval), with 0.625 ms as the basic unit, the range is between 20 ms ~ 10.24 s, and intervalMin is less than or equal to intervalMax.

The SDK uses intervalMin for advertising intervals in the connected state and intervalMax for advertising intervals in the non-connected state.

If intervalMin > intervalMax is set, intervalMin will be forced to equal intervalMax.

According to different advertising packet types, the values of intervalMin and intervalMax are limited. Please refer to (Vol 6/Part B/ 4.4.2.2 "Advertising Events").

b. advType

Referring to Bluetooth Core Specification, the four basic types of advertising events are as follows:

BLE Protocol Stack Four Advertising Events

In the above figure, the "Allowable response PDUs for advertising event" section uses YES and NO to indicate whether various types of advertising events respond to Scan Requests and Connect Requests from other devices. For example, the first Connectable Undirected Event can respond to both Scan Requests and Connect Requests, whereas a Non-connectable Undirected Event responds to neither.

Note that the second Connectable Directed Event adds a "*" sign in the upper right corner of the "YES" response to the Connect Request, indicating that it will definitely respond as long as it receives a matching Connect Request, and will not be a whitelist filter. The remaining three "YES" means that you can respond to the corresponding request, but the actual need to rely on the settings of the whitelist, according to the whitelist filter conditions to determine whether the final response, the latter will be described in detail in the whitelist.

Among the above four advertising events, Connectable Directed Event is further divided into Low Duty Cycle Directed Advertising and High Duty Cycle Directed Advertising, so that a total of five types of advertising events can be obtained, as defined below (stack/ble/ble_common.h):

/* Advertisement Type */
typedef enum{
ADV_TYPE_CONNECTABLE_UNDIRECTED          = 0x00, // ADV_IND
ADV_TYPE_CONNECTABLE_DIRECTED_HIGH_DUTY  = 0x01, //ADV_INDIRECT_IND (high duty cycle)
ADV_TYPE_SCANNABLE_UNDIRECTED            = 0x02 //ADV_SCAN_IND
ADV_TYPE_NONCONNECTABLE_UNDIRECTED       = 0x03, //ADV_NONCONN_IND
ADV_TYPE_CONNECTABLE_DIRECTED_LOW_DUTY   = 0x04, //ADV_INDIRECT_IND (low duty cycle)
}adv_type_t;

The default most commonly used advertising type is ADV_TYPE_CONNECTABLE_UNDIRECTED.

c. ownAddrType

When specifying the advertising address type, the 4 optional values of ownAddrType are as follows:

typedef enum{
        OWN_ADDRESS_PUBLIC = 0,
        OWN_ADDRESS_RANDOM = 1,
        OWN_ADDRESS_RESOLVE_PRIVATE_PUBLIC = 2,
        OWN_ADDRESS_RESOLVE_PRIVATE_RANDOM = 3,
}own_addr_type_t;

Only the first two parameters are described here.

OWN_ADDRESS_PUBLIC means the public MAC address is used when advertising, the actual address comes from the setting of API blc_initMacAddress(flash_sector_mac_address, mac_public, mac_random_static) when MAC address is initialized.

OWN_ADDRESS_RANDOM indicates the use of a random static MAC address when advertising, which is derived from the value set by the following API:

ble_sts_t   blc_ll_setRandomAddr(u8 *randomAddr);

d. peerAddrType & *peerAddr

When advType is set to direct advertising packet type (ADV_TYPE_CONNECTABLE_DIRECTED_HIGH_DUTY and ADV_TYPE_CONNECTABLE_DIRECTED_LOW_DUTY), peerAddrType and *peerAddr are used to specify the type and address of the peer device MAC Address.

When advType is of other types, the values of peerAddrType and *peerAddr are invalid and can be set to 0 and NULL.

e. adv_channelMap

Set the advertising channel, you can select any one or more of the channels 37, 38, 39. The value of adv_channelMap can be set as follows: 3, or arbitrarily, or combined with them.

typedef enum{
    BLT_ENABLE_ADV_37   =       BIT(0),
    BLT_ENABLE_ADV_38   =       BIT(1),
    BLT_ENABLE_ADV_39   =       BIT(2),
    BLT_ENABLE_ADV_ALL  =       (BLT_ENABLE_ADV_37 | BLT_ENABLE_ADV_38 | BLT_ENABLE_ADV_39),
}adv_chn_map_t;

f. advFilterPolicy

It is used to set the filtering policy for scan requests and connect requests of other devices when sending advertising packets. The filtered addresses need to be stored in the whitelist in advance. It is explained in detail later in the whitelist introduction.

The four filter types that can be set are as follows, if you do not need the whitelist filter function, select ADV_FP_NONE.

typedef enum {
    ADV_FP_ALLOW_SCAN_ANY_ALLOW_CONN_ANY = 0x00,
    ADV_FP_ALLOW_SCAN_WL_ALLOW_CONN_ANY = 0x01,     
    ADV_FP_ALLOW_SCAN_ANY_ALLOW_CONN_WL = 0x02,     
    ADV_FP_ALLOW_SCAN_WL_ALLOW_CONN_WL = 0x03,  
    ADV_FP_NONE = ADV_FP_ALLOW_SCAN_ANY_ALLOW_CONN_ANY
} adv_fp_type_t;

The possible values and reasons for the return value ble_sts_t are shown in the following table:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success

The return value ble_sts_t is only BLE_SUCCESS, the API will not carry out parameter reasonableness check, the user needs to pay attention to the reasonableness of setting parameters.

According to the design of the Host command in the HCI part of the Bluetooth Core Specification, when setting the advertising parameters, the eight parameters listed above are configured simultaneously. The idea of setting them at the same time is reasonable because there is a coupling relationship between some different parameters, such as advType and advInterval. The range limits for intervalMin and intervalMax will be different under different advType, so there will be different range checks. If set advType and set advInterval are split into two different APIs, the range checks between them cannot be controlled.

(5) blc_ll_setAdvEnable

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.8.9 LE Set Advertising Enable command.

ble_sts_t   blc_ll_setAdvEnable(adv_en_t adv_enable);

When en is 1, enable advertising; when en is 0, disable advertising.

For the state machine changes of enabling or disabling advertising, please refer to the section Link Layer State Combinations.

The possible values and reasons for the return value ble_sts_t are shown in the following table:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success
HCI_ERR_CONN_REJ_LIMITED_RESOURCES 0x0D appMaxSlaveNum is 0, not allowed to set

(6) blc_ll_setAdvCustomedChannel

This API is used to customize special advertising & scanning channels, and is only meaningful for some very special applications, such as BLE mesh.

void    blc_ll_setAdvCustomedChannel(u8 chn0, u8 chn1, u8 chn2);

For chn0/chn1/chn2, fill in the frequency points that need to be customized; the default standard frequency points are 37/38/39, for example, set 3 advertising channels for 2420 MHz, 2430 MHz, 2450 MHz, respectively, and can be called as follows.

blc_ll_setAdvCustomedChannel (8, 12, 22);

Regular BLE applications can use this API to achieve such a function. If the user wants to use single-channel advertising & single-channel scanning in some usage scenarios, for example, fixing the advertising channel & scanning channel to 39, you can call as follows:

blc_ll_setAdvCustomedChannel (39, 39, 39);

Note that this API will change both the advertising and scan channels.

(7) blc_ll_setScanParameter

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.8.10 LE Set Scan Parameters command.

ble_sts_t   blc_ll_setScanParameter ( scan_type_t scan_type, 
u16 scan_interval, u16 scan_window, 
own_addr_type_t ownAddrType, 
scan_fp_type_t scanFilter_policy);

Parameter description:

a. scan_type

Users can choose passive scan and active scan. The difference is that active scan will send scan_req based on receiving ADV packet to get more information about scan_rsp of the device, and the scan_rsp packet will also be sent to the BLE Host through ADV report event; passive scan does not send scan_req.

typedef enum {
        SCAN_TYPE_PASSIVE   = 0x00,
        SCAN_TYPE_ACTIVE    = 0x01,
} scan_type_t;

b. scan_interval/scan window

The scan_interval parameter sets the time interval between frequency point transitions during scanning, with a unit of 0.625 ms; scan_window specifies the duration of the scanning window. If the scan_window > scan_interval is set, the actual scan window is set to scan_interval.

For specific details on scanning, please refer to the relevant description of scanning in the preceding section.

c. ownAddrType

When specifying the scan req packet address type, the ownAddrType has four optional values as follows:

typedef enum{
    OWN_ADDRESS_PUBLIC = 0,
    OWN_ADDRESS_RANDOM = 1,
    OWN_ADDRESS_RESOLVE_PRIVATE_PUBLIC = 2,
    OWN_ADDRESS_RESOLVE_PRIVATE_RANDOM = 3,
}own_addr_type_t;

OWN_ADDRESS_PUBLIC indicates that the public MAC address is used during scanning, and the actual address comes from the setting of the API blc_initMacAddress(int flash_addr, u8 *mac_public, u8 *mac_random_static) when the MAC address is initialized.

OWN_ADDRESS_RANDOM indicates that the random static MAC address is used during scanning, which is derived from the value set by the following API:

ble_sts_t   blc_ll_setRandomAddr(u8 *randomAddr);

d. scan filter policy

typedef enum {
    SCAN_FP_ALLOW_ADV_ANY=0x00,//except direct ADV address not match
    SCAN_FP_ALLOW_ADV_WL=0x01,//except direct ADV address not match
    SCAN_FP_ALLOW_UNDIRECT_ADV=0x02,//and direct ADV address match initiator's resolvable private MAC
    SCAN_FP_ALLOW_ADV_WL_DIRECT_ADV_MACTH=0x03, //and direct ADV address match initiator's resolvable private MAC
} scan_fp_type_t;    

The currently supported scan filter policies are the following two:

SCAN_FP_ALLOW_ADV_ANY indicates that the Link Layer does not filter the scanned ADV packet and reports it directly to the BLE Host.

SCAN_FP_ALLOW_ADV_WL requires that the scanned ADV packet must be in the whitelist before reporting to the BLE Host.

The possible values and reasons for the return value ble_sts_t are shown in the following table:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success

The return value ble_sts_t is only BLE_SUCCESS, the API will not carry out parameter reasonableness check, the user needs to pay attention to the reasonableness of setting parameters.

(8) blc_ll_setScanEnable

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.8.11 LE Set Scan Enable command.

ble_sts_t   blc_ll_setScanEnable(scan_en_t scan_enable, dupFilter_en_t filter_duplicate);

The scan_enable parameter type has the following two optional values:

typedef enum {
    BLC_SCAN_DISABLE = 0x00,
    BLC_SCAN_ENABLE  = 0x01,
} scan_en_t;

When scan_enable is 1, enable scanning; when scan_enable is 0, disable scanning.

For the state machine changes of enabling/disabling scanning, please refer to "Link Layer State Combinations".

The filter_duplicate parameter type has the following two optional values:

typedef enum {
    DUP_FILTER_DISABLE = 0x00,
    DUP_FILTER_ENABLE  = 0x01,
} dupFilter_en_t;

When filter_duplicate is 1, it means that duplicate packet filtering is enabled; at this time, for each different ADV packet, the Controller will only report the ADV report event to the Host once. When filter_duplicate is 0, duplicate packet filtering is disabled, and the ADV packet scanned is always reported to the Host.

The return value ble_sts_t is shown in the following table:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success

After setting scan_type to active scan (blc_ll_setScanParameter) and enabling scanning, read scan_rsp once for each device and report it to the Host. Because after each enable scanning, the Controller will record the scan_rsp of different devices and store them in the scan_rsp list to ensure that the scan_req of the device will not be read again later.

If the user needs to report the scan_rsp of the same device multiple times, enable scanning repeatedly by configuring blc_ll_setScanEnable, because each time scanning is enabled/disabled, the scan_rsp list of the device will be cleared to 0.

(9) blc_ll_createConnection

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.8.12 LE Create Connection command.

ble_sts_t blc_ll_createConnection(u16 scan_interval,u16 scan_window, 
    init_fp_type_t initiator_filter_policy, 
    u8 adr_type, u8 *mac, u8 own_adr_type,
    u16 conn_min, u16 conn_max,u16 conn_latency, 
    u16 timeout, u16 ce_min, u16 ce_max )

a. scan_inetrval/scan window

The scan_interval/scan_window parameters are not currently handled in this API. If you need to set it, you can use the blc_ll_setScanParameter.

b. initiator_filter_policy

This parameter specifies the policy of the currently connected device. The following two options are available:

typedef enum {
    INITIATE_FP_ADV_SPECIFY = 0x00,  //connect ADV specified by host
    INITIATE_FP_ADV_WL = 0x01,  //connect ADV in whiteList
} init_fp_t; 

INITIATE_FP_ADV_SPECIFY indicates that the connected device address is adr_type/mac.

INITIATE_FP_ADV_WL means to connect according to the devices in the whitelist; at this time, adr_type/mac is meaningless.

c. adr_type/ mac

When initiator_filter_policy is INITIATE_FP_ADV_SPECIFY, the device with address type adr_type(BLE_ADDR_ PUBLIC or BLE_ADDR_RANDOM) and address mac[5...0] is connected.

d. own_adr_type

This parameter specifies the type of MAC address used by the Master that establishes the connection. The four optional values of ownAddrType are as follows.

typedef enum{
        OWN_ADDRESS_PUBLIC = 0,
        OWN_ADDRESS_RANDOM = 1,
        OWN_ADDRESS_RESOLVE_PRIVATE_PUBLIC = 2,
        OWN_ADDRESS_RESOLVE_PRIVATE_RANDOM = 3,
}own_addr_type_t;

OWN_ADDRESS_PUBLIC indicates that the public MAC address is used when connecting, and the actual address comes from the setting of the API blc_llms_initStandby_module(mac_public) when the MAC address is initialized.

OWN_ADDRESS_RANDOM indicates that the random static MAC address is used when connecting, which is derived from the value set by the following API:

ble_sts_t   blc_ll_setRandomAddr (u8 *randomAddr);

e. conn_min/ conn_max/ conn_latency/ timeout

These four parameters specify the connection parameters of the Master role after the connection is established. At the same time, these parameters will also be sent to the Slave through the connection request, and the Slave will use the same connection settings.

The conn_min/conn_max specifies the range of the connection interval in 1.25 ms. If appMaxMasterNum > 1, the conn_min/conn_max parameter is invalid, the Master connection interval in the SDK is fixed to 25 by default (the actual interval is 31.25 ms = 25 × 1.25 ms), in which case the setting can be changed by calling blc_ll_setAclMasterConnectionInterval before establishing the connection; if appMaxMasterNum is 1, the value of conn_max is used directly for the Master connection interval in the SDK.

The conn_latency specifies connection latency, generally set to 0.

The timeout specifies the connection supervision timeout with a unit of 10 ms.

f. ce_min/ ce_max

The tc_ble_sdk does not handle ce_min/ ce_max yet.

Return value list:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success
HCI_ERR_CONN_REJ_ LIMITED_RESOURCES 0x0D The Link Layer is already in the Initiating state and no longer accepts new connection requests, or the device is currently in the Connection state.

The API does not check the rationality of parameters, and users need to pay attention to the rationality of setting parameters.

(10) blc_ll_setCreateConnectionTimeout

ble_sts_t   blc_ll_setCreateConnectionTimeout(u32 timeout_ms);

The return value is BLE_SUCCESS, and the unit of timeout_ms is ms.

After blc_ll_createConnection is triggered to enter the initiating state, if the connection cannot be established for a long time, it will trigger an initialization timeout and exit the initiating state.

The default initialization timeout of tc_ble_sdk is 5 seconds. If the user does not want to use this default time, they can call blc_ll_setCreateConnectionTimeout to set the initialization timeout they need.

(11) blc_ll_setAclMasterConnectionInterval

ble_sts_t   blc_ll_setAclMasterConnectionInterval(u16 conn_interval);

The return value is BLE_SUCCESS, and the connection interval unit is 1.25 ms.

This API sets the Master base connection interval benchmark, through which the timing of multiple Masters can be staggered, ensuring that the timing does not conflict when multiple Masters are connected at the same time, and improving the efficiency of data transmission. The actual Master connection interval in effect is 1/2/3/4/6/8/12 times this benchmark.

(12) blc_ll_setDataLengthReqSendingTime_after_connCreate

void blc_ll_setDataLengthReqSendingTime_after_connCreate(int time_ms)

Set the pending time.

It is used to set the waiting time (unit: milliseconds) before performing DLE interaction.

(13) blc_ll_disconnect

ble_sts_t   blc_ll_disconnect(u16 connHandle, u8 reason);

Call this API to send a termination on the Link Layer to the peer Master/Slave device to actively disconnect the connection.

The connHandle is the handle value of the current connection.

Reason is the reason for disconnection, please refer to Bluetooth Core Specification V5.3, Vol 1, Part F, 2 Error code descriptions for details of the reason setting.

If termination is not caused by a system operation exception, the application layer generally specifies the reason as HCI_ERR_REMOTE_USER_TERM_CONN = 0x13, blc_ll_disconnect(connHandle, HCI_ERR_REMOTE_USER_TERM_CONN).

After calling this API to initiate disconnection, the HCI_EVT_DISCONNECTION_COMPLETE event must be triggered. Users can see in the callback function of this event that the corresponding termination reason is the same as the manually set reason.

In general, a direct call to this API can successfully send the termination and disconnect the connection, but there are some special cases that will cause the API call to fail, according to the return value ble_sts_t, which can help understand the corresponding error cause.

It is recommended that when the application layer calls this API, check whether the return value is BLE_SUCCESS.

The return value is as follows:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success
HCI_ERR_UNKNOWN_CONN_ID 0x02 connHandle error or cannot find the corresponding connection
HCI_ERR_CONN_REJ_LIMITED_RESOURCES 0x3E A large amount of data is being sent, the command cannot be accepted at the moment

(14) rf_set_power_level_index

The tc_ble_sdk provides an API for setting the energy of the BLE RF packet.

The API prototype of B85m is as follows:

void rf_set_power_level_index (RF_PowerTypeDef level);

The level setting of B85m refers to the enumeration variable RF_PowerTypeDef defined in drivers/8258(8278)/rf_drv.h.

This RF packet-sending energy set by this API is valid for both advertising packets and connection packets, and can be set anywhere in the program, the actual packet-sending energy is based on the most recent setting in time.

Note

  • The rf_set_power_level_index function internally sets some registers related to MCU RF, and once the MCU enters sleep (including suspend/deepsleep retention), the values of these registers will be lost. So the user needs to pay attention that this function has to be set again after each sleep wake-up. For example, the BLT_EV_FLAG_SUSPEND_EXIT event callback is used in the SDK demo to ensure that the RF power is reset every time the suspend wakes up.

(15) Whitelist & Resolvinglist

As mentioned earlier, the filter_policy of Advertising/Scanning/Initiating state all involve Whitelist, and the corresponding operations will be performed according to the devices in the Whitelist. The actual Whitelist concept includes two parts, Whitelist and Resolvinglist.

Whether the peer device address type is RPA (resolvable private address) can be determined by peer_addr_type and peer_addr. Use the following macro to determine.

#define IS_NON_RESOLVABLE_PRIVATE_ADDR(type, addr) 
((type)==BLE_ADDR_RANDOM && (addr[5] & 0xC0) == 0x00)

Only non-RPA addresses can be stored in whitelist. Currently, the whitelist in the tc_ble_sdk can store up to 4 devices.

Whitelist related APIs are as follows:

#define     MAX_WHITE_LIST_SIZE                 4

Whitelist related APIs are as follows:

ble_sts_t ll_whiteList_reset(void);

Reset the whitelist; the return value is BLE_SUCCESS.

ble_sts_t ll_whiteList_add(u8 type, u8 *addr);

Add a device to the whitelist, return a list of values:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success
HCI_ERR_MEM_CAP_EXCEEDED 0x07 whitelist is full, add failed
ble_sts_t ll_whiteList_delete(u8 type, u8 *addr);

Delete the previously added device from the Whitelist, and the return value is BLE_SUCCESS.

RPA (resolvable private address) device needs to use ResolvingList. In order to save RAM usage, currently the Resolvinglist in the tc_ble_sdk can store up to 2 devices:

#define         MAX_RESOLVING_LIST_SIZE             2

Resolvinglist related APIs are as follows:

ble_sts_t  ll_resolvingList_reset(void);

Reset Resolvinglist. The return value is BLE_SUCCESS.

ble_sts_t  ll_resolvingList_setAddrResolutionEnable(u8 resolutionEn);

Device address parsing and use: if you want to use Resolvelist to parse the address, you must enable it. You can disable it when you do not need to parse.

ble_sts_t  ll_resolvingList_add(u8 peerIdAddrType, u8 *peerIdAddr, 
u8 *peer_irk, u8 *local_irk);

To add a device using an RPA address, set peerIdAddrType/peerIdAddr and peer_irk to the peer device’s advertised identity address and IRK. These parameters are stored in Flash during the pairing and encryption process. Users can find the APIs for obtaining this information in the SMP section. For local_irk, it is not currently handled by the SDK, fill in NULL.

ble_sts_t  ll_resolvingList_delete(u8 peerIdAddrType, u8 *peerIdAddr);

Delete the previously added device. Return value: BLE_SUCCESS.

Whitelist/Resolvinglist implements the use of address filtering, please refer to the 8258_multi_conn_feature_test project (feature_whitelist.c).

Define macros in vendor/feature_test/app_config.h:

#define FEATURE_TEST_MODE   TEST_WHITELIST

Host Controller Interface

HCI (Host Controller Interface) is the bridge between Host and Controller, it defines the various types of data that Host and Controller interact with, such as CMD, Event, ACL, SCO, ISO, etc. HCI makes it possible to implement Bluetooth Host and Controller on different hardware platforms. For details of the HCI, see Bluetooth Core Specification V5.3, Vol 4: Host Controller Interface.

HCI Transport is the transport layer of HCI and is responsible for the transport of data of the various data types of HCI. HCI Transport defines the Type Indicator for the different data types of HCI, as shown in the figure below.

HCI Packet Type Indicator

HCI Software Structure

The architecture of the HCI software in the tc_ble_sdk is shown in the figure below; HCI Transport is the software implementation of the HCI Transport Layer, the source code of which is completely open to users; Controller HCI mainly implements the parsing and processing of HCI CMD and HCI ACL, generates HCI Events, and provides functional interfaces for HCI Transport. This section describes the use of Telink HCI in detail around the HCI Transport and Controller HCI interfaces.

Telink HCI Software Structure

HCI Transport is used to transport HCI protocol packets, it does not need to parse HCI protocol packets, it only needs to receive HCI protocol packets according to HCI Type and then hand them over to Controller HCI for processing. HCI Transport supports various hardware interfaces, such as USB, UART, SDIO, etc., among which UART is commonly used. The tc_ble_sdk currently only provides HCI Transport for the UART interface, the software implementation of Telink BLE HCI transport can be found in the vendor/common/hci_transport folder of the SDK.

HCI UART Transport supports two protocols, H4 and H5, both of which are supported by the tc_ble_sdk and are available as open source code. The software architecture of the Telink SDK HCI Transport is shown in the figure below.

Telink HCI Transport Software Structure

H4 Protocol is the software implementation of the HCI UART Transport H4 protocol; H5 Protocol is the software implementation of the HCI UART Transport H5 protocol; HCI Transport Control is the configuration management layer of HCI Transport and provides everything a user needs to use HCI Transport, so users using HCI Transport need only focus on this layer.

(1) H4 Protocol

a. H4 PDU

The H4 PDU format is shown in the figure below.

H4 PDU Format

The H4 PDU consists of the HCI Type Indicator and Payload. The HCI Type Indicator specifies the content of the Payload, and the value of the HCI Type Indicator as shown below; the Payload can be Protocol packets such as HCI CMD, HCI ACL, HCI Event, etc.

The Host sends H4 PDUs to the Controller, and the H4 Protocol software implements the receiving and parsing of H4 PDUs, which can be found in the hci_tr_h4.c and hci_tr_h4.h files in the vendor/common/hci_transport folder.

The user needs to configure the UART RX buffer size and the number of buffers according to the requirements when using the H4 protocol software. This can be configured via the macros HCI_H4_TR_RX_BUF_SIZE and HCI_H4_TR_RX_BUF_NUM in the hci_tr_h4.h file. In fact, the SDK calculates the H4 UART buffer size automatically for the user's convenience. The user only needs to configure the macro HCI_TR_RX_BUF_SIZE in the hci_tr.h file. HCI_H4_TR_RX_BUF_NUM does not normally need to be modified by the user, unless there is a special requirement.

b. H4 API

H4 Protocol provides 3 API:

void HCI_Tr_H4Init(hci_fifo_t *pFifo);
void HCI_Tr_H4RxHandler(void);
void HCI_Tr_H4IRQHandler(void);

void HCI_Tr_H4Init(hci_fifo_t *pFifo)

Function: This function is the initialization of H4, including UART, RX Buffer, etc.

Parameter:

Parameter Description
pFifo Points to the Controller HCI RX FIFO, which is used to store the received and parsed HCI protocol packets for processing by the Controller. This parameter is provided by the Controller HCI.

!!! note This function is not normally called by the user, it is called by the HCI Transport Control layer.

 

**void HCI_Tr_H4RxHandler(void) **

Function: This function implements the parsing and processing of H4 PDUs.

Parameter: none.

Note: This function is not normally called by the user, it is called by the HCI Transport Control layer.

 

void HCI_Tr_H4IRQHandler(void)

Function: This function implements the UART RX/TX interrupt handling.

Parameter: none.

Note: This function is not normally called by the user, it is called by the HCI Transport Control layer.

(2) H5 Protocol

H5, also known as 3-wire UART, supports software flow control and retransmission mechanisms. H5 has higher reliability than H4, but is not as efficient as H4. See Bluetooth Core Specification V5.3, Vol 4, Part D: Three-wire UART Transport Layer for details.

H5 PDUs (Protocol Data Units) need to be encoded before transmission and decoded before the H5 PDUs can be parsed, the encoding and decoding of H5 PDUs are done by the Slip Layer.

The Telink H5 software architecture is shown as follows: UART is used for sending and receiving data; Slip layer implements the encoder and decoder of H5 PDUs, which is responsible for encoding and decoding H5 PDUs; H5 Handler implements the parsing and processing of H5 PDUs, creation of H5 Link, and traffic control and retransmission control. Users can view the H5 implementation in the hci_tr_h5.c, hci_slip.c and hci_h5.c files in the vendor/common/hci_transport folder.

H5 Software Architecture

a. Slip Layer

Slip Encode

Slip layer encoding will place a byte of C0 at the beginning and end of each H5 packet, all C0s appearing in the H5 packet will be encoded as DB DC sequences; all DBs appearing in the H5 packet will be encoded as DB DD sequences; here DB DCs and DB DDs are called Slip's escape sequences, and all Slip's escape sequences start with DBs. The table of Slip's escape sequences is shown below.

HCI Slip Packet

HCI Slip Sequence List

The encoding of the Slip in the tc_ble_sdk is done via the API HCI_Slip_EncodePacket(u8 *p, u32 len). The encoded data will be stored in the Slip Encode buffer. The Encode Buffer Size of the Slip can be set by the macro HCI_SLIP_ENCODE_BUF_SIZE, which is automatically calculated by the SDK for the convenience of the user. It does not need to be configured by the user. The user only needs to configure the macro HCI_TR_RX_BUF_SIZE in the vendor/common/hci_transport/hci_tr.h file.

Slip Decode

Once the Slip packet is received, a complete Slip packet is obtained by using the Slip packet start and end flags C0. Then the DB DC and DB DD sequences are converted to C0 and DB according to the Slip escape byte table, so that the Slip is decoded and the H5 PDU is parsed next.

The decoding of the Slip in tc_ble_sdk is implemented by void HCI_Slip_DecodePacket(u8 *p, 32 len), and the decoded data is stored in the Slip Decode Buffer. The size of the Slip Decode Buffer can be set by the macro HCI _SLIP_DECODE_BUF_SIZE. For user convenience, the SDK has implemented automatic calculation of HCI_SLIP_DECODE_BUF_SIZE, which does not require user configuration. The user only needs to configure the macro HCI_TR_RX_BUF_SIZE in the vendor/common/hci_transport/hci_tr.h file.

b. H5 Handle

The H5 Handler implements the parsing and processing of H5 PDUs, H5 Link creation, traffic control and retransmission control.

H5 PDU

H5 PDU (Protocol Data Unit). The H5 PDU contains 3 fields: Packet Header, Payload and an optional Data Integrity Check.

H5 PDU

The H5 PDU Header is constructed as follows.

H5 PDU Header

Sequence Number(SEQ): For unreliable packets, SEQ should be set to 0. For reliable packets, SEQ indicates the serial number of the packet. For each new packet received, SEQ should be increased by 1. The range of SEQ is 0 ~ 7. SEQ remains unchanged to indicate retransmission.

Acknowledgment Number(ACK): ACK should be set to the next packet sequence number expected by the device, in the range 0 ~ 7.

Data Integrity Check Present: Set to 1 to indicate that the PDU's Payload segment needs to be CRC-checked, that is, the Data Integrity Check in the PDU is present, otherwise it is not present.

Reliable Packet: Set to 1, use reliable transmission and SEQ will take effect.

Packet Type: H5 defines 8 packet types.

H5 Packet Type

Payload length: The length of the payload segment in the PDU.

Header CheckSum: The sum check value of the H5 PDU Header.

H5 Handler implements the parsing and processing of H5 PDUs through the function void HCI_H5_PacketHandler(u8 *p, u32 len), which is a function used internally by H5 and does not need to be called by the user.

H5 Link establishment and configuration information exchange

The Host and Controller need to establish an H5 connection and negotiate configuration information before exchanging H5 packets. The H5 link is established using the SYNC message, SYNC_RSP message, CONFIG message and CONFIG_RSP message.

Initially, the H5 is in the Uninitialized state and keeps sending SYNC messages. When the SYNC_RSP message is received, the H5 enters the Initialized state and keeps sending the CONFIG message. When the CONFIG_RSP message is received, the H5 enters the Active state. At this point the H5 link is successfully established and data can be sent and received.

Telink H5 initially sends a SYNC message at 250 ms intervals. After receiving the SYNC_RSP message, it enters the Initialized state and sends a CONFIG message at 250 ms intervals. After receiving the CONFIG_RSP message, it enters the connected state.

The CONFIG message and the CONFIG_RSP message contain the connection parameters used by the Host and the Controller, with the common parts being the final connection parameters. The configuration information for H5 connections mainly includes Sliding Window Size, OOF Flow Control, Data Integrity Check Type and Version.

Sliding Window Size: Sets the maximum number of packets that do not require an immediate ACK. When Sliding Window Size = 1, it means that after the Controller sends a packet, it must wait for the Host ACK before transmitting the next packet. When Sliding Window Size = N (N>1), the Controller can send N packets without waiting for an ACK from the Host, but after the Controller sends the Nth packet, it must wait for an ACK from the Host before sending other packets. The purpose of Sliding Window Size is to improve the efficiency of H5 transmission. Currently the Telink SDK only supports the case where Sliding Window = 1, and the case where Sliding Window > 1 is easily extended.

OOF Flow Control: Enables software flow control, this is not commonly used and therefore will not be detailed.

Data Integrity Check Type: Set to 1, the segments associated with Data Integrity Check in the H5 PDU will all be enabled.

Version: Set the H5 (3-wire UART) version. Currently it is v1.0.

In tc_ble_sdk, users can configure the H5 connection parameters with the following macros.

#define HCI_H5_SLIDING_WIN_SIZE        1                                 
#define HCI_H5_OOF_FLW_CTRL             HCI_H5_OOF_FLW_CTRL_NONE
#define HCI_H5_DATA_INTEGRITY_LEVEL    HCI_H5_DATA_INTEGRITY_LEVEL_NONE
#define HCI_H5_VERSION                   HCI_H5_VERSION_V1_0

H5 data interaction and retransmission

Once an H5 connection has been established between the Host and Controller, the packets can be exchanged between them. H5 supports flow control and retransmission mechanisms, which are implemented through the SEQ and ACK fields in the H5 PDU Header. The following diagram shows an example of H5 data interaction and retransmission.

Device A sends a SEQ of 6 and an ACK of 3 to Device B. A SEQ of 6 means that Device B expects packet number 6 and an ACK of 3 means that Device A expects the next packet number to be 3. Device B receives a packet from device A and sends a SEQ of 3 and an ACK of 7 to device A. The SEQ of device B is 3 because device A expects a packet of 3; the ACK of device B is 7 because device B has received a packet with serial number 6 and expects a new packet of 7 and so on.

Device A sends a packet with SEQ 0 and ACK 5 to Device B, but for some reason Device B does not receive this packet. Device B will retransmit the previous packet after some time because it has not received the packet from Device A.

H5 Data flow

Flow control:

After the Host and Controller have established an H5 connection, the main interaction is between data packets and pure ACK packets, which are packets with an H5 Packet Type of 0. Under normal circumstances, the peer device sends a data packet, the local device replies with a data packet, and then the two devices repeat this process, but there is always a time when the peer device and the local device have no data packets to send, so the device can send a pure ACK packet instead of a data packet. For example, if the Host enables scan, the Controller will keep reporting ADV reports, the Controller will have a constant stream of data packets, but the Host does not have a large number of data packets to send, if the Controller sends data packets to the Host, but does not receive a reply from the Host, then it will cause the Controller to keep retransmitting, the Host will never receive a new ADV report. The Host needs to send a pure ACK packet instead of a data packet, which is equivalent to a reply to the Controller, so that the Host will keep receiving new ADV reports.

Retransmission:

There are several cases of retransmission: after the local device sends a data packet, if it does not receive a reply (data packet or pure ACK packet) from the other device before the timeout is reached, the local device will resend; if the ACK value in the data packet or pure ACK packet received from the other device indicates that a local retransmission is required, the local device will resend. The SEQ should remain the same.

c. H5-related APIs

H5 has two important APIs.

void HCI_H5_Init(hci_fifo_t *pHciRxFifo, hci_fifo_t *pHciTxFifo);
void HCI_H5_Poll(void);

void HCI_H5_Init(hci_fifo_t *pHciRxFifo, hci_fifo_t *pHciTxFifo)

Function: This function is used to initialize H5.

Parameters

Parameter Description
pHciRxFifo Point to Controller HCI RX FIFO
pHciTxFifo Point to Controller HCI TX FIFO, H5 will take over the HCI TX FIFO and does not need to be managed by the user, reducing the ease of use.   Note: This function does not normally need to be called by the user, it is called by the HCI Transport control layer.

Note: This function is not normally called by the user, it is called by the HCI Transport control layer.

 

void HCI_H5_Poll(void)

Function: This function is used to manage the parsing, sending, resend and flow control of H5 packets.

Parameter: none.

Note: This function is not normally called by the user, it is called by the HCI Transport control layer.

(3) HCI Transport Control

HCI Transport control is the centralized management layer of Telink HCI Transport, it is the bridge between HCI Transport and Controller HCI, and it also provides the macros and APIs needed for the user to configure and use HCI Transport. For those using the Telink Controller project, it is only necessary to focus on the HCI Transport Control layer. The macros and APIs provided by HCI Transport Control can be found in hci_tr.h in the Controller project.

a. HCI Transport Configuration

HCI Transport Control provides a range of configuration macros, which are described in detail below.

The user can select the Transport protocol to be used by using the following macros. The tc_ble_sdk uses HCI_TR_H4 by default.

/*! HCI transport layer protocol selection. */
#define HCI_TR_H4            0
#define HCI_TR_H5            1
#define HCI_TR_USB           2 /*!< Not currently supported */
#define HCI_TR_MODE          HCI_TR_H4

The user can set the maximum Size of the UART buffer on the Transport RX path and TX path with the following macros. HCI_TR_RX_BUF_SIZE should be set to the size of the maximum possible HCI packet to be received; HCI_TR_TX_BUF_SIZE should be set to the size of the maximum possible HCI packet to be sent, which applies for both H4 and H5. For example, if the maximum RX HCI ACL is 27B and the maximum RX HCI CMD Payload is 65B, then HCI_TR_RX_BUF_SIZE should be set to 1B (HCI Type Length) + 4B (HCI ACL Header Length) + max(27, 65) = 70B, and HCI_TR_TX_BUF_SIZE is calculated in the same way.

#define HCI_ACL_BUF_SIZE     (1 + 4 + HCI_RX_FIFO_SIZE)
#define HCI_TR_TX_BUF_SIZE   (1 + 2 + HCI_TX_FIFO_SIZE)
/*! HCI UART transport pin define */
#define HCI_TR_RX_PIN        GPIO_PB0
#define HCI_TR_TX_PIN        GPIO_PA2
#define HCI_TR_BAUDRATE      (115200)

HCI_TR_RX_PIN and HCI_TR_TX_PIN are used to set the UART TX/RX pins. HCI_TR_BAUDRATE is used to set the UART baud rate.

Regarding the UART baud rate selection, it should be noted that since BLE can use 1 Mbps and 2 Mbps, the UART baud rate should be matched accordingly, otherwise there may not be enough buffer when transmitting a large amount of ACL data. In addition, when the baud rate is low, increasing the buffer size and the number of buffers will consume more RAM, so a good baud rate matching is needed. We recommend that when the BLE rate is 1 Mbps, the UART baud rate should be greater than or equal to 1 Mbps; when the BLE rate is 2 Mbps, the UART baud rate should be greater than or equal to 2 Mbps.

b. HCI Transport API

In order to make it easier for users, HCI Transport eventually leaves the necessary APIs available to users, who only need to call them when actually using it.

void HCI_Tr_Init(void);
void HCI_Tr_Poll(void);
void HCI_Tr_IRQHandler(void);

void HCI_Tr_Init(void)

Function: This function is a wrapper for the initialization of the various Transport protocols. This function is required to initialize HCI Transport before the user can use the HCI Transport function.

Parameter: none.

 

void HCI_Tr_Poll(void)

Function: This function is a wrapper around the various transport protocol task handlers and needs to be called by the user in the main loop.

Parameter: none.

 

void HCI_Tr_IRQHandler(void)

Function: This function is a wrapper for the various transport protocols using interrupts. The user needs to call this API from within an interrupt.

Parameter: none.

(4) Controller HCI

The Controller HCI interface implements the parsing and processing of HCI protocol packets and generates HCI events, see Bluetooth Core Specification V5.3 Vol4 for details of HCI. This section will explain a few important APIs.

The Controller HCI provides the necessary APIs for users to use.

ble_sts_t blc_ll_initHciRxFifo(u8 *pRxbuf, int fifo_size, int fifo_number);
ble_sts_t blc_ll_initHciTxFifo(u8 *pTxbuf, int fifo_size, int fifo_number);
ble_sts_t blc_ll_initHciAclDataFifo(u8 *pAclbuf, int fifo_size, int fifo_number);
int blc_hci_handler(u8 *p, int n);

blc_ll_initHciRxFifo(u8 *pRxbuf, int fifo_size, int fifo_number)

Function: This function is used to initialize the HCI RX FIFO. The HCI RX FIFO can manage multiple sets of RX buffers. The HCI RX FIFO is used to store incoming HCI packets. The HCI RX Buffer needs to be defined by the user at the application level and registered by this function. HCI RX Buffer is defined in the Controller project, users can refer to the app_buffer.c and app_buffer.h files in the project.

Parameter:

Parameter Description
pRxbuf Point to RX buffer
fifo_size Size of each buffer in the RX FIFO, must be 16 bytes aligned.
fifo_number Number of buffer in RX FIFO, must be an exponential power of 2.

Note: The user needs to call when initialization.

 

blc_ll_initHciTxFifo(u8 *pTxbuf, int fifo_size, int fifo_number)

Function: This function is used to initialize the HCI TX FIFO, which can manage multiple TX buffer sets, and to store the HCI Evt and HCI ACL that the controller will send to the Host. The HCI TX Buffer needs to be defined by the user at the application level and registered with this function. The HCI TX Buffer is already defined in the controller project, you can refer to the app_buffer.c and app_buffer.h files in the project.

Parameter:

Parameter Description
pTxbuf Point to TX buffer
fifo_size Size of each buffer in the TX FIFO, must be 4 bytes aligned.
fifo_number Number of buffer in TX FIFO, must be an exponential power of 2.

Note: The user needs to call when initialization.

 

blc_ll_initHciAclDataFifo(u8 *pAclbuf, int fifo_size, int fifo_number)

Function: This function is used to initialize the HCI ACL FIFO, which can manage multiple ACL buffer sets. The HCI ACL FIFO is used to store the ACL data sent from the Host to the Controller. The HCI ACL buffer needs to be defined by the user at the application level and registered by this function. The HCI ACL buffer is already defined in the Controller project, the user can refer to the app_buffer.c and app_buffer.h files in the project.

Parameter:

Parameter Description
pAclbuf Point to ACL buffer
fifo_size Size of each buffer in the ACL FIFO, must be 4 bytes aligned.
fifo_number Number of buffer in ACL FIFO, must be an exponential power of 2.

Note: The user needs to call when initialization.

 

int blc_hci_handler(u8 *p,  int n)

Function: This function is the Controller HCI packet processor and implements the parsing and processing of HCI CMD and ACL packets.

Parameter:

Parameter Description
p Point to the received HCI protocol packets (using H4 PDU format)
n Not used because of the length information contained in the HCI protocol package.

Note: This function is called by the HCI Transport control layer and does not normally need to be called by the user.

Controller Project Introduction

The tc_ble_sdk provides a dedicated Controller project to facilitate integration with other Hosts. This section uses the Controller project from the tc_ble_sdk as an example. Its project file structure is shown in the figure below:

Controller Project File Structure

Except for the HCI section, the code in the Controller project is identical to that in other demo projects. The demo projects were introduced in previous sections; this section focuses on explaining the HCI-related code.

(1) main.c

The main.c file provides the program entry point and the global interrupt handler entry point. The main function, which serves as the program entry point, implements the initialization of hardware and software modules, as well as a while(1) loop. The global interrupt handler calls the interrupt handler functions of the BLE stack and the HCI Transport, as shown below:

void irq_handler(void)
{   
    blc_sdk_irq_handler ();

    HCI_Tr_IRQHandler();();
}

(2) app.c

The initialization program for the Controller HCI interface and HCI Transport is called in the user_init_normal() function in the app.c file. HCI Transport task processing is called in the main_loop() function, as follows:

void user_init_normal(void)
{
     ……

#if (HCI_NEW_FIFO_FEATURE_ENABLE)
    /* HCI RX FIFO */
    blc_ll_initHciRxFifo(app_bltHci_rxfifo, HCI_RX_FIFO_SIZE, HCI_RX_FIFO_NUM);
    /* HCI TX FIFO */
    blc_ll_initHciTxFifo(app_bltHci_txfifo, HCI_TX_FIFO_SIZE, HCI_TX_FIFO_NUM);
    /* HCI RX ACL FIFO */
    blc_ll_initHciAclDataFifo(app_hci_aclDataFifo, HCI_ACL_DATA_FIFO_SIZE, HCI_ACL_DATA_FIFO_NUM);
#endif

    /* HCI Data && Event */
    blc_hci_registerControllerDataHandler (blc_hci_sendACLData2Host);
    blc_hci_registerControllerEventHandler(blc_hci_send_data); //controller hci event to host all processed in this function.

    //bluetooth event
    blc_hci_setEventMask_cmd (HCI_EVT_MASK_DISCONNECTION_COMPLETE);
    //bluetooth low energy(LE) event, all enable
    blc_hci_le_setEventMask_cmd( 0xFFFFFFFF );
    blc_hci_le_setEventMask_2_cmd( 0x7FFFFFFF );

     ……
    ////////////////// SPP initialization ///////////////////////////////////
#if HCI_TR_EN
    HCI_Tr_Init();
    blc_register_hci_handler(rx_from_uart_cb, tx_to_uart_cb);

    DFU_Init();
#endif
}

void main_loop(void)
{
#if HCI_TR_EN
    HCI_Tr_Poll();
    DFU_TaskStart();
#endif

    ……
}

(3) app_buffer.c and app_buffer.h

These two files primarily define all the buffers used by the SDK. The HCI section of the Controller uses three buffers: app_bltHci_rxfifo[], app_bltHci_txfifo[], and app_hci_aclDataFifo[]. The sizes of these buffers are controlled by the following macros.

#define HCI_MAX_TX_SIZE                 max2(ACL_SLAVE_MAX_TX_OCTETS, ACL_MASTER_MAX_TX_OCTETS) //support common tx max

#define HCI_TX_FIFO_SIZE                HCI_FIFO_SIZE(HCI_MAX_TX_SIZE)
#define HCI_TX_FIFO_NUM                 8

#define HCI_RX_FIFO_SIZE                HCI_FIFO_SIZE(ACL_CONN_MAX_RX_OCTETS)
#define HCI_RX_FIFO_NUM                 4

#define HCI_ACL_DATA_FIFO_SIZE          CALCULATE_HCI_ACL_DATA_FIFO_SIZE(LE_ACL_DATA_PACKET_LENGTH)
#define HCI_ACL_DATA_FIFO_NUM           8

HCI_TX_FIFO_SIZE: Used to set the size of the HCI TX buffer. It should be set to the larger of the maximum possible HCI Event payload and HCI ACL payload, plus 4 bytes for the HCI ACL header, 1 byte for the HCI type, and 2 bytes for internal Telink memory, and then aligned to 4 bytes. For example: if the maximum HCI Event payload is 65 bytes and the maximum HCI ACL payload is 27 bytes, then HCI_TX_FIFO_SIZE = align16(2 bytes (Telink) + 1 byte (HCI Type) + 4 bytes (HCI ACL Header) + MAX(27, 65)) = 80 bytes.

HCI_TX_FIFO_NUM: Used to set the number of HCI TX buffers; must be a power of 2, such as 2, 4, 8…

HCI_RX_FIFO_SIZE: Used to set the size of the HCI RX buffer. It should be set to the larger of the maximum possible HCI CMD payload and HCI ACL payload, plus 4 bytes for the HCI ACL header and 1 byte for the HCI type, then aligned to 16 bytes. For example, if the maximum HCI command payload is 65 bytes and the maximum HCI ACL payload is 27 bytes, then HCI_RX_FIFO_SIZE = align16(1 byte (HCI Type) + 4 bytes (HCI ACL Header) + max(27, 65)) = 80 bytes.

HCI_RX_FIFO_NUM: Used to set the number of HCI RX buffers; must be a power of 2, such as 2, 4, 8…

LE_ACL_DATA_PACKET_LENGTH: Used to set the maximum HCI ACL payload; this value cannot exceed 252.

HCI_ACL_DATA_FIFO_SIZE: Used to set the ACL data buffer size. Users can directly call the macro CALCULATE_HCI_ACL_DATA_FIFO_SIZE(LE_ACL_DATA_PACKET_LENGTH) to automatically calculate this value.

HCI_ACL_DATA_FIFO_NUM: Used to set the number of HCI ACL data buffers; this value must be a power of 2, such as 2, 4, 8, etc.

Controller Event

In order to satisfy the recording and processing of some key actions on the bottom layer of multiple connection BLE stack in the application layer, the SDK provides two types of events as shown below: one is the standard HCI event defined by the BLE controller; the second is the BLE host defined by some protocol stack process interaction event notification type GAP event (can also be considered as host event, please refer to the "GAP event" Section of this document for details). This section mainly introduces the Controller event.

BLE SDK Event Structure

Note

  • In the TC BLE Single Connection SDK, Telink provides a set of Controller events defined by itself, which are mostly the same as the HCI events specified in the Bluetooth Core Specification, and in the tc_ble_sdk, the event defined by Telink in the repeated part is removed, and the user can use the standard event.

(1) Controller HCI Event Classification

The Controller HCI event is designed according to the Bluetooth Core Specification standard.

The Host + Controller architecture is shown in the figure below, the Controller HCI event reports all events of the Controller to the Host through HCI.

Host + Controller Architecture

For the definition of Controller HCI event, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.7 Events for details. Among them, the 7.7.65 LE Meta Event refers to HCI LE(low energy) Event, and the others are ordinary HCI events. The tc_ble_sdk also divides Controller HCI events into two categories: HCI Event and HCI LE Event. Since tc_ble_sdk mainly focuses on low-power Bluetooth, only a few basic HCI events are supported, while the vast majority of HCI LE events are supported.

Controller HCI event-related macro definitions, interface definitions, etc., please refer to the header file in the stack/ble/hci directory.

If the user needs to receive Controller HCI event in the Host or application layer, first need to register the callback function of Controller HCI event, and then open the mask of the corresponding event, mask open API see below event analysis.

The callback function prototype and registration interface of the Controller HCI event are:

typedef int (*hci_event_handler_t) (u32 h, u8 *para, int n);
void  blc_hci_registerControllerEventHandler(hci_event_handler_t  handler);

The u32 h in the callback function prototype is a marker, which is used in many places in the lower layer protocol stack. The user only needs to know the following:

#define         HCI_FLAG_EVENT_BT_STD               (1<<25)

The HCI_FLAG_EVENT_BT_STD flag indicates that the current event is a Controller HCI event.

In the callback function prototype, para and n represent the data and data length of the event, which is consistent with that defined in the Bluetooth Core Specification. Users can refer to the following usage in the code and the specific implementation of the app_controller_event_callback function.

blc_hci_registerControllerEventHandler(app_controller_event_callback);

(2) Common Controller HCI Event

Most HCI events are supported in the tc_ble_sdk, and the following are the events that users may use.

#define HCI_EVT_DISCONNECTION_COMPLETE                         0x05
#define HCI_EVT_LE_META                                        0x3E

a. HCI_EVT_DISCONNECTION_COMPLETE

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.7.5 Disconnection Complete event. The data structure pointed to by the callback pointer is as follows:

typedef struct {
    u8         status;
    u16        connHandle;
    u8         reason;
} hci_disconnectionCompleteEvt_t;

b. HCI_EVT_LE_META

It indicates that the current event is an HCI LE event, and the specific event type is determined according to the following sub-event code.

In the HCI event, except for HCI_EVT_LE_META (HCI_EVT_LE_META uses blc_hci_le_setEventMask_cmd to open the event mask), all others have to open the event mask through the following API.

ble_sts_t blc_hci_setEventMask_cmd(u32 evtMask); //eventMask: BT/EDR

The definition of event mask is shown below:

#define HCI_EVT_MASK_DISCONNECTION_COMPLETE           0x0000000010    

If the user does not set the HCI event mask through this API, the SDK only opens the mask corresponding to HCI_EVT_MASK_DISCONNECTION_COMPLETE by default, that is, to ensure the reporting of the Controller disconnect event.

(2) Common HCI LE Event

When the event code in the HCI event is HCI_EVT_LE_META, it is the HCI LE event. The subevent code is the most commonly used, and the user may need to understand the following, the others will not be introduced.

#define HCI_SUB_EVT_LE_CONNECTION_COMPLETE                           0x01
#define HCI_SUB_EVT_LE_ADVERTISING_REPORT                            0x02
#define HCI_SUB_EVT_LE_CONNECTION_UPDATE_COMPLETE                    0x03
#define HCI_SUB_EVT_LE_PHY_UPDATE_COMPLETE                           0x0C

a. HCI_SUB_EVT_LE_CONNECTION_COMPLETE

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.7.65.1 LE Connection Complete event. The data structure pointed to by the callback pointer is as follows:

typedef struct {
    u8         subEventCode;
    u8         status;
    u16        connHandle;
    u8         role;
    u8         peerAddrType;
    u8         peerAddr[6];
    u16        connInterval;
    u16        slaveLatency;
    u16        supervisionTimeout;
    u8         masterClkAccuracy;
} hci_le_connectionCompleteEvt_t;

b. HCI_SUB_EVT_LE_ADVERTISING_REPORT

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.7.65.2 LE Advertising Report even. The data structure pointed to by the callback pointer is as follows:

typedef struct {
    u8  subcode;
    u8  nreport;
    u8  event_type;
    u8  adr_type;
    u8  mac[6];
    u8  len;
    u8  data[1];
} event_adv_report_t;

After the Controller's Link Layer scans the correct ADV packet, it is reported to the Host through HCI_SUB_EVT_LE_ADVERTISING_REPORT.

The data length of this event is variable (depending on the payload of the ADV packet), as shown below, please refer to the Bluetooth Core Specification directly for the specific data meaning.

ADVERTISING_REPORT Event Packet Format

Note

  • The LE Advertising Report Event in the tc_ble_sdk only reports one ADV packet at a time, that is, i is 1 in the above figure.

c. HCI_SUB_EVT_LE_CONNECTION_UPDATE_COMPLETE

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.7.65.3 LE Connection Update Complete event.

When the connection update on the Controller takes effect, report HCI_SUB_EVT_LE_CONNECTION_UPDATE_COMPLETE to the Host. The data structure pointed to by the callback pointer is as follows:

typedef struct {
    u8         subEventCode;
    u8         status;
    u16        connHandle;
    u16        connInterval;
    u16        connLatency;
    u16        supervisionTimeout;
} hci_le_connectionUpdateCompleteEvt_t;

d. HCI_SUB_EVT_LE_PHY_UPDATE_COMPLETE

For details, please refer to Bluetooth Core Specification V5.3, Vol 4, Part E, 7.7.65.12 LE PHY Update Complete event.

The data structure pointed to by the callback pointer is as follows:

typedef struct {
    u8         subEventCode;
    u8         status;
    u16        connHandle;
    u8         tx_phy;
    u8         rx_phy;
} hci_le_phyUpdateCompleteEvt_t;

HCI LE event needs to enable the mask through the following API.

ble_sts_t   blc_hci_le_setEventMask_cmd(u32 evtMask); //eventMask: LE

The definition of evtMask also corresponds to some given above, and other event users can check in hci_const.h.

#define HCI_LE_EVT_MASK_CONNECTION_COMPLETE         0x00000001
#define HCI_LE_EVT_MASK_ADVERTISING_REPORT          0x00000002
#define HCI_LE_EVT_MASK_CONNECTION_UPDATE_COMPLETE  0x00000004

If the user does not set the HCI LE event mask through this API, all HCI LE events are not open by the SDK by default.

Host

L2CAP

The logical link control and adaptation protocol is usually referred to as L2CAP (Logical Link Control and Adaptation Protocol), which connects the application layer upward and the controller layer downward, and plays the role of an adapter between the host and the controller, enabling the upper-layer application to operate no need to care about the controller's data processing details.

The L2CAP layer of BLE is a simplified version of the classic Bluetooth L2CAP layer, in the basic mode, it does not perform Segmentation and Reassembly, does not involve flow control and retransmission mechanism, and only uses fixed channels for communication. The simplified structure of L2CAP is shown in the figure below, which simply means that the application layer data is fragmented and sent to the BLE controller, and the data received by the BLE controller is packetized into different CID data and reported to the host layer.

BLE L2CAP Structure and ATT Packet Assembly Model

The L2CAP is designed according to the Bluetooth Core Specification, the main function is to complete the data docking of Controller and Host, most of which is done at the bottom of the protocol stack, and there are very few places where the user participates. The user can set it according to the following APIs.

(1) Register L2CAP Data Processing Function

In the tc_ble_sdk architecture, the data of the Controller is interfaced with Host through HCI, and data from HCI to Host is first processed at the L2CAP layer, using the following API to register this processing function:

void        blc_hci_registerControllerDataHandler(void *p);

The functions of the L2CAP layer to process Controller data are:

int         blt_l2cap_pktHandler(u16 connHandle, u8 *raw_pkt);

This function has been implemented in the protocol stack, it parses the received data and transmits it upwards to ATT, SIG, or SMP.

Initialization:

blc_hci_registerControllerDataHandler (blt_l2cap_pktHandler);

(2) Update Connection Parameters

a. Slave request to update connection parameters

In the BLE protocol stack, the Slave applies a set of new connection parameters to the Master through the L2CAP layer CONNECTION PARAMETER UPDATE REQUEST command. The command format is shown below, please refer to Bluetooth Core Specification V5.3, Vol 3, Part A, 4.20 L2CAP_CONNECTION_PARAMETER_UPDATE_REQ (code 0x12) for details.

Connection Para Update Req Format in BLE Protocol Stack

The tc_ble_sdk provides an API for the Slave to actively apply for updating connection parameters, which is used to send the CONNECTION PARAMETER UPDATE REQUEST command to the Master.

void  bls_l2cap_requestConnParamUpdate (u16 connHandle, u16 min_interval, u16 max_interval, u16 latency, u16 timeout);

This API is only used by the Slave. The unit of min_interval and max_interval is 1.25 ms, and the unit of timeout is 10 ms.

The tc_ble_sdk provides an API for the Slave to set the time to send requests to update connection parameters:

void bls_l2cap_setMinimalUpdateReqSendingTime_after_connCreate( u16 connHandle,  int time_ms)

Taking the connection establishment moment as the time reference point, the connection parameter update request will be sent out after time_ms has passed, this API is not called, and the default setting is 1000 ms.

If the API bls_l2cap_requestConnParamUpdate is called after time_ms after the connection is established, the connection parameter update request is sent immediately.

BLE Sniffer Packet Sample Conn Para Update Reqeust and Response

In the application, the SDK provides a gap event "GAP_EVT_L2CAP_CONN_PARAM_UPDATE_RSP" to obtain the connection request result, which is used to notify the user whether the connection parameter request applied by the Slave is accepted or not, as shown in the figure above, the Master accepts the Connection_Param_Update_Req parameter of the Slave.

The app_host_event_callback function reference is as follows:

int app_host_event_callback(u32 h, u8 *para, int n)
{
    u8 event = h & 0xFF;
    switch(event){
        .......
        case GAP_EVT_L2CAP_CONN_PARAM_UPDATE_RSP:
        {
            (gap_l2cap_connParamUpdateRspEvt_t*) p= (gap_l2cap_connParamUpdateRspEvt_t*) para;
            if( p->result == CONN_PARAM_UPDATE_ACCEPT ){
                //the LE Master Host has accepted the connection parameters
            }
            else if( p->result == CONN_PARAM_UPDATE_REJECT ){
                //The LE Master Host has rejected the connection parameter
            }
        }
        Break;
        ......
    }
  return 0;
}

b. Master responds to update requests

After the peer Slave applies for new connection parameters, the Master receives the command and returns the CONNECTION PARAMETER UPDATE RESPONSE command. For details, please refer to Bluetooth Core Specification V5.3, Vol 3, Part A, 4.21 L2CAP_CONNECTION_PARAMETER_UPDATE _RSP (code 0x13).

Regarding whether the actual Android and iOS devices accept the connection parameters applied by the user, it has to do with the practice of each manufacturer's BLE Master, and the standard is not uniform among them.

In the tc_ble_sdk, regardless of whether the Slave's parameter request is accepted or not, the following API is used to reply to this request:

void blc_l2cap_SendConnParamUpdateResponse(connHandle, req->id, connParaRsp);

This API is only available to Master. connHandle specifies the current connection handle, req->id is the Identifier value in the connection parameter update request, and connParaRsp reference is as follows:

typedef enum{
        CONN_PARAM_UPDATE_ACCEPT = 0x0000,
        CONN_PARAM_UPDATE_REJECT = 0x0001,
}conn_para_up_rsp;

In the tc_ble_sdk, after the Master determines the appropriate connection parameter request, if the user has registered GAP_EVT_L2CAP_CONN_PARAM_UPDATE_REQ, determines whether to agree to the connection parameter update to be performed by the user in the event callback, if not registered, the Master will directly enter the connection parameter update process at the bottom layer.

When GAP_EVT_L2CAP_CONN_PARAM_UPDATE_REQ takes effect, if the user does not agree to the connection parameter request, blc_l2cap_SendConnParamUpdateResponse needs to be called in the callback, and the third parameter set to CONN_PARAM_UPDATE_REJECT. If the user agrees to the connection parameter request, you need to first call blc_l2cap_SendConnParamUpdateResponse in the callback and set the third parameter to CONN_PARAM_UPDATE_ACCEPT, and then call blm_l2cap_processConnParamUpdatePending to enter the connection parameter update process.

void blm_l2cap_processConnParamUpdatePending(u16 connHandle, u16 min_interval, u16 max_interval, u16 latency, u16 timeout);

c. update connection parameters on Link Layer

The Master can perform connection parameter updates directly, or the Slave can send Connection_Update_Req, and after the Master replies with the Connection_Update_Rsp to accept the request, there will be the following process.

The Master will send the LL_CONNECTION_UPDATE_REQ command of the link layer, as shown in the following figure.

Sniffer Packet Display ll Conn Update Req

After the Slave receives the update request, it updates the connection parameters. Both the Master and the Slave will trigger the HCI event of HCI_SUB_EVT_LE_CONNECTION_UPDATE_COMPLETE.

ATT & GATT

(1) GATT Basic Unit Attribute

GATT defines two roles: Server and Client. In the tc_ble_sdk, the Slave device is the Server, and the Android, iOS, or Master device is the Client. Server needs to provide multiple services for the Client to access.

The essence of GATT's service is composed of multiple Attributes, each of which has a certain amount of information. When multiple Attributes of different kinds are combined, a basic service can be reflected.

Attribute Constitutes GATT Service

The basic content and attributes of an Attribute include the following:

a. Attribute Type: UUID

UUID is used to distinguish the type of each attribute, and its full length is 16 bytes. The UUID length in the BLE standard protocol is defined as 2 bytes, because the peer devices follow the same set of conversion methods to convert the UUID of 2 bytes into 16 bytes.

When the user directly uses the 2 bytes UUID of the Bluetooth standard, the Master device knows the device type represented by these UUIDs. Some standard UUIDs have been defined in the SDK and distributed in the following files: stack/ble/service/hids.h, stack/ble/attr/gatt_uuid.h.

Telink private some profiles (OTA, SPP, MIC, etc.) are not supported in standard Bluetooth. These private UUIDs are defined in stack/ble/service/uuid.h with a length of 16 bytes.

b. Attribute Handle

The service has multiple Attributes, and these Attributes form an Attribute Table. In the Attribute Table, each Attribute has an Attribute Handle value, which is used to distinguish each different Attribute. After the Slave and Master establish a connection, the Master parses and reads the Attribute Table of the Slave through the Service Discovery process, and corresponds to each different Attribute according to the value of the Attribute Handle, so that the data communication behind them, as long as they bring the Attribute Handle, the other party will know which Attribute's data.

c. Attribute Value

Each Attribute has a corresponding Attribute Value, which is used as data for request, response, notification, and indication. In this SDK, Attribute Value is described by a pointer and the length of the area pointed to by the pointer.

(2) Attribute and ATT Table

In order to implement the Slave's GATT service, the SDK designs an Attribute Table, which consists of multiple basic Attributes. The basic Attribute is defined as:

typedef struct attribute
    {
        u16  attNum;
        u8   perm;
        u8   uuidLen;
        u32  attrLen;    //4 bytes aligned
        u8*  uuid;
        u8*  pAttrValue;
        att_readwrite_callback_t  w;
        att_readwrite_callback_t  r;
    } attribute_t;

Combined with the current SDK reference Attribute Table to illustrate the meaning of the above. The Attribute Table code can be found in app_att.c, as shown in the following screenshot:

BLE SDK Attribute Table

Please note that const is added before the definition of the attribute table:

const attribute_t my_Attributes[ ] = { ... };

The const keyword will cause the compiler to store the data of this array in flash to save RAM space. All the contents of this Attribute Table definition on flash are read-only and cannot be rewritten.

a. attNum

attNum has two functions.

The first role of attNum is to indicate the number of all valid Attributes in the current Attribute Table, i.e., the maximum value of Attribute Handle, which is only used for the invalid Attribute in the item 0 of the Attribute Table array:

{57,0,0,0,0,0}, // ATT_END_H – 1 = 57

attNum = 57 indicates that there are 57 attributes in the current Attribute Table.

In BLE, the Attribute Handle value starts from 0x0001 and increases by one, while the subscript of the array starts from 0, adding the above virtual attribute in the Attribute Table makes the subscript number of each attribute in the data equal to the value of its Attribute Handle. After defining the Attribute Table, count the subscript of the Attribute in the current Attribute Table array to know the current Attribute Handle value of the Attribute.

After counting all the Attributes in the Attribute Table, the last number is the number attNum of valid Attributes in the current Attribute Table, which is currently 57 in the SDK. If the user adds or deletes Attribute, this attNum needs to be modified, you can refer to the enumeration ATT_HANDLE of vendor/acl_connection_demo/app_att.h.

The second role of attNum is to specify that the current service consists of several Attributes.

The UUID of the first Attribute of each service must be GATT_UUID_PRIMARY_SERVICE(0x2800), and the attNum on this Attribute specifies the count from the current Attribute, and there are a total of attNum Attributes that belong to the component of the service.

As shown in the figure above, the gap service UUID is the first Attribute of GATT_UUID_PRIMARY_SERVICE, and its attNum is 7, then the 7 attributes of Attribute Handle 0x0001 ~ Attribute Handle 0x0007 belong to the description of the gap service.

Similarly, after the attNum of the first Attribute of the HID service in the above graph is set to 27, the 27 consecutive Attributes from this Attribute are HID services.

Except for the 0th Attribute and each service's first Attribute, the attNum value of all other Attributes must be set to 0.

b. perm

The perm is the abbreviation of permission.

The perm is used to specify the permission of the current Attribute to be accessed by the Client.

There are 10 permissions as follows, and the permissions of each Attribute must be one of the following value or their combination.

#define ATT_PERMISSIONS_READ                 0x01 
#define ATT_PERMISSIONS_WRITE                0x02 
#define ATT_PERMISSIONS_AUTHEN_READ          0x61 
#define ATT_PERMISSIONS_AUTHEN_WRITE         0x62
#define ATT_PERMISSIONS_SECURE_CONN_READ     0xE1
#define ATT_PERMISSIONS_SECURE_CONN_WRITE    0xE2
#define ATT_PERMISSIONS_AUTHOR_READ          0x11 
#define ATT_PERMISSIONS_AUTHOR_WRITE         0x12
#define ATT_PERMISSIONS_ENCRYPT_READ         0x21
#define ATT_PERMISSIONS_ENCRYPT_WRITE        0x22 

Note

  • Currently, the tc_ble_sdk does not support authorized read and authorized write.

c. uuid and uuidLen

As mentioned earlier, there are two types of UUIDs: BLE standard 2 bytes UUID and Telink proprietary 16 bytes UUID. Both UUIDs can be described simultaneously by uuid and uuidLen.

The UUID is a u8 type pointer, and uuidLen means the content of consecutive uuidLen bytes from the beginning of the pointer is the current UUID. The Attribute Table is existed on flash, all the UUID is also existed on flash, so the UUID is a pointer to flash.

BLE standard 2 bytes UUID:

If Attribute Handle = devNameCharacter attribute of 0x0002, the relevant code is as follows:

#define GATT_UUID_CHARACTER              0x2803
static const u16 my_characterUUID = GATT_UUID_CHARACTER;
static const u8 my_devNameCharVal[5] = {
    CHAR_PROP_READ,
    U16_LO(GenericAccess_DeviceName_DP_H), U16_HI(GenericAccess_DeviceName_DP_H),
    U16_LO(GATT_UUID_DEVICE_NAME), U16_HI(GATT_UUID_DEVICE_NAME)
};
{0,1,2,5,(u8*)(&my_characterUUID), (u8*)(my_devNameCharVal), 0},

UUID = 0x2803 means character in BLE, uuid points to my_devNameCharVal address in flash, uuidLen is 2, when peer Master comes to read this Attribute, UUID will be 0x2803.

Telink's private 16 bytes UUID:

Such as OTA's Attribute, related code:

#define TELINK_SPP_DATA_OTA 
0x12,0x2B,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00
static const u8 otaOutUuid[16]      = {TELINK_SPP_DATA_OTA};
static u8 my_OtaData        = 0x00;
{0,3,16,1,(u8*)(&my_OtaUUID), (&my_OtaData), &otaMyWrite, &otaRead},

The UUID points to the address of my_OtaData in flash, uuidLen is 16, when Master comes to read this Attribute, the UUID will be 0x000102030405060708090a0b0c0d2b12.

d. pAttrValue and attrLen

Each Attribute will have a corresponding Attribute Value. pAttrValue is a u8 pointer to the address of the RAM/Flash where the Attribute Value is located, and attrLen is used to reflect the length of that data on the RAM/Flash. When the Master reads the Attribute Value of an Attribute of the Slave, the SDK starts from the area (RAM/Flash) pointed to by the pAttrValue pointer of the Attribute, and takes attrLen data back to the Master.

UUID is read-only, so UUID is a pointer to flash; and Attribute Value may involve a write operation, if there is a write operation must be put in RAM, so pAttrValue may point to RAM, or to Flash.

Attribute Handle=0x0027 hid Information's Attribute, relevant code:

const u8 hidInformation[] =
{
    U16_LO(0x0111), U16_HI(0x0111), // bcdHID (USB HID version),0x11,0x01
    0x00,                           // bCountryCode
    0x01                            // Flags
};
{0,1,2, sizeof(hidInformation),(u8*)(&hidinformationUUID),  (u8*)(hidInformation), 0},

In practical applications, hidInformation 4 bytes 0x01 0x00 0x01 0x11 are read-only and do not involve write operations, so it can be stored on Flash using the const keyword when defined. pAttrValue points to the address of hidInformation on the flash, at this time attrlen takes the value of the actual length of hidInformation. When Master reads the Attribute, it returns 0x01000111 to Master based on pAttrValue and attrLen.

When the Master reads the Attribute, the BLE sniffer packet as shown in the figure below, the Master uses the ATT_Read_Req command, assuming that AttHandle = 0x0023 = 35 is set to be read, which corresponds to the hid information in the Attribute Table in the SDK.

BLE Sniffer Packet Sample when Master Reads HidInformation

Attribute Handle=0x002C Attribute of battery value, related code:

u8 my_batVal[1]     = {99};
{0,ATT_PERMISSIONS_READ,2,sizeof(my_batVal),(u8*)(&my_batCharUUID),     (u8*)(my_batVal), 0}

In practical applications, the my_batVal value which reacts to the current battery level will change according to the power level sampled by the ADC, and then transmitted to the Master by Slave active notify or Master active read, so my_batVal should be placed in the memory, at this time pAttrValue points to the address of my_batVal in RAM.

e. callback function w

The callback function w is the write function. Function prototype:

typedef int (*att_readwrite_callback_t)(void* p);

If the user needs to define a callback writing function, it needs to follow the above format. The callback function w is optional, for a specific Attribute, the user can set the callback write function or not set the callback (when the callback is not set, it is represented by a null pointer 0).

The callback function w trigger condition is: when the Attribute Opcode of the Attribute PDU received by the Slave is one of the following three, the Slave will check whether the callback function w is set:

a) opcode = 0x12, Write Request.

b) opcode = 0x52, Write Command.

c) opcode = 0x18, Execute Write Request.

After the Slave receives the above write command, if the callback function w is not set, the Slave will automatically write the value passed by the Master to the area pointed to by the pAttValue pointer, and the length of the write is l2capLen-3 in the Master data packet format; if the user sets the callback function w, the Slave executes the user's callback function w after receiving the above write command, and no longer writes data to the area pointed to by the pAttrValue pointer. These two write operations are mutually exclusive, and only one can take effect.

The user sets the callback function w to process the Master's Write Request, Write Command and Execute Write Request commands at the ATT layer, if the callback function w is not set, it is necessary to evaluate whether the area pointed to by pAttrValue can complete the processing of the above commands (for example, pAttrValue points to flash cannot complete the write operation; or the length of attrLen is not enough, the Master's write operation will be out of bounds, causing other data to be incorrectly rewritten).

Write Request in BLE Stack

Write Command in BLE Stack

Execute Write Request in BLE Stack

The void-type p pointer to the callback function w points to the specific value of the Master write command. The actual p points to a piece of memory, and the value in the memory is shown in the following structure.

typedef struct{
    u8  type;
    u8  rf_len;     //Users do not use this member, because it may be changed by the stack layer.
    u16 l2capLen;
    u16 chanId;

    u8  opcode;
    u16 handle;

    u8  dat[20];
}rf_packet_att_t;

The p points to the first element type. The valid length of the written data is l2cap - 3, and the first valid data is dat[0].

int my_WriteCallback(u16 connHandle, void * p)
{
    rf_packet_att_t *pw = (rf_packet_att_t *)p;
    int len = pw->l2capLen - 3;
    //add your code
    //valid data is pw->dat[0] ~ pw->dat[len-1]
    return 1;
}

The location of the above structure rf_packet_att_t is stack/ble/ble_format.h.

Note

  • The rf_len in the structure rf_packet_att_t should not be used by the user, rf_len may be rewritten when assembling the package, please use the l2capLen conversion before use.

f. callback function r

The callback function r is a read function. Function prototype:

typedef int (*att_readwrite_callback_t)(void* p);

If the user needs to define a callback read function, it needs to follow the above format. The callback function R is optional, for a specific Attribute, the user can set the callback read function, or not set the callback (when no callback is set, it is represented by a null pointer 0).

The callback function R trigger condition is: when the Attribute Opcode of the Attribute PDU received by the Slave is one of the following two, the Slave will check whether the callback function R is set:

a) opcode = 0x0A, Read Request.

b) opcode = 0x0C, Read Blob Request.

After the Slave receives the above read command:

a) If the user sets the callback read function, execute the function, and decide whether to reply Read Response/Read Blob Response according to the return value of the function:

  • If the return value is 1, Slave does not reply Read Response/Read Blob Response to Master.

  • If the return value is other values, the Slave reads attrLen values ​​from the area pointed to by the pAttrValue pointer and replies to the Master with Read Response/Read Blob Response.

b) If the user does not set the callback read function, the Slave reads attrLen values ​​from the area pointed to by the pAttrValue pointer and replies to the Master with Read Response/Read Blob Response.

If users want to modify the content of the Read Response/Read Blob Response that will be replied to after receiving the Master's Read Request/Read Blob Request, they can register the corresponding callback function r and modify the content of the RAM pointed to by the pAttrValue pointer in the callback function, and the value returned can only be 0.

g. Attribute Table structure

According to the above detailed description of Attribute, use the Attribute Table to construct the Service structure as shown in the following figure. The attnum of the first Attribute is used to indicate the current number of ATT Table Attributes, the remaining Attributes are firstly grouped by Service, and the first Attribute of each group is the declaration of the Service, and the attnum is used to specify how many of the immediately following Attribute belongs to the specific description of the Service. The first one of each group of services is a Primary Service.

#define GATT_UUID_PRIMARY_SERVICE        0x2800     //!< Primary Service
const u16 my_primaryServiceUUID = GATT_UUID_PRIMARY_SERVICE;

Service Attribute Layout

h. ATT table Initialization

GATT & ATT initialization only needs to transmit the pointer of the Attribute Table of the application layer to the protocol stack. The API provided:

void        bls_att_setAttributeTable (u8 *p);

p is the pointer of Attribute Table.

(3) GATT Service Security

Before introducing GATT Service Security, users can learn about SMP related contents.

Please refer to the relevant detailed introduction in the "SMP" Section to understand the basic knowledge of the LE pairing method and encryption level, etc.

The picture below is a mapping relationship between the GATT service security level service request given by Bluetooth Core Specification, you can refer to core5.0 (Vol3/Part C/10.3 AUTHENTICATION PROCEDURE) for details.

Service Request Response Mapping Relationship

Users can clearly see:

  • The first column is related to whether the currently connected Slave device is in the encrypted state or not.

  • The second column (local Device's Access Requirement for service) is related to the permission (Permission Access) setting of the characteristic in the ATT table set by the user, as shown in the following figure;

  • The third column is divided into four sub-columns, and these four sub-columns correspond to the four levels of the current LE security mode 1 (specifically, whether the current device pairing status is one of the following four):

a) No authentication and no encryption

b) Unauthenticated pairing with encryption

c) Authenticated pairing with encryption

d) Authenticated LE Secure Connections

ATT Permission Definition

The final implementation of GATT service security is related to the parameter configuration during SMP initialization, including the highest supported security level setting, the characteristic permission settings in the ATT table, etc., and it is also related to the Master, for example, the highest level that the SMP set by the Slave can support is Authenticated pairing with encryption, but the highest security level of Master is Unauthenticated pairing with encryption, at this time if the authority of a writing characteristic in the ATT table is ATT_PERMISSIONS_AUTHEN_WRITE, then when the Master writes this characteristic, we will reply to the mistake of insufficient encryption level.

The user can set characteristic permissions in the ATT table to achieve the following applications:

For example, the highest security level supported by the Slave device is Unauthenticated pairing with encryption, but don't want to use the method of sending a Security Request to trigger the Master to start pairing after connecting, then the client can set the permissions of some client characteristic configuration (CCC) attributes that have notify attributes to ATT_PERMISSIONS_ENCRYPT_WRITE, then Master only writes the CCC, the Slave will reply that its security level is not enough, which will trigger the Master to start the pairing encryption process.

Note

  • The security level set by the user only represents the highest security level that the device can support, as long as the permissions of the characteristic (ATT Permission) in the ATT table do not exceed the highest level that is actually in effect, it can be controlled by GATT service security. For level 4 in LE security mode 1, if the user only sets one level of Authenticated LE Secure Connections, it means that the current setting supports LE Secure Connections only.

(4) Attribute PDU and GATT API

According to the Bluetooth Core Specification, the tc_ble_sdk currently supports Attribute PDUs in the following categories:

  • Requests: the data request sent by the client to the server.

  • Responses: the data reply sent by the server after receiving the client's request.

  • Commands: The commands sent by the client to the server.

  • Notifications: the data sent by the server to the client.

  • Indications: the data sent by the server to the client.

  • Confirmations: the confirmation of the server Indication data by the client.

The following is an analysis of all the ATT PDUs in the ATT layer in conjunction with the Attribute structure and Attribute Table structure introduced previously.

a. Read by Group Type Request, Read by Group Type Response

For details of Read by Group Type Request and Read by Group Type Response, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.4.9 ATT_READ_BY_GROUP_TYPE_REQ/3.4.4.10 ATT_READ_BY_GROUP_TYPE_RSP.

The Master sends a Read by Group Type Request, specifying the initial and ending attHandle in this command, and specifying attGroupType. After the Slave receives the Request, it iterates through the current Attribute table, finds the Attribute Group that conforms to the attGroupType in the specified starting and ending attHandle, and replies to the Attribute Group information through Read by Group Type Response.

Read by Group Type Request Read by Group Type Response

As shown in the figure above, the Master queries the Slave for the Attribute Group information of the primaryServiceUUID whose UUID is 0X2800.

#define GATT_UUID_PRIMARY_SERVICE        0x2800
const u16 my_primaryServiceUUID = GATT_UUID_PRIMARY_SERVICE;

Referring to the current demo code, the Attribute table has the following sets that meet this requirement:

a) attHandle is the Attribute Group from 0x0001 ~ 0x0007, and the Attribute Value is SERVICE_UUID_GENERIC _ACCESS(0x1800).

b) attHandle is the Attribute Group from 0x0008 ~ 0x000B, and the Attribute Value is SERVICE_UUID_GENERIC _ATTRIBUTE(0x1801).

c) attHandle is the Attribute Group from 0x000C ~ 0x000E, and the Attribute Value is SERVICE_UUID_DEVICE_ INFORMATION(0x180A).

d) attHandle is the Attribute Group from 0x000F ~ 0x0029, and the Attribute Value is SERVICE_UUID_HUMAN_ INTERFACE_DEVICE(0x1812).

e) attHandle is the Attribute Group from 0x002A ~ 0x002D, and the Attribute Value is SERVICE_UUID_BATTERY (0x180F).

f) attHandle is the Attribute Group from 0x002E ~ 0x0035, and the Attribute Value is TELINK_SPP_UUID_ SERVICE(0x10,0x19,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00).

g) attHandle is the Attribute Group from 0x0036 ~ 0x0039, and the Attribute Value is TELINK_OTA_UUID_ SERVICE(0x12,0x19,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00).

The Slave replies the information of attHandle and attValue of the above 7 sets to the Master through Read by Group Type Response, the last ATT_Error_Response indicates that all Attribute Sets have been replied, the Response is over, and the Master will also stop sending Read by Group Type Request when it sees this packet.

Use the following API to implement Read by Group Request:

ble_sts_t   blc_gatt_pushReadByGroupTypeRequest(u16 connHandle, u16 start_attHandle, u16 end_attHandle, u8 *uuid, int uuid_len);

The data of Read by Group Response can be read and processed in the app_gatt_data_handler function.

b. Find by Type Value Request, Find by Type Value Response

For details of Find by Type Value Request and Find by Type Value Response, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.3.3 ATT_FIND_BY_TYPE_VALUE_REQ/3.4.3.4 ATT_FIND_BY_TYPE_VALUE_RSP.

The Master sends Find by Type Value Request, specifying the starting and ending attHandle, AttributeType, and Attribute Value in this command. After the Slave receives the Request, it iterates through the current Attribute table and finds the Attribute that matches the AttributeType and Attribute Value in the specified starting and ending attHandle, and replies with a Find by Type Value Response.

Find by Type Value Request Find by Type Value Response

Use the following API to implement Find by Type Value Request:

ble_sts_t   blc_gatt_pushFindByTypeValueRequest(u16 connHandle, u16 start_attHandle, u16 end_attHandle, u16 uuid, u8 *attr_value, int len);

The data of Find by Type Value Response can be read and processed in the app_gatt_data_handler function.

c. Read by Type Request, Read by Type Response

For details of Read by Type Request and Read by Type Response, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.4.1 ATT_READ_BY_TYPE_REQ/3.4.4.2 ATT_READ_BY_TYPE_RSP.

The Master sends a Read by Type Request, specifying the start and end attHandle in the command, and specifies AttributeType. After the Slave receives the Request, it traverses the current Attribute table, finds the Attribute that matches AttributeType in the specified starting and ending attHandle, and replies to the Attribute through Read by Type Response.

Read by Type Value Request Find by Type Value Response

As shown in the figure above, Master reads Attribute with attType 0x2A00, Attribute Handle in Slave is 0x0003 Attribute:

static const u8 my_devName[] = {'m','u','l','t','i','_','c','o','n','n'};
#define GATT_UUID_DEVICE_NAME            0x2a00
const u16 my_devNameUUID = GATT_UUID_DEVICE_NAME;
{0,ATT_PERMISSIONS_READ,2,sizeof(my_devName), (u8*)(&my_devNameUUID), (u8*)(my_devName), 0}

The length of the Read by Type response is 8, the first two bytes in attData are the current attHandle 0003, and the last six bytes are the corresponding Attribute Value.

Use the following API to implement Read by Type Request:

ble_sts_t   blc_gatt_pushReadByTypeRequest(u16 connHandle, u16 start_attHandle, u16 end_attHandle, u8 *uuid, int uuid_len);

The data of Read by Type Response can be read and processed in the app_gatt_data_handler function.

d. Find information Request, Find information Response

For details of Find information request and Find information response, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.3.1 ATT_FIND_INFORMATION_REQ/3.4.3.2 ATT_FIND_INFORMATION_RSP.

The Master sends a Find information request, specifying the starting and ending attHandle. After the Slave receives this command, it will reply to the Master with the UUID of the Attribute corresponding to all attHandles at the beginning and ending via Find information response. As shown in the following figure, Master requires to obtain information of attHandle 0x0016 ~ 0x0018 three Attributes, and Slave responds to the UUID of these three Attributes.

Find Information Request Find Information Response

e. Read Request, Read Response

For details of Read Request and Read Response, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.4.3 ATT_READ_REQ/3.4.4.4 ATT_READ_RSP.

The Master sends a Read Request, specifying an attHandle as 0x0017, after receiving it, the Slave replies with the Attribute Value of the specified Attribute through the Read Response (if the callback function r is set, execute this function), as shown in the figure below.

Read Request Read Response

Use the following API to implement the Read Request:

ble_sts_t   blc_gatt_pushReadRequest(u16 connHandle, u16 attHandle);

The data of Read Response can be read and processed in the app_gatt_data_handler function.

f. Read Blob Request, Read Blob Response

For details of Read Blob Request and Read Blob Response, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.4.5 ATT_READ_BLOB_REQ/3.4.4.6 ATT_READ_BLOB_RSP.

When the length of the Attribute Value of an Attribute of the Slave exceeds MTU_SIZE (the default value is 23 in the current tc_ble_sdk), the Master needs to enable Read Blob Request to read this Attribute Value, so that the Attribute Value can be sent by subpacketing. The Master specifies attHandle and ValueOffset in the Read Blob Request, after receiving the command, the Slave finds the corresponding Attribute, and replies the Attribute Value through the Read Blob Response according to the ValueOffset value (if the callback function r is set, execute it).

As shown in the figure below, when the Master reads the HID report map of the Slave (the report map is large, far exceeding 23), it first sends the Read Request, the Slave returns the Read response, and returns the previous part of the report map to the Master. After that, the Master uses the Read Blob Request, and the Slave returns data to the Master through Read Blob Response.

Read Blob Request Read Blob Response

Use the following API to implement Read Blob Request:

ble_sts_t   blc_gatt_pushReadBlobRequest(u16 connHandle, u16 attHandle, u16 offset);

The data of the Read Blob Response can be read and processed in the app_gatt_data_handler function.

g. Exchange MTU Request, Exchange MTU Response

For details of Exchange MTU Request and Exchange MTU Response, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.2.1 ATT_EXCHANGE_MTU_REQ/3.4.2.2 ATT_EXCHANGE_MTU_RSP.

As shown below, Master and Slave learn each other's MTU size through Exchange MTU Request and Exchange MTU Response.

Exchange MTU Request Exchange MTU Response

When the data access process of the GATT layer appears more than one RF packet length data, which involves the GATT layer subpacket and parcels, it is necessary to exchange the RX MTU size of both parties with the peer Master/Slave in advance, that is, the process of MTU size exchange. The purpose of the MTU size exchange is to enable the transceiver of long packet data at the GATT layer.

a) The user can obtain EffectiveRxMTU by registering a GAP event callback and turning on eventMask: Gap_evt_Mask_ATT_EXCHANGE_MTU, where:

EffectiveRxMTU=min(ClientRxMTU, ServerRxMTU)

The GAP events are described in detail in the "GAP event" Section of this document.

b) The GATT layer receives long packet data for processing.

The default value of ServerRxMTU and ClientRxMTU is 23, and the maximum ServerRxMTU/ClientRxMTU can support the same as the theoretical value (only limited by RAM space). When the application needs to use subcontracting and reassembly, use the following API to modify the RX size on the Slave:

ble_sts_t   blc_att_setMasterRxMTUSize(u16 master_mtu_size);

Use the following API to modify the RX size on the Slave:

ble_sts_t   blc_att_setSlaveRxMTUSize(u16 slave_mtu_size);

Return value list:

ble_sts_t Value ERR Reason
BLE_SUCCESS 0 Success
GATT_ERR_INVALID_ PARAMETER See the definition in SDK Larger than the defined buffer size, i.e.: mtu_s_rx_fifo or mtu_m_rx_fifo

Note

  • The above two API settings are the MTU values when the ATT_Exchange_MTU_req/ATT_Exchange_MTU_rsp commands interact. The value cannot be greater than the actually defined buffer size, that is, the variable: mtu_m_rx_fifo[ ] and mtu_s_rx_fifo[ ], these two array variables are defined in the app_buffer.c.

As long as the MTU set using the above API is not the default value of 23, after the connection is established, the SDK will actively initiate the interaction process of MTU. By registering the Host event GAP_EVT_ATT_EXCHANGE_MTU, you can see the result of MTU interaction in the callback function.

h. Write Request, Write Response

For details of Write Request and Write Response, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.5.1 ATT_WRITE_REQ/3.4.5.2 ATT_WRITE_RSP.

The Master sends a Write Request, specifies an attHandle, and comes with relevant data. After the Slave receives it, it finds the specified Attribute, and determines whether the data is processed by the callback function w or directly written to the corresponding Attribute Value according to whether the user has set the callback function w, and replies with Write Response.

As shown in the figure below, the Master writes the Attribute Value of 0x0001 to the Attribute whose attHandle is 0x0016, and the Slave executes the write operation after receiving it and replies with a Write Response.

Write Request Write Response

Use the following API to implement Write Request:

ble_sts_t   blc_gatt_pushWriteRequest (u16 connHandle, u16 attHandle, u8 *p, int len);

The data of Write Response can be read and processed in the app_gatt_data_handler function.

i. Write Command

For details of Write Command, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.5.3 ATT_WRITE_CMD.

The Master sends a Write Command, specifies an attHandle, and comes with relevant data. After the Slave receives it, it finds the specified Attribute, and determines whether the data is processed by the callback function w or directly written to the corresponding Attribute Value according to whether the user has set the callback function w, and does not reply any information.

Use the following API to implement Write Command:

ble_sts_t   blc_gatt_pushWriteCommand (u16 connHandle, u16 attHandle, u8 *p, int len);

j. Queued Writes

Queued Writes includes ATT protocols such as Prepare Write Request/Response and Execute Write Request/Response. For details, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.6 Queued writes.

Note

  • When using Queued Writes, the API blc_att_setPrepareWriteBuffer needs to be called at initialization time to allocate the storage buffer for the prepare write, which is not initially set by default in order to save RAM.

Prepare Write Request and Execute Write Request can implement the following two functions:

a) Provides write functionality for long attribute values.

b) Allows multiple values to be written in a single atomic operation.

Prepare Write Request contains AttHandle, ValueOffset, and PartAttValue, which is similar to the Read_Blob_Req/Rsp. This means that the Client can either prepare multiple attribute values in the queue or prepare each part of a long attribute value. In this way, before actually executing the prepare queue, the client can be sure that all parts of an attribute can be written to the server.

Note

  • The current version of the tc_ble_sdk only supports a) long attribute value write function, and the maximum length of long attribute value is less than or equal to 244 bytes.

As shown in the figure below, when Master writes long string to a characteristic of the Slave: "I am not sure what a new song" (The number of bytes is much more than 23, using the default MTU case), first send a Prepare Write Request with an offset of 0x0000, write the "I am not sure what" part of the data to the Slave, and the Slave returns a Prepare Write Response to the Master. After that, the Master sends a Prepare Write Request with an offset of 0x12, and writes the data of "a new song" to the Slave, and the Slave returns a Prepare Write Response to the Master. When the Master has finished writing all the long attribute values, it sends an Execute Write Request to the Slave, and the flag is 1: it means that the write takes effect immediately, the Slave replies with an Execute Write Response, and the entire Prepare write process ends.

Here we can know that Prepare Write Response also includes AttHandle, ValueOffset, and PartAttValue in the request, and the purpose of this is for the reliability of data transmission. The client can compare the field values of Response and Request to ensure that the prepared data is received correctly.

Example for Write Long Characteristic Values

k. Handle Value Notification

For details of Handle Value Notification, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.7.1 ATT_HANDLE_VALUE_NTF.

Handle Value Notification in Bluetooth Core Specification

The figure above shows the format of Handle Value Notification in the Bluetooth Core Specification.

The tc_ble_sdk provides an API for handling value notification of an Attribute. The user calls this API to push the data that it needs to notify to the lower layer BLE software FIFO. The protocol stack will push the data of the software FIFO to the hardware FIFO at the nearest transceiver packet interval, and finally send it out through RF.

ble_sts_t   blc_gatt_pushHandleValueNotify(u16 connHandle, u16 attHandle, u8 *p, int len);

The connHandle is the connHandle corresponding to the Connection state, attHandle is the attHandle corresponding to the Attribute, p is the head pointer of the continuous memory data to be sent, and len specifies the number of bytes of the data to be sent. The API supports an automatic unpacking function (Perform subpacket handling according to EffectiveMaxTxOctets, that is, the smaller value of the maximum number of transceiver bytes in the link layer RF RX/TX, DLE may modify this value, and the default value is 27), which can split a long data into multiple BLE RF Packets and send out, so len can support very large values.

When the Link Layer is in Conn state, generally calling this API directly can successfully push data to the lower layer software FIFO, but there are some special cases that may cause the API call to fail. You can learn the corresponding error cause according to the return value ble_sts_t.

When calling this API, it is recommended that the user check whether the return value is BLE_SUCCESS; if it is not BLE_SUCCESS, need to wait for a period of time and push again.

The list of return values is as follows:

ble_sts_t Value ERR reason
BLE_SUCCESS 0 Success
GAP_ERR_INVALID_PARAMETER 0xC0 Invalid parameter
SMP_ERR_PAIRING_BUSY 0xA1 In the pairing stage
GATT_ERR_DATA_LENGTH_EXCEED_ MTU_SIZE 0xB5 len is greater than ATT_MTU-3, the length of the data to be sent exceeds the maximum data length ATT_MTU supported by the ATT layer
LL_ERR_CONNECTION_NOT_ESTABLISH 0x80 Link Layer is in None Conn state
LL_ERR_ENCRYPTION_BUSY 0x82 In the encryption stage, and it cannot send data
LL_ERR_TX_FIFO_NOT_ENOUGH 0x81 There are large data volume tasks running, and the software TX FIFO is not enough
GATT_ERR_DATA_PENDING_DUE_TO_ SERVICE_DISCOVERY_BUSY 0xB4 In the traversal service stage, and it cannot send data

l. Handle Value Indication

For details of Handle Value Indication, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.7.2 ATT_HANDLE_VALUE_IND.

Handle Value Indication in BLE Spec

The figure above shows the format of Handle Value Indication in the Bluetooth Core Specification.

The tc_ble_sdk provides an API for Handle Value Indication of an Attribute. The user calls this API to push the data that it needs to indicate to the lower layer BLE software FIFO. The protocol stack will push the data of the software FIFO to the hardware FIFO at the nearest transceiver packet interval, and finally send it out through RF.

ble_sts_t   blc_gatt_pushHandleValueIndicate (u16 connHandle, u16 attHandle, u8 *p, int len);

The connHandle is the connHandle corresponding to the Connection state, attHandle is the attHandle corresponding to the Attribute, p is the head pointer of the continuous memory data to be sent, and len specifies the number of bytes of the data to be sent. The API supports automatic unpacking function (Perform subpacket handling according to EffectiveMaxTxOctets, that is, the smaller value of the maximum number of transceiver bytes in the link layer RF RX/TX, DLE may modify this value, and the default value is 27, its replacement API will be introduced below, see remarks), which can split a long data into multiple BLE RF Packets and sent out, so len can support very large.

The Bluetooth Core Specification stipulates that each indication must wait for the client’s confirmation before being considered successful. If unsuccessful, the next indication cannot be sent.

When the Link Layer is in Connection state, generally calling this API directly can successfully push data to the lower layer software FIFO, but there are some special cases that may cause the API call to fail. You can learn the corresponding error cause according to the return value ble_sts_t.

When calling this API, it is recommended that the user check whether the return value is BLE_SUCCESS; if it is not BLE_SUCCESS, need to wait for a period of time and push again.

The list of return values is as follows:

ble_sts_t Value ERR reason
BLE_SUCCESS 0 Success
GAP_ERR_INVALID_PARAMETER 0xC0 Invalid parameter
SMP_ERR_PAIRING_BUSY 0xA1 In the pairing stage
GATT_ERR_DATA_LENGTH_EXCEED_ MTU_SIZE 0xB5 len is greater than ATT_MTU-3, the length of the data to be sent exceeds the maximum data length ATT_MTU supported by the ATT layer
LL_ERR_CONNECTION_NOT_ESTABLISH 0x80 Link Layer is in None Conn state
LL_ERR_ENCRYPTION_BUSY 0x82 In the encryption stage, and it cannot send data
LL_ERR_TX_FIFO_NOT_ENOUGH 0x81 There are large data volume tasks running, and the software TX FIFO is not enough
GATT_ERR_DATA_PENDING_DUE_TO_ SERVICE_DISCOVERY_BUSY 0xB4 In the traversal service stage, and it cannot send data
GATT_ERR_PREVIOUS_INDICATE_ DATA_HAS_NOT_CONFIRMED 0xB1 The previous indicated data has not been confirmed by the Master

m. Handle Value Confirmation

For details of Handle Value Confirmation, please refer to Bluetooth Core Specification V5.3, Vol 3, Part F, 3.4.7.3 ATT_HANDLE_VALUE_CFM.

Each time the application layer calls blc_gatt_pushHandleValueIndicate, after sending the indication data to the Master, the Master will reply with a confirmation, indicating the confirmation of this data, and then the Slave can continue to send the next indication data.

Handle Value Confirmation in BLE

As can be seen from the above figure, Confirmation does not specify which specific handle is confirmed, and the indication data on all different handles are uniformly replied to a Confirmation.

In order to let the application layer know whether the sent indication data has been confirmed, the user can register the GAP event callback and enable the corresponding eventMask: GAP_EVT_GATT_HANDLE_VLAUE_ CONFIRM to obtain the confirmation event. This document "GAP event" Section will introduce the GAP event in detail.

n. blc_att_setServerDataPendingTime_upon_ClientCmd

The bottom layer of tc_ble_sdk does not allow notify and indicate operations during the SDP process, and the data pending time (default 300 ms) after the SDP. If the user needs to change the pending data time, this API can be used.

void        blc_att_setServerDataPendingTime_upon_ClientCmd(u8 num_10ms);

The parameter is in units of 10 ms, e.g., if the parameter is substituted with 30, it means 30 * 10 ms, that is, 300 ms.

GAP

(1) GAP Initialization

In the tc_ble_sdk, because Master and Slave play at the same time on one device, the Master and Slave devices are not distinguished during initialization.

Initialization function:

void blc_gap_init(void)

From the foregoing, we know that the data interaction between the application layer and host is not controlled through GAP, the protocol stack provides relevant interfaces in ATT, SMP, and L2CAP, and can directly interact with the application layer. At present, the GAP layer of the SDK mainly processes events on the host layer, and the GAP initialization is mainly registered with the host layer event processing function entry.

(2) GAP Event

A GAP event is an event generated during the interaction of ATT, GATT, SMP, and GAP host protocol layers. From the previous article, we can know that the current SDK events are mainly divided into two categories: Controller event and GAP (host) event, in which the controller event is divided into HCI event and LE HCI event.

The tc_ble_sdk has added GAP event handling, which mainly makes the protocol stack event layering clearer and the protocol stack processing user layer interaction events more convenient, especially SMP related processing, such as Passkey input, pairing result notification to the user, etc.

If the user needs to receive the GAP event at the App layer, the callback function of the GAP event needs to be registered first, and then the mask of the corresponding event needs to be opened.

The callback function prototype and registration interface of the GAP event are:

typedef int (*gap_event_handler_t) (u32 h, u8 *para, int n);
void blc_gap_registerHostEventHandler (gap_event_handler_t  handler);

The u32 h in the callback function prototype is the GAP event tag, which is used in many places in the lower layer protocol stack.

The following lists several events that users may use:

#define GAP_EVT_SMP_PAIRING_BEGIN                         0
#define GAP_EVT_SMP_PAIRING_SUCCESS                       1
#define GAP_EVT_SMP_PAIRING_FAIL                          2
#define GAP_EVT_SMP_CONN_ENCRYPTION_DONE                  3
#define GAP_EVT_SMP_TK_DISPALY                            4
#define GAP_EVT_SMP_TK_REQUEST_PASSKEY                    5
#define GAP_EVT_SMP_TK_REQUEST_OOB                        6
#define GAP_EVT_SMP_TK_NUMERIC_COMPARE                    7
#define GAP_EVT_ATT_EXCHANGE_MTU                          16
#define GAP_EVT_GATT_HANDLE_VLAUE_CONFIRM                 17

Para and n in the callback function prototype represent the data and data length of the event, and the GAP event listed above will be described in detail below. Users can refer to the following usage in the demo code and the specific implementation of the app_host_event_callback function.

blc_gap_registerHostEventHandler( app_host_event_callback ); 

The GAP event needs to open the mask through the following API.

void    blc_gap_setEventMask(u32 evtMask);

The definition of eventMask also corresponds to some of those given above; other event masks can be found by the user in ble/gap/gap_event.h.

#define GAP_EVT_MASK_SMP_PAIRING_BEGIN         (1<<GAP_EVT_SMP_PAIRING_BEGIN)
#define GAP_EVT_MASK_SMP_PAIRING_SUCCESS       (1<<GAP_EVT_SMP_PAIRING_SUCCESS)
#define GAP_EVT_MASK_SMP_PAIRING_FAIL           (1<<GAP_EVT_SMP_PAIRING_FAIL)
#define GAP_EVT_MASK_SMP_CONN_ENCRYPTION_DONE   (1<<GAP_EVT_SMP_CONN_ENCRYPTION_DONE)
#define GAP_EVT_MASK_SMP_TK_DISPALY             (1<<GAP_EVT_SMP_TK_DISPALY)
#define GAP_EVT_MASK_SMP_TK_REQUEST_PASSKEY     (1<<GAP_EVT_SMP_TK_REQUEST_PASSKEY)
#define GAP_EVT_MASK_SMP_TK_REQUEST_OOB         (1<<GAP_EVT_SMP_TK_REQUEST_OOB)
#define GAP_EVT_MASK_SMP_TK_NUMERIC_COMPARE     (1<<GAP_EVT_SMP_TK_NUMERIC_COMPARE)
#define GAP_EVT_MASK_ATT_EXCHANGE_MTU           (1<<GAP_EVT_ATT_EXCHANGE_MTU)
#define GAP_EVT_MASK_GATT_HANDLE_VLAUE_CONFIRM  (1<<GAP_EVT_GATT_HANDLE_VLAUE_CONFIRM)

If the user does not set the GAP event mask through this API, then the application layer will not be notified when the GAP corresponding event is generated.

Note

  • When the GAP event is discussed below, all are set to register the GAP event callback, and the corresponding eventMask is opened.

a. GAP_EVT_SMP_PAIRING_BEGIN

Event trigger conditions: When the Master and Slave are just connected and enter the connection state, after the Slave sends the SM_Security_Req command, the Master sends the SM_Pairing_Req request to start pairing. When the Slave receives the pairing request command, this event is triggered, indicating that pairing begins.

SMP_PAIRING_BEGIN Event Trigger Conditions

Data length n: 4.

Return pointer p: Points to a piece of memory data, corresponding to the following structure:

typedef struct {
    u16 connHandle;
    u8  secure_conn;
    u8  tk_method;
} gap_smp_pairingBeginEvt_t;

The connHandle indicates the current connection handle.

The secure_conn is 1 to use the secure encryption characteristic (LE Secure Connections), otherwise LE legacy pairing will be used.

The tk_method indicates what TK value method is used for the next pairing: such as JustWorks, PK_Init_Dsply_ Resp_Input, PK_Resp_Dsply_Init_Input, Numric_Comparison, etc.

b. GAP_EVT_SMP_PAIRING_SUCCESS

Event trigger conditions: The event is generated when the whole pairing process is completed correctly, and this stage is the key distribution stage 3 (Key Distribution, Phase 3) of the LE pairing stage, if there is a key to be distributed, the pairing success event will be triggered after the key distribution of both parties is completed, otherwise, the pairing success event will be triggered directly.

Data length n: 4.

Return pointer p: Points to a piece of memory data, corresponding to the following structure:

typedef struct {
    u16 connHandle;
    u8  bonding;
    u8  bonding_result;
} gap_smp_pairingSuccessEvt_t;

The connHandle indicates the current connection handle.

The bonding is 1 indicating the bonding function is enabled, otherwise it is not enabled.

The bonding_result indicates the result of bonding: if the bonding function is not enabled, it is 0; if the bonding function is enabled, it is also necessary to check whether the encryption key is correctly stored in the flash, and the storage success is 1, otherwise it is 0.

c. GAP_EVT_SMP_PAIRING_FAIL

Event trigger conditions: Triggered when the pairing process is terminated because either the Slave or Master does not follow the standard pairing procedure, or because of abnormal conditions such as communication errors.

Data length n: 2.

Return pointer p: Points to a piece of memory data, corresponding to the following structure:

typedef struct {
    u16 connHandle;
    u8  reason;
} gap_smp_pairingFailEvt_t;

The connHandle indicates the current connection handle.

The reason indicates the reason for the pairing failure. Here are several common pairing failure cause values. For other pairing failure cause values, we can refer to the "stack/ble/smp/smp_const.h" file in the SDK directory.

For the specific meaning of the pairing failure value, please refer to Bluetooth Core Specification V5.3, Vol 3, Part H, 3.5.5 Pairing Failed.

#define PAIRING_FAIL_REASON_CONFIRM_FAILED        0x04
#define PAIRING_FAIL_REASON_PAIRING_NOT_SUPPORTED  0x05
#define PAIRING_FAIL_REASON_DHKEY_CHECK_FAIL      0x0B
#define PAIRING_FAIL_REASON_NUMUERIC_FAILED       0x0C
#define PAIRING_FAIL_REASON_PAIRING_TIEMOUT       0x80
#define PAIRING_FAIL_REASON_CONN_DISCONNECT       0x81

d. GAP_EVT_SMP_CONN_ENCRYPTION_DONE

Event trigger conditions: Triggered when Link Layer encryption is complete (Link Layer receives start encryption response from Master).

Data length n: 3.

Return pointer p: Points to a piece of memory data, corresponding to the following structure:

typedef struct {
    u16 connHandle;
    u8  re_connect;   //1: re_connect, encrypt with previous distributed LTK;   0: pairing , encrypt with STK
} gap_smp_connEncDoneEvt_t;

The connHandle indicates the current connection handle.

The re_connect is 1, which means fast reconnection (the previously distributed LTK encrypted link will be used); if this value is 0, it means that the current encryption is the first encryption.

e. GAP_EVT_SMP_TK_DISPALY

Event trigger conditions: After the Slave receives the Pairing_Req sent by the Master, according to the pairing parameters of the peer device and the pairing parameter configuration of the local device, we can know what TK (pincode) value method is used for the next pairing. If the PK_Resp_Dsply_Init_Input (that is, the Slave displays the 6 bits pincode code, and the Master is responsible for inputting the 6 bits pincode code) mode is enabled, it will be triggered immediately.

Data length n: 4.

Return pointer p: Points to a u32 variable tk_set, the value is the 6 bits pincode code that the Slave needs to notify the application layer, and the application layer needs to display the 6 bits code value. The reference code is as follows:

case GAP_EVT_SMP_TK_DISPALY:
{
    char pc[7];
    u32 pinCode = *(u32*)para;
    sprintf(pc, "%d", pinCode);
    printf("TK display:%s\n", pc);
}
break;

The pincode can be set during initialization through the following API, such as setting it to 123456:

blc_smp_setDefaultPinCode(123456);

If the value is set to 0, or if the above API is not called to set it, the Pincode value is random.

Users input the 6 bits pincode code seen on the Slave to Master device (such as a mobile phone), complete the TK input, and the pairing process can be continued. If the user input pincode error or clicks cancel, the pairing process fails.

f. GAP_EVT_SMP_TK_REQUEST_PASSKEY

Event trigger conditions: When the Passkey Entry method is enabled on the Slave device and the PK_Init_Dsply_Resp_Input or PK_BOTH_INPUT pairing mode is used, this event will be triggered to notify the user that the TK value needs to be entered. After receiving the event, the user needs to input the TK value through the IO input ability (if the timeout is 30s, it has not been input, the pairing will fail). The API for inputting TK value: blc_smp_setTK_by_PasskeyEntry is described in the "SMP parameter configuration" Section.

Data length n: 0.

Return pointer p: NULL.

g. GAP_EVT_SMP_TK_REQUEST_OOB

Event trigger conditions: When the traditional pairing OOB method is enabled on the Slave device, this event will be triggered to notify the user that the 16 bits TK value needs to be input through the OOB method. After receiving the event, the user needs to input 16 bits TK value through the IO (if the timeout is 30s, it has not been input, the pairing will fail). The API for inputting TK value: blc_smp_setTK_by_OOB is described in the "SMP parameter configuration" Section.

Data length n: 0.

Return pointer p: NULL.

h. GAP_EVT_SMP_TK_NUMERIC_COMPARE

Event trigger conditions: After the Slave receives the Pairing_Req sent by the Master, according to the pairing parameters of the peer device and the pairing parameter configuration of the local device, we can know what TK (pincode) value method is used for the next pairing. If the Numeric_Comparison method is enabled, it will be triggered immediately. (Numeric_Comparison method, i.e. numeric comparison, belongs to smp4.2 secure encryption, both Master and Slave devices will pop up the 6 bits pincode code and the "YES" and "No" dialog box, users need to check whether the pincode displayed on both devices is the same, and needs to confirm whether to click "YES" on both devices to confirm whether the TK checksum is passed).

Data length n: 4.

Return pointer p: Points to a u32 variable pinCode, the value is the 6 bits pincode code that the Slave needs to notify the application layer, and the application layer needs to display the 6 bits code value and provide the confirmation mechanism of "YES" and "NO".

i. GAP_EVT_ATT_EXCHANGE_MTU

Event trigger conditions: Whether the Master sends an Exchange MTU Request, the Slave reply Exchange MTU Response, or the Slave sends an Exchange MTU Request, the Master reply Exchange MTU Response, both cases will trigger.

Data length n: 6.

Return pointer p: Points to a piece of memory data, corresponding to the following structure:

typedef struct {
    u16 connHandle;
    u16 peer_MTU;
    u16 effective_MTU;
} gap_gatt_mtuSizeExchangeEvt_t;

The connHandle indicates the current connection handle.

The peer_MTU indicates the RX MTU value of the peer.

The effective_MTU = min(CleintRxMTU, ServerRxMTU), CleintRxMTU indicates the RX MTU size value for the client, and ServerRxMTU indicates the RX MTU size value for the server. After the Master and Slave have interacted with each other's MTU size, take the minimum value of the two as the maximum MTU size value of each other's interaction.

j. GAP_EVT_GATT_HANDLE_VLAUE_CONFIRM

Event trigger condition: Each time the application layer calls bls_att_pushIndicateData (or calls blc_gatt_pushHandleValueIndicate), after sending the indication data to the Master, the Master will reply with a confirmation, indicating the confirmation of the data, which is triggered when the Slave receives the confirmation.

Data length n: 0.

Return pointer p: NULL.

SMP

The Security Manager (SM) provides LE devices with various keys required for encryption to ensure data confidentiality. The encrypted link can prevent third-party "attackers" from intercepting, deciphering, or tampering with the original content of the air data. For SMP details, please refer to Bluetooth Core Specification V5.3, Vol 3, Part H: Security Manager Specification.

(1) MP Security Level

Bluetooth Core Specification V4.2 adds a new pairing method called LE Secure Connections, which further enhances security. The previous pairing method is collectively referred to as LE Legacy Pairing.

The tc_ble_sdk provides the following 4 security levels, refer to Bluetooth Core Specification V5.3, Vol 3, Part C, 10.2 LE security modes:

a) No authentication and no encryption (LE security Mode 1 Level 1)

b) Unauthenticated pairing with encryption (LE security Mode 1 Level 2)

c) Authenticated pairing with encryption (LE security Mode 1 Level 3)

d) Authenticated LE Secure Connections (LE security Mode 1 Level 4)

Note

  • All connections are supported to the highest security level; the Master and Slave can configure different security levels.
  • Currently, connections with different security level configurations are not supported;
  • The security level set by the local device only indicates the highest security level that the local device may reach, and achieving the set security level is related to two factors: (a) peer device set the highest security level that can support >= local device set the highest security level that can support; (b) local device and peer device process the entire pairing process correctly according to the SMP parameters set for each (if a pairing exists).

For example, if the user sets the highest security level that the Slave can support is Mode 1 Level 3, but the Master connecting to the Slave is set to not support pairing encryption (only Mode 1 Level 1 is supported at the highest), then Slave and Master will not be paired after connection, and the actual security level used by the Slave is Mode 1 Level 1.

void blc_smp_setSecurityLevel(le_security_mode_level_t  mode_level);
void blc_smp_setSecurityLevel_slave(le_security_mode_level_t  mode_level);
void blc_smp_setSecurityLevel_master(le_security_mode_level_t  mode_level);

Description:

In the tc_ble_sdk, the API for configuring SMP-related parameters, unless otherwise specified, will have the following three configuration forms:

  • API(...) for unified configuration of Master role and Slave role parameters;

  • API_Master(...) for configuring all Master role parameters individually;

  • API_Slave(...) for configuring all Slave role parameters individually.

(2) SMP Parameter Configuration

When the initialization of GAP is called, the SMP is initialized, and the parameters of the SMP are initialized to default values:

  • The highest security level supported by default: Unauthenticated_Paring_with_Encryption, which is Mode 1 Level 2;
  • Default bonding mode: Bondable_Mode (refer to blc_smp_setBondingMode() API description);
  • Default IO Capability: IO_CAPABILITY_NO_INPUT_NO_OUTPUT;
  • Default pairing method: Legacy Pairing Just Works.

After the initialization is completed, first configure the SMP parameters through the API of SMP parameter configuration, and then use the following API to bring the parameters configured at the application layer into the bottom layer for initial configuration.

void    blc_smp_smpParamInit(void);

The following describes the related APIs for SMP parameter configuration.

void blc_smp_setPairingMethods(pairing_methods_t  method);  //_slave()/_master()

This set of APIs is used to configure the SMP pairing method, Legacy or Secure Connections.

Note

  • The secure pairing method of Secure Connection requires MTU >= 65.
void blc_smp_setIoCapability(pairing_methods_t  method);    //_slave()/_master()

This set of APIs is used to configure the SMP IO capability (see the figure below) and determine the key generation method. Refer to Bluetooth Core Specification V5.3, Vol 3, Part H, 2.3.5.1 Selecting Key Generation Method.

Mapping KEY Generation Method According to Different IO Capabilities

void    blc_smp_enableAuthMITM(int MITM_en);        //_slave()/_master()

This set of APIs is used to configure the MITM (Man in the Middle) flag of SMP to provide Authentication. When the security level is Mode 1 Level 3 and above, this parameter is required to be 1. The value of parameter MITM_en is 0, corresponding to disabled; 1, corresponding to enabled.

void    blc_smp_enableOobAuthentication(int OOB_en);    //_slave()/_master()

This set of APIs is used to configure the OOB flag of SMP, which requires the security level to be Mode 1 Level 3 and above. The value of parameter OOB_en is 0, corresponding to disabled; 1, corresponding to enabled.

The device will decide whether to use the OOB mode or the IO capability according to the OOB and MITM flags of the local device and the peer device, as described in Bluetooth Core Specification V5.3, Vol 3, Part H, 2.3.5.1 Selecting Key Generation Method.

void    blc_smp_setBondingMode(bonding_mode_t mode);    //_slave()/_master()

This set of APIs is used to configure whether to store the Key generated by the SMP process in the Flash. If it is set to Bondable_Mode, the user can use SMP information for automatic reconnection, and will not re-pair during reconnection; if it is set to Non_Bondable_Mode, the generated Key will not be stored in Flash, and will not be able to reconnect automatically after disconnection, and re-pairing is required.

void    blc_smp_enableKeypress(int keyPress_en);    //_slave()/_master()

This set of APIs is used to configure whether to enable the Key Press function. The value of the parameter keyPress_en is 0, corresponding to disabled; 1, corresponding to enabled.

void    blc_smp_setSecurityParameters(bonding_mode_t mode, int MITM_en, pairing_methods_t method, int OOB_en, int keyPress_en, io_capability_t ioCapablility);  //_slave()/_master()

This set of APIs is used to configure the aforementioned SMP parameters as a whole. The corresponding relationship between each parameter and the above APIs is as follows:

parameter API
mode void blc_smp_setBondingMode(bonding_mode_t mode);
MITM_en void blc_smp_enableAuthMITM(int MITM_en);
method void blc_smp_setPairingMethods(pairing_methods_t method);
OOB_en void blc_smp_enableOobAuthentication(int OOB_en);
keyPress_en void blc_smp_enableKeypress(int keyPress_en);
ioCapablility void blc_smp_setIoCapability(pairing_methods_t method);
void blc_smp_setEcdhDebugMode(ecdh_keys_mode_t mode);   //_slave()/_master()

This set of APIs is used to configure whether Security Connections enables the Debug key pair for elliptical encryption keys. Using the elliptic encryption algorithm in the case of secure connection pairing can effectively avoid eavesdropping, but users cannot parse BLE air packets through the sniffer tool, so the Bluetooth Core Specification provides a set of elliptic encryption private/public key pairs for debugging. As long as this mode is open, some BLE sniffer tools can use this known key to decrypt the link.

Note

  • Slave and Master only allow one party's key to be configured as a Debug key pair, otherwise the connection is not secure, and the meaning of pairing is lost, and the protocol stipulates that it is illegal.
void    blc_smp_setDefaultPinCode(u32 pinCodeInput);    //_slave()/_master()

This set of APIs is used to configure the default Pincode displayed by the Display device in Passkey Entry or Numeric Comparison pairing mode. The parameter range is in [0,999999].

u8 blc_smp_setTK_by_PasskeyEntry (u16 connHandle, u32 pinCodeInput); //connHandle distinguishes connection links

This API is used to input the TK value of the Input device in Passkey Entry pairing mode. The return value 1 means the setting is successful, and 0 means that the Input device is not currently required to input the TK value.

Info:

- Here is an explanation of the relationship between TK, Passkey, and Pincode. TK (Temporary Key), as the most basic original key in the SMP process, can be generated in various ways: e.g., Just Works generates TK = 0 by default; the Passkey Entry method enters the TK value, which is called Pincode in the application layer; the OOB method generates the TK through OOB data. It can be simply understood that Pincode generates Passkey, and Passkey generates TK, but this "generation" does not necessarily change its value.
u8 blc_smp_setTK_by_OOB (u16 connHandle, u8 *oobData); //connHandle distinguishes connection links

This API is used to set the OOB data of the device in OOB pairing mode. The parameter oobData indicates the head pointer of the 16-bit OOB data array to be set. The return value 1 means the setting is successful, and 0 means that the Input device is not currently required to input the TK value.

u8 blc_smp_isWaitingToSetTK(u16 connHandle); //connHandle distinguishes connection links

This API is used to obtain whether the Input device is waiting for TK input in Passkey Entry or OOB pairing mode. Returns 1 to indicate waiting for input.

void    blc_smp_setNumericComparisonResult(u16 connHandle, bool YES_or_NO); //connHandle distinguishes connection links

This API is used to set YES or NO for device input in Numeric Comparison pairing mode under Security Connections. When the user confirms that the displayed 6-bit value is consistent with the peer device, he can enter 1 ("YES"), and if it is inconsistent, enter 0 ("NO").

u8      blc_smp_isWaitingToCfmNumericComparison(u16 connHandle);    //connHandle distinguishes connection links

This API is used to get whether the device is waiting for Yes or No input in Numeric Comparison pairing mode under Security Connections. Returns 1 to indicate waiting for input.

int     blc_smp_isPairingBusy(u16 connHandle);  //connHandle distinguishes connection links

This API is used to query whether a connection is being paired. Return value 0 indicates not being paired, and 1 indicates being paired.

(3) SMP Process Configuration

  • The SMP Security Request can only be sent by the Slave, and is used to actively request the peer Master to perform the pairing process, which is an optional process of SMP.
  • The SMP Pairing Request can only be sent by the Master to notify the Slave to start the pairing process.

a. SMP Security Request

blc_smp_configSecurityRequestSending(secReq_cfg newConn_cfg,  secReq_cfg reConn_cfg, u16 pending_ms);

This API is used to flexibly configure the timing of the Slave sending the Security Request.

Note

  • The call is effective only before the connection is established, and it is recommended to configure it during initialization.

The enumeration type secReq_cfg is defined as follows:

typedef enum {
    SecReq_NOT_SEND = 0,            //After the connection is established, Slave will not actively send Security Request
    SecReq_IMM_SEND = BIT(0),       //After the connection is established, the Slave will immediately send a Security Request
    SecReq_PEND_SEND = BIT(1),      //After the connection is established, the Slave waits pending_ms (in milliseconds) before deciding whether to send a Security Request
}secReq_cfg;

newConn_cfg: Used to configure new connections. If the Slave is configured as SecReq_PEND_SEND and receives the Pairing Request packet from the Master before pending_ms, it will not send the Security Request.

reConn_cfg: Used to configure a reconnection. Pairing the bound device, the next time it connects (ie, reconnection), the Master may not necessarily initiate LL_ENC_REQ to encrypt the link. At this time, if the Slave sends a Security Request, it can trigger the Master to encrypt the link. If the Slave is configured as SecReq_PEND_SEND and has received the LL_ENC_REQ packet from the Master before pending_ms, the Security Request will not be sent again.

pending_ms: This parameter only works when either newConn_cfg or reConn_cfg is configured as SecReq_PEND_SEND.

b. SMP Pairing Request

void blc_smp_configPairingRequestSending( PairReq_cfg newConn_cfg,  PairReq_cfg reConn_cfg);

This API is used to flexibly configure the timing of Pairing Requests sent by the Master.

Note

  • It can only be called before connection and is recommended to be configured during initialisation.

The enumeration type PairReq_cfg is defined as follows:

typedef enum {
    PairReq_SEND_upon_SecReq = 0,   // Master sending Pairing Request is dependent on receiving Security Request from Slave
    PairReq_AUTO_SEND = 1,          // The Master will automatically send a Pairing Request as soon as it is connected.
}PairReq_cfg;

(4) SMP Pairing Method

The SMP pairing method mainly focuses on the configuration of the four security levels of SMP.

a. Mode 1 Level 1

The device does not support encrypted pairing; that is, disable the SMP function, and initialize the configuration:

blc_smp_setSecurityLevel(No_Security);

b. Mode 1 Level 2

The device supports up to Unauthenticated_Paring_with_Encryption, such as Just Works pairing mode in Legacy Pairing and Secure Connections pairing modes.

  • Initial configuration for LE Legacy Just works:
//blc_smp_setPairingMethods(LE_Legacy_Pairing);     //Default
//blc_smp_setSecurityLevel_master(Unauthenticated_Pairing_with_Encryption); //Default
blc_smp_smpParamInit();
  • Initial configuration for LE Security Connections Just works:
blc_smp_setPairingMethods(LE_Secure_Connection); 
blc_smp_smpParamInit();

c. Mode 1 Level 3

The device supports up to Authenticated pairing with encryption, such as Passkey Entry and Out of Band of Legacy Pairing.

This level requires the device to support Authentication, which can ensure the legitimacy of the identity of both parties.

  • Initial configuration of LE Legacy Passkey Entry mode Display device:
blc_smp_setSecurityLevel(Authenticated_Pairing_with_Encryption);
blc_smp_enableAuthMITM(1);
blc_smp_setIoCapability(IO_CAPABILITY_DISPLAY_ONLY);
//blc_smp_setDefaultPinCode(123456);
blc_smp_smpParamInit();

or

blc_smp_setSecurityLevel(Authenticated_Pairing_with_Encryption);
blc_smp_setSecurityParameters(Bondable_Mode, 1, LE_Legacy_Pairing, 0, 0, IO_CAPABILITY_DISPLAY_ONLY);
blc_smp_smpParamInit();

Here, it concerns the display of TK's GAP event: GAP_EVT_SMP_TK_DISPALY, please refer to "GAP event" Section.

  • Initial configuration of LE Legacy Passkey Entry mode Input device:
blc_smp_setSecurityLevel(Authenticated_Pairing_with_Encryption);
blc_smp_enableAuthMITM(1);
blc_smp_setIoCapability(IO_CAPABLITY_KEYBOARD_ONLY);
blc_smp_smpParamInit();

or

blc_smp_setSecurityLevel(Authenticated_Pairing_with_Encryption);
blc_smp_setSecurityParameters(Bondable_Mode, 1, LE_Legacy_Pairing, 0, 0, IO_CAPABLITY_KEYBOARD_ONLY);
blc_smp_smpParamInit();

This concerns the GAP event for requesting TK: GAP_EVT_SMP_TK_REQUEST_PASSKEY, please refer to "GAP event" Section.

The user calls the following API to set up TK:

void blc_smp_setTK_by_PasskeyEntry (u16 connHandle, u32 pinCodeInput);  
  • Initial configuration of LE Legacy OOB:
blc_smp_setSecurityLevel(Authenticated_Pairing_with_Encryption);
blc_smp_enableOobAuthentication(1);
blc_smp_smpParamInit();

or

blc_smp_setSecurityLevel_slave(Authenticated_Pairing_with_Encryption);
blc_smp_setSecurityParameters_slave(Bondable_Mode, 1, LE_Legacy_Pairing, 1, 0, IO_CAPABILITY_KEYBOARD_DISPLAY);
blc_smp_smpParamInit();

Here, the GAP event for requesting OOB data is involved: GAP_EVT_SMP_TK_REQUEST_OOB, please refer to "GAP event" Section.

d. Mode 1 Level 4

The device supports up to Authenticated LE Secure Connections, such as Numeric Comparison, Passkey Entry, and Out of Secure Connections Band.

  • Initial configuration of Secure Connections Passkey Entry mode:

It is basically the same as Legacy Pairing Passkey Entry; the only difference is that the pairing method needs to be set to "secure connection pairing" at the very beginning of initialization:

blc_smp_setSecurityLevel(Authenticated_LE_Secure_Connection_Pairing_with_Encryption);
blc_smp_setParingMethods(LE_Secure_Connection);
...//Refer to Mode 1 Level 3 configuration method
  • Initial configuration of Secure Connections Numeric Comparison:
blc_smp_setSecurityLevel(Authenticated_LE_Secure_Connection_Pairing_with_Encryption);
blc_smp_setParingMethods(LE_Secure_Connection);
blc_smp_enableAuthMITM(1);
blc_smp_setIoCapability(IO_CAPABLITY_DISPLAY_YESNO);
blc_smp_smpParamInit();

or

blc_smp_setSecurityLevel_master(Authenticated_LE_Secure_Connection_Pairing_with_Encryption);
blc_smp_setSecurityParameters_master(Bondable_Mode, 1, LE_Secure_Connection, 0, 0, IO_CAPABLITY_DISPLAY_YESNO);
blc_smp_smpParamInit();

This concerns the GAP event for requesting Yes/No: GAP_EVT_SMP_TK_NUMERIC_COMPARE, please refer to "GAP event" Section.

  • Secure Connections OOB method, not supported by SDK at this time.

(5) SMP Storage

Whether the device is a Master or a Slave, after SMP bonding with another device, some SMP-related information needs to be stored in Flash, so that it can be automatically reconnected after the device is powered on again. This process is called SMP Storage.

a. SMP Storage Area

The area in Flash for storing SMP bonding information is called the SMP Storage area.

For the tc_ble_sdk, the starting position of the SMP Storage area is specified by macro FLASH_ADR_SMP_PAIRING (default 0xFA000 for 1MB Flash, 0x78000 for 512KB Flash). The SMP Storage area is divided into 2 areas, called area A and area B, which occupy the same space and are specified by the macro FLASH_SMP_PAIRING_MAX_SIZE (the default is 0x2000, which is 8KB, so the total SMP Storage area size is 16KB). The (FLASH_SMP_PAIRING_MAX_SIZE-0x10) offset (default is 0x1FF0) position of each zone is the "zone valid Flag", 0x3C means valid, 0xFF means not valid. Users can reconfigure the SMP Storage area using the following API:

void blc_smp_configPairingSecurityInfoStorageAddressAndSize (int address, int size_byte);  //address and size must be 4K aligned
  • address: The starting address of the SMP Storage area (also the starting address of area A);
  • size_byte: The size of each SMP area, area A and area B are equal in size.

The following API is used to get the starting address of the current SMP Storage valid area:

u32 blc_smp_getBondingInfoCurStartAddr(void);
  • Return value: The starting address of the current valid area in SMP Storage, such as 0xFC000.

After pairing, the SMP bonding information is stored in the SMP Storage area A by default. When the amount of bonding information in the area A reaches the warning line (8KB *3/4 = 96 Bytes * 64, that is, a maximum of 64 bonding information can be stored), it will migrate the valid binding information to area B, set "area valid Flag" to 0x3C, and clear area A. Similarly, when the bonding information in area B reaches the warning line, switch to area A and clear area B. The following APIs can be used to confirm whether the information volume of the current SMP Storage effective area has reached the warning line:

bool blc_smp_isBondingInfoStorageLowAlarmed(void);
  • Return value: 0 means not to the warning line, 1 means has reached the warning line.

If you need to clear the information in the SMP Storage and reset the SMP bonding information, it is recommended that you call the following API in a non-connected state:

void blc_smp_eraseAllBondingInfo(void);

b. Bonding Info

Each set of SMP bonding information stored in SMP Storage is called a Bonding Info block. By default, SMP Storage fills in Bonding Info into the SMP Storage area according to the pairing sequence. Refer to its structure, smp_param_save_t, to get:

  • A Bonding Info block of size 96 bytes (0x60);

  • The first Byte of the Bonding Info block, i.e. the flag member, represents the status of the Bonding Info block, and if flag & 0x0F == 0x0A, it means that the SMP bonding information is valid; if the flag member value of Master's Bonding Info block is 0x00, it means that the device has been unbound; if the bit7 of the flag is 0, it means that RPA is supported. For details, please refer to the RPA function section (this function is not yet fully released in the SDK).

  • The second Byte of the Bonding Info block, role_dev_idx, represents the role played by itself. If bit7 is 1, it represents itself as the Master; if bit7 is 0, it represents the role of Slave in the connection.

  • The peer ID Address, and local/peer IRK obtained by SMP are stored in the Bonding Info block.

The following figure is a reference to the content of SMP Storage, which indicates that the Bonding Info block is valid and the device is the Master:

SMP Storage Bonding Info Master

The following API can be used to obtain its Bonding Info through the MAC address of the peer device :

u32 blc_smp_loadBondingInfoByAddr(u8 isMaster, u8 slaveDevIdx, u8 addr_type, u8* addr, smp_param_save_t* smp_param_load);
  • isMaster: Its own role, 0 means Slave, non-0 means Master;
  • SlaveDevIdx: When the multi-address function is not involved, this parameter is 0;
  • addr_type: The address type of the peer device, refer to BLE_ADDR_PUBLIC and BLE_ADDR_RANDOM;
  • addr: MAC address of peer device;
  • smp_param_load: The output parameter points to the Bonding Info block corresponding to the peer device.
  • return value: the first address in Flash of the Bonding Info block corresponding to the peer device.

For the convenience of the application layer, an API is provided for the Master role to obtain its pairing state according to the peer Slave's MAC :

u32 blc_smp_searchBondingSlaveDevice_by_PeerMacAddress( u8 peer_addr_type, u8* peer_addr);
  • peer_addr_type: The address type of the peer Slave, refer to BLE_ADDR_PUBLIC and BLE_ADDR_RANDOM;
  • peer_addr: The MAC address of the peer Slave;
  • return value: The first address in Flash of the Bonding Info block of the Bonding Device found; 0 means that no valid bonding information was found.

Use the following API to remove the corresponding Bonding Info through the MAC address of the peer device (actually, it is not removed, but it is invalidated by setting the flag):

int blc_smp_deleteBondingSlaveInfo_by_PeerMacAddress(u8 peer_addr_type, u8* peer_addr);
  • peer_addr_type: The address type of the peer Slave, refer to BLE_ADDR_PUBLIC and BLE_ADDR_RANDOM;
  • peer_addr: The MAC address of the peer Slave;
  • return value: Find and delete the first address of the Bonding Info block of the Bonding Device in Flash; 0 means no valid bonding information is found.

c. Max Bonding Quantity

For tc_ble_sdk, the SMP information of up to 8 valid peer Slave and the SMP information of 4 valid peer Master can be saved by default ("Valid" means that the device can be reconnected successfully, that is, the flag member of the Bonding Info block indicates that the current state is valid), which are called the maximum bonding number of Master and Slave in SDK (Bonding Device Max Number). Users can also reconfigure the maximum bonding number of SMP Storage through the following API :

ble_sts_t blc_smp_setBondingDeviceMaxNumber ( int master_max_bonNum, int slave_max_bondNum);
  • Master_max_bonNum: The maximum number of peer Slave bonding for the device acting as a Master role, with a maximum limit of 8. If the input parameter exceeds 8, it will return error 0xA0 (SMP_ERR_INVALID_PARAMETER).
  • Slave_max_bondNum: The maximum number of peer Master bonding for the device acting as a Slave role, with a maximum limit of 4. If the input parameter exceeds 4, it will return error 0xA0 (SMP_ERR_INVALID_PARAMETER).

Note

  • Before V4.0.1.3, use the following API declaration:
void blc_smp_setBondingDeviceMaxNumber ( int peer_slave_max, int peer_master_max)
  • peer_slave_max: The maximum number of peer Slaves that can be bound when acting as a Master. The maximum is 8; if the input parameter exceeds 8, it is set to 8.
  • peer_master_max: The maximum number of peer Masters that can be bound when acting as a Slave. The maximum is 4; if the input parameter exceeds 4, it is set to 4.

When the maximum number of bonds is reached, the next bound device will replace the earliest bound device of the currently valid devices in the same role. Specifically, the Bonding Info of the new device will continue to be written into the Flash, the flag will be set as valid, and the flag of the first device in the valid Bonding Info of the same role will be set as invalid.

For example, if the blc_smp_setBondingDeviceMaxNumber(8, 4) is set, when 8 peer Slaves are bound, once the 9th peer Slave is bound, the Bonding Info of the oldest (first) peer Slave will fail, and the Bonding Info of the 9th peer Slave device continues to be stored in Flash.

The user can get the current Slave or Master bonding quantity through the following API:

u8 blc_smp_param_getCurrentBondingDeviceNumber(u8 isMasterRole, u8 slaveDevIdx);
  • isMasterRole: Its own role, 0 means Slave, non-0 means Master;
  • perDevIdx: When the multi-address function is not involved, this parameter is 0;
  • return value: The number of valid bound devices. When isMasterRole is 0, it indicates the number of valid peer Master bound; when isMasterRole is not 0, it indicates the number of valid peer Slave bound.

d. SMP Bonding Info Index

The bonding information of each Bonding Device is assigned a serial number in SMP, which is called the Bonding Info Index. The value of the Bonding Info Index is assigned in Bonding Device Max Number by default according to the order of bonding. For example, if the Master's Bonding Device Max Number is 2, the Bonding Info Index of the two peer Slave pairs will be 0 and 1, respectively.

In this way, in addition to obtaining Bonding Info by peer device MAC address as described above, you can also obtain the Bonding Info through the Bonding Info Index when the device Bonding Info Index is known:

u32 blc_smp_loadBondingInfoFromFlashByIndex(u8 isMaster, u8 slaveDevIdx, u8 index, smp_param_save_t* smp_param_load);
  • isMaster: Its own role, 0 means Slave, non-0 means Master;
  • SlaveDevIdx: When the multi-address function is not involved, this parameter is 0;
  • index: Bonding Info Index representing the Master or Slave information to read.
  • smp_param_load: The output parameter points to the Bonding Info block corresponding to the peer device.
  • return value: the first address in Flash of the Bonding Info block corresponding to the peer device.

The following API is used to configure the allocation rules for the Bonding Info Index. This feature is not yet available in the SDK and is intended only for specific user requirements:

void blc_smp_setBondingInfoIndexUpdateMethod(index_updateMethod_t method);
  • method: Refer to index_updateMethod_t. It can be set to allocate Bonding Info Index values based on the order in which connections are established or the order in which devices are bound.

Custom Pair

In multi-master, multi-slave device configurations, if the Master device disables SMP, the SDK cannot automatically perform pairing and unpairing operations; pairing management must be implemented at the application layer. Based on this, Telink provides a custom pairing and unpairing solution.

If users need to use custom pairing management, initialize this feature first by calling the following API:

blc_smp_setSecurityLevel_master(No_Security);//Disable SMP functionality
user_master_host_pairing_management_init();//Custom method

(1) Flash Storage Method Design

By default, the flash data sector range is 0x7C000 - 0x7CFFF. Users can modify the following macro in app_config.h:

#define FLASH_ADR_CUSTOM_PAIRING                0x7C000
#define FLASH_CUSTOM_PAIRING_MAX_SIZE           4096

The flash area starting at 0x7C000 is divided into 8-byte segments, referred to as 8-byte areas. Each area can store a single Slave MAC address, where the first byte is a flag, the second byte is the address type, and the remaining 6 bytes constitute the 6-byte MAC address.

typedef struct {
        u8 bond_mark;
        u8 adr_type;
        u8 address[6];
} macAddr_t;

During the flash storage process, the method of sequentially shifting the 8-byte areas forward is used. The first valid Slave MAC is stored at 0x7C000–0x7C007, and the first byte (flag) at 0x7C000 is set to 0x5A to indicate that the current address is valid; When storing the second valid MAC address, it is stored at 0x7C008–0x7C00F, and the first byte of 0x7C008 is marked with 0x5A; when storing the third valid MAC address, it is stored at 0x7C010–0x7C017, and the first byte of 0x7C010 is marked with 0x5A.

If a slave device needs to be unpaired, the multi-master/multi-slave system must erase that device’s MAC address. This is achieved by setting the flag bit of the 8-byte area previously storing that MAC address to 0x00. For example, to erase the first device among the three mentioned above, set 0x7C000 to 0x00.

The reason for using this 8-byte sequential method is that the program cannot call the flash_erase_sector function to erase the flash during runtime, as this operation takes between 20 and 200 ms to erase a 4KB sector of flash, and this duration would cause BLE timing errors.

The storage and erasure of all Slave MAC pairings are indicated by the 0x5A and 0x00 flag bits. When the 8-byte area grows larger and risks filling the entire 4K flash sector, causing errors, special handling is added during initialization: Read the 8-byte area information starting from 0x7C000, and load all valid MAC addresses into the Slave MAC table in RAM. During this process, the system checks whether there are too many 8-byte areas. If there are too many, the entire sector is erased, and the Slave MAC table maintained in RAM is rewritten to the 8-byte area starting at 0x7C000.

(2) Slave MAC table

Slave MAC table

user_salveMac_t user_tbl_slaveMac;

Use the structure above to maintain the Slave MAC table in RAM for all paired devices. Users can define the maximum number of allowed pairs by modifying the macro USER_PAIR_SLAVE_MAX_NUM. The default value in tc_ble_sdk is 4, meaning it maintains pairs for 4 devices; users can modify this value.

In the user_tbl_slaveMac, curNum indicates the number of valid Slave devices currently recorded in flash. The bond_flash_idx array records the offset relative to 0x7C000 for the starting address of the 8-byte area in flash where valid addresses are stored (when unpairing a device, this offset can be used to locate the flag bit in the 8-byte area and set it to 0x00). The bond_device array stores MAC addresses.

(3) Related API Descriptions

Based on the above Flash storage design and the Slave MAC table design in RAM, the following APIs are available for calling.

a. user_master_host_pairing_management_init

void    user_master_host_pairing_management_init(void);

It is the user-defined pairing management initialization function, required when enabling custom pairing.

b. user_tbl_slave_mac_add

int user_tbl_slave_mac_add(u8 adr_type, u8 *adr);

This function is used to add a Slave MAC. A return value of 1 indicates success, while 0 indicates failure. It is called when a new device pairs.

The function first checks whether the current capacity of the flash and the Slave MAC table has reached its maximum. If the maximum has not been reached, the MAC is added to the Slave MAC table unconditionally and stored in an 8-byte area of the flash.

If the maximum has been reached, a policy decision must be made: whether to disallow pairing or to overwrite the oldest entry. The Telink demo method overwrites the oldest entry by first using user_tbl_slave_mac_delete_by_index(0) to remove the current device, then adding the new one to the Slave MAC table. Users may modify the implementation of this function according to their own policies.

c. user_tbl_slave_mac_search

int user_tbl_slave_mac_search(u8 adr_type, u8 * adr)

Search the Slave MAC table using the device address from the ADV report to determine whether the device is already present. This checks whether the device currently sending the advertising packet has previously paired with the Master; if so, it can connect directly.

d. user_tbl_slave_mac_delete_by_adr

int user_tbl_slave_mac_delete_by_adr(u8 adr_type, u8 *adr)

This function is used to delete a paired device by specifying its address.

e. user_tbl_slave_mac_delete_by_index

void user_tbl_slave_mac_delete_by_index(int index)

This function is used to delete a paired device by specifying an index. The index value reflects the order in which the device is paired. If the maximum number of pairs is 1, the paired device will always have an index of 0; if the maximum number of pairs is 2, the first paired device has an index of 0 and the second paired device has an index of 1; and so on.

f. user_tbl_slave_mac_delete_all

void user_tbl_slave_mac_delete_all(void)

This function is used to delete all paired devices.

(4) Connection and Pairing

When the Master receives an advertising packet reported by the Controller, it connects to the Slave under the following two conditions:

a. Call the function user_tbl_slave_mac_search to check whether the current device has already been paired with the Master and has not been unpaired; if it has been paired, it can connect automatically.

master_auto_connect = user_tbl_slave_mac_search(pa->adr_type, pa->mac);
if(master_auto_connect) { create connection }

b. If the current advertising device is not in the Slave MAC table and does not meet the conditions for automatic connection, check whether the conditions for manual pairing are met. The SDK provides two default manual pairing scenarios. Assuming the advertising device is within sufficient proximity: first, the pairing button is pressed on a multi-master/multi-slave device; second, the current advertising data is a pairing advertising packet defined by Telink. Code:

Connection Pairing Code Example 1

if(user_manual_pairing) { create connection }

If the connection is established via manual pairing, after the connection is successfully established (i.e., when the HCI LE CONNECTION ESTABLISHED EVENT is reported), add the current device to the Slave MAC table:

Connection Pairing Code Example 2

c. Unpairing

When the unpairing condition is met, the multi-Master/multi-Slave device first calls blc_llms_disconnect to disconnect, then calls the user_tbl_slave_mac_delete_by_adr function to remove the device.

Device Management & Simple SDP

As described above for GATT, in BLE, when a Slave acts as a GATT Server, it maintains a table of GATT Services, with each Attribute in the table corresponding to an Attribute handle value. For the Master to obtain this information for the Slave, it is necessary to obtain it through the SDP process and maintain it for use when needed.

For ease of use, the tc_ble_sdk provides the user with an implementation of device Management as a connected device management solution and a simple implementation for the Master to do SDP to get the GATT Service table of the peer Slave. Not only is it possible to manage the GATT Service table of a peer Slave for the Master, but it can also be used to retrieve other information about the peer device at any time by using some of the information from that device. The solution is provided in source code form, and users can refer to the vendor/common/device_manage.* and vendor/common/simple_sdp.* files in the SDK.

The tc_ble_sdk uses the following data structure to manage "Attribute handle" and "Connection handle".

typedef struct
{
    u16     conn_handle;
    u8      conn_role;              // 0: master; 1: slave
    u8      conn_state;             // 1: connect;  0: disconnect

    u8      char_handle_valid;      // 1: peer device's attHandle is available;   0: peer device's attHandle not available
    u8      rsvd[3];                // for 4 Byte align

    u8      peer_adrType;
    u8      peer_addr[6];
    u8      peer_RPA;         //RPA: resolvable private address

    u16     char_handle[CHAR_HANDLE_MAX];
}dev_char_info_t;

In the SDK, the array "conn_dev_list[]" is used to record and maintain the "attribute handle" of the peer device, as shown in the following figure.

conn_dev_list Definition

When a connection is established with another device, the identity information of the peer device is stored in the conn_dev_list[] by calling dev_char_info_insert_by_conn_event() in the connection complete event.

Connection Complete Event Handle

If the device is the Master and Simple SDP is enabled, first check if the GATT Service table of the peer device is already in Flash via dev_char_info_search_peer_att_handle_by_peer_mac(), and if so, retrieve it directly from Flash in conn_dev_list[] via dev_char_info_add_peer_att_handle().

simpleSDP info in Flash

If not, it will be fetched via app_service_discovery(). Once obtained, the functions dev_char_info_add_peer_ att_handle() and dev_char_info_store_peer_att_handle() will be called to store the peer Slave GATT Service table into RAM and flash, respectively, for subsequent use. This is shown in the figure below.

Service Discovery

Note

  • The SDP is a very complicated part. For tc_ble_sdk, due to limited chip resources, SDP cannot be as complicated as a mobile phone. Given here is a simple reference.

The user can fetch the Attribute handle from the GATT Service table by following the connHandle dev_char_info_search_by_connhandle(), whose return value is a pointer to the conn_dev_list[index] structure, pointing to that element of the conn_dev_list[] array to which the connHandle corresponds.

LE Advertising Extensions

As BLE applications become more widespread, their functionality has significantly increased. Below, we will introduce the LE Advertising Extensions introduced from core 5.0, which include: Extended Advertising, Periodic Advertising, Extended Scan, and Periodic Scan, etc. The introduction of these features also prepares the way for LE Audio, though they can certainly be utilized for other purposes depending on the actual situation.

(1) Extended Advertising

Before BLE core 5.0, there was a significant limitation: the payload of advertising data was too small, only 31 bytes. Simply increasing the payload size wasn't feasible, as channels 37, 38, and 39 are shared advertising physical channels, leading to a high probability of collisions-the longer the payload, the greater the chance of conflict. Core 5.0 introduced Advertising Extensions, incorporating concepts like advertising sets and periodic advertising. This not only addresses the small payload issue but also reduces collision probabilities by utilizing the other 37 data channels, which can employ frequency-hopping mechanisms to further mitigate conflicts.

Note

  • The core specification also limits the maximum payload to 1650 bytes, meaning that the effective data of AUX_** plus all corresponding chain packets does not exceed 1650B.

In versions up to and including Core_V4.2, channels 0 to 36 (37 channels in total) were primarily used as data channels for LE-ACL connections. In the Core_V5.0 advertising channel definition, channels 37, 38, and 39 are defined as primary advertising channels, while the other 37 channels are referred to as secondary advertising channels. Secondary advertising channels can also be used to send or receive advertising data.

For extended advertising, only ADV_EXT_IND is sent on the primary advertising channel, while advertising packets on the secondary advertising channels are named AUX_**.

Note

  • On the primary advertising channel, 1M and coded PHY are permitted, but 2M PHY is not (at least according to the latest Core Spec 5.4).

Advertising Extensions lay the foundation for implementing LE Audio. During the initial stage of establishing an ACL connection, a considerable amount of information about the peer device is required, such as audio parameters, timing information, encryption information, and so on. This requires a larger advertising payload.

Extended advertising includes ADV_EXT_IND, AUX_ADV_IND, and the corresponding AUX_CHAIN_IND. If more data needs to be advertised (up to 1650 bytes), the controller can segment the data and use AuxPtr to "chain" the segments together. Each segment can be transmitted on different channels: ADV_EXT_IND (AuxPtr) --> AUX_ADV_IND (AuxPtr) --> AUX_CHAIN_IND (AuxPtr) --> AUX_CHAIN_IND...

The core idea of extended advertising: Advertising data can be transmitted using data channels.

Extended Advertising with Packet Chaining Extended Advertising Sniffer Packet Capture Log

All Extended Advertising Names and Detailed Descriptions:

Extended Advertising PDU

Legacy Advertising compared with Extended Advertising:

Legacy Advertising compared with Extended Advertising

a. Advertising Sets

Extended advertising introduces the concept of advertising sets, meaning a device can "simultaneously" perform multiple advertising operations, each using different intervals, PDU data, PDU types, PHYs, etc. For example, it can "simultaneously" send connectable and non-connectable advertising. Different advertising sets are distinguished by the ADI field in the extended header.

Note

  • Currently, the tc_ble_sdk supports the creation of a maximum of 4 advertising sets.

b. Extended Advertising API

 ble_sts_t  blc_ll_initExtendedAdvModule_initExtendedAdvSetParamBuffer(u8 *pBuff_advSets, int num_advSets);

If using extended advertising, the API must be used for initialization. This is to initialize the extended advertising module and allocate the necessary buffer space for the corresponding advertising parameters. Only after the relevant module is initialized will the corresponding features become active, and the functionality will be linked to the executable (bin) file.

pBuff_advSets: This is the starting address of the buffer space used by the underlying control for extended advertising. The stack will use this buffer to store various control variables needed for the runtime of extended advertising. Each advertising set requires such buffer space. It is managed by the upper-layer user to allow for allocation based on the actual number of advertising sets in use, thereby saving RAM size.

num_advSets: The number of advertising sets actually used by the user. Note: The tc_ble_sdk supports a maximum of 4 advertising sets.

void        blc_ll_initExtendedAdvDataBuffer(u8 *pExtAdvData, int max_len_advData);

Set the buffer for storing extended advertising data, which is used to store the data set by blc_ll_setExtAdvData.

pExtAdvData: Starting address of the buffer.

max_len_advData: Buffer size. The length of the data set by blc_ll_setExtAdvData must not exceed this value.

void        blc_ll_initExtendedScanRspDataBuffer(u8 *pScanRspData, int max_len_scanRspData);

Set the buffer for storing scan response data, which is used to store the data set by blc_ll_setExtScanRspData.

pScanRspData: Starting address of scan response buffer.

max_len_scanRspData: buffer size. The length of the data set by blc_ll_setExtScanRspData must not exceed this value.

ble_sts_t   blc_ll_setExtAdvParam(  u8 adv_handle,              advEvtProp_type_t adv_evt_prop,   u32 pri_advInter_min,     u32 pri_advInter_max,
                                    adv_chn_map_t pri_advChnMap,    own_addr_type_t ownAddrType,    u8 peerAddrType,            u8  *peerAddr,
                                    adv_fp_type_t advFilterPolicy,  tx_power_t adv_tx_pow,          le_phy_type_t pri_adv_phy,  u8 sec_adv_max_skip,
                                    le_phy_type_t sec_adv_phy,      u8 adv_sid,                     u8 scan_req_noti_en);

This is the BLE Spec standard interface used to set extended advertising parameters. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.53 “LE Set Extended Advertising Parameters Command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_setExtAdvData (u8 adv_handle, int advData_len, u8 *advData);

This is the BLE Spec standard interface used to set data sent by extended advertising. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.54 “LE Set Extended Advertising Data command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_setExtScanRspData(u8 adv_handle, int scanRspData_len, u8 *scanRspData);

This is the BLE Spec standard interface used to set the data of the extended scan response. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.55 “LE Set Extended Scan Response Data command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_setExtAdvEnable(adv_en_t enable, u8 adv_handle, u16 duration, u8 max_extAdvEvt);

This is the BLE Spec standard interface used to enable/disable Extended Advertising. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.56 “LE Set Extended Advertising Enable Command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_setAdvRandomAddr(u8 adv_handle, u8* rand_addr);

This is the BLE Spec standard interface used to set the random address of devices. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.52 “LE Set Advertising Set Random Address command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_removeAdvSet(u8 adv_handle);

This is the BLE Spec standard interface used to remove the corresponding advertising sets. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.59 “LE Remove Advertising Set command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_clearAdvSets(void);

This is the BLE Spec standard interface used to remove all advertising sets. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.60 “LE Clear Advertising Sets command") and consult the SDK comments for this API for a better understanding.

(2) Periodic Advertising

Periodic advertising is another concept introduced in Core 5.0. If data needs to be sent at fixed intervals, periodic advertising should be used. The concept of periodic advertising intervals is similar to that of ACL intervals, with each interval using different frequency points and employing the CSA#2 frequency-hopping algorithm. Periodic advertising utilizes 37 secondary advertising channels.

Periodic advertising consists of: ADV_EXT_IND leading to AUX_ADV_IND via AuxPtr, and AUX_ADV_IND leading to AUX_SYNC_IND via SyncInfo. If data needs to be continued with chained packets, AUX_SYNC_IND leads to the corresponding AUX_CHAIN_IND via AuxPtr. This can be represented as:

ADV_EXT_IND (AuxPtr) ---> AUX_ADV_IND (SyncInfo) ---> AUX_SYNC_IND (AuxPtr) ---> AUX_CHAIN_IND (AuxPtr) ---> AUX_CHAIN_IND...

Periodic Advertising Event

Note

  • After periodic advertising is initiated, the ADV_EXT_IND and AUX_ADV_IND can either continue to send or stop. If stopped, it does not affect devices that have already synchronized with the periodic advertising, but devices that have not yet synchronized or are newly powered on will not be able to sync with the corresponding periodic advertising. Whether to stop is up to the user to decide based on the actual situation.
  • If the extended advertising interval is greater than the periodic advertising interval, multiple AUX_SYNC_IND will appear between each ADV_EXT_IND. Conversely, if the extended advertising interval is less than the periodic advertising interval, multiple ADV_EXT_IND will ultimately point to the same AUX_SYNC_IND.

Periodic Advertising Event

Periodic Advertising Event

Periodic advertising allows scanning devices to save power, as they only need to scan at fixed time points. Periodic advertising is a key component of the LE Audio advertising solution.

The periodic advertising interval determines the frequency at which periodic advertising for a given advertising set can occur. It starts with the transmission of the AUX_SYNC_IND PDU, followed by a series of zero or more AUX_CHAIN_IND PDUs, as shown in the diagram below.

Periodic Advertising Event

Periodic Advertising Event

Periodic Advertising API:

void        blc_ll_initPeriodicAdvModule_initPeriodicdAdvSetParamBuffer(u8 *pBuff, int num_periodic_adv);

Initialize the periodic advertising module; only after the corresponding module is initialized will the relevant features become active, and the corresponding functions will be linked to the executable (bin) file.

Initialize the buffer space required for the parameters of the periodic advertising module. The stack will use this buffer to store various control variables needed for the runtime of periodic advertising. Each advertising set requires such buffer space. It is managed by the upper-layer user so that they can allocate based on the actual number of advertising sets in use, thus saving RAM size.

pBuff: The address of the buffer space used for periodic advertising parameters.

num_periodic_adv: The number of periodic advertising sets actually used by the user. Note: The maximum number of periodic advertising sets supported by the underlying system is 2.

ble_sts_t   blc_ll_setPeriodicAdvParam(adv_handle_t adv_handle, u16 advInter_min, u16 advInter_max, perd_adv_prop_t property);

This is the BLE Spec standard interface used to set periodic advertising parameters. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.61 "LE Set Periodic Advertising Parameters command") and consult the SDK comments for this API for a better understanding.

void        blc_ll_initPeriodicAdvDataBuffer(u8 *perdAdvData, int max_len_perdAdvData);

This is used to set the buffer of periodic advertising data, which is used to store the data set by blc_ll_setPeriodicAdvData.

ble_sts_t   blc_ll_setPeriodicAdvData(adv_handle_t adv_handle, u16 advData_len, u8 *advdata);

This is the BLE Spec standard interface used to set the data sent by periodic advertising. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.62 "LE Set Periodic Advertising Data command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_setPeriodicAdvEnable(u8 per_adv_enable, adv_handle_t adv_handle);

This is the BLE Spec standard interface used to enable/disable periodic Advertising. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.63 "LE Set Periodic Advertising Enable command") and consult the SDK comments for this API for a better understanding.

(3) Extended Scan

For acquiring traditional advertising packets, conventional scanning devices only need to scan the three channels 37, 38, and 39 (Primary Advertising channels). They simply switch back and forth between these three channels according to the scanning window and scanning interval, processing any received advertising data according to protocol requirements during the scanning window.

However, to scan extended advertising packets, it is necessary to scan and obtain the ADV_EXT_IND advertising packets on the primary advertising channels 37, 38, and 39. The scanning device must then parse whether AuxPtr information is included. If it exists, the device needs to retrieve the timing, frequency-hopping information, PHY, and other details carried by the AuxPtr for the next auxiliary advertising packet. The scanning device must listen for the advertising packets during the appropriate window based on this information. In cases of longer packet chains, the scanning device continues to process the next AuxPtr until there are no more AuxPtr fields. After receiving the complete packet, the device will handle it according to whether the advertising packet is scannable or connectable, as well as its packet type. For complete and detailed information, please refer to the Bluetooth Core Specification, "Core_V5.4," Vol 6, Part B 4.4.3 Scanning state.

Extended Scan Event

The complexity of extended scanning lies in how to acquire advertising packets on the auxiliary channels, as there may be multiple levels of guidance for these advertising packets.

Extended Scan API

void        blc_ll_initExtendedScanning_module(void);

Initialize the extended scan module; only after the corresponding module is initialized will the relevant features become active, and the corresponding functions will be linked to the executable (bin) file.

ble_sts_t   blc_ll_setExtScanParam ( own_addr_type_t  ownAddrType, scan_fp_type_t scan_fp, scan_phy_t   scan_phys, scan_type_t scanType_0, scan_inter_t scanInter_0,  scan_wind_t scanWindow_0, scan_type_t scanType_1, scan_inter_t   scanInter_1, scan_wind_t scanWindow_1);

This is the BLE Spec standard interface used to set extended scan parameters. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.64 "LE Set Extended Scan Parameters command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_setExtScanEnable (scan_en_t extScan_en, dupe_fltr_en_t filter_duplicate, scan_durn_t duration, scan_period_t period);

This is the BLE Spec standard interface used to enable/disable extended scan. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.65 "LE Set Extended Scan Enable command") and consult the SDK comments for this API for a better understanding.

(4) Periodic Scan

The scanning device can synchronize with the periodic advertising sequence (train) in the following two ways:

a) The device itself can scan the ADV_EXT_IND and AUX_ADV_IND PDUs and use the contents of the SyncInfo field, such as the periodic advertising interval, timing offset, and the channels to be used, to establish synchronization with periodic advertising. (Refer to the sections on Periodic Advertising and Extended Scan).

Periodic Advertising Event

b) The device can receive this information from another device via an LE-ACL connection, allowing it to synchronize with the periodic advertising device without the need to scan the primary channel. This process is referred to as Periodic Advertising Sync Transfer (PAST). Refer to the section on Periodic Advertising Sync Transfer (PAST).

Periodic Scan API

void    blc_ll_initPeriodicAdvertisingSynchronization_module(void);

Initialize the periodic scan module; only after the corresponding module is initialized will the relevant features become active, and the corresponding functions will be linked to the executable (bin) file.

ble_sts_t   blc_ll_periodicAdvertisingCreateSync ( option_msk_t options, u8 adv_sid, u8 adv_adrType, u8 *adv_addr, u16 skip, sync_tm_t sync_timeout, u8 sync_cte_type);

This is the BLE Spec standard interface used to synchronize with periodic advertising and begin receiving periodic advertising packets. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.67 "LE Periodic Advertising Create Sync command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_periodicAdvertisingCreateSyncCancel (void);

This is the BLE Spec standard interface. After using blc_ll_periodicAdvertisingCreateSync to prepare for periodic advertising synchronization, if synchronization has not yet been established, calling this API can cancel synchronization. However, if synchronization has already taken place, calling this API will have no effect and will return a command disallowed error. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.68 "LE Periodic Advertising Create Sync Cancel command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_periodicAdvertisingTerminateSync (u16 sync_handle);

This is the BLE Spec standard interface used to stop synchronization with the periodic advertising specified by sync_handle. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.69 "LE Periodic Advertising Terminate Sync command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_addDeivceToPeriodicAdvertiserList (u8 adv_adrType, u8 *adv_addr, u8 adv_sid);

This is the BLE Spec standard interface used to add the specified device and advertising set ID to the Periodic Advertiser List, similar to a white list. For details, please refer to Vol 4/Part E/7.8.70 "LE Add Device To Periodic Advertiser List command" and consult the SDK comments for this API for a better understanding.

For the periodic sync establishment filter policy, please refer to "Core_5.3" (Vol 6/Part B/4.3.5 Periodic sync establishment filter policy).

ble_sts_t   blc_ll_removeDeivceFromPeriodicAdvertiserList (u8 adv_adrType, u8 *adv_addr, u8 adv_sid);

This is the BLE Spec standard interface used to remove the specified device from the Periodic Advertiser List. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.71 "LE Remove Device From Periodic Advertiser List command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_clearPeriodicAdvertiserList (void);

This is the BLE Spec standard interface used to remove all devices in the Periodic Advertiser List. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.72 "LE Clear Periodic Advertiser List command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_readPeriodicAdvertiserListSize (u8 *perdAdvListSize);

This is the BLE Spec standard interface used to read the maximum number of devices that can be stored in the Periodic Advertiser List. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.73 "LE Read Periodic Advertiser List Size command") and consult the SDK comments for this API for a better understanding.

ble_sts_t   blc_ll_periodicAdvertisingReceiveEnable (u16 sync_handle, sync_adv_rcv_en_msk enable);

This is the BLE Spec standard interface used to enable or disable the reporting of received periodic advertising to the host. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.88 "LE Set Periodic Advertising Receive Enable command") and consult the SDK comments for this API for a better understanding.

(5) Periodic Advertising Sync Transfer (PAST)

PAST is a new feature introduced in Core_V5.1, primarily used to inform the receiving device on how to synchronize with periodic advertising. For complete and detailed information, please refer to Core_V5.4, Vol 6, Part B 4.6.23 Periodic Advertising Sync Transfer - Sender and 4.6.24 Periodic Advertising Sync Transfer - Recipient.

PAST Mode 1

PAST Event

As shown in the figure above, without PAST, the smartphone has already synchronized with the TV via periodic advertising. This allows the smartphone to receive the periodic advertising packet AUX_SYNC_IND. At the same time, the smartphone can establish an ACL connection with the smartwatch. If the smartwatch wishes to receive periodic advertising packets from the TV, it must scan for the TV on its own and synchronize with it via periodic advertising. Completing this process requires additional time and power from the smartwatch, but such devices often have limited battery life.

With PAST enabled, the situation is entirely different. In the same scenario, if the smartwatch wishes to scan and synchronize with the TV’s periodic advertising, the smartphone can transmit the periodic advertising synchronization information via the LL_PERIODIC_SYNC_IND packet through the LE ACL to the smartwatch, allowing the watch to synchronize with the TV’s periodic advertising. PAST simplifies this process and helps devices with limited battery life conserve power.

Some device types, with limited power, may not be able to afford the energy cost associated with the periodic advertising synchronization procedure or may have limitations in duty cycle or scan time that prevent them from working. The new Periodic Advertising Sync Transfer (PAST) feature allows another less constrained device to perform the synchronization procedure and then pass the acquired synchronization details over a point-to-point Bluetooth Low Energy connection to the other, constrained device. For example, a smartphone could scan for AUX_SYNC_IND packets from a TV and then pass them over a connection to an associated smart watch via LL_PERIODIC_SYNC_IND so that the watch can then benefit from using periodic advertising and scanning to acquire data from the TV.

Note

  • PAST involves three devices: the advertiser (TV), the assistant (smartphone, acting as the master), and the receiver (smartwatch, acting as the slave). In implementation, the advertiser (TV) and the assistant (smartphone) may be the same device or may exist independently. This means PAST supports two modes: (1) the assistant (master) obtains advertising source timing information through extended scanning; (2) the assistant (master) itself is the advertising source. The figure below illustrates Mode 2 of PAST.

PAST Event

PAST API

void        blc_ll_initPAST_module(void);

Initialize the PAST module; only after the corresponding module is initialized will the relevant features become active, and the corresponding functions will be linked to the executable (bin) file.

ble_sts_t   blc_ll_periodicAdvSyncTransfer(u16 connHandle, u16 serviceData, u16  syncHandle);

This is the BLE Spec standard interface used to instruct the controller to send an LL_PERIODIC_SYNC_IND to the device connected via ACL, with the specific device designated by API parameters. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.89 "LE Periodic Advertising Sync Transfer command") and consult the SDK comments for this API for a better understanding.

Note

  • This API is used in PAST Mode 1, where the advertising device and the assistant device are not the same device. The syncHandle is reported by the LE Periodic Advertising Sync Established event and serves as the periodic advertising ID.
ble_sts_t   blc_ll_periodicAdvSetInfoTransfer(u16   connHandle, u16     serviceData, u8     advHandle);

This is the BLE Spec standard interface used to instruct the controller to send an LL_PERIODIC_SYNC_IND to the device connected via ACL, with the specific device designated by API parameters. For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.90 "LE Periodic Advertising Set Info Transfer command") and consult the SDK comments for this API for a better understanding.

Note

  • This API applies to PAST Mode 2, where the advertising device and the assistant device are the same device. In this case, there will be no LE Periodic Advertising Sync Established event, so no syncHandle is available to identify periodic advertising. However, since the device knows its own advertising set ID, it can use the advertising set ID to specify the periodic advertising.
ble_sts_t   blc_ll_setPeriodicAdvSyncTransferParams(u16 connHandle, u8 mode, u16 skip, u16 syncTimeout, u8 cteType);

This is the BLE Spec standard interface used to instruct how the controller should handle receiving an LL_PERIODIC_SYNC_IND on the receiving device (Slave in Mode 1/2). The controller can either synchronize to the corresponding periodic advertising based on the information in the LL_PERIODIC_SYNC_IND or ignore the LL_PERIODIC_SYNC_IND.

For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.91 "LE Set Periodic Advertising Sync Transfer Parameters command") and consult the SDK comments for this API for a better understanding.

The receiving device may have multiple ACL connections, and you can use connHandle to specify a particular ACL connection.

ble_sts_t   blc_ll_setDftPeriodicAdvSyncTransferParams(u8 mode, u8 skip, u16 syncTimeout, u8 cteType);

This is the BLE Spec standard interface; its functions are similar to blc_ll_setPeriodicAdvSyncTransferParams. The difference is that this API applies to all ACL connections. If a specific ACL connection requires different parameters from the others, blc_ll_setPeriodicAdvSyncTransferParams can be used to set it for that specific ACL connection (connHandle). For details, please refer to "Core_5.3" (Vol 4/Part E/7.8.92 "LE Set Default Periodic Advertising Sync Transfer Parameters command") and consult the SDK comments for this API for a better understanding.

Periodic Advertising with Response (PAwR)

PAwR is a new feature added to "Core_V5.4". It is used to send data and commands to specific synchronous devices through periodic advertising (for details, please refer to the section on periodic advertising), and at the same time, it can receive response messages from the synchronous devices. Currently, the typical case of PAwR is to support the deployment of Electronic Shelf Label (ESL).

(1) PAwR Basics

According to the different functions, there are two roles in PAwR: the advertizer and the observer. The advertiser is responsible for making PAwR advertising, sending control commands and data to observers, and simultaneously receiving response data from observers. The observer is responsible for listening to relevant PAwR advertising and responding to it. PAwR makes full use of the interval time of periodic events on the basis of periodic advertising and divides this period of time into several subevents. As shown in the following figure, each subevent is assigned a unique number. Taking ESL as an example, this number corresponds to the group ID of the ESL device, i.e., ESL devices with the same group ID will listen to the corresponding subevent at the same time.

PAWR_subevent

The structure of the subevent is shown below:

PAWR_rspSlots

At the subevent start position, the advertiser sends the following two types of synchronisation packets:

  • AUX_SYNC_SUBEVENT_IND: a synchronisation request packet containing control commands and data

  • AUX_CONNECT_REQ: ACL connection request

After sending AUX_SYNC_SUBEVENT_IND, wait for a certain delay and enter the receive state. It can be seen that the timing of the reception is divided into multiple slots, which are called response slots. There are often multiple observers listening to the subevent, each of which needs to respond within the time of their respective response slots. The allocation of response slots can be set according to specific scenarios. Taking ESL as an example, the response slot number when an observer responds is dynamically allocated within each subevent, which is determined by the order of commands in the synchronous advertising packet of the advertiser and the observer's own ID, and the specific allocation process can be referred to the "Electronic Shelf Label Profile" (5.3.1.4.2 "Allocation of response slots to ESLs").

(2) PAwR Synchronisation

It was mentioned above that PAwR is an extension to periodic advertising and that subevent listening and responding are performed based on the synchronisation that has been established. To achieve synchronisation, the observer first needs to know the PAwR event period (periodic advertising interval), and the next PAwR event moment (syncPacketWindowOffest). Then, in conjunction with the subevent ID and response slot number configured by the observer, the following information also needs to be known to determine the moment to listen and respond:

  • Num_Subevents: number of subevents in a period

  • Subevent_interval: the time from the start of one subevent to the start of the next subevent

  • Response_Slot_Delay: the time from the start of a subevent to the first response slot

  • Response_Slot_spacing: the time from the start of one response slot to the start of the next response slot

  • Num_Response_Slots: the number of response slots in the subevent

The above information can be obtained in two ways: one is through the observer device directly scanning to obtain, and the ACAD section in AUX_ADV_IND contains the above information. The second is through PAST. PAST means that the advertiser or third-party device first establishes an ACL connection with the observer device, and sends the PAST packet containing the synchronisation information to the observer device to complete the synchronisation (for details, please refer to the PAST section).

(3) PAwR Related APIs

For the tc_ble_sdk, due to SRAM size limitations, only the TC321x chips support the PAwR feature, and they only support the observer role.

Observer API

ble_sts_t   blc_ll_initPAwRsync_module(int num_pawr_sync);
ble_sts_t   blc_ll_initPAwRsync_rspDataBuffer(u8 *pdaRspData, int maxLen_pdaRspData);

Custom function to initialize the PAwR observer module and the allocated response data space.

ble_sts_t    blc_hci_le_setPeriodicSyncSubevent(u16 sync_handle, 
                        u16 pda_prop, 
                        u8 num_subevent, 
                        u8* pSubevent)

The BLE spec standard interface for observers to set the subevent they need to listen to, which can be more than one. Please refer to "core5.4" (vol4/Part E/7.8.127 "LE Set Periodic Sync Subevent command") for details.

ble_sts_t   blc_hci_le_setPAwRsync_rspData( u16 sync_handle, 
                        u16 req_pdaEvtCnt, 
                        u8 req_subEvtCnt, 
                        u8 rsp_subEvtCnt, 
                        u8 rsp_slotIdx,
                        u8 rspDataLen,  
                        u8* pRspData)

The BLE spec standard interface for setting response data. For details, please refer to "core5.4" (vol4/Part E/7.8.126 "LE Set Periodic Advertising Response Data command").

Low Power Management

Low Power Management is also called Power Management, or PM as referred by this document.

Low Power Driver

Low Power Mode

When MCU works in normal mode, or working mode, current is about 3 ~ 7mA. To save power consumption, MCU should enter low power mode.

There are three low power modes, or sleep modes: Suspend mode, Deepsleep mode, and Deepsleep retention mode.

Module Suspend Deepsleep Retention Deepsleep
SRAM 100% keep first 32K/64K/96K keep, others lost 100% lost
digital register 99% keep 100% lost 100% lost
analog register 100% keep 99% lost 99% lost

The table above illustrates statistically data retention and loss for SRAM, digital registers and analog registers during each sleep mode.

(1) Suspend mode (sleep mode 1)

In this mode, program execution pauses, most hardware modules of MCU are powered down, and the PM module still works normally. In this mode, the power consumption of B91 is about 40-50uA. Program execution continues after wake-up from suspend mode.

In suspend mode, data of the SRAM, all analog registers and most digital registers are maintained. A few digital registers will power down, such as a small number of digital registers in the baseband circuit. User should pay close attention to the registers configured by the API "rf_set_power_level_index()". This API needs to be re-invoked after each wakeup from suspend mode.

(2) Deepsleep mode (sleep mode 2)

In this mode, program execution pauses, the vast majority of hardware modules are powered down, and the PM module still works. In this mode, power consumption is less than 1uA, but if flash standby current comes up at 1uA or so, total current may reach 1~2uA. When waking up from deep sleep mode, the MCU will reset and reboot(similar to a power-on reset) and the program will restart to initialize.

In deepsleep mode, except a few retention analog registers, data of all registers (analog & digital) and SRAM are lost.

(3) Deepsleep retention mode (sleep mode 3)

In deepsleep mode, power consumption is minimal, but all SRAM data are lost; while in suspend mode, though SRAM and most registers are maintained, current is increased.

In order to implement some application scenarios that require very low current during sleep and be able to ensure that the state can be restored immediately after waking up from sleep (such as BLE long sleep to maintain connection), the TC series chips add a sleep mode 3: deepsleep with SRAM retention mode, referred to as deepsleep retention (or deep retention). Based on the size of the SRAM retention area, the TC series chips are categorized into deepsleep retention 16 KB SRAM and deepsleep retention 32 KB SRAM. The TC321X supports deepsleep retention 64 KB SRAM.

Deepsleep retention mode is also a kind of deepsleep. Most of the hardware modules of the MCU are powered off, and the PM hardware modules remain active. Power consumption is the power consumed by retention SRAM plus that of deepsleep mode, and the current is between 2~3uA. When waking up from deep sleep retention mode, the MCU will restart and the program will restart to initialize.

Deepsleep retention mode and deepsleep mode are consistent in register state, almost all of them are powered off. Compare with in deepsleep mode, in deepsleep retention mode, the first 16/32 KB of SRAM can be kept without power-off, and the remaining SRAM is powered off.

Low Power Wake-up Source

The low-power wake-up source diagram of the TC series chips MCU is shown below, suspend/ deepsleep/ deepsleep retention can all be awakened by GPIO PAD and timer. In tc_ble_sdk, only two types of wake-up sources are concerned, as shown below (note that the two definitions of PM_TIM_RECOVER_START and PM_TIM_RECOVER_END in the code are not wake-up sources):

typedef enum {
     PM_WAKEUP_PAD   = BIT(4),
     PM_WAKEUP_TIMER = BIT(6),
}SleepWakeupSrc_TypeDef;

MCU HW Wakeup Source

As shown in the figure above, the MCU's suspend/deepsleep/deepsleep retention has two wake-up sources in hardware: TIMER and GPIO PAD.

  • The "PM_WAKEUP_TIMER" comes from 32kHz HW timer (32kHz RC timer or 32kHz Crystal timer). Since 32kHz timer is correctly initialized in the SDK, no configuration is needed except setting wakeup source in the "cpu_sleep_wakeup ()".

  • The "PM_WAKEUP_PAD" comes from GPIO module. Except 4 MSPI pins, all GPIOs (PAx/PBx/PCx/PDx) support high or low level wakeup.

The API below serves to configure GPIO PAD as wakeup source for sleep mode.

typedef enum{
    Level_Low=0,
    Level_High,
} GPIO_LevelTypeDef; 
void cpu_set_gpio_wakeup (GPIO_PinTypeDef pin, GPIO_LevelTypeDef pol, int en);
  • pin: GPIO pin

  • pol: wakeup polarity, Level_High: high level wakeup, Level_Low: low level wakeup

  • en: 1-enable, 0-disable.

Examples:

cpu_set_gpio_wakeup (GPIO_PC2, Level_High, 1);  //GPIO_PC2 PAD唤醒打开, 高电平唤醒
cpu_set_gpio_wakeup (GPIO_PC2, Level_High, 0);  //GPIO_PC2 PAD唤醒关闭
cpu_set_gpio_wakeup (GPIO_PB5, Level_Low, 1);  //GPIO_PB5 PAD唤醒打开, 低电平唤醒
cpu_set_gpio_wakeup (GPIO_PB5, Level_Low, 0);  //GPIO_PB5 PAD唤醒关闭

Sleep and Wake-up from Low Power Mode

The stack manages suspend and deepsleep retention in Telink tc_ble_sdk. It is not recommended for users to configure suspend/deepsleep retention by themselves, but users can set deepsleep entry mode.

The API below serves to configure MCU sleep and wakeup.

int cpu_sleep_wakeup (SleepMode_TypeDef sleep_mode, SleepWakeupSrc_TypeDef wakeup_src, 
unsigned int wakeup_tick);
  • sleep_mode: This para configures sleep mode. Currently, users can only select deepsleep mode. (suspend and deepsleep retention are controlled by stack.)
typedef enum {
    ......
    DEEPSLEEP_MODE          = 0x80,
    ......
}SleepMode_TypeDef;
  • wakeup_src: This para configures wakeup source for suspend/deep retention/deepsleep as one or combination of PM_WAKEUP_PAD and PM_WAKEUP_TIMER. If set to 0, MCU wakeup is disabled for sleep mode.

  • wakeup_tick: if PM_WAKEUP_TIMER is assigned as wakeup source, the "wakeup_tick" configures MCU wakeup time. If PM_WAKEUP_TIMER is not assigned, this para is negligible.

The "wakeup_tick" is an absolute value, which equals current value of System Timer tick plus intended sleep duration. When System Timer tick reaches the time defined by the wakeup_tick, MCU wakes up from sleep mode. Without taking current System Timer tick value as reference point, wakeup time is uncontrollable.

Since the wakeup_tick is an absolute time, it follows the max range limit of 32bit System Timer tick. In current SDK, 32bit max sleep time corresponds to 7/8 of max System Timer tick. Since max System Timer tick is 268s or so, max sleep time is 268*7/8=234s, which means the "delta_Tick" below should not exceed 234s.

cpu_sleep_wakeup(SUSPEND_MODE, PM_WAKEUP_TIMER, clock_time() + delta_tick);

The return value is an ensemble of current wakeup sources. Following shows each bit in the return value corresponds to a specific wakeup source.

enum {
        WAKEUP_STATUS_TIMER  = BIT(1),
        WAKEUP_STATUS_PAD    = BIT(3),

        STATUS_GPIO_ERR_NO_ENTER_PM  = BIT(7),
};

a) If WAKEUP_STATUS_TIMER bit = 1, wakeup source is Timer.

b) If WAKEUP_STATUS_PAD bit = 1, wakeup source is GPIO PAD.

c) If both WAKEUP_STATUS_TIMER and WAKEUP_STATUS_PAD equal 1, wakeup source is Timer and GPIO PAD.

d) STATUS_GPIO_ERR_NO_ENTER_PM is a special state indicating GPIO wakeup error. E.g. Suppose a GPIO is set as high level PAD wakeup (PM_WAKEUP_PAD). When MCU attempts to invoke the "cpu_sleep_wakeup" to enter suspend, if this GPIO is already at high level, MCU will fail to enter suspend and immediately exit the "cpu_sleep_wakeup" with return value STATUS_ GPIO_ERR_NO_ENTER_PM.

Sleep time is typically set in the following way:

cpu_sleep_wakeup (SUSPEND_MODE , PM_WAKEUP_TIMER,  clock_time() + delta_Tick);

The "delta_Tick", a relative time (e.g. 100* CLOCK_16M_SYS_TIMER_CLK_1MS), plus "clock_time()" becomes an absolute time.

Some examples on cpu_sleep_wakeup:

cpu_sleep_wakeup (SUSPEND_MODE , PM_WAKEUP_PAD, 0);

When it's invoked, MCU enters suspend, and wakeup source is GPIO PAD.

cpu_sleep_wakeup (DEEPSLEEP_MODE , PM_WAKEUP_TIMER, clock_time() + 10* CLOCK_16M_SYS_TIMER_CLK_1MS);

When it's invoked, MCU enters deepsleep, wakeup source is timer, and wakeup time is current time plus 10 ms, so the deepsleep duration is 10 ms.

cpu_sleep_wakeup (DEEPSLEEP_MODE , PM_WAKEUP_PAD | PM_WAKEUP_TIMER, 
                  clock_time() + 50* CLOCK_16M_SYS_TIMER_CLK_1MS);

When it's invoked, MCU enters deepsleep, wakeup source includes timer and GPIO PAD, and timer wakeup time is current time plus 50 ms. If GPIO wakeup is triggered before 50 ms expires, MCU will be woke up by GPIO PAD in advance; otherwise, MCU will be woke up by timer.

cpu_sleep_wakeup (DEEPSLEEP_MODE, PM_WAKEUP_PAD, 0);

When the program executes this function, it enters deepsleep mode and can be woken up by GPIO PAD.

Low Power Wake-up Procedure

When user calls the API cpu_sleep_wakeup(), the MCU enters the sleep mode; when the wake-up source triggers the MCU to wake up, the MCU software operation flow is inconsistent for different sleep modes.

The following is a detailed description of the MCU operating process after the suspend, deepsleep, and deepsleep retention three sleep modes are awakened. Please refer to the figure below.

Sleep Mode Wakeup Work Flow

Detailed process after the MCU is powered on is introduced as following:

(1) Run hardware bootloader

It is pure MCU hardware operation without involvement of software.

Couple of examples: Read the boot flag of flash to determine whether the firmware that should be run currently is stored on flash address 0 or on flash address 0x20000 (related to OTA); read the value of the corresponding location of flash to determine how much data currently needs to be copied from flash to SRAM as resident memory data (refer to the introduction of SRAM allocation in Chapter 2).

The part of running the hardware bootloader involves copying data from flash to sram, which generally takes a long time to execute. For example, it takes about 5ms to copy 10K data.

(2) Run software bootloader

After the hardware bootloader finishes running, the MCU starts to run the software bootloader. Software bootloader is the vector end introduced earlier.

Software bootloader is used to set up memory environment for C program execution, so it can be regarded as memory initialization.

(3) System initialization

System initialization corresponds to the initialization of each hardware module (including cpu_wakeup_init, rf_drv_init, gpio_init, clock_init) from cpu_wakeup_init to user_init in the main function, and sets the digital/analog register status of each hardware module.

(4) User initialization

User initialization corresponds to user_init, or user_init_normal/ user_init_deepRetn in the SDK.

(5) main_loop

After User initialization, program enters main_loop inside while(1). The operation is called "Operation Set A" before main_loop enters sleep mode, and called "Operation Set B" after wakeup from sleep.

Analyze the sleep mode flow from the above figure.

(6) no sleep

Without sleep mode, MCU keeps looping inside while(1) between "Operation Set A" -> "Operation Set B".

(7) suspend

If the cpu_sleep_wakeup function is called to enter the suspend mode, when the suspend is woken up, it is equivalent to the normal exit of the cpu_sleep_wakeup function, and the MCU runs to "Operation Set B".

Suspend is the cleanest sleep mode. During suspend, all SRAM data can remain unchanged, and all digital/analog register states also remain unchanged (with a few special exceptions); after suspend wakes up, the program continues to run in its original position, hardly any sram and register state restoration needs to be considered. The disadvantage of suspend is the high power consumption.

(8) deepsleep

If the cpu_sleep_wakeup function is called to enter deepsleep mode, when deepsleep is woken up, MCU will return to Run hardware bootloader.

It can be seen that the process of deepsleep wake_up and Power on are almost the same, and all software and hardware initializations have to be redone.

After the MCU enters deepsleep, all SRAM and digital/analog registers (except a few analog registers) are power down, so the power consumption is very low and the MCU current is less than 1uA.

(9) deepsleep retention

If the cpu_sleep_wakeup function is called to enter deepsleep retention mode, when deepsleep retention is woken up, MCU will return to Run software bootloader.

The deepsleep retention is an intermediate sleep mode between suspend and deepsleep.

In suspend, the current is high because it needs to save all SRAMm and register states; deepsleep retention does not need to save the register state, SRAM only retains the first 16K/32K/64K without power down, so the power consumption is much lower than suspend, only about 2uA.

After deepsleep wake_up, all processes need to be run again, while deepsleep retention can skip the step of "Run hardware bootloader", this is because the data on the first 16K/32K/64K of SRAM is not lost, no need re-copy from flash. However, due to the limited retention area on SRAM, "run software bootloader" cannot be skipped and must be executed; since deepsleep retention cannot save the register state, system initialization must be executed, and the initialization of registers needs to be reset. User initialization deep retention after deepsleep retention wake_up can be optimized and improved to distinguish User initialization normal after processing MCU power on/deepsleep wake_up.

API pm_is_MCU_deepRetentionWakeup

As can be seen from the above figure "sleep mode wakeup work flow", MCU power on, deepsleep wake_up and deepsleep retention wake_up all need to go through Running software bootloader, System initialization, and User initialization.

When running system initialization and user initialization, user needs to know whether the current MCU is woke up from deepsleep retention, so as to differentiate from power on and deepsleep wake_up. PM driver provides API for judging whether deepsleep retention wake_up is:

int pm_is_MCU_deepRetentionWakeup(void);

Return value: 1 - deepsleep retention wake_up; 0 - power on or deepsleep wake_up.

BLE Low Power Management

BLE PM Initialization

For applications with low power mode, BLE PM module needs to be initialized by following API.

void    blc_ll_initPowerManagement_module(void);

If low power is not required, DO NOT use this API, to skip compiling of related code and variables into program and thus save FW and SRAM space.

Telink tc_ble_sdk applies low power management to Legacy advertising state, Scanning state, ACL connection Master and ACL connection Slave.

It should be noted that the SDK currently has restrictions on the use of latency by Slave. If the restrictions are not met, packets will be sent and received at each interval. Even if it accepts the connection parameters of the opposite Master as a Slave, and the latency is not 0, the SDK will send and receive RF data according to the latency of 0.

Restrictions on the use of latency by Slave:

(1) Only Legacy advertising and ACL Slave tasks.

(2) ACL Slave has only 1 connection (later the SDK will be optimized to support more Slave connections).

(3) If there is Legacy advertising, the minimum advertising interval needs to be greater than 195 ms.

The SDK does not apply low power management to Idle state either. In Idle state, since there is no RF activity, i.e. the "blt_sdk_main_loop" function is not valid, user can use PM driver for certain low power management.

sleep for advertising “only advertising”

When only advertising is enabled and scan is turned off, i.e., the Link Layer is in the advertising state, the timing is as follows.

Sleep for Advertising Timing

When the advertising time is reached, it will wake up from sleep and then process the advertising event. After processing, stack will determine the difference between the time point of the next Adv Event and the current time, and if the condition is met, it will go into sleep to reduce power consumption. The time consumed by the Adv Event is related to the specific situation, for example: the users only set 37channel, ADV packet length is relatively small, in channel 37 or 38 received SCAN_REQ or CONNECT_IND, etc.

sleep for scanning “only scanning”

sleep for scanning for only scanning

The actual scanning time is determined according to the size of the Scan window. If the Scan window is equal to the Scan interval, all the time is scanning; if the Scan window is less than the Scan interval, from the front part of the Scan interval to allocate time to scanning, equivalent time reference Scan window.

The Scan window shown in the figure is about 40% of the Scan interval. In the first 40% of the time, the Link Layer is in scanning state, and the PHY layer is receiving packets. At the same time, users can use this time to execute their own UI tasks in the main_loop. During the last 60% of the time, the MCU enters sleep to reduce the power consumption of the whole machine.

The API for setting the percentage is as follows.

blc_ll_setScanParameter(SCAN_TYPE_PASSIVE, SCAN_INTERVAL_200MS, SCAN_WINDOW_50MS, OWN_ADDRESS_PUBLIC, SCAN_FP_ALLOW_ADV_ANY);
sleep for connection

sleep for connection

The conditions for entering sleep are:

(1) The time interval between the next task and the end of the current task;

(2) Whether there is unprocessed data in the RX FIFO;

(3) The execution of BRX POST and BTX POST is completed;

(4) The device itself does not have any event pending.

If the time interval from the next task is relatively large, and there is no data in the RX FIFO, and no event pending, when the BRX POST or BTX POST is executed, the bottom layer will let the MCU enter sleep. When the next task is about to come, the timer wakes up the MCU to start the task.

BLE PM Variables

The variables in this section are helpcful to understand BLE PM software flow.

The struct "st_ll_pm_t" is defined in Telink tc_ble_sdk. Following lists some variables of the struct which will be used by PM APIs.

typedef struct {
    u8  deepRt_en;
    u8  deepRet_type;
    u8  wakeup_src;
    u16 sleep_mask;
    u16 user_latency;
    u32 deepRet_thresTick;
    u32 deepRet_earlyWakeupTick;
    u32 sleep_taskMask;
    u32 next_task_tick;
    u32 current_wakeup_tick;
}st_llms_pm_t;

st_llms_pm_t  blmsPm;

Note

  • The above structure variable is encapsulated in the library. The definitions given here are only for the convenience of the following introduction. Users are not allowed to perform any operations on this structure variable.

The variables like "blmsPm.sleep_mask" will appear frequently in the following introduction.

API blc_pm_setSleepMask

API for configuring low power management:

void blc_pm_setSleepMask (sleep_mask_t mask);

The "blmsPm.sleep_mask" is set by the "blc_pm_setSleepMask" and its default value is PM_SLEEP_DISABLE.

Following shows source code of the API.

void    blc_pm_setSleepMask (sleep_mask_t mask)
{
    u32 r = irq_disable();
    ......  
    blmsPm.sleep_mask = mask;
    ......
    u32 r = irq_disable();
}

The "blmsPm.sleep_mask" can be set as any one or the "or-operation" of following values:

typedef enum {
    PM_SLEEP_DISABLE        = 0,
    PM_SLEEP_LEG_ADV        = BIT(0),
    PM_SLEEP_LEG_SCAN       = BIT(1),
    PM_SLEEP_ACL_SLAVE      = BIT(2),
    PM_SLEEP_ACL_MASTER     = BIT(3),
}sleep_mask_t; 

PM_SLEEP_DISABLE means sleep is disabled which stops MCU to enter sleep.

PM_SLEEP_LEG_ADV and PM_SLEEP_LEG_SCAN decide whether MCU at Legacy advertising state and Scanning state can enter sleep.

PM_SLEEP_ACL_PERIPHR and PM_SLEEP_ACL_MASTER decide whether MCU at ACL connection Slave and ACL connection Master can enter sleep.

Following shows 2 typical use cases:

(1) blc_pm_setSleepMask(PM_SLEEP_DISABLE);

MCU will not enter sleep.

(2) blc_pm_setSleepMask(PM_SLEEP_LEG_ADV | PM_SLEEP_LEG_SCAN | PM_SLEEP_ACL_PERIPHR | PM_SLEEP_ ACL_MASTER);

At Legacy advertising state, Scanning state, ACL connection Slave and ACL connection Master, MCU can enter sleep.

API blc_pm_setWakeupSource

User can set the blc_pm_setSleepMask to enable MCU to enter sleep mode (suspend or deepsleep retention), and use the following API to set wakeup source.

void  blc_pm_setWakeupSource (SleepWakeupSrc_TypeDef wakeup_src)
{
    blmsPm.wakeup_src = (u8)wakeup_src;
}

wakeup_src: Wakeup source, can be set as PM_WAKEUP_PAD.

This API sets the bottom-layer variable "blmsPm.wakeup_src".

When MCU enters sleep mode at Legacy advertising state, Scanning state, ACL connection Master and ACL connection Slave, its actual wakeup source is:

blmsPm.wakeup_src | PM_WAKEUP_TIMER

The PM_WAKEUP_TIMER is mandatory, not depending on user setup. This guarantees that MCU will wake up at specified time to handle ADV task, SCAN task, Master task and Slave task.

Everytime wakeup source is set by the "blc_pm_setWakeupSource", after MCU wakes up from sleep mode, the blmsPm.wakeup_src is set to 0.

API blc_pm_setDeepsleepRetentionType

Deepsleep retention further separates into 16K32K/64K SRAM retention. When the deepsleep retention mode in sleep mode takes effect, the SDK will enter the corresponding deepsleep retention mode according to the settings.

The following API allows users to select the deepsleep retention mode. However, since the current SDK uses the maximum retention SRAM size by default, users are generally not needed.

void blc_pm_setDeepsleepRetentionType(SleepMode_TypeDef sleep_type)
{
    blmsPm.deepRet_type = sleep_type;
}

Note

  • The API must be called after the blc_ll_initPowerManagement_module to take effect.

API blc_pm_setDeepsleepRetentionEnable

This API is used to enable deepsleep retention mode.

typedef enum {
    PM_DeepRetn_Disable = 0x00,
    PM_DeepRetn_Enable  = 0x01,
} deep_retn_en_t;

void blc_pm_setDeepsleepRetentionEnable (deep_retn_en_t en)
{
    blmsPm.deepRt_en = en;
}

API blc_pm_setDeepsleepRetentionThreshold

In the presence of a BLE task, suspend will be automatically switched to deepsleep retention if the following conditions are met:

//Determine whether sleep mode is suspend mode or deepsleep retention mode
SleepMode_TypeDef  sleep_M = SUSPEND_MODE;
if( blmsPm.deepRt_en && (u32)(blmsPm.current_wakeup_tick - clock_time() - blmsPm.deepRet_thresTick) < BIT(30) ){
    sleep_M = (SleepMode_TypeDef)blmsPm.deepRet_type;
}

The first condition, blmsPm.deepRt_en, needs to be enabled by calling the API blc_pm_ setDeepsleepRetentionEnable, which has been introduced earlier.

The second condition (u32)(blmsPm.current_wakeup_tick - clock_time() - blmsPm.deepRet_thresTick) < BIT(30), which means that the duration of sleep (ie wakeup time minus real-time time) exceeds a specific time threshold (ie blmsPm .deepRet_thresTick), the sleep mode of the MCU will automatically switch from suspend to deepsleep retention.

The API blc_pm_setDeepsleepRetentionThreshold is used to set the time threshold for suspend to switch to the deepsleep retention trigger condition. This design is to pursue lower power consumption.

void blc_pm_setDeepsleepRetentionThreshold(u32 thres_ms)
{
    blmsPm.deepRet_thresTick = thres_ms * SYSTEM_TIMER_TICK_1MS;
}

PM Software Processing Flow

The software processing flow of low power management is described below using a combination of code and pseudo-code, in order to let the user understand all the logical details of the processing flow.

blc_sdk_main_loop

In Telink tc_ble_sdk, "blc_sdk_main_loop" is called repeatedly in a while(1) structure.

while(1)
{
    ////////////////////////////////////// BLE entry /////////////////////////////////
    blc_sdk_main_loop();

    ////////////////////////////////////// UI entry /////////////////////////////////
    // UI  task

    ////////////////////////////////////// PM entry /////////////////////////////////
    app_process_power_management();
}

The blc_sdk_main_loop function is executed continuously in while(1), and the code for BLE low-power management is in the blc_sdk_main_loop function, so the code for low-power management is also executed all the time.

Following shows the implementation of BLE PM logic inside the "blc_sdk_main_loop".

void blc_sdk_main_loop (void)
{
    ......
    if( blmsPm.sleep_mask == PM_SLEEP_DISABLE ) 
    {    
        return; // PM_SLEEP_DISABLE, can not enter sleep mode;sleep time           
    }

    if( !tick1_exceed_tick2(blmsPm.next_task_tick, clock_time() + PM_MIN_SLEEP_US) ) 
    {    
        return; //too short, can not enter sleep mode.                    
    }  

    if( bltSche.task_mask && (blmsPm.sleep_taskMask & bltSche.task_mask) != bltSche.task_mask )
    //Whether there is a task (adv, scan, Master, Slave)
    //Whether sleep_taskMask allows this state (adv, scan, Master, Slave) to enter sleep
    {
        return;
    }

    if ( (brx_post | btx_post | adv_post | scan_post) == 0 )
    {
        return; //Sleep can only be allowed after each task is completed
    }
    else
    {
        blt_sleep_process(); //process sleep & wakeup
    }
    ......
}

(1) When the "bltmsPm.sleep_mask" is PM_SLEEP_DISABLE, the SW directly exits without executing the "blt_sleep_process" function. So when using the "blc_pm_setSleepMask(PM_SLEEP_DISABLE)", PM logic is completely ineffective; MCU will never enter sleep and the SW always execute while(1) loop.

(2) If the sleep time is too short, it will not enter sleep.

(3) When there are tasks, such as adv task, scan task, Master task, Slave task, but if the sleep_taskMask of the corresponding task is not enabled, it will not enter low power mode.

(4) If the Adv Event or Scan Event or Btx Event of Conn state Master role or Brx Event of Conn state Slave role is being executed, the "blt_sleep_process" function will not be executed, this is because RF task is running at this time, and the SDK needs to ensure that the sleep mode can only be entered after the Adv Event/Scan Event/Btx Event/Brx Event ends.

Only when both cases above are valid, the blt_sleep_process will be executed.

blt_sleep_process

Following shows logic implementation of the "blt_sleep_process" function.

void blt_sleep_process (void)
{
    ......
    blmsPm.current_wakeup_tick = blmsPm.next_task_tick;//Record wake-up time

    //Execute the BLT_EV_FLAG_SLEEP_ENTER callback function
    blt_p_event_callback (BLT_EV_FLAG_SLEEP_ENTER, NULL, 0);

    //Enter low power function
    u32 wakeup_src = cpu_sleep_wakeup (sleep_M, PM_WAKEUP_TIMER | blmsPm.wakeup_src, blmsPm.current_wakeup_tick);

    //Execute the BLT_EV_FLAG_SUSPEND_EXIT callback function
    blt_p_event_callback (BLT_EV_FLAG_SUSPEND_EXIT, (u8 *)&wakeup_src, 1);

    blmsPm.wakeup_src = 0;
    ......
}

The above is a brief flow of the blt_sleep_process function. Here we see the execution timing of the two sleep-related event callback functions: BLT_EV_FLAG_SLEEP_ENTER, BLT_EV_FLAG_SUSPEND_EXIT.

Regarding how to enter sleep mode, the API cpu_sleep_wakeup in the driver is finally called:

cpu_sleep_wakeup(SleepMode_TypeDef sleep_mode,  SleepWakeupSrc_TypeDef wakeup_src, unsigned int  wakeup_tick);

This API sets wakeup source as PM_WAKEUP_TIMER | blmsPm.wakeup_src, so Timer wakeup is mandatory to guarantee MCU wakeup before next task.

When exiting the "blt_sleep_process" function, the "blmsPm.wakeup_src" reset. So the API "blc_pm_ setWakeupSource" is only effective for the latest sleep mode.

API blc_pm_getWakeupSystemTick

The following API is used to get the sleep wakeup time point (System Timer tick) for low-power management calculations, i.e. T_wakeup.

u32 blc_pm_getWakeupSystemTick (void);

The calculation of T_wakeup is close to the processing of the cpu_sleep_wakeup function, and the application layer can only get the accurate T_wakeup in the BLT_EV_FLAG_SLEEP_ENTER event callback function.

Suppose the user needs to press the key to wake up when the sleep time is relatively long. Below we explain the setting method.

We need to use the BLT_EV_FLAG_SLEEP_ENTER event callback function and blc_pm_getWakeupSystemTick.

The callback registration method of BLT_EV_FLAG_SLEEP_ENTER is as follows:

blc_ll_registerTelinkControllerEventCallback (BLT_EV_FLAG_SLEEP_ENTER, &app_set_kb_wakeup);
_attribute_ram_code_ void app_set_kb_wakeup (u8 e, u8 *p, int n)
{
    /* sleep time > 100ms. add GPIO wake_up */
    if(((u32)(blc_pm_getWakeupSystemTick() - clock_time())) > 100 * SYSTEM_TIMER_TICK_1MS){
        blc_pm_setWakeupSource(PM_WAKEUP_PAD);  //GPIO PAD wake_up
    }
}

For the above example, if the sleep time exceeds 100 ms, add GPIO wakeup. User can adjust it according to the actual situation.

Here just provides an interface, and the user decides whether to use it according to the actual situation.

Issues in GPIO Wake-up

Fail to enter sleep mode when wake-up level is valid

In Telink MCU, GPIO wakeup is level triggered instead of edge triggered, so when GPIO PAD is configured as wakeup source, for example, suspend wakeup triggered by GPIO high level, MCU needs to make sure when MCU invokes cpu_sleep_wakeup to enter suspend, that the wakeup GPIO is not at high level. Otherwise, once entering cpu_sleep_wakeup, it would exit immediately and fail to enter suspend.

If the above situation occurs, it may cause unexpected problems, for example, it was intended to enter deepsleep and be woken up and the program re-executed, but it turns out that the MCU cannot enter deepsleep, resulting in the code continuing to run, not in the state we expected, and the whole flow of the program may be messed up.

User should pay attention to avoid this problem when using Telink's GPIO PAD to wake up.

If the APP layer does not avoid this problem, and GPIO PAD wakeup source is already effective at invoking of cpu_sleep_wakeup, PM driver makes some improvement to avoid flow mess:

(1) suspend & deepsleep retention mode

For both suspend and deepsleep retention mode, the SW will fast exit cpu_sleep_wakeup with two potential return values:

  • Return WAKEUP_STATUS_PAD if the PM module has detected effective GPIO PAD state.

  • Return STATUS_GPIO_ERR_NO_ENTER_PM if the PM module has not detected effective GPIO PAD state.

(2) deepsleep mode

For deepsleep mode, PM diver will reset MCU automatically in bottom layer (equivalent to watchdog reset). The SW restarts from "Run hardware bootloader".

Timer Wake-up by Application Layer

BLE task exists and without GPIO PAD wakeup, once MCU enters sleep mode, it only wakes up at T_wakeup pre-determined by SDK. User can not wake up MCU at an earlier time which might be needed at certain scenario. To provide more flexibility, application layer wakeup and associated callback function are added in the SDK:

Application layer wakeup API:

void blc_pm_setAppWakeupLowPower(u32 wakeup_tick, u8 enable);

"wakeup_tick" is wakeup time at System Timer tick value.

"enable": 1-wakeup is enabled; 0-wakeup is disabled.

Registered call back function blc_pm_registerAppWakeupLowPowerCb is executed at application layer wakeup:

typedef void (*pm_appWakeupLowPower_callback_t)(int);
pm_appWakeupLowPower_callback_t  pm_appWakeupLowPowerCb = NULL;
void blc_pm_registerAppWakeupLowPowerCb(pm_appWakeupLowPower_callback_t cb)
{
    pm_appWakeupLowPowerCb = cb;
}

Take Conn state Peripheral role as an example:

When the user uses blc_pm_setAppWakeupLowPower to set the app_wakeup_tick for the application layer to wake up regularly, the SDK will check whether app_wakeup_tick is before T_wakeup before entering sleep.

(1) If app_wakeup_tick is before T_wakeup, as shown in the figure below, it will trigger sleep in app_wakeup_tick to wake up early;

(2) If app_wakeup_tick is after T_wakeup, MCU will still wake up at T_wakeup.

Early wake_up at app_wakup_tick

Low Battery Detect

For low battery detection, the overall procedure is exactly the same as in the single connection. Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

OTA

For OTA, the overall procedure is exactly the same as in the single connection. Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

Compared with the TC BLE Single Connection SDK, tc_ble_sdk adds one restriction: OTA is allowed on only one slave connection at a time.

Key Scan

Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

LED Management

Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

Software Timer

Please refer to the corresponding section in the TC BLE Single Connection SDK Handbook for this part.

Feature Demonstrations

This section will introduce the usage and phenomenon of each function under vendor/feature_test, and users can refer to the code implementation to add the required function to their own code.

feature_backup

  • Function: Demonstration of BLE basic functions. This includes advertising, passive scanning, connectivity, and etc. The demo also serves as a relatively "clean" base version for users developing BLE applications (with SMP, SDP, etc. disabled by default).
  • Main hardware: TLSR8258 development board x 2

The application layer code is under TC_BLE_SDK/vendor/feature_test/feature_backup, it is needed to modify the definition in TC_BLE_SDK/vendor/feature_test/feature_config.h to activate this part of the code:

#define FEATURE_TEST_MODE               TEST_FEATURE_BACKUP

The code sets the advertising parameters, advertising content, scan response content, scan parameters, and the configuration to enable advertising and enable scan, refer to the following in the initialization code.

Configure Advertising Scanning Parameters in the Initialization

Compile and burn the generated firmware into each of the two development boards. After powering up, the two devices named "feature" can be scanned by scanning the advertising packets, which can be connected by other Master or Slave devices, respectively. To interconnect the two devices, press SW4 on one of the boards to initiate the connection, and read the list of variable values on the left side through the Tdebug tab of the BDT tool (see the Debug Method chapter for details on how to use it), and see that the value of conn_master_num for that board is 1.

conn_master_num=1 in Tdebug

Check the value of the conn_slave_num variable on the other development board, which also has a value of 1.

conn_slave_num=1 in Tdebug

It means the connection between the two is successful.

feature_2M_coded_phy

  • Function: BLE 1M/2M/Coded PHY function demonstration.
  • Main hardware: TLSR8258 development board x 2

The application layer code is under TC_BLE_SDK/vendor/feature_test/feature_2M_coded_phy, it is needed to modify the definition in TC_BLE_SDK/vendor/feature_test/feature_config.h to activate this part of the code to do a demo of dynamically switching PHYs:

#define FEATURE_TEST_MODE               TEST_2M_CODED_PHY_CONNECTION

Initialization call blc_ll_init2MPhyCodedPhy_feature() to enable the PHY switching function. Dynamic switching of PHYs is implemented in feature_2m_phy_test_mainloop() in main_loop().

(1) After establishing the connection for the Master, do a WriteCmd to each Slave every 1s, with a valid data length of 8 bytes (the actual sending interval is also related to the Connection Interval).

(2) After establishing the connection for the Slave, notify each Master every 1s, with a valid data length of 8 bytes (the actual sending interval is also related to the Connection Interval).

(3) Switch PHY once every 10s for each connection, in the following order: Coded_S8 \(\rightarrow\) 2M \(\rightarrow\) 1M \(\rightarrow\) Coded_S8...

The actual packet capture is as follows.

feature_2M_coded_phy Coded Req

feature_2M_coded_phy 2M Req

feature_2M_coded_phy 1M Req

feature_gatt_api

  • Function: BLE GATT command function demonstration and API usage. These commands are used in the reference implementation of the SDP process for some of the examples in the tc_ble_sdk, and users can use this demo code for single instruction testing.
  • Main hardware: TLSR8258 development board x 2

The application layer code is under TC_BLE_SDK/vendor/feature_test/feature_gatt_api, it is needed to modify the definition in TC_BLE_SDK/vendor/feature_test/feature_config.h to activate this part of the code:

#define FEATURE_TEST_MODE               TEST_GATT_API

In app.c, test the different GATT commands by modifying the definition of TEST_API.

Compile and burn the generated firmware into the two development boards. When powered on, the red light on the development board toggles on and off every 2s. The connection is triggered by pressing the SW4 button on one of the boards (as Master), and by capturing the packets, we can see that every 2s, the Master sends a test command and the Slave replies accordingly. The implementation of the reply refers to the function app_gatt_data_handler().

The following is the packet capture when TEST_API is defined separately for different command test definitions.

GATT API test TEST_READ_BY_GROUP_TYPE_REQ

GATT API test TEST_FIND_INFO_REQ

GATT API test TEST_FIND_BY_TYPE_VALUE_REQ

GATT API test TEST_READ_REQ

GATT API test TEST_READ_BLOB_REQ

feature_ll_more_data

  • Function: demonstration of BLE MD=1. MD, More Data, is a flag bit MD flag in the data channel PDU Header. At the same time, the demo also provides users to do throughput testing.
  • Main hardware: TLSR8258 development board x 2

The application layer code is under TC_BLE_SDK/vendor/feature_test/feature_ll_more_data, it is needed to modify the definition in tc_ble_sdk/vendor/feature_test/feature_config.h to activate this part of the code:

#define FEATURE_TEST_MODE               TEST_LL_MD

Compile and burn the generated firmware into each of the two development boards. Power on the board and press SW4 on one of the boards (as Master) to trigger the connection. When the connection is successful, the red light will be on (if more than one Slave is connected, the red, white, green and blue lights will be on respectively). By pressing SW3 of the Master, you can see from the packet capture that the Master keeps sending WriteCmd to the Slave and the MD flag in its packet is set to 1, which means the next packet is ready to be sent.

feature_ll_more_data WirteCmd

By pressing SW2 of the Master to stop sending.

By pressing SW3 of the Slave, you can see from the packet capture that the Slave keeps sending Notify to the Master and the MD flag in its packet is set to 1:

feature_ll_more_data Notify

By pressing SW2 of the Slave to stop sending.

feature_dle

  • Function: Demonstration of BLE DLE (Data Length Extension) and MTU Exchange and L2CAP packet splitting and grouping features.
  • Main hardware: TLSR8258 development board x 2

The application layer code is under TC_BLE_SDK/vendor/feature_test/feature_dle, it is needed to modify the definition in TC_BLE_SDK/vendor/feature_test/feature_config.h to activate this part of the code and do a demonstration of DLE and MTU:

#define FEATURE_TEST_MODE               TEST_LL_DLE

Modify DataLength by modifying the definition of DLE_LENGTH_SELECT, as demonstrated by:

(1) The test is triggered by pressing two buttons at the same time.

(2) After the Master board triggers the test, each connection sends a WriteCmd every 1s.

(3) After the Slave board triggers the test, each connection sends a Notify every 1s.

The packet capture is as follows:

feature_dle DLE & MTU Exchange

Master and Slave Make WriteCmd and Notify Respectively

Since the DLE is smaller than the MTU, you can see the effect of packet splitting.

Packet-split Sending

feature_smp

  • Function: function demonstration of BLE SMP (Security Manager Protocol)
  • Main hardware: TLSR8258 development board x 2

The application layer code is under TC_BLE_SDK/vendor/feature_test/feature_smp, it is needed to modify the definition in TC_BLE_SDK/vendor/feature_test/feature_config.h to activate this part of the code:

#define FEATURE_TEST_MODE               TEST_SMP

By default, the SMP functions are enabled for both Master and Slave.

Instruction:

(1) When configuring SMP related parameters, if the Master and Slave configurations are the same, use the API without suffix, if different, use the API with _Master/_Slave suffix as required. The demo code uses the API with suffix for the SMP encryption enablement demonstration in order to facilitate the user to configure it separately for testing.

(2) The roles in SMP are Initiator and Responder, correspond to the Master and Slave in BLE.

(3) Note: The pairing method of Security Connections requires MTU ≥ 65. Therefore, the definitions in default_buffer.h are not used.

Both Master and Slave Do Not Enable SMP

To disable the demonstration of the SMP function for Master and Slave, define it in feature_smp/app_config.h:

#define BLE_SLAVE_SMP_ENABLE                        0   
#define BLE_MASTER_SMP_ENABLE                       0   

Compile feature_test, and burn the generated firmware into each of the two development boards. Press SW3 on one of the boards (as Master) to trigger the connection. When the connection is successful, the red and green of the two boards lights are on respectively.

The packet capture is as follows:

Packet Capture of Successful Master and Slave Connection when SMP is Disabled

The two devices first send out advertisements respectively. After a connection is established, the device acting as the Slave stops advertising.

Enable SMP on Slave Only

This demonstration enables the SMP feature on the Slave side only. To use it, define the following macro in feature_smp/app_config.h:

#define BLE_SLAVE_SMP_ENABLE                        1   
#define BLE_MASTER_SMP_ENABLE                       0  

Compile feature_test, and burn the generated firmware into each of the two development boards. Press SW4 on one of the boards (as Master) to trigger the connection. From packet capture, it can be observed that after connection establishment, the Slave sends a Security Request. However, since the Master does not support SMP, the Master will not respond, as shown in the figure below:

Packet Capture – Slave SMP Enabled Only

Note

  • If users does not want the Slave to send a Security Request after connection establishment, add the following to the initialization code:
    blc_smp_configSecurityRequestSending( SecReq_NOT_SEND,  SecReq_NOT_SEND, 0);
    

This API applies only to the Slave (only the Slave sends a Security Request), so there are no corresponding APIs with _Slave / _Master suffixes.

Enable SMP on Master Only

This demonstration enables the SMP feature on the Master side only. To use it, define the following macro in feature_smp/app_config.h:

#define BLE_SLAVE_SMP_ENABLE                0   
#define BLE_MASTER_SMP_ENABLE               1 

Compile feature_test, and burn the generated firmware into each of the two development boards. Press SW4 on one of the boards (as Master) to trigger the connection. From packet capture, it can be observed that after connection establishment, the Master sends a Pairing Request. However, since the Slave does not support SMP, the Slave responds with Pairing Failed, as shown in the figure below:

Packet Capture – Master SMP Enabled Only

Note

  • If users want the Master to wait for a Security Request from the Slave after connection establishment before sending the Pairing Request, add the following to the initialization code:
    blc_smp_configPairingRequestSending(PairReq_SEND_upon_SecReq, PairReq_SEND_upon_SecReq);
    

This API applies only to the Master (only the Master sends a Pairing Request), so there are no corresponding APIs with _Slave / _Master suffixes.

Legacy Just Works

This demonstration enables the SMP feature on both Master and Slave, paired as Legacy Just Works. This demonstration is defined in feature_smp/app_config.h:

#define BLE_SLAVE_SMP_ENABLE                1   
#define BLE_MASTER_SMP_ENABLE               1 

Enable the code in feature_smp/app.c user_init_normal():

blc_smp_setSecurityLevel(LE_Security_Mode_1_Level_2);
blc_smp_setSecurityParameters(Bondable_Mode, 0, LE_Legacy_Pairing, 0, 0, IO_CAPABILITY_NO_INPUT_NO_OUTPUT);

Compile feature_test, and burn the generated firmware into each of the two development boards. Press SW4 on one of the boards (as Master) to trigger the connection. The packet capture is as follows:

Packet Capture – Legacy Just Works

At this time, press the SW1 button on the Master or Slave board to re-power the board, the Master will automatically connect back when it scans the Slave device that has been paired (refer to the master_auto_connect variable in the demonstration code), the reconnection does not go through the Pair process, directly through the LTK encryption process.

Instruction:

Sometimes, we need to keep running the pairing process and do not want the Master and Slave to skip the Pair process after re-powering, we just need the Bonding Flag of either the Master or Slave to be set to 0. Two methods are given here. (It should be noted that, when using the following methods, erase the SMP Storage area because it was bonding in previous tests).

  • Method 1: Initially configure the Security Parameters with the Bonding Flag set to 0. Take the Slave as an example (the default is commented out).
blc_smp_setSecurityParameters_slave(Non_Bondable_Mode, 0, LE_Legacy_Pairing,    0, 0, IO_CAPABILITY_NO_INPUT_NO_OUTPUT);

The bonding_mode on the Slave side is set to Non-Bondable mode, and the other settings are set to the Just Works default configuration, as is the Master side.

After calling this API, when pairing, you will see Bonding Flags=0 in the Pairing Response replied by the Slave, so neither the Master nor the Slave will do Bonding, that is, the pairing information will not be stored in the Flash. When the Master or Slave device is restarted, they are still "brand new" and do not "know" each other:

Packet Capture – Legacy Just Works Slave NoBonding

If this API is not called, the initial values of SecurityParameters set for Slave and Master in blc_gap_init() are used by default:

mode_level = Unauthenticated_Pairing_with_Encryption;
bond_mode = Bondable_Mode;
MITM_en = 0;
method = LE_Legacy_Pairing;
OOB_en = 0;
keyPress_en = 0;
ioCapablility = IO_CAPABILITY_NO_INPUT_NO_OUTPUT;
ecdh_debug_mode = non_debug_mode;
passKeyEntryDftTK = 0;
  • Method 2: Initially configure the Security Parameters and configure Bonding Mode separately, taking Master as an example:
blc_smp_setBondingMode_master(Non_Bondable_Mode);

Here, the bonding_mode is set to Non-Bondable mode on the Master side, and the same on the Slave side. The packet capture shows the same effect as method 1 above.

Packet Capture – Legacy Just Works Master NoBonding

Secure Connections Just Works

This demonstration enables the SMP feature on both Master and Slave, paired as Legacy Just Works. This demonstration is defined in feature_smp/app_config.h:

#define BLE_SLAVE_SMP_ENABLE                1   
#define BLE_MASTER_SMP_ENABLE               1 

Enable the code in feature_smp/app.c user_init_normal():

blc_smp_setSecurityLevel(LE_Security_Mode_1_Level_2);
blc_smp_setSecurityParameters(Bondable_Mode, 0, LE_Secure_Connection, 0, 0, IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
blc_smp_setEcdhDebugMode(debug_mode);

Compile feature_test, and burn the generated firmware into each of the two development boards. Press SW4 on one of the boards (as Master) to trigger the connection. The packet capture is as follows:

Packet Capture – SC Just Works

Refer to the SMP section for Debug Mode, which can be configured by the user as follows, depending on the requirements.

Master Slave Description
disabled enabled Demo code default configuration
enabled disabled The effect is similar to the demo code
disabled disabled The encrypted packets cannot be parsed by packet capture tools

Note

  • The combination of Slave debug mode enable + Master debug mode enable is not allowed, the SMP Pairing Failed event will occur.

Packet Capture – Both DebugMode Pair Failed

Legacy Passkey Entry MDSI

Enable SMP on both Master and Slave, using the Legacy Passkey Entry pairing method, demonstrated via the MDSI approach. MDSI means that the user enters the PIN code displayed on the Master side into the Slave side. Users can refer to TL SDK feature_smp for configuration.

Legacy Passkey Entry MISD

Enable SMP on both Master and Slave, using the Legacy Passkey Entry pairing method, demonstrated via the MISD approach. MISD means that the user enters the PIN code displayed on the Slave side into the Master side. Users can refer to TL SDK feature_smp for configuration.

Legacy Passkey Entry Both Input

Enable SMP on both Master and Slave, using the Legacy Passkey Entry pairing method, demonstrated via the Both Input approach. Both Input means that the user enters the same PIN code on both Slave Master. Users can refer to TL SDK feature_smp for configuration.

Secure Connections Passkey Entry

Enable SMP on both Master and Slave, using the Secure Connections Passkey Entry pairing method. Corresponding to Legacy, Secure Connections Passkey Entry also supports three association models: MDSI, MISD, and Both Input. Users can refer to TL SDK feature_smp for configuration.

Secure Connections Numeric Comparison

Enable SMP on both Master and Slave, using the Secure Connections Numeric Comparison pairing method. According to the Bluetooth specification, when Secure Connections is used and both devices have DisplayYesNo or KeyboardDisplay capabilities, the Numeric Comparison method will be applied. Users can refer to TL SDK feature_smp for configuration.

Legacy OOB

Enable SMP on both Master and Slave, using the Legacy OOB (Out-of-Band) method pairing method. According to the Bluetooth specification, under Legacy pairing, when both devices support OOB and possess each other’s OOB data, the Legacy OOB method will be used. Users can refer to TL SDK feature_smp for configuration.

Secure Connections OOB

Enable SMP on both Master and Slave, using the Secure Connections OOB method pairing method. This is currently not supported in the SDK.

Custom Pair

When SMP is disabled on both Master and Slave, Telink provides an alternative method to achieve device bonding (automatic reconnection) without using SMP — Custom Pair. Users can refer to TL SDK feature_smp for configuration.

Exception Handling

(1) No Response When Pressing the Button

Environment reason: Probably due to the development board not having reset to make the program run after burning.

(2) LED Is Not On

Hardware reasons: Probably due to the LED jumper is not connected correctly, please refer to the following figure.

LED 跳线帽接错

feature_ota

  • Function: demonstration of BLE OTA.
  • Main hardware: TLSR8258 development board x 2

The application layer code is under TC_BLE_SDK/vendor/feature_test/feature_ota, it is needed to modify the definition in TC_BLE_SDK/vendor/feature_test/feature_config.h to activate this part of the code to perform the OTA demonstration:

#define FEATURE_TEST_MODE               TEST_OTA

Compile and burn the generated firmware into each of the two development boards. Then burn the OTA firmware (the same compiled firmware as above) to the Flash address 0x80000 of one of the boards.

After powering on both boards again, press the SW2 or SW3 button on the Master five times consecutively to trigger OTA test mode. The blue and green LEDs will slowly blink three times, indicating that OTA test mode has been successfully enabled.

Press the SW2 button on the Master to start OTA. The blue LEDs on both boards will turn on, indicating that OTA is in progress. Packet capture shows that the Master continuously sends Write Command packets to the Slave:

feature_ota OTA Start

After the upgrade is completed, the Slave sends a Notify command to the Master, indicating a successful upgrade, followed by a Terminate command to disconnect. Since SMP is enabled between the two devices and the Master has stored the Slave’s information, it immediately reconnects:

feature_ota OTA Complete

feature_whitelist

  • Function: function demonstration of BLE whitelist
  • Main hardware: TLSR8258 development board x 2

The application layer code is under TC_BLE_SDK/vendor/feature_test/feature_whitelist, it is needed to modify the definition in TC_BLE_SDK/vendor/feature_test/feature_config.h to activate this part of the code and do a demonstration of the whitelist function:

#define FEATURE_TEST_MODE               TEST_WHITELIST

Since the Master connection whitelist and Slave connection whitelist share the same whitelist list in the SDK, demonstrations are provided here for connecting to Master whitelist devices and Slave whitelist devices separately.

First, compile a dual-role (Master + Slave) firmware with whitelist functionality enabled but not actually using the whitelist (referred to as NoWhiteList for convenience). In the initialization code, modify:

blc_ll_setAdvParam(ADV_INTERVAL_30MS, ADV_INTERVAL_30MS, ADV_TYPE_CONNECTABLE_UNDIRECTED, OWN_ADDRESS_PUBLIC, 0, NULL, BLT_ENABLE_ADV_ALL, ADV_FP_ALLOW_SCAN_WL_ALLOW_CONN_WL);

to:

blc_ll_setAdvParam(ADV_INTERVAL_30MS, ADV_INTERVAL_30MS, ADV_TYPE_CONNECTABLE_UNDIRECTED, OWN_ADDRESS_PUBLIC, 0, NULL, BLT_ENABLE_ADV_ALL, ADV_FP_ALLOW_SCAN_ANY_ALLOW_CONN_ANY);

Then change:

blc_ll_setScanParameter(SCAN_TYPE_PASSIVE, SCAN_INTERVAL_100MS, SCAN_WINDOW_100MS, OWN_ADDRESS_PUBLIC, SCAN_FP_ALLOW_ADV_WL);

to:

blc_ll_setScanParameter(SCAN_TYPE_PASSIVE, SCAN_INTERVAL_100MS, SCAN_WINDOW_100MS, OWN_ADDRESS_PUBLIC, SCAN_FP_ALLOW_ADV_ANY);

Compile to generate NoWhiteList.bin.

Master Configures Scan for Slave Whitelist

This demonstration for connecting to a Slave device in the whitelist involves adding the MAC address of the target Slave to the whitelist. As a Master device, the Telink platform filters the received broadcast packets by address to determine whether to initiate a connection.

Based on the NoWhiteList code, restore the scan parameter configuration:

blc_ll_setScanParameter(SCAN_TYPE_PASSIVE, SCAN_INTERVAL_100MS, SCAN_WINDOW_100MS, OWN_ADDRESS_PUBLIC, SCAN_FP_ALLOW_ADV_ANY);

to:

blc_ll_setScanParameter(SCAN_TYPE_PASSIVE, SCAN_INTERVAL_100MS, SCAN_WINDOW_100MS, OWN_ADDRESS_PUBLIC, SCAN_FP_ALLOW_ADV_WL);

Compile to generate the Master firmware, and burn it onto one development board to act as the Master. Burn the other board with NoWhiteList.bin, and modify its MAC address to the whitelist address 33:88:99:99:99:99:

Set MAC Address of Whitelist Device

After burning is complete, press SW4 on the Master to start the connection. The Master and Slave will connect successfully, as shown in the packet capture below:

Packet Capture - Master Connecting to Whitelisted Slave

If the Slave’s MAC address is not 33:88:99:99:99:99, pressing SW4 on the Master will produce no response, and no connection establishment packets will appear in the capture.

Slave sets Master connection whitelist

This demonstration for connecting to a Master device in the whitelist involves adding the MAC address of the target Master to the whitelist. As a Slave device, the Telink platform filters the scan packets or connection packets to determine whether to response.

Based on the NoWhiteList code, restore the scan parameter configuration:

blc_ll_setAdvParam(ADV_INTERVAL_30MS, ADV_INTERVAL_30MS, ADV_TYPE_CONNECTABLE_UNDIRECTED, OWN_ADDRESS_PUBLIC, 0, NULL, BLT_ENABLE_ADV_ALL, ADV_FP_ALLOW_SCAN_ANY_ALLOW_CONN_ANY);

to:

blc_ll_setAdvParam(ADV_INTERVAL_30MS, ADV_INTERVAL_30MS, ADV_TYPE_CONNECTABLE_UNDIRECTED, OWN_ADDRESS_PUBLIC, 0, NULL, BLT_ENABLE_ADV_ALL, ADV_FP_ALLOW_SCAN_WL_ALLOW_CONN_WL);

Compile to generate the Slave firmware, and burn it onto one development board to act as the Slave. Burn the other board with NoWhiteList.bin, and modify its MAC address to the whitelist address 33:88:99:99:99:99. After burning is complete, press SW4 on the Master to start the connection. The Master and Slave will connect successfully, as shown in the packet capture below:

Packet Capture - Slave Connecting to Whitelisted Master

If the Master’s MAC address is not 33:88:99:99:99:99, pressing SW4 on the Master will show that the Master has sent a Connect Request, but the Slave does not respond, causing the master to keep trying to connect.

Packet Capture - Slave Connecting to NoWhitelisted Master

feature_soft_timer

  • Function: Demonstration of the Soft Timer function. And as a reference for the demonstration of the IO Debug method.
  • Main hardware: TLSR8258 development board, logic analyzer or oscilloscope

The application layer code is under TC_BLE_SDK/vendor/feature_test/feature_soft_timer, it is needed to modify the definition in TC_BLE_SDK/vendor/feature_test/feature_config.h to activate this part of the code:

#define FEATURE_TEST_MODE               TEST_SOFT_TIMER

And enable IO Debug in app_config.h to do a functional demonstration of Soft Timer and IO Debug:

#define DEBUG_GPIO_ENABLE               1

Connect PD0 on the development board as PM IO to observe hibernation wake-up. Connect PA2, PA3, PA4, PB0 out as Debug IO 4, 5, 6, 7 for signal display respectively, refer to macro definition DEBUG_GPIO_ENABLE.

After compiling the firmware, burning it into the development board, powering up the logic analyzer to capture the Debug IOs signals, you can see the effect of the 5 IOs captured on the logic analyzer as follows.

SoftTimer Debug IO Capture

The graph shows that the advertising interval is 50ms (or so, with a little dynamic adjustment at the bottom), PA2 toggles every 23ms, PA3 toggles at alternating intervals of 7ms and 17ms, PA4 toggles every 13ms, PB0 toggles every 100ms, refer to the code enabled by the macro definition BLT_SOFTWARE_TIMER_ENABLE. It can be seen that the device is woken up early when the Soft Timer event is triggered, which means that the events set by Soft Timer are not affected by hibernation.

Other Modules

Capacitors for 24MHz Crystal

Refer to the position of the 24MHz crystal matching capacitor C1/C4 in the figure below.

The SDK uses the internal capacitor of the TC series chips (i.e. the cap corresponding to ana_8a<5:0>) as the matching capacitor for the 24MHz crystal by default. In this case, C1/C4 do not need to be soldered. The advantage of using this solution is that the capacitor can be measured and adjusted on the Telink fixture to achieve the best frequency value for the final application product.

24MHz Crystal Schematic

If using external soldered capacitors as matching capacitors for the 24MHz crystal oscillator (C1/C4 capacitors), just call the following API at the beginning of the main function (must be after the cpu_wakeup_init() function and before blc_app_loadCustomizedParameters()):

static inline void blc_app_setExternalCrystalCapEnable(u8  en)
{
    blt_miscParam.ext_cap_en = en;
    analog_write(0x8a, analog_read(0x8a) | 0x80);//disable internal cap
}

32KHz Clock Source Selection

The SDK uses the MCU’s internal 32kHz RC oscillator circuit by default, referred to as 32kHz RC. The 32kHz RC oscillator has relatively large deviation, so for applications with long suspend or deep retention durations, time accuracy will be poorer.

At present, the maximum long connection interval supported by default with 32kHz RC cannot exceed 3 seconds. Once this time is exceeded, ble_timing will go wrong, resulting in inaccurate packet reception timing. This can lead to packet retry in transmission and reception, increased power consumption, and even disconnection.

If users need to achieve lower connection power consumption and more accurate clock timing under low-power sleep mode, they can choose to use an external 32kHz crystal oscillator, referred to as 32k Pad. This mode is currently supported by the SDK.

Users only need to call one of the following two APIs at the beginning of the main function (must be before the cpu_wakeup_init function):

void blc_pm_select_internal_32k_crystal(void);
void blc_pm_select_external_32k_crystal(void);

The above APIs are used to select 32k RC and 32k Pad respectively. By default, the SDK calls blc_pm_select_internal_32k_crystal to select the 32k RC oscillator. To use 32k Pad instead, simply replace it with blc_pm_select_external_32k_crystal.

PA

If using RF PA, please refer to drivers/B85(B87,TC321X)/driver_ext/rf_pa.h.

First enable the macro below, by default it is disabled.

#ifndef PA_ENABLE
#define PA_ENABLE                    0
#endif

During system initialization, call PA initialization.

void rf_pa_init(void);

Referring to the code implementation, in this initialization, PA_TXEN_PIN and PA_RXEN_PIN are set to GPIO output mode and the initial state is output 0. The GPIOs corresponding to the PA of TX and RX need to be defined by the user:

#ifndef PA_TXEN_PIN
#define PA_TXEN_PIN                  GPIO_PB2
#endif

#ifndef PA_RXEN_PIN
#define PA_RXEN_PIN                  GPIO_PB3
#endif

Register void (*rf_pa_callback_t)(int type) as a callback handler function for PA, which actually handles the following 3 PA states: PA off, TX PA on, RX PA on.

#define PA_TYPE_OFF                  0
#define PA_TYPE_TX_ON                1
#define PA_TYPE_RX_ON                2

User only needs to call rf_pa_init above, app_rf_pa_handler is registered to the bottom layer callback, and BLE will automatically call app_rf_pa_handler's processing when it is in various states.

Version Function

Users can obtain the current SDK version information via the following function.

unsigned char blc_get_sdk_version(unsigned char *pbuf,unsigned char number);

The parameter pbuf is a pointer to the location where the version information is stored. The parameter number indicates the length of the version information, which should be 5 ~ 16. Currently, only 5 bytes are used to represent the version information.

A return value of 0 indicates success, and 1 indicates failure. For example, if the data obtained after calling the blc_get_sdk_version function is {4, 1, 0, 0, 1}, it means the current SDK version is 4.1.0.0 patch 1.

Debug Method

This chapter introduces several debugging methods commonly used in the development process.

GPIO Simulates UART Printing

To facilitate the debugging for users, the tc_ble_sdk provides an implementation of GPIO simulating UART serial port to output debugging information. This method is only for reference, not an officially recommended method for outputting debugging information. After defining the macro UART_PRINT_DEBUG_ENABLE in app_config.h in the routine as 1, directly use the printf interface consistent with the C language syntax rules in the code for serial output. The default configuration of the GPIO for simulating UART is available in each application routine and can be changed by the user as required.

Definition of GPIO for Simulating UART

In general, only modify the baud rate and other IO names in the definition of each line (all PD7 in the picture).

Note

  • The baud rate currently supports up to 1 Mbps.
  • Since the printing of the GPIO simulating UART serial port will be interrupted by the interrupts, the timing of the simulated UART will be inaccurate, so in the actual use process, there will be occasional garbled printing.
  • Since the printing of the GPIO simulating UART serial port will occupy the CPU, it is not recommended to add printing to the interrupts, which will affect the interrupt tasks with high timing requirements.

BDT Tool Reads the Global Variables Value

The official BDT tool provided by Telink can be used not only for burning firmware, but also for some online debug, where the values of global variables in the code can be read under the Tdebug tab. Users can add global variables in the code according to their needs and read them in Tdebug. For details, please refer to the Debug chapter of the BDT User Guide.

BDT Tool Reads the Global Variables Value

Note

  • When the chip is in sleep state, the read value is all 0.
  • This function depends on the generated list file (the default file names generated by TC series chips are xxx.lst), so the user should also provide the list file as much as possible when providing debug firmware to the official, so that Telink engineers can read the values of some underlying variables through the list file.

BDT Memory Access Function

The official BDT tool provided by Telink can also read the contents of specified locations in Flash, memory and other spaces, and write data through the Memory Access function. It is necessary to make sure that the SWS is on before reading. For details, please refer to the Debug chapter of the BDT User Guide.

BDT Memory Access Function

BDT Reads PC Pointer

The official BDT tool provided by Telink can be used to read the PC (Program Counter) pointer of TC series chips, which is very helpful when analyzing the dead problem. For details, please refer to the Debug chapter of the BDT User Guide.

BDT Reads PC Pointer

Debug IO

Each corresponding development board header file under the vendor/common/boards directory contains an IO-related definition enclosed in the macro DEBUG_GPIO_ENABLE.

Debug IO Definition

This function is not enabled by default, it is a unified Debug IO definition for Telink engineers to use internally to grab the waveform of IO for debugging by logic analyzer or oscilloscope. However, users can add their own Debug IO at the application layer similarly, refer to common/default_config.h.

Note

  • In the official release SDK, the Debug IO debugging information in the Stack is included in the library file in a disabled state. So even if the user defines DEBUG_GPIO_ENABLE as 1 at the application layer, it will not enable the Debug IO debugging information in the Stack.
  • If Debug IO is enabled, although the Debug IO in the Stack will not work, the Debug IO in the application layer will work, such as rf_irq_handler represented by CHN14 and stimer_irq_handler represented by CHN15 in the tc_ble_sdk.

USB my_dump_str_data

The my_dump_str_data API can be called to output debug information via the USB interface. This function is not supported on the TC series. The tc_ble_sdk retains this interface and some related calls to maintain compatibility with part of the code logic from the TL series. This will be addressed in the next SDK update.

Appendix

Appendix 1: crc16 Algorithm

unsigned shortcrc16 (unsigned char *pD, int len)
{
   static unsigned short poly[2]={0, 0xa001};
   unsigned short crc = 0xffff;
   unsigned char ds;
   int i,j;

   for(j=len; j>0; j--)
   {
    unsigned char ds = *pD++;
    for(i=0; i<8; i++)
     {
        crc = (crc >> 1) ^ poly[(crc ^ ds ) & 1];
        ds = ds >> 1;
     }
   }

  return crc;
}

Appendix 2: Checking for Stack Overflow

Principle

In cstartup_flash.S, all contents of the stack are initialized to 0xFF. During program execution, the used stack data will be overwritten with other values. By checking the size of the overwritten stack area, the stack usage can be evaluated, and it can be determined whether a stack overflow has occurred.

Method

(1) Open boot/cstartup_flash.S and enable the code under _FILL_STK.

FLL_STK:
    tcmp    r1, r2
    tjge    FLL_STK_END
    tstorer r0, [r1, #0]
    tadd    r1, #4
    tj      FLL_STK
FLL_STK_END:

......

FLL_D:
    .word   0xFFFFFFFF
    .word   (_start_data_)
    .word   (0x850000)
    @.word  (_start_data_ + 32)

(2) Refer to the SRAM allocation described in the section SRAM Memory Allocation to determine the stack base address.

(3) Use the BDT tool to download the .bin file to the device. After downloading, click Reset to start program execution, then connect and pair the slave with the master device.

(4) After successful pairing, use "Tool -> Memory Access" in BDT to read the RAM data, as shown below.

BDT RAM read configuration

(5) Press the Tab key on the keyboard to generate a Read.bin file and save the data. The file path is: BDT installation directory -> config -> user -> Read.bin.

(6) Open Read.bin with a hex editor. If there is no continuous region of 0xFF, it indicates that the stack has overflowed into the .bss section. Alternatively, a more accurate method is to locate the allocated stack top address in the generated .lst file, as shown in the figure below. Then check whether this address has been overwritten in Read.bin. If it has, a stack overflow has occurred.

Viewing Stack Top Address via .lst File