3.4.2. APP - Bluetooth Multi connections

3.4.2.1. 概述

Note

ble多连例子,支持2.4g模式,支持状态形态如下:一主…

3.4.2.2. 工程配置

  • app配置,在工程代码中找到对应的文件(apps/demo/transfer/include/app_config.h)进行APP选择,本案例中选择多连,其结果如下所示:

#define CONFIG_APP_MULTI                  1 //AW33N暂仅支持主机, 待迭代. BLE多机应用,支持2.4g code,支持4主,3主1从,2主2从...

apps/demo/transfer/include/app_config.h

如下代码段所示,配置宏解释如下

  • CONFIG_BLE_CONNECT_SLOT:低时延配置,如果要使用这种方式需要主从双方都打开此配置,主机将默认使用1ms的interval的连接间隔发起连接

  • CONFIG_BT_NOCONN_ADV_NUM:多机状态机的非连接adv,最多打开一路

  • CONFIG_BT_NOCONN_SCAN_NUM:多机状态的非连接scan,最多打开一路

  • CONFIG_BT_GATT_CLIENT_NUM:主机连接链路数配置,最多打开一路

  • CONFIG_BT_GATT_SERVER_NUM主机连接链路配置,最多打开两路

  • CFG_USE_24G_CODE_ID_ADV:打开从机的2.4g功能

  • CFG_USE_24G_CODE_ID_SCAN:打开主机的2.4g功能

#define CONFIG_BLE_CONNECT_SLOT            0 //BLE高回报率设置, 支持私有协议,所有周期单位为us

#define CONFIG_BT_NOCONN_ADV_NUM           0 //range(0~1)
#define CONFIG_BT_NOCONN_SCAN_NUM          0 //range(0~1)

#define CONFIG_BT_GATT_CLIENT_NUM          1 //range(0~4)
#define CONFIG_BT_GATT_SERVER_NUM          0 //range(0~4)
......

//2.4G模式: 0---ble, 非0---2.4G配对码; !!!主从欲连接,需保持配对码一致
//!!!初始化之后任意非连接时刻修改配对码API:rf_set_conn_24g_coded
#define CFG_USE_24G_CODE_ID_ADV            0
#define CFG_USE_24G_CODE_ID_SCAN           0
#define CFG_RF_24G_CODE_ID_SCAN            (0xAF9A9357) //<=24bits 主机扫描2.4G配对码, 可用void access_addr_generate(u8 *aa);生成
#define CFG_RF_24G_CODE_ID_ADV             (0xAF9A9357) //<=24bits 从机广播2.4G配对码, 可用void access_addr_generate(u8 *aa);生成
#define CONFIG_BLE_HIGH_SPEED              0 //BLE提速模式: 使能DLE+2M, payload要匹配pdu的包长

由于ram和功能的限制,配置宏的冲突介绍如下:

Warning

  1. CONFIG_BT_NOCONN_ADV_NUM和CONFIG_BT_GATT_SERVER_NUM不能同时配置(有了非链接adv就不要有从机的角色了)

  2. CONFIG_BT_NOCONN_SCAN_NUM和CONFIG_BT_GATT_CLIENT_NUM不能同时配置(有了非连接scan就不要有主机的角色了)

  3. 由于ram限制,大于两台状态机就需要关闭低功耗功能(在board_aw32n_demo_cfg.h配置TCFG_LOWPOWER_LOWPOWER_SEL为LOWPOWER_CLOSE),并且目前最多支持四个状态机的存在(主机+从机+adv+scan <= 4)

部分配置例子如下,以此类推,用户可根据自己需要进行配置:

一主(产品需要与一台从机连接进行数据交互)

#define CONFIG_BT_NOCONN_ADV_NUM           0 //range(0~1)
#define CONFIG_BT_NOCONN_SCAN_NUM          0 //range(0~1)

#define CONFIG_BT_GATT_CLIENT_NUM          1 //range(0~4)
#define CONFIG_BT_GATT_SERVER_NUM          0 //range(0~4)
  • 板级选择,接着在文件(apps/demo/transfer/board/bd57/board_config.h)下进行对应的板级选择如下:

#define CONFIG_BOARD_AW33N_DEMO
#include "board_aw33n_demo_cfg.h"

