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
CONFIG_BT_NOCONN_ADV_NUM和CONFIG_BT_GATT_SERVER_NUM不能同时配置(有了非链接adv就不要有从机的角色了)
CONFIG_BT_NOCONN_SCAN_NUM和CONFIG_BT_GATT_CLIENT_NUM不能同时配置(有了非连接scan就不要有主机的角色了)
由于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从机做功能联动) |
出现正确现象 |