3.4.2.3. 主要文件代码说明

  • LE公共处理 ble_multi_conn.c,配置Gatt common模块初始化,蓝牙初始化等操作

  • LE的GATT server实现 ble_multi_peripheral.c,配置Gatt Server端的广播、连接、事件处理等,和le_trans case工程功能类似,就不重点讲解

  • LE的GATT client 实现 ble_multi_central.c,配置Gatt Cleint端的搜索、连接、事件处理等

3.4.2.4. ble主机主要代码讲解(ble_multi_central.c)

  • 配置主机连接参数

//连接周期
#if CONFIG_BLE_CONNECT_SLOT
#define BASE_INTERVAL_MIN   (1000) // 最小的interval
#define SET_CONN_INTERVAL   (BASE_INTERVAL_MIN) //(unit:us)
#else
#define BASE_INTERVAL_MIN   (6)//最小的interval
#define SET_CONN_INTERVAL   (BASE_INTERVAL_MIN*4) //(unit:1.25ms)
#endif

//连接latency
#define SET_CONN_LATENCY                      0  //(unit:conn_interval)
//连接超时
#define SET_CONN_TIMEOUT                      400 //(unit:10ms)

//建立连接超时
#define SET_CREAT_CONN_TIMEOUT                8000 //(unit:ms)

  • 指定scan ble从机名字进行主动连接,以及指定的uuid控制,如下代码所示:

        //指定搜索uuid
static const target_uuid_t  multi_client_search_uuid_table[] = {

        // for uuid16
        // PRIMARY_SERVICE, ae30
        // CHARACTERISTIC,  ae01, WRITE_WITHOUT_RESPONSE | DYNAMIC,
        // CHARACTERISTIC,  ae02, NOTIFY,

        {
                .services_uuid16 = 0xae30,
                .characteristic_uuid16 = 0xae01,
                .opt_type = ATT_PROPERTY_WRITE_WITHOUT_RESPONSE,
        },

        {
                .services_uuid16 = 0xae30,
                .characteristic_uuid16 = 0xae02,
                .opt_type = ATT_PROPERTY_NOTIFY,
        },

        //for uuid128,sample
        //      PRIMARY_SERVICE, 0000F530-1212-EFDE-1523-785FEABCD123
        //      CHARACTERISTIC,  0000F531-1212-EFDE-1523-785FEABCD123, NOTIFY,
        //      CHARACTERISTIC,  0000F532-1212-EFDE-1523-785FEABCD123, WRITE_WITHOUT_RESPONSE | DYNAMIC,
        /*
                {
                        .services_uuid16 = 0,
                        .services_uuid128 =       {0x00,0x00,0xF5,0x30 ,0x12,0x12 ,0xEF, 0xDE ,0x15,0x23 ,0x78,0x5F,0xEA ,0xBC,0xD1,0x23} ,
                        .characteristic_uuid16 = 0,
                        .characteristic_uuid128 = {0x00,0x00,0xF5,0x31 ,0x12,0x12 ,0xEF, 0xDE ,0x15,0x23 ,0x78,0x5F,0xEA ,0xBC,0xD1,0x23},
                        .opt_type = ATT_PROPERTY_NOTIFY,
                },

                {
                        .services_uuid16 = 0,
                        .services_uuid128 =       {0x00,0x00,0xF5,0x30 ,0x12,0x12 ,0xEF, 0xDE ,0x15,0x23 ,0x78,0x5F,0xEA ,0xBC,0xD1,0x23} ,
                        .characteristic_uuid16 = 0,
                        .characteristic_uuid128 = {0x00,0x00,0xF5,0x32 ,0x12,0x12 ,0xEF, 0xDE ,0x15,0x23 ,0x78,0x5F,0xEA ,0xBC,0xD1,0x23},
                        .opt_type = ATT_PROPERTY_WRITE_WITHOUT_RESPONSE,
                },
        */

};

//配置多个扫描匹配设备
static const uint8_t multi_client_test_remoter_name1[] = "AW31N_BLE";
static const client_match_cfg_t multi_client_match_table[] = {
        {
                .create_conn_mode = BIT(CLI_CREAT_BY_NAME),
                .compare_data_len = sizeof(multi_client_test_remoter_name1) - 1, //去结束符
                .compare_data = multi_client_test_remoter_name1,
                .filter_pdu_bitmap = 0,
        },
};

//配置扫描匹配连接的设备,已经连上后搜索匹配的profile uuid
static const gatt_search_cfg_t multi_client_search_config = {
        .match_devices = multi_client_match_table,
        .match_devices_count = (sizeof(multi_client_match_table) / sizeof(client_match_cfg_t)),
        .match_rssi_enable = 0,

        .search_uuid_group = multi_client_search_uuid_table,
        .search_uuid_count = (sizeof(multi_client_search_uuid_table) / sizeof(target_uuid_t)),
        .auto_enable_ccc = 1,
};

//配置扫描匹配连接绑定后的设备
static gatt_search_cfg_t multi_client_bond_config = {
        .match_devices = NULL,
        .match_devices_count = 0,
        .match_rssi_enable = 0,

        .search_uuid_group = multi_client_search_uuid_table,
        .search_uuid_count = (sizeof(multi_client_search_uuid_table) / sizeof(target_uuid_t)),
        .auto_enable_ccc = 1,
};
  • 在(ble_multi_central.c)中打开MULTI_TEST_WRITE_SEND_DATA使能(数据发送使能):

#define  MULTI_TEST_WRITE_SEND_DATA      1   \ *//测试发数
  • 连接从机之后,可以通过按键来发送ble数据来控制从机,下图为测试发送接口:

static void multi_client_write_data(uint8_t *data, uint16_t send_len)
{
#if MULTI_TEST_WRITE_SEND_DATA
        int i, ret = 0;
        uint16_t tmp_handle;
        for (i = 0; i < SUPPORT_MAX_GATT_CLIENT; i++) {
                tmp_handle = ble_comm_dev_get_handle(i, GATT_ROLE_CLIENT);
                if (ble_comm_att_check_send(tmp_handle, send_len)) {
                        ret = ble_comm_att_send_data(tmp_handle, multi_client_write_handle, data, send_len, ATT_OP_WRITE_WITHOUT_RESPOND);
                }
        }
#endif
}
  • 按键发送数据,搭配le_trans case ble从机测试,完成连接后,按键发送数据控制从机led灯亮灭(单击k1 led1亮,双击灭,单击k2 led3亮,双击灭)

void multi_client_key_test(uint8_t key_type, uint8_t key_value)
{
        // ble主从通信test,
        // k1单击亮led1,k1双击灭led1
        // k2单机亮led2,k2双击灭led2
        on_off_opcode_t on_off_opcode;
        log_info(">>>>>>>>>>cetl_client_app key test");
        on_off_opcode.led_opcode = LED_ON_OFF_OPCODE;
        if (key_value == TCFG_ADKEY_VALUE0) {
                on_off_opcode.led_num = LED1;
                on_off_opcode.led_status = (key_type == KEY_EVENT_CLICK) ? LED_ON_STATUS : LED_OFF_STATUS;
        } else if (key_value == TCFG_ADKEY_VALUE1) {
                on_off_opcode.led_num = LED2;
                on_off_opcode.led_status = (key_type == KEY_EVENT_CLICK) ? LED_ON_STATUS : LED_OFF_STATUS;
        }

        if (key_type == KEY_EVENT_DOUBLE_CLICK) {
                on_off_opcode.led_status = LED_OFF_STATUS;
        }
        multi_client_write_data(&on_off_opcode, sizeof(on_off_opcode_t));

}

3.4.2.5. ble scan主要代码讲解

apps/demo/transfer/examples/multi_conn/ble_multi_scan.c

/*************************************************************************************************/
/*!
 *  \brief      扫描接收到的数据
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
static void multi_scan_data_handle(const uint8_t *data, uint16_t len)
{
    log_info("rx_data: %d", len);
    log_info_hexdump((uint8_t *)data, len);
}

/*************************************************************************************************/
/*!
 *  \brief      接收机数据处理回调
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
void multi_scan_report_handle(adv_report_t *report_pt, uint16_t len)
{
    /* log_info("event_type,addr_type:%x,%x\n", report_pt->event_type, report_pt->address_type); */
    /* log_info_hexdump(report_pt->address, 6); */
    static uint32_t scan_packet_num = 0;//for test
    /* r_printf("rx data:%d",report_pt->length); */
    /* put_buf(report_pt->data,report_pt->length);	 */
    if (report_pt == NULL) {
        return;
    }

    if (0 == report_pt->length) {
        return;
    }

    uint8_t data_len = report_pt->length - 1;

    if (!data_len) {
        return;
    }

    if (report_pt->data[0] == RSP_TX_HEAD) {
        memcpy(&multi_scan_buffer[ADV_RSP_PACKET_MAX - 1], &report_pt->data[1], data_len);
        log_info("long_packet =%d\n", multi_scan_len);
    } else {
        memcpy(multi_scan_buffer, &report_pt->data[1], data_len);
        multi_scan_len = report_pt->data[0];
        if (multi_scan_len > ADV_RSP_PACKET_MAX - 1) {
            log_info("first_packet =%d,wait next packet\n", data_len);
            return;
        } else {
            /* log_info("short_packet =%d\n", multi_scan_len); */
        }
    }

    //for debug
#if MULTI_SCAN_LOG_ENABLE
	static int cnt = 0;
	cnt++;
	if (cnt % 500 == 0) {
        log_info("rssi:%d,packet_num:%u\n", report_pt->rssi, ++scan_packet_num);
        multi_scan_data_handle(multi_scan_buffer, multi_scan_len);
		cnt = 0;
	}

#endif
    multi_scan_len = 0;
}

3.4.2.6. 4k/8k测试demo配置

Note

应用配置、板级配置以及扫描匹配参数配置与上述内容一致

  • 使能4k/8k,apps/include_lib/bt_controller_include/btcontroller_mode.h

// #define CONFIG_BLE_HRATE_FEATURE       	 0   /*!< BLE 高速率(HRATE)特性开关,0 表示关闭 */
// #define CONFIG_BLE_HRATE_FEATURE       	     CONFIG_BLE_HRATE_1K
// #define CONFIG_BLE_HRATE_FEATURE       	 CONFIG_BLE_HRATE_4K
#define CONFIG_BLE_HRATE_FEATURE       	 (CONFIG_BLE_HRATE_4K  | CONFIG_BLE_HRATE_8K)
  • 选择4k或者8k,apps/demo/transfer/examples/multi_conn/ble_multi_peripheral.c,主机不区分4k或者8k

#define BLE_HRATE_RATE_MODE     0  /*!< 1:8k,0:4k */
  • 连接之后调用 ble_op_hr_conn_param_update 更新参数 apps/demo/transfer/examples/multi_conn/ble_multi_peripheral.c

/**
 * @brief 高回报率测试初始化, 记录连接句柄并启动定时切换
 *
 * @param handle  连接句柄
 */
void ble_test_hrate_init(u16 handle)
{
    peri_conn_handle  = handle;
    struct conn_update_hr_param_t update_param ;
    update_param.latency  = 0;
#if BLE_HRATE_RATE_MODE
    update_param.interval = 1;   //8k
#else
    update_param.interval = 2;  //4k
#endif
    ble_op_hr_conn_param_update(peri_conn_handle, &update_param);
}
  • 发数接口,区分4k和8k apps/demo/transfer/examples/multi_conn/ble_multi_peripheral.c

/**
 * @brief 带事件类型的基带中断钩子: ntx 和按键 ACL 都要发
 *
 * 8K 一个连接事件 = 6 个 NTX 子事件 + 1 个 SUCCESS 回调:
 *   BLE_EVT_IRQ_NTX    : ring 有空位时压 1 包 ntx。CMAC 每事件消费 5 个新包
 *                        (slot0 重发上一事件最后一包), ring 满时本回调由库跳过
 *   BLE_EVT_IRQ_SUCCESS: 压 1 包按键 ACL(间隔 HRATE_KEY_SEND_EVENT_INTERVAL 个事件)
 *
 * @param priv_hw  基带硬件私有数据, 原样透传给发包接口
 * @param link     链路私有数据
 * @param hw_state 硬件状态
 * @param flag     0: 可以发包; 1: ntx ring 已满 / ACL fast buf 忙(库已在回收后计算)
 * @param event    BLE_EVT_IRQ_NTX / BLE_EVT_IRQ_SUCCESS / BLE_EVT_IRQ_OTHER
 *
 * @note  本钩子在中断上下文执行, 只做计数、喂狗与发包, 不得调用阻塞接口
 */
void ble_event_irq_hook_ex(void *priv_hw, void *link, uint8_t hw_state, uint8_t flag, uint8_t event)
{
    mouse_reset_cnt++;
    if (mouse_reset_cnt > MOUSE_1MS_CLEAR_WDT_CNT_MAX) {
        wdt_clear();
        mouse_reset_cnt = 0;
    }
#if BLE_HRATE_RATE_MODE
    if (event == BLE_EVT_IRQ_NTX) {
        if (flag) {
            /* ntx ring 已写满, 本 sub-event 没有新的消费空位, 跳过本次喂包。
               这是 8K 的正常节流(每事件 6 个回调、只有 5 个新包被消费),不是故障 */
            return;
        }
        /* 每次 NTX 回调发 1 包 ntx */
        le_hw_send_packet_fast_ntx(priv_hw, hrate_test_payload, HRATE_TEST_PAYLOAD_LEN);
    } else if (event == BLE_EVT_IRQ_SUCCESS) {
        /* 每 HRATE_KEY_SEND_EVENT_INTERVAL 个连接事件发 1 包 ACL */
        if ((++key_event_cnt < HRATE_KEY_SEND_EVENT_INTERVAL) || flag) {
            return;
        }

        if (le_hw_send_packet_fast(priv_hw, hrate_test_payload, HRATE_TEST_PAYLOAD_LEN) == HW_S_FAST_RET_SUCESS) {
            key_event_cnt = 0;
        }
    }
#else
    /* 4K 模式: 每个连接事件发 1 包 ACL 数据 */
    if (event == BLE_EVT_IRQ_SUCCESS) {

        if (flag) {
            return;
        }
        le_hw_send_packet_fast(priv_hw, hrate_test_payload, HRATE_TEST_PAYLOAD_LEN);
    }
#endif
}
  • 接收接口 apps/demo/transfer/examples/multi_conn/ble_multi_central.c

/**
 * @brief 基带接收数据钩子: 高速率模式下统计收包速率并喂狗
 *
 * @param priv  链路私有数据, 用于获取连接句柄
 * @param data  接收到的 PDU 数据
 * @param len   PDU 长度
 * @return      true 表示该 PDU 已由本钩子消费, 不再走常规链路层处理
 *
 * @note        本钩子在基带收包路径中调用, 执行时间应尽可能短
 */
bool bb_le_rx_data_pdu_hook_get(void *priv, const u8 *data, u8 len)
{
    uint16_t conn_handle = ll_vendor_get_link_handle(priv);
    if (!conn_handle) {
        return false;
    }

    mouse_reset_cnt++;
    if (mouse_reset_cnt > MOUSE_1MS_CLEAR_WDT_CNT_MAX) {
        wdt_clear();
        mouse_reset_cnt = 0;
        // put_buf((u8 *)data, len);
    }

    rx_rate_cnt++;
    return true;
}

3.4.2.7. case默认应用资源(默认使用AW33N DEMO开发板)

Note

demo默认占用外设资源如下,具体可以查看board_aw33n_demo_cfg.h

I/O口占用情况

PA口 I/O

默认占用情况

PA4

默认打印UART0引脚

PA1

adkey检测引脚

PA9

LED控制引脚

硬件定时器占用情况

定时器

默认占用情况

TIMER0

暂无使用

TIMER1

暂无使用

TIMER2

暂无使用

TIMER3

暂无使用

串口占用情况

串口

默认占用情况

UART0

SDK打印

UART1

暂无使用

UART2

暂无使用

3.4.2.8. case默认应用功能现象

(默认使用AW33N DEMO开发板 LED1-PA9 LED2-PA10(开启LED2宏) ADKEY-PA1) 需与TRANSFER BLE从机联动功能测试 (默认ble主机功能即CONFIG_BT_GATT_CLIENT_NUM选择1)

操作

现象结果

扫描scan功能,上电会自动连接上ble从机

能够连接并且进行ble数据传输

ble主从数据 传输led控制(见BLE从机应用UART功能)

应用UART功能

K1按三下软关机

出现正确现象

K1单击:控制ble 从机LED1开(需要与BLE从机做功能联动)

出现正确现象

K1双击:控制ble 从机LED1关(需要与BLE从机做功能联动)

出现正确现象

K2单击:控制 对方LED2开(需要与BLE从机做功能联动)

出现正确现象

K2双击:控制 对方LED2关(需要与BLE从机做功能联动)

出现正确现象