BES BLE低功耗蓝牙技术实现分析
目录
-
- 3.1 BLE连接状态机
- 3.2 连接建立过程
- 3.2.1 HCI层面的连接事件
- 3.2.2 连接参数
- 3.3 连接建立代码流程
- 3.4 连接参数更新请求机制
- 3.4.1 连接参数更新协议
- 3.4.2 连接参数更新触发场景
- 3.4.3 防止BT/BLE时间片冲突的保护机制
- 3.4.4 HCI层连接参数更新事件
-
- 4.1 GATT协议概述
- 4.1.1 GATT协议栈
- 4.1.2 [GATT over BR/EDR](#GATT over BR/EDR)
- 4.2 数据路径服务定义
- 4.3 [MTU (Maximum Transmission Unit) 交换](#MTU (Maximum Transmission Unit) 交换)
- 4.4 数据接收流程
- 4.4.1 GATT写入事件处理
- 4.4.2 数据接收回调
- 4.5 数据发送流程
- 4.5.1 通知(Notification)发送
- 4.5.2 指示(Indication)发送
- 4.6 CCCD (Client Characteristic Configuration Descriptor)
- 4.1 GATT协议概述
-
- 8.0 BLE协议栈层次交互总览
- 8.1 BLE广播流程详细时序图
- 8.1.1 广播启动完整时序 (基于Extended Advertising)
- 8.1.2 广播数据包在各层的封装
- 8.1.3 [Legacy vs Extended Advertising对比](#Legacy vs Extended Advertising对比)
- 8.2 BLE扫描流程详细时序图
- 8.2.1 扫描启动完整时序
- 8.2.2 主动扫描vs被动扫描
- 8.2.3 扫描时间参数
- 8.3 BLE连接建立流程详细时序图
- 8.3.1 连接建立完整时序 (Initiator发起)
- 8.3.2 [CONNECT_IND PDU详细结构](#CONNECT_IND PDU详细结构)
- 8.3.3 连接建立时间线
- 8.4 BLE配对鉴权流程详细时序图
- 8.4.1 [LE Legacy Pairing (传统配对)](#LE Legacy Pairing (传统配对))
- 8.4.2 [LE Secure Connections (安全连接配对 - BLE 4.2+)](#LE Secure Connections (安全连接配对 - BLE 4.2+))
- 8.4.3 配对方法选择决策表
- 8.5 BLE拒绝连接流程详细时序图
- 8.5.1 广播端拒绝连接 (使用白名单过滤)
- 8.5.2 应用层主动拒绝连接请求
- 8.5.3 链路层拒绝连接 (无资源)
- 8.6 BLE断开连接流程详细时序图
- 8.6.1 主动断开连接 (Host发起)
- 8.6.2 监督超时断开 (Supervision Timeout)
- 8.6.3 链路层即时断开 (LL_TERMINATE_IND)
- 8.7 HCI日志分析实例
- 8.7.1 广播启动日志解析
- 8.8 CTKD跨传输密钥派生机制
- 8.8.1 CTKD定义与应用场景
- 8.8.2 CTKD密钥派生原理
- 8.8.3 CTKD密钥分发标志
- 8.8.4 CTKD代码实现示例
- 8.8.5 CTKD完整时序图
- 8.8.6 CTKD实际操作例程
- 8.8.7 CTKD常见问题排查
-
- 11.1 RF配置和功率控制
- 11.2 BT_CLK时钟单位和Slot机制
- 11.3 物理层Packet类型
- 11.4 AFH自适应跳频
-
- 12.1 SMP状态机 (26个Phase)
- 12.2 配对方法 (4种)
- 12.3 [LE Legacy Pairing vs LE Secure Connections](#LE Legacy Pairing vs LE Secure Connections)
- 12.4 密钥分发 (LTK/IRK/CSRK/EDIV/RAND)
- 12.5 CTKD跨传输密钥派生
-
- 13.1 HCI错误码 (bt_common_define.h:829-889)
- 13.2 错误码分类与处理
-
- 14.1 [LMP Opcode定义](#LMP Opcode定义)
-
- 15.1 [BLE Connection Event Slot分配](#BLE Connection Event Slot分配)
- 15.2 TWS私有Slot调度优化
- 15.3 A2DP/SCO/BLE多链路Slot协调
- 15.3.1 SCO与BLE物理层时间片冲突深度分析
- 15.4 Sniff模式的Slot管理
1. BLE基础知识
1.1 BLE协议栈层次
┌─────────────────────────────────────┐
│ 应用层 (Application) │
├─────────────────────────────────────┤
│ GATT (Generic Attribute) │
├─────────────────────────────────────┤
│ ATT (Attribute Protocol) │
├─────────────────────────────────────┤
│ L2CAP (Logical Link Control) │
├─────────────────────────────────────┤
│ HCI (Host Controller Interface)│
├─────────────────────────────────────┤
│ Link Layer (链路层) │
├─────────────────────────────────────┤
│ Physical Layer (物理层) │
└─────────────────────────────────────┘
1.2 BLE角色定义
- Broadcaster (广播者): 周期性发送广播数据包
- Observer (观察者): 扫描并接收广播数据包
- Peripheral (外设/从设备): 可连接的设备,等待中心设备连接
- Central (中心/主设备): 发起连接的设备
1.3 BLE地址类型详解
BLE设备支持多种地址类型以满足不同的隐私和安全需求:
| 地址类型 | 值 | 说明 | 应用场景 |
|---|---|---|---|
| Public (公共地址) | 0x00 | 类似MAC地址,全球唯一,由IEEE分配 | 固定设备、需要全球唯一标识的场景 |
| Random Static (随机静态地址) | 0x01 | 上电时随机生成,设备运行期间保持不变 | 需要持久标识但不需要全球唯一性 |
| Private Resolvable (RPA) | 0x02 | 使用IRK加密生成,可被授权设备解析 | 隐私保护、已配对设备重连 |
| Private Non-resolvable | 0x03 | 随机生成且不可解析 | 临时广播、最高隐私级别 |
1.3.1 地址类型枚举定义
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:211-233
enum bes_addr_type
{
/// Public BD address (IEEE分配的全球唯一地址)
BES_ADDR_PUBLIC = 0x00,
/// Random BD Address (随机地址)
BES_ADDR_RAND,
/// Controller generates Resolvable Private Address based on the
/// local IRK from resolving list. If resolving list contains no matching
/// entry, use public address.
BES_ADDR_RPA_OR_PUBLIC,
/// Controller generates Resolvable Private Address based on the
/// local IRK from resolving list. If resolving list contains no matching
/// entry, use random address.
BES_ADDR_RPA_OR_RAND,
/// mask used to determine Address type in the air
BES_ADDR_MASK = 0x01,
/// mask used to determine if an address is an RPA
BES_ADDR_RPA_MASK = 0x02,
/// Random device address (controller unable to resolve)
BES_ADDR_RAND_UNRESOLVED = 0xFE,
/// No address provided (anonymous advertisement)
BES_ADDR_NONE = 0xFF,
};
1.3.2 Own Address Type (本地地址源类型)
设备使用的本地地址类型定义:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:278-288
typedef enum
{
/// Public or Private Static Address according to device address configuration
/// 根据设备配置使用公共地址或私有静态地址
BES_GAP_STATIC_ADDR,
/// Generated resolvable private random address
/// 生成可解析私有随机地址(RPA)
BES_GAP_GEN_RSLV_ADDR,
/// Generated non-resolvable private random address
/// 生成不可解析私有随机地址
BES_GAP_GEN_NON_RSLV_ADDR,
} BES_GAP_OWN_ADDR_E;
1.3.3 地址相关API
设置本地IRK (Identity Resolving Key):
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:961-965
/**
* @brief Set local irk
* @param[in] p_irk IRK (16字节)
*/
void bes_ble_gap_set_local_irk(const uint8_t *p_irk);
获取本地地址:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:1123-1128
/**
* @brief Get local static ble IA addr
* @return ble_bdaddr_t 本地Identity Address
*/
ble_bdaddr_t bes_ble_gap_get_current_ble_addr(void);
获取本地RPA地址:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:1138-1144
/**
* @brief Get local rpa addr
* @param[in] conidx Conn idx
* @return const uint8_t* RPA地址指针
*/
const uint8_t *bes_ble_gap_get_local_rpa_addr(uint8_t conidx);
通过广播handle读取RPA地址:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:1146-1151
/**
* @brief Read local rpa addr by adv hdl--HCI cmd
* @param[in] adv_hdl Adv handle
*/
void bes_ble_gap_read_local_rpa_by_adv_hdl(uint8_t adv_hdl);
1.3.4 地址解析与隐私保护
RPA超时设置:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:641-649
/**
* @brief Set the length of time the Controller uses a
* Resolvable Private Address before a new resolvable private
* address is generated and starts being used
* @param[in] rpa_timeout Range: 0x0001 to 0x0E10, in unit of seconds
* (1秒到3600秒,默认15分钟)
*/
void bes_ble_gap_set_rpa_timeout(uint16_t rpa_timeout);
添加设备到解析列表(Resolving List):
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:629-634
/**
* @brief Set rpa list
* @param[in] ble_addr BLE addr
* @param[in] irk IRK (Identity Resolving Key, 16字节)
*/
void bes_ble_gap_set_rpa_list(const ble_bdaddr_t *ble_addr, const uint8_t *irk);
添加已配对设备到解析列表:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:636-640
/**
* @brief Set bonded devices of nv record to rpa list
* 将NV记录中的已配对设备添加到RPA解析列表
*/
void bes_ble_gap_set_bonded_devs_rpa_list(void);
1.3.5 地址使用建议
| 场景 | 推荐地址类型 | 原因 |
|---|---|---|
| 初次配对/广播 | Public 或 Random Static | 设备易于识别,方便扫描发现 |
| 已配对设备重连 | RPA (BES_ADDR_RPA_OR_PUBLIC) | 保护隐私,防止设备被追踪 |
| 临时广播 | Non-resolvable Private | 最高隐私级别 |
| 固定设备(如信标) | Public | 需要全局唯一标识 |
2. 广播机制与低功耗优化
2.1 广播数据包结构
BLE广播数据包最大31字节(Legacy ADV)或255字节(Extended ADV):
┌────────┬──────┬────────────────┐
│ Length │ Type │ Data │
├────────┼──────┼────────────────┤
│ 1 byte │1 byte│ N bytes │
└────────┴──────┴────────────────┘
常用广播类型(AD Type):
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:109-186
enum bes_gap_ad_type
{
BES_GAP_AD_TYPE_FLAGS = 0x01, // Flags
BES_GAP_AD_TYPE_MORE_16_BIT_UUID = 0x02, // 部分16-bit UUID列表
BES_GAP_AD_TYPE_COMPLETE_LIST_16_BIT_UUID = 0x03, // 完整16-bit UUID列表
BES_GAP_AD_TYPE_MORE_128_BIT_UUID = 0x06, // 部分128-bit UUID列表
BES_GAP_AD_TYPE_COMPLETE_LIST_128_BIT_UUID = 0x07,// 完整128-bit UUID列表
BES_GAP_AD_TYPE_SHORTENED_NAME = 0x08, // 短设备名称
BES_GAP_AD_TYPE_COMPLETE_NAME = 0x09, // 完整设备名称
BES_GAP_AD_TYPE_TRANSMIT_POWER = 0x0A, // 发射功率
BES_GAP_AD_TYPE_SERVICE_16_BIT_DATA = 0x16, // 16-bit UUID Service Data
BES_GAP_AD_TYPE_SERVICE_128_BIT_DATA = 0x21, // 128-bit UUID Service Data
BES_GAP_AD_TYPE_APPEARANCE = 0x19, // 外观
BES_GAP_AD_TYPE_MANU_SPECIFIC_DATA = 0xFF, // 厂商特定数据
};
2.1.1 BLE广播通道与频率
BLE使用2.4GHz ISM频段,定义了40个RF通道(0-39),其中3个是广播通道(Advertising Channels) ,37个是数据通道(Data Channels)。
广播通道特性
c
/// bthost/adapter/inc/ble/common/co_bt_defines.h:175-177
#define ADV_CHANNEL_37 37 // 频率: 2402 MHz
#define ADV_CHANNEL_38 38 // 频率: 2426 MHz
#define ADV_CHANNEL_39 39 // 频率: 2480 MHz
| 广播通道 | 频率(MHz) | 说明 |
|---|---|---|
| Channel 37 | 2402 | 低频段,与WiFi信道1部分重叠 |
| Channel 38 | 2426 | 中频段,位于WiFi信道6-7之间 |
| Channel 39 | 2480 | 高频段,与WiFi信道13-14部分重叠 |
通道选择策略:
- 通常同时使用全部3个广播通道以提高发现概率
- 可以根据环境干扰情况选择性禁用某些通道
- 广播设备依次在这3个通道上发送广播包
广播通道映射
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:235-247
enum bes_adv_channel_map
{
/// Byte value for advertising channel map for channel 37 enable
BES_ADV_CHNL_37_EN = 0x01,
/// Byte value for advertising channel map for channel 38 enable
BES_ADV_CHNL_38_EN = 0x02,
/// Byte value for advertising channel map for channel 39 enable
BES_ADV_CHNL_39_EN = 0x04,
/// Byte value for advertising channel map for channel 37, 38 and 39 enable
BES_ADV_ALL_CHNLS_EN = 0x07,
};
使用示例:
c
// 启用所有3个广播通道(推荐)
adv_param.adv_channel_map = BES_ADV_ALL_CHNLS_EN;
// 仅启用通道37和39(跳过通道38,如遇到WiFi干扰)
adv_param.adv_channel_map = BES_ADV_CHNL_37_EN | BES_ADV_CHNL_39_EN;
2.1.2 广播Flags设置
Flags是广播数据包中的重要字段,用于指示设备的发现性和能力。
Flags位定义
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:188-207
enum
{
/// Limited discovery flag - AD Flag - bit mask
/// 限时可发现模式(通常用于快速配对场景)
BES_GAP_LE_LIM_DISCOVERABLE_FLG_BIT = 0x01,
BES_GAP_LE_LIM_DISCOVERABLE_FLG_POS = 0,
/// General discovery flag - AD Flag - bit mask
/// 通用可发现模式(持续可被发现)
BES_GAP_LE_GEN_DISCOVERABLE_FLG_BIT = 0x02,
BES_GAP_LE_GEN_DISCOVERABLE_FLG_POS = 1,
/// Legacy BT not supported - AD Flag - bit mask
/// 不支持传统蓝牙BR/EDR
BES_GAP_BR_EDR_NOT_SUPPORTED_BIT = 0x04,
BES_GAP_BR_EDR_NOT_SUPPORTED_POS = 2,
/// Dual mode for controller supported (BR/EDR/LE) - AD Flag - bit mask
/// 双模控制器支持(BR/EDR和LE同时支持)
BES_GAP_SIMUL_BR_EDR_LE_CONTROLLER_BIT = 0x08,
BES_GAP_SIMUL_BR_EDR_LE_CONTROLLER_POS = 3,
};
Flags设置API
c
/// bthost/service/ble_app_new/inc/app_ble.h:451
/**
* @brief Set advertising flags
* @param[in] adv_param 广播参数结构
* @param[in] simu_bredr_support 是否同时支持BR/EDR
*/
void app_ble_dt_set_flags(gap_adv_param_t *adv_param, bool simu_bredr_support);
使用示例:
c
// 示例1: 纯BLE设备,通用可发现模式
gap_adv_param_t adv_param;
app_ble_dt_set_flags(&adv_param, false);
// 生成Flags: 0x06 (BLE only + General Discoverable)
// 示例2: 双模设备(支持BLE和经典蓝牙)
app_ble_dt_set_flags(&adv_param, true);
// 生成Flags: 0x1A (BR/EDR + LE + General Discoverable)
2.1.3 广播数据构建API
系统提供了多种API用于构建广播数据包,支持灵活的数据组装。
原始数据添加
c
/// bthost/adapter/inc/adapter_service/gap_service.h:3107-3109
/**
* @brief Add raw data to advertising buffer
* @param[in] buf Gap data buffer
* @param[in] data Raw data pointer
* @param[in] len Data length
* @return bool Success or failed
*/
bool gap_dt_add_raw_data(gap_dt_buf_t *buf, const uint8_t *data, uint16_t len);
使用场景:
- 添加自定义格式的广播数据
- 直接插入完整的AD结构(Length + Type + Data)
- 添加厂商特定数据
使用示例:
c
gap_dt_buf_t *adv_buf = &adv_param->adv_data;
// 添加厂商特定数据
uint8_t manu_data[] = {
0x05, // Length = 5 bytes
0xFF, // Type = Manufacturer Specific Data
0x4C, 0x00, // Company ID (Apple = 0x004C)
0x01, 0x02 // Manufacturer data
};
gap_dt_add_raw_data(adv_buf, manu_data, sizeof(manu_data));
添加Flags
c
/**
* @brief Add flags to advertising data
* @param[in] buf Gap data buffer
* @param[in] flags Flags value (e.g., GAP_FLAGS_LE_GENERAL_DISCOVERABLE_MODE)
* @param[in] bredr_support BR/EDR support flag
* @return bool Success or failed
*/
bool gap_dt_add_flags(gap_dt_buf_t *buf, uint8_t flags, bool bredr_support);
添加本地名称
c
/// bthost/service/ble_app_new/inc/app_ble.h:452
/**
* @brief Set local name in advertising data
* @param[in] adv_param Advertising parameter
* @param[in] cust_le_name Custom LE name (NULL to use default)
*/
void app_ble_dt_set_local_name(gap_adv_param_t* adv_param, const char* cust_le_name);
添加Service UUID
c
/**
* @brief Add 16-bit service UUID
* @param[in] buf Gap data buffer
* @param[in] uuid16 16-bit UUID value
* @param[in] complete True for complete list, false for partial
* @return bool Success or failed
*/
bool gap_dt_add_service_uuid_16(gap_dt_buf_t *buf, uint16_t uuid16, bool complete);
/**
* @brief Add 128-bit service UUID
* @param[in] buf Gap data buffer
* @param[in] uuid128 128-bit UUID array (16 bytes, LSB first)
* @param[in] complete True for complete list, false for partial
* @return bool Success or failed
*/
bool gap_dt_add_service_uuid_128(gap_dt_buf_t *buf, const uint8_t *uuid128, bool complete);
完整示例:构建广播数据包
c
void build_custom_adv_data(gap_adv_param_t *adv_param)
{
gap_dt_buf_t *adv_data = &adv_param->adv_data;
// 1. 添加Flags (通用可发现,仅LE)
app_ble_dt_set_flags(adv_param, false);
// 2. 添加设备名称
app_ble_dt_set_local_name(adv_param, "MyDevice");
// 3. 添加16-bit Service UUID (例如: Heart Rate Service = 0x180D)
uint16_t uuid = 0x180D;
gap_dt_add_service_uuid_16(adv_data, uuid, true);
// 4. 添加厂商特定数据
uint8_t manu_data[] = {
0x06, // Length
0xFF, // Type = Manufacturer Specific Data
0x59, 0x00, // Company ID (Nordic = 0x0059)
0x01, 0x02, 0x03 // Custom data
};
gap_dt_add_raw_data(adv_data, manu_data, sizeof(manu_data));
}
2.2 充电盒定向广播实现
本系统实现了智能的快慢广播切换机制以优化功耗:
2.2.1 广播参数定义
c
// 广播间隔定义
#define CHARGEBOX_ADV_FAST_INTERVAL_MS 30 // 快速广播:30ms
#define CHARGEBOX_ADV_SLOW_INTERVAL_MS 480 // 慢速广播:480ms
#define CHARGEBOX_ADV_FAST_DURATION_MS 10000 // 快速广播持续时间:10秒
2.2.2 广播状态机
c
typedef enum {
BOX_ADV_RUNNING_NONE = 0, // 未运行
BOX_ADV_RUNNING_FAST = 1, // 快速广播中
BOX_ADV_RUNNING_SLOW = 2, // 慢速广播中
} box_adv_running_e;
2.2.3 低功耗优化策略
快慢广播切换逻辑:
启动阶段(0-10秒)
↓
快速广播(30ms间隔) ──── 提高发现概率
↓ (10秒后)
慢速广播(480ms间隔) ──── 降低功耗
↓
已连接/放入盒中
↓
停止广播 ──────────── 最低功耗
代码实现片段 (bes_ui_ble_adv.c:251-273):
c
void ui_ble_chargebox_start_fast_adv_10s(void)
{
// 检查连接状态,已连接则停止广播
if (ble_is_box_connected() || ui_is_box_get_ble_connected()) {
ble_adv_enable(BLE_TYPE_CHARGEBOX, false);
return;
}
// 检查初始化状态
if (g_box_adv_state.adv_initialized == false) {
g_box_adv_state.need_delay_adv = true;
return;
}
// 设置为快速广播状态
g_box_adv_state.adv_running = BOX_ADV_RUNNING_FAST;
// 设置快速广播间隔
ui_ble_chargebox_set_interval(CHARGEBOX_ADV_FAST_INTERVAL_MS);
// 启动10秒定时器,到期后切换到慢速广播
os_timer_start(g_chargebox_adv_slow_timer, CHARGEBOX_ADV_FAST_DURATION_MS);
// 使能广播
ble_adv_enable(BLE_TYPE_CHARGEBOX, true);
}
慢速广播切换回调 (bes_ui_ble_adv.c:233-249):
c
static void chargebox_adv_slow_timer_cb(const void *param)
{
// 10秒后被调用,切换到慢速广播
if (ble_is_box_connected() || ui_is_box_get_ble_connected()) {
ble_adv_enable(BLE_TYPE_CHARGEBOX, false);
return;
}
g_box_adv_state.adv_running = BOX_ADV_RUNNING_SLOW;
// 设置慢速广播间隔(480ms)
ui_ble_chargebox_set_interval(CHARGEBOX_ADV_SLOW_INTERVAL_MS);
// 刷新广播
ble_adv_enable(BLE_TYPE_CHARGEBOX, true);
}
2.2.4 广播数据构造
充电盒广播数据格式:
┌─────┬──────┬─────────────────────────────────┐
│ Len │ Type │ Value │
├─────┼──────┼─────────────────────────────────┤
│ 02 │ 01 │ 06 (LE General Discoverable) │
│ 07 │ FF │ MAC Address (6 bytes) │
└─────┴──────┴─────────────────────────────────┘
代码实现 (bes_ui_ble_adv.c:170-193):
c
static void ui_ble_adv_chargebox_build_and_write(void)
{
uint8_t useradv[ADV_MAX_LENGTH + 1] = { 0 };
uint8_t data_size = 0;
// Flags字段
useradv[data_size++] = 0x02; // Length
useradv[data_size++] = 0x01; // Type: Flags
useradv[data_size++] = LE_GENERAL_DISCOVERABLE_MODE | BR_EDR_NOT_SUPPORTED;
// Manufacturer Specific Data
useradv[data_size++] = 0x07; // Length
useradv[data_size++] = 0xFF; // Type: Manufacturer Specific Data
// 填充MAC地址(反序)
for (int i = 0; i < 6; i++) {
useradv[data_size++] = 0xFF; // 实际为MAC地址
}
// 获取盒子MAC地址
uint8_t box_mac[6];
for(int i=0; i<6; i++){
box_mac[i] = ui_get_para()->box_info.box_mac[5-i];
}
// 写入广播数据
ble_adv_write_data(BLE_TYPE_CHARGEBOX, box_mac, NULL, 0, NULL, 0);
}
2.3 广播控制HCI命令
启动广播 (HCI: 0x2039 - LE Set Extended Advertising Enable):
日志示例:
[TX]: 01 39 20 06 01 01 08 00 00 00
解析:
01 - HCI Command Packet
39 20 - Opcode (0x2039)
06 - Parameter Length
01 - Enable (0x01=Enable, 0x00=Disable)
01 - Number of sets
08 - Advertising Handle
00 00 00 - Duration, Max Events
设置广播参数 (HCI: 0x2036 - LE Set Extended Advertising Parameters):
c
// app_ble.c 中的广播参数设置逻辑
gap_adv_param_t adv_param = {
.own_addr_type = own_addr_type, // 本地地址类型
.connectable = true, // 可连接
.scannable = false, // 不可扫描
.adv_interval_min_slots = interval_min, // 最小广播间隔
.adv_interval_max_slots = interval_max, // 最大广播间隔
.peer_type = peer_addr_type, // 对端地址类型
.peer_addr = peer_bdaddr, // 对端地址
};
2.3 HCI广播指令深度解析: 01 36 20
2.3.1 HCI指令格式
HCI Command: 01 36 20 = LE Set Extended Advertising Parameters V1
这是BLE 5.0引入的扩展广播参数设置指令,用于配置扩展广播的各项参数。
完整HCI指令格式:
HCI Command Packet Structure:
01 36 20 LL [Parameters...]
│ │ │ │ └─ 参数字段 (25+ bytes)
│ │ │ └──── Parameter Length
│ │ └─────── Opcode LSB (0x36)
│ └────────── Opcode MSB (0x20)
└───────────── HCI Command Packet Type (0x01)
Opcode分解:
0x2036 = OGF(0x08) << 10 | OCF(0x36)
│ │ └─ OCF (Opcode Command Field) = 0x36
│ └───────────────────── OGF (Opcode Group Field) = 0x08 (LE Controller Commands)
完整指令示例 (设置30ms间隔广播):
01 36 20 19 08 13 00 30 00 30 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ └─ Primary_Advertising_Interval_Min/Max (3 bytes each)
│ │ │ │ │ │ │ └──────────── Advertising_Event_Properties (2 bytes)
│ │ │ │ │ └─────────────────── Advertising_Handle (1 byte)
│ │ │ │ └────────────────────── Parameter Length = 0x19 (25 bytes)
│ │ │ └───────────────────────── Opcode = 0x2036
│ └──────────────────────────────── HCI Command Packet
└─────────────────────────────────── Packet Type
2.3.2 参数字段详解
参数结构 (hci_i.h:1770, BLE Core Spec 5.0 Vol 4 Part E 7.8.53):
c
typedef struct {
uint8_t advertising_handle; // [0] 广播句柄 (0x00-0xEF)
uint16_t advertising_event_properties; // [1-2] 广播事件属性 (bitmap)
uint8_t primary_adv_interval_min[3]; // [3-5] 最小广播间隔 (0.625ms单位)
uint8_t primary_adv_interval_max[3]; // [6-8] 最大广播间隔 (0.625ms单位)
uint8_t primary_adv_channel_map; // [9] 主广播信道映射 (bit0=Ch37, bit1=Ch38, bit2=Ch39)
uint8_t own_address_type; // [10] 本地地址类型
uint8_t peer_address_type; // [11] 对端地址类型
uint8_t peer_address[6]; // [12-17] 对端蓝牙地址
uint8_t advertising_filter_policy; // [18] 广播过滤策略
int8_t advertising_tx_power; // [19] 广播发射功率 (dBm, 0x7F=不关心)
uint8_t primary_adv_phy; // [20] 主广播PHY (0x01=1M, 0x03=Coded)
uint8_t secondary_adv_max_skip; // [21] 次广播最大跳过数
uint8_t secondary_adv_phy; // [22] 次广播PHY
uint8_t advertising_sid; // [23] 广播集ID (0x00-0x0F)
uint8_t scan_request_notification; // [24] 扫描请求通知使能
} __attribute__((packed)) hci_le_set_ext_adv_params_v1_t;
关键参数解析:
- Advertising_Event_Properties (广播事件属性, 2 bytes):
c
Bitmap定义 (hci_i.h:1772-1775):
┌─────────────┬──────┬─────────────────────────────┐
│ Bit │ 值 │ 说明 │
├─────────────┼──────┼─────────────────────────────┤
│ Bit 0 │ 0x01 │ Connectable advertising │
│ Bit 1 │ 0x02 │ Scannable advertising │
│ Bit 2 │ 0x04 │ Directed advertising │
│ Bit 3 │ 0x08 │ High Duty Cycle Directed │
│ Bit 4 │ 0x10 │ Use legacy PDUs │
│ Bit 5 │ 0x20 │ Anonymous advertising │
│ Bit 6 │ 0x40 │ Include TxPower │
│ Bit 7-15 │ │ Reserved (RFU) │
└─────────────┴──────┴─────────────────────────────┘
Legacy ADV类型映射:
0x0013 (0001 0011b) = Connectable + Scannable + Legacy = ADV_IND
0x0010 (0001 0000b) = Legacy only = ADV_NONCONN_IND
0x0015 (0001 0101b) = Connectable + Directed + Legacy = ADV_DIRECT_IND
- Primary_Advertising_Interval (广播间隔, 3 bytes, Little-Endian):
c
// 间隔范围: 0x000020 - 0xFFFFFF (单位: 0.625ms)
// 有效范围: 20ms (0x000020) - 10485.759375s (0xFFFFFF)
示例1: 30ms广播间隔
30ms / 0.625ms = 48 (0x0030)
HCI字节序 (小端): 30 00 00
示例2: 480ms广播间隔 (慢速广播)
480ms / 0.625ms = 768 (0x0300)
HCI字节序 (小端): 00 03 00
代码转换:
uint32_t interval_ms = 30; // 30ms
uint32_t interval_slots = interval_ms * 1000 / 625; // = 48 slots
uint8_t interval_bytes[3];
interval_bytes[0] = (interval_slots >> 0) & 0xFF; // 0x30
interval_bytes[1] = (interval_slots >> 8) & 0xFF; // 0x00
interval_bytes[2] = (interval_slots >> 16) & 0xFF; // 0x00
- Primary_Advertising_Channel_Map (广播信道, 1 byte):
c
BLE广播使用3个固定信道:
┌──────┬─────────┬────────────┐
│ Bit │ 信道 │ 频率(MHz) │
├──────┼─────────┼────────────┤
│ Bit0 │ Ch 37 │ 2402 │
│ Bit1 │ Ch 38 │ 2426 │
│ Bit2 │ Ch 39 │ 2480 │
└──────┴─────────┴────────────┘
典型值:
0x07 (0000 0111b) = 使用全部3个信道 (推荐)
0x03 (0000 0011b) = 仅使用Ch37 + Ch38
0x01 (0000 0001b) = 仅使用Ch37 (不推荐,降低发现概率)
2.3.3 实际日志解析示例
场景1: 设置快速广播参数 (30ms间隔)
日志原始数据:
01 36 20 19 08 13 00 30 00 30 00 07 00 00 00 00 00 00 00 00 00 00 00 00 7F 01 00 01 00 00
解析:
[00] 01 = HCI Command Packet
[01-02] 36 20 = Opcode 0x2036 (LE_SET_EXT_ADV_PARAMS_V1)
[03] 19 = Parameter Length (25 bytes)
[04] 08 = Advertising_Handle (句柄8)
[05-06] 13 00 = Event_Properties (0x0013 = Connectable + Scannable + Legacy)
[07-09] 30 00 00 = Adv_Interval_Min (0x000030 = 48 × 0.625ms = 30ms)
[10-12] 30 00 00 = Adv_Interval_Max (0x000030 = 48 × 0.625ms = 30ms)
[13] 07 = Channel_Map (Ch37 + Ch38 + Ch39)
[14] 00 = Own_Address_Type (Public)
[15] 00 = Peer_Address_Type (Public)
[16-21] 00...00 = Peer_Address (全0 = 未指定)
[22] 00 = Filter_Policy (接受所有)
[23] 7F = Tx_Power (127 = Host不关心,由Controller决定)
[24] 01 = Primary_PHY (1M PHY)
[25] 00 = Secondary_Max_Skip (0)
[26] 01 = Secondary_PHY (1M PHY)
[27] 00 = Advertising_SID (0)
[28] 00 = Scan_Request_Notification (禁用)
对应代码 (bes_ui_ble_adv.c:201-209):
adv_param.interval_min = CHARGEBOX_ADV_FAST_INTERVAL_MS; // 30ms
adv_param.interval_max = CHARGEBOX_ADV_FAST_INTERVAL_MS; // 30ms
ble_adv_set_properties(BLE_TYPE_CHARGEBOX, &adv_param);
场景2: 设置慢速广播参数 (480ms间隔)
日志原始数据:
01 36 20 19 08 13 00 00 03 00 00 03 00 07 00 00 00 00 00 00 00 00 00 00 7F 01 00 01 00 00
解析:
[07-09] 00 03 00 = Adv_Interval_Min (0x000300 = 768 × 0.625ms = 480ms)
[10-12] 00 03 00 = Adv_Interval_Max (0x000300 = 768 × 0.625ms = 480ms)
对应代码 (bes_ui_ble_adv.c:246):
ui_ble_chargebox_set_interval(CHARGEBOX_ADV_SLOW_INTERVAL_MS); // 480ms
2.3.4 HCI响应事件
Command Complete Event:
HCI Event Packet:
04 0E 05 01 36 20 00 XX
│ │ │ │ │ │ │ └─ Selected_Tx_Power (Controller选择的发射功率, dBm)
│ │ │ │ │ │ └──── Status (0x00 = Success)
│ │ │ │ └──────────── Opcode (0x2036)
│ │ │ └───────────────── Num_HCI_Command_Packets (可发送的命令数)
│ │ └──────────────────── Parameter Length (5 bytes)
│ └─────────────────────── Event Code (0x0E = Command Complete)
└────────────────────────── HCI Event Packet
成功示例:
04 0E 05 01 36 20 00 08
表示: 命令执行成功,Controller选择的发射功率为+8dBm
2.3.5 与Legacy广播指令对比
Legacy广播指令 (BLE 4.x): 0x2006 - LE Set Advertising Parameters
c
// 主要区别:
┌──────────────────────┬─────────────┬──────────────────┐
│ 特性 │ Legacy │ Extended │
├──────────────────────┼─────────────┼──────────────────┤
│ Opcode │ 0x2006 │ 0x2036 │
│ 参数长度 │ 15 bytes │ 25 bytes │
│ 最大广播数据 │ 31 bytes │ 255 bytes (分段) │
│ 广播间隔 │ 2 bytes │ 3 bytes │
│ 支持PHY │ 1M only │ 1M/2M/Coded │
│ 广播集ID │ 无 │ 支持 (SID) │
│ 定向广播地址 │ 必须 │ 可选 │
│ 发射功率控制 │ 无 │ 支持 │
└──────────────────────┴─────────────┴──────────────────┘
向后兼容性:
- 设置Event_Properties中的"Use legacy PDUs"位 (bit 4)
- Legacy模式下,广播包格式与BLE 4.x兼容
- 本系统使用0x0013 = Legacy模式,保证与旧设备兼容
2.3.6 常见问题与调试技巧
问题1: 广播间隔不生效
原因: 间隔值超出范围或字节序错误
检查:
1. 确保间隔值在0x000020-0xFFFFFF范围内
2. 确认使用小端序 (Little-Endian)
3. 验证min <= max
正确: 30ms → 48 slots → 0x30 00 00
错误: 30ms → 48 slots → 0x00 00 30 (大端序)
问题2: 设备无法发现
原因: Channel_Map设置不当
检查:
1. 确保至少使用2个信道 (建议0x07 = 全部3个)
2. 检查信道是否与环境冲突 (WiFi 2.4GHz)
3. 验证Event_Properties包含Connectable或Scannable
推荐配置:
- Channel_Map = 0x07 (Ch37+38+39)
- Event_Properties = 0x0013 (ADV_IND)
- Interval = 30-100ms (快速发现) 或 100-1000ms (省电)
问题3: HCI指令返回错误码
常见错误码:
0x12 = Invalid HCI Command Parameters (参数非法)
→ 检查参数长度、范围、格式
0x0C = Command Disallowed (命令不允许)
→ 检查广播状态,确保先禁用广播再修改参数
0x01 = Unknown HCI Command (指令不支持)
→ Controller不支持Extended Advertising,使用Legacy指令
2.8 广播控制API
系统提供了完整的广播生命周期管理API,包括启动、停止、刷新和参数设置。
2.8.1 广播启动与停止
启动广播:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:599-610
/**
* @brief Start connectable adv with adv interval
* @param[in] advInterval Adv interval (ms)
*/
void bes_ble_gap_start_connectable_adv(uint16_t advInterval);
/**
* @brief Refresh start all it's flag is enabled adv
* 刷新并启动所有已启用标志的广播
*/
void bes_ble_gap_start_adv(void);
停止广播:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:580-584
/**
* @brief Stop all adv
* 停止所有广播活动
*/
void bes_ble_gap_stop_adv_all(void);
停止指定广播:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:688-693
/**
* @brief Stop custom adv
* @param[in] actv_user Actv user (BLE_ADV_ACTIVITY_USER_E)
*/
void bes_ble_gap_custom_adv_stop(BLE_ADV_ACTIVITY_USER_E actv_user);
强制切换广播状态:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:572-578
/**
* @brief Set force adv enable or not
* @param[in] user Set user (BLE_ADV_SWITCH_USER_E)
* @param[in] isToEnableAdv Enable or not
*/
void bes_ble_gap_force_switch_adv(enum BLE_ADV_SWITCH_USER_E user, bool isToEnableAdv);
2.8.2 广播刷新机制
刷新广播状态:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:803-806
/**
* @brief Refresh all enabled adv
* @param[in] advInterval Advertising interval (ms)
*
* 功能说明:
* - 停止当前所有广播
* - 更新广播参数
* - 重新启动已启用的广播
*/
void bes_ble_gap_refresh_adv_state(uint16_t advInterval);
检查广播状态:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:796-801
/**
* @brief If any adv started
* @return true/false
*/
bool bes_ble_gap_is_in_advertising_state(void);
应用层刷新接口:
c
/// bthost/service/ble_app_new/inc/app_ble.h:470
/**
* @brief Refresh all adv state (app layer wrapper)
* 刷新所有广播状态
*/
void app_ble_refresh_adv_state_generic(void);
使用示例:
c
// 场景1: 更新广播间隔并刷新
void update_adv_interval(uint16_t new_interval_ms)
{
// 检查当前是否有广播正在运行
if (bes_ble_gap_is_in_advertising_state()) {
// 停止并使用新间隔重启广播
bes_ble_gap_refresh_adv_state(new_interval_ms);
}
}
// 场景2: 从快速广播切换到慢速广播
void switch_to_slow_adv(void)
{
const uint16_t SLOW_ADV_INTERVAL = 480; // 480ms
bes_ble_gap_refresh_adv_state(SLOW_ADV_INTERVAL);
}
// 场景3: 动态启用/禁用广播
void control_advertising(bool enable)
{
if (enable) {
bes_ble_gap_start_adv(); // 启动所有已配置的广播
} else {
bes_ble_gap_stop_adv_all(); // 停止所有广播
}
}
2.8.3 广播参数设置
设置广播间隔:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:780-787
/**
* @brief Set adv interval
* @param[in] adv_intv_user 广播间隔请求用户 (BLE_ADV_INTERVALREQ_USER_E)
* @param[in] adv_user 广播用户 (BLE_ADV_USER_E)
* @param[in] interval Adv interval (ms)
*/
void bes_ble_gap_param_set_adv_interval(BLE_ADV_INTERVALREQ_USER_E adv_intv_user,
BLE_ADV_USER_E adv_user,
uint32_t interval);
设置广播发射功率:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:789-794
/**
* @brief Set all adv tx pwr
* @param[in] txpwr_dbm Tx pwr in dbm
*/
void bes_ble_set_all_adv_txpwr(int8_t txpwr_dbm);
设置广播参数结构:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:651-656
/**
* @brief Set adv param with user
* @param[in] param Adv param (BLE_ADV_PARAM_T)
*/
void bes_ble_gap_set_adv_param(BLE_ADV_PARAM_T *param, uint8_t user);
2.9 白名单与过滤策略
白名单机制允许设备仅与特定的已知设备通信,提高安全性和连接效率。
2.9.1 白名单API
设置白名单:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:612-619
/**
* @brief Set white list
* @param[in] user List user (BLE_WHITE_LIST_USER_E)
* @param[in] bdaddr Addr list (ble_bdaddr_t array)
* @param[in] size Addr list size
*
* 功能说明:
* - 最多支持8个设备地址
* - 可以包含Public和Random地址
* - 与Filter Policy配合使用
*/
void bes_ble_gap_set_white_list(BLE_WHITE_LIST_USER_E user,
const ble_bdaddr_t *bdaddr,
uint8_t size);
移除白名单:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:621-626
/**
* @brief Remove white list by user
* @param[in] user White list user (BLE_WHITE_LIST_USER_E)
*/
void bes_ble_gap_remove_white_list_user_item(BLE_WHITE_LIST_USER_E user);
清除白名单:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:869-873
/**
* @brief Clear white list for user BLE_WHITE_LIST_USER_MOBILE
* 清除移动设备的白名单
*/
void bes_ble_gap_clear_white_list_for_mobile(void);
2.9.2 广播过滤策略
广播过滤策略定义了设备如何响应扫描和连接请求。
过滤策略枚举:
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:249-261
enum bes_adv_filter_policy
{
/// Allow both scan and connection requests from anyone
/// 允许任何设备的扫描和连接请求
BES_ADV_ALLOW_SCAN_ANY_CON_ANY = 0x00,
/// Allow both scan req from White List devices only and connection req from anyone
/// 仅允许白名单设备扫描,但允许任何设备连接
BES_ADV_ALLOW_SCAN_WLST_CON_ANY,
/// Allow both scan req from anyone and connection req from White List devices only
/// 允许任何设备扫描,但仅允许白名单设备连接
BES_ADV_ALLOW_SCAN_ANY_CON_WLST,
/// Allow scan and connection requests from White List devices only
/// 仅允许白名单设备扫描和连接
BES_ADV_ALLOW_SCAN_WLST_CON_WLST,
};
使用示例:
c
// 示例1: 设置白名单并使用过滤策略
void setup_whitelist_advertising(void)
{
// 1. 构建白名单地址列表
ble_bdaddr_t whitelist[2];
// 设备1: Public地址
whitelist[0].addr_type = BES_ADDR_PUBLIC;
memcpy(whitelist[0].addr, "\x11\x22\x33\x44\x55\x66", 6);
// 设备2: Random地址
whitelist[1].addr_type = BES_ADDR_RAND;
memcpy(whitelist[1].addr, "\xAA\xBB\xCC\xDD\xEE\xFF", 6);
// 2. 设置白名单
bes_ble_gap_set_white_list(BLE_WHITE_LIST_USER_MOBILE, whitelist, 2);
// 3. 配置广播参数使用白名单
BLE_ADV_PARAM_T adv_param;
adv_param.adv_filter_policy = BES_ADV_ALLOW_SCAN_WLST_CON_WLST;
bes_ble_gap_set_adv_param(&adv_param, BLE_ADV_USER_0);
// 4. 启动广播
bes_ble_gap_start_adv();
}
// 示例2: 清除白名单
void clear_whitelist(void)
{
bes_ble_gap_remove_white_list_user_item(BLE_WHITE_LIST_USER_MOBILE);
}
2.9.3 扫描过滤策略
扫描时也可以使用过滤策略。
扫描过滤策略:
c
/// bthost/adapter/inc/adapter_service/gap_service.h:625-640
enum gap_scanning_filter_policy
{
/// Basic unfiltered scanning - 接受所有广播
GAP_SCAN_FILTER_POLICY_BASIC_UNFILTERED = 0b0000,
/// Basic filtered scanning - 仅接受白名单设备的广播
GAP_SCAN_FILTER_POLICY_BASIC_FILTERED = 0b0001,
/// Extended unfiltered scanning - 接受所有广播和定向广播
GAP_SCAN_FILTER_POLICY_EXT_UNFILTERED = 0b0010,
/// Extended filtered scanning - 仅接受白名单设备
GAP_SCAN_FILTER_POLICY_EXT_FILTERED = 0b0011,
};
2.9.4 白名单最佳实践
| 应用场景 | 推荐策略 | 原因 |
|---|---|---|
| 初次配对 | BES_ADV_ALLOW_SCAN_ANY_CON_ANY | 允许任何设备发现和连接 |
| 已配对设备重连 | BES_ADV_ALLOW_SCAN_ANY_CON_WLST | 允许扫描但仅允许已知设备连接 |
| 高安全场景 | BES_ADV_ALLOW_SCAN_WLST_CON_WLST | 仅允许白名单设备 |
| 降低功耗 | 使用白名单 + Resolving List | 减少不必要的连接请求处理 |
2.10 连接发现性模式
发现性模式控制设备是否可以被其他设备发现。
广播类型
c
/// bthost/adapter/inc/ble/gap/bes_gap_api.h:263-276
enum
{
/// Connectable Undirected advertising - 可连接非定向广播
BES_ADV_CONN_UNDIR = 0x00,
/// Connectable high duty cycle directed advertising - 可连接高占空比定向广播
BES_ADV_CONN_DIR,
/// Discoverable undirected advertising - 可发现非定向广播
BES_ADV_DISC_UNDIR,
/// Non-connectable undirected advertising - 不可连接非定向广播
BES_ADV_NONCONN_UNDIR,
/// Connectable low duty cycle directed advertising - 可连接低占空比定向广播
BES_ADV_CONN_DIR_LDC,
};
广播类型说明:
| 类型 | 可连接 | 可扫描 | 占空比 | 应用场景 |
|---|---|---|---|---|
| CONN_UNDIR | ✓ | ✓ | - | 通用外设广播 |
| CONN_DIR | ✓ | ✗ | 高(3.75ms) | 快速重连,仅对特定设备 |
| DISC_UNDIR | ✗ | ✓ | - | 信标广播 |
| NONCONN_UNDIR | ✗ | ✗ | - | 单向数据广播 |
| CONN_DIR_LDC | ✓ | ✗ | 低(≥1s) | 低功耗定向重连 |
3. 连接建立流程
3.1 BLE连接状态机
┌──────────┐
│ IDLE │ 初始状态
└────┬─────┘
│ 开始广播
↓
┌──────────────┐
│ ADVERTISING │ 广播中
└────┬─────────┘
│ 收到连接请求
↓
┌──────────────┐
│ CONNECTING │ 连接建立中
└────┬─────────┘
│ 连接成功
↓
┌──────────────┐
│ CONNECTED │ 已连接
└────┬─────────┘
│ MTU交换
↓
┌──────────────┐
│ MTU_EXCHANGED│ MTU已交换
└────┬─────────┘
│ 服务发现
↓
┌──────────────┐
│ ENCRYPTED │ 已加密(可选)
└────┬─────────┘
│ 数据传输就绪
↓
┌──────────────┐
│ READY │ 就绪状态
└──────────────┘
3.2 连接建立过程
3.2.1 HCI层面的连接事件
LE Meta Event (0x3E) - Connection Complete:
事件格式:
04 3E 13 01 00 XX XX XX XX XX XX XX XX ...
│ │ │ │ │ └─ 连接参数
│ │ │ │ └─── Status (0x00 = Success)
│ │ │ └────── Subevent Code (0x01 = Connection Complete)
│ │ └───────── Parameter Length
│ └──────────── Event Code (0x3E = LE Meta Event)
└─────────────── HCI Event Packet
3.2.2 连接参数
c
// BLE连接参数配置 (app_ble.c:855-882)
typedef struct {
BLE_CONN_PARAM_MODE_E mode; // 连接模式
uint8_t priority; // 优先级
uint16_t conn_interval_min; // 最小连接间隔(1.25ms单位)
uint16_t conn_interval_max; // 最大连接间隔
uint16_t conn_slave_latency_cnt; // 从设备延迟
} BLE_CONN_PARAM_CONFIG_T;
// 连接参数模式定义
static const BLE_CONN_PARAM_CONFIG_T ble_conn_param_config[] = {
// 默认模式: BT空闲状态
{BLE_CONN_PARAM_MODE_DEFAULT, PRIORITY_NORMAL, 36, 36, 0},
// AI流传输模式
{BLE_CONN_PARAM_MODE_AI_STREAM_ON, PRIORITY_ABOVE_NORMAL1, 16, 16, 0},
// A2DP音乐播放模式
{BLE_CONN_PARAM_MODE_A2DP_ON, PRIORITY_ABOVE_NORMAL0, 48, 48, 0},
// HFP通话模式
{BLE_CONN_PARAM_MODE_HFP_ON, PRIORITY_ABOVE_NORMAL2, 48, 48, 0},
// OTA升级模式
{BLE_CONN_PARAM_MODE_OTA, PRIORITY_HIGH, 12, 12, 0},
// 空闲省电模式
{BLE_CONN_PARAM_MODE_IDLE, PRIORITY_NORMAL, 400, 400, 0},
};
连接间隔说明:
- 1单位 = 1.25ms
- 36单位 = 45ms (默认)
- 12单位 = 15ms (OTA高速)
- 400单位 = 500ms (空闲省电)
3.3 连接建立代码流程
连接打开事件处理 (app_ble.c:1370-1389):
c
static void app_ble_connection_opened(gap_conn_item_t *p_conn)
{
ble_global_t *g = ble_get_global();
// 加载GATT服务端缓存
app_ble_check_load_server_cache(p_conn);
// 加载GATT客户端缓存
app_ble_check_load_client_cache(p_conn);
// 重置连接参数模式
app_ble_reset_conn_param_mode(p_conn->con_idx);
// 重置MTU大小为最小值
g->curr_mtu_size[gap_zero_based_conidx(p_conn->con_idx)] = L2CAP_LE_MIN_MTU;
// 如果连接数达到最大值,取消所有待发起的连接
if (app_ble_connection_count() + 1 >= BLE_CONNECTION_MAX) {
gap_cancel_all_pend_initiating(app_is_arrive_at_max_ble_connections());
}
}
连接事件回调 (app_ble.c:1435-1459):
c
case GAP_CONN_EVENT_OPENED:
{
gap_conn_param_t *opened = param.opened;
const bt_bdaddr_t *la = &opened->conn->own_addr;
const bt_bdaddr_t *pa = &opened->peer_addr;
// 打印连接日志
CO_LOG_INFO("CNNS: idx=%d role=%d hdl=%04x",
opened->con_idx,
opened->conn->conn_flag.is_central ? 0 : 1,
opened->connhdl);
// 调用连接打开处理
app_ble_connection_opened(opened->conn);
// 通知应用层
cb_event.evt_type = BLE_LINK_CONNECTED_EVENT;
cb_event.p.connect_handled.conidx = gap_zero_based_conidx(opened->con_idx);
cb_event.p.connect_handled.connhdl = opened->connhdl;
cb_event.p.connect_handled.peer_bdaddr.addr_type = opened->peer_type & 0x01;
memcpy(cb_event.p.connect_handled.peer_bdaddr.addr,
opened->peer_addr.address,
sizeof(bt_bdaddr_t));
app_ble_global_handle(&cb_event, NULL);
// 刷新广播状态
app_ble_refresh_adv_state_generic();
break;
}
3.4 连接参数更新请求机制
BLE连接建立后,Peripheral(从设备/耳机)可以主动发起连接参数更新请求,以适应不同的应用场景需求。
3.4.1 连接参数更新协议
L2CAP信令层提供了两种连接参数更新方式:
-
L2CAP Connection Parameter Update Request (BLE 4.0+)
- Peripheral通过L2CAP信令通道(CID=0x0005)发送更新请求
- Central可以接受或拒绝该请求
-
LL Connection Update Procedure (BLE 4.1+)
- 通过Link Layer控制PDU进行参数协商
- 双方都可以发起更新
L2CAP连接参数更新请求格式:
c
// L2CAP Connection Parameter Update Request (l2cap_i.h:510-513)
typedef struct {
uint16_t conn_interval_min_1_25ms; // 最小连接间隔 (1.25ms单位)
uint16_t conn_interval_max_1_25ms; // 最大连接间隔
uint16_t max_peripheral_latency; // 从设备延迟 (跳过的连接事件数)
uint16_t superv_timeout_ms; // 监督超时 (10ms单位)
} gap_conn_prefer_params_t;
// Peripheral主动发起连接参数更新
void gap_recv_l2cap_conn_param_update_req(uint16_t connhdl,
uint8_t trans_id,
const gap_conn_prefer_params_t *params);
// Central响应连接参数更新请求
bt_status_t l2cap_accept_le_conn_parameters(uint16_t connhdl,
uint8_t trans_id,
bool accept,
bool send_cmd_rej);
3.4.2 连接参数更新触发场景
耳机会根据不同的应用场景主动请求切换连接参数:
场景切换触发表 (app_ble.c:855-882):
| 模式 | 连接间隔 | 优先级 | 触发场景 |
|---|---|---|---|
DEFAULT |
45ms (36×1.25ms) | NORMAL | BT空闲状态,初始连接 |
AI_STREAM_ON |
20ms (16×1.25ms) | ABOVE_NORMAL1 | AI语音助手激活 |
A2DP_ON |
60ms (48×1.25ms) | ABOVE_NORMAL0 | 音乐播放开始 |
HFP_ON |
60ms (48×1.25ms) | ABOVE_NORMAL2 | 通话开始(SCO激活) |
OTA |
15ms (12×1.25ms) | HIGH | 固件升级 |
IDLE |
500ms (400×1.25ms) | NORMAL | 长时间空闲省电 |
连接参数更新代码实现 (app_ble.c:914-1014):
c
void app_ble_update_conn_param_mode_of_specific_connection(
uint8_t con_idx,
BLE_CONN_PARAM_MODE_E mode,
bool enable)
{
uint8_t conidx = gap_zero_based_conidx(con_idx);
const BLE_CONN_PARAM_CONFIG_T *pConfig = NULL;
// 根据模式查找配置
for (uint8_t index = 0; index < BLE_CONN_PARAM_MODE_NUM; index++) {
if (mode == ble_conn_param_config[index].ble_conn_param_mode) {
pConfig = &ble_conn_param_config[index];
break;
}
}
if (enable) {
// 启用新模式,设置对应bit
existingBleConnParamModes[conidx] |= (1 << mode);
// 检查是否有更高优先级的模式正在运行
for (uint8_t index = 0; index < BLE_CONN_PARAM_MODE_NUM; index++) {
if ((1 << ble_conn_param_config[index].ble_conn_param_mode) &
existingBleConnParamModes[conidx]) {
if (ble_conn_param_config[index].priority > pConfig->priority) {
// 已有更高优先级模式,不更新参数
return;
}
}
}
} else {
// 禁用模式,清除对应bit
existingBleConnParamModes[conidx] &= ~(1 << mode);
// 查找剩余模式中优先级最高的
const BLE_CONN_PARAM_CONFIG_T *pHighestConfig = NULL;
for (uint8_t index = 0; index < BLE_CONN_PARAM_MODE_NUM; index++) {
if ((1 << ble_conn_param_config[index].ble_conn_param_mode) &
existingBleConnParamModes[conidx]) {
if (pHighestConfig == NULL ||
ble_conn_param_config[index].priority > pHighestConfig->priority) {
pHighestConfig = &ble_conn_param_config[index];
}
}
}
pConfig = pHighestConfig;
}
// 执行参数更新
if (pConfig != NULL) {
gap_update_params_t params;
params.conn_interval_min_1_25ms = pConfig->conn_interval_min;
params.conn_interval_max_1_25ms = pConfig->conn_interval_max;
params.max_peripheral_latency = pConfig->conn_slave_latency_cnt;
// 发起连接参数更新请求
gap_update_le_conn_parameters(gap_zero_based_ble_conidx_as_hdl(conidx), ¶ms);
}
}
3.4.3 防止BT/BLE时间片冲突的保护机制
关键防护逻辑 (app_ble.c:1074-1099):
c
static void app_ble_conn_param_update_req(gap_conn_update_req_t *update_req)
{
bool accept = true;
uint16_t conn_interval_min_1_25ms = update_req->params_req.conn_interval_min_1_25ms;
uint32_t conn_interval_min_us = conn_interval_min_1_25ms * 1250;
// ⚠️ 关键保护: 防止BLE连接间隔过短导致与BT Classic冲突
// 如果连接间隔小于15ms,会导致BLE频繁占用时间片,
// 影响A2DP/SCO等BR/EDR链路的稳定性
if (conn_interval_min_us < 15000) { // 小于15ms
// 延迟10秒后重新评估并更新参数
fp_update_ble_connect_param_start(gap_zero_based_conidx(update_req->con_idx));
// 先接受当前请求,避免连接异常
accept = true;
}
// 响应连接参数更新请求
gap_update_le_conn_parameters_rsp(update_req->con_idx,
&update_req->params_req,
accept);
}
// 延迟更新定时器回调
static void fp_update_ble_connect_param_timer_handler(void const *param)
{
if (delay_update_conidx != GAP_INVALID_CONIDX) {
// 检查是否有SCO通话正在进行
if (amgr_is_bluetooth_sco_on()) {
// SCO激活时,强制使用HFP_ON模式(60ms间隔)
app_ble_update_conn_param_mode_of_specific_connection(
delay_update_conidx, BLE_CONN_PARAM_MODE_HFP_ON, true);
} else {
// SCO未激活,恢复到默认模式(45ms间隔)
app_ble_update_conn_param_mode_of_specific_connection(
delay_update_conidx, BLE_CONN_PARAM_MODE_DEFAULT, true);
}
delay_update_conidx = GAP_INVALID_CONIDX;
}
}
防冲突机制总结:
- 连接间隔下限保护: 拒绝小于15ms的BLE连接间隔请求
- SCO自动避让: 检测到SCO激活时,自动延长BLE连接间隔至60ms
- 优先级仲裁: SCO (实时语音) > A2DP (音频流) > BLE (数据传输)
- 延迟评估: 使用10秒定时器延迟参数更新,避免频繁切换
3.4.4 HCI层连接参数更新事件
LE Connection Update Complete Event (HCI Event 0x3E Subevent 0x03):
事件格式:
04 3E 0A 03 00 XX XX YY YY ZZ ZZ WW WW
│ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ └─ Supervision Timeout (10ms单位)
│ │ │ │ │ │ │ │ │ │ └──────── Peripheral Latency
│ │ │ │ │ │ │ │ └───────────── Connection Interval (1.25ms单位)
│ │ │ │ │ │ └──────────────────── Connection Handle
│ │ │ │ │ └───────────────────────── Status (0x00=成功)
│ │ │ │ └──────────────────────────── Subevent Code (0x03)
│ │ │ └─────────────────────────────── Parameter Length
│ │ └────────────────────────────────── Event Code (0x3E=LE Meta Event)
│ └───────────────────────────────────── HCI Event Packet
└──────────────────────────────────────── Packet Type
示例:
04 3E 0A 03 00 40 00 30 00 00 00 C8 00
表示:
- Connection Handle: 0x0040
- Interval: 0x0030 (48 × 1.25ms = 60ms)
- Latency: 0x0000 (0个连接事件)
- Timeout: 0x00C8 (200 × 10ms = 2000ms)
日志示例 (R_COM4_time_data@01-20_15-49-45.log):
# 连接建立,初始参数: 45ms间隔
[BLE] Connection Complete: interval=36 (45ms)
# 音乐开始播放,切换到A2DP_ON模式
[BLE] Update conn param: mode=A2DP_ON, interval=48 (60ms)
# 接听电话,SCO激活,切换到HFP_ON模式
[SCO] SCO connected, codec=mSBC
[BLE] Update conn param: mode=HFP_ON, interval=48 (60ms), priority=HIGH
# 通话结束,SCO断开,恢复到A2DP_ON模式
[SCO] SCO disconnected
[BLE] Update conn param: mode=A2DP_ON, interval=48 (60ms)
4. 数据传输机制
4.1 GATT协议概述
GATT (Generic Attribute Profile) 是BLE数据传输的核心协议,定义了服务和特征值的层次结构。
4.1.1 GATT协议栈
┌─────────────────────────────────┐
│ 应用层 (Application) │
├─────────────────────────────────┤
│ GATT (服务发现与数据交互) │
├─────────────────────────────────┤
│ ATT (属性协议) │
├─────────────────────────────────┤
│ L2CAP (逻辑链路控制) │
├─────────────────────────────────┤
│ HCI/LL (链路层) │
└─────────────────────────────────┘
4.1.2 GATT over BR/EDR
GATT over BR/EDR 允许在经典蓝牙(BR/EDR)连接上使用BLE的GATT服务,实现双模设备的无缝数据传输。
特性:
- 使用L2CAP信道承载ATT协议
- 固定L2CAP PSM = 0x001F (ATT)
- MTU协商机制与BLE相同
- 支持所有GATT服务和特征值
启用条件:
c
/// bthost/adapter/inc/adapter_service/gatt_service.h
// 定义了GATT over BR/EDR相关功能
// 需要编译时启用 BLE_GATT_OVER_BR/EDR 宏
应用场景:
| 场景 | 说明 |
|---|---|
| 双模设备数据同步 | BR/EDR连接传输大文件,BLE连接传输控制命令 |
| 音频设备配置 | 使用BR/EDR传输音频,通过GATT传输配置参数 |
| 向后兼容 | 支持仅有BR/EDR的老设备访问GATT服务 |
连接建立流程:
BR/EDR连接建立
↓
L2CAP连接到PSM 0x001F
↓
ATT层初始化
↓
MTU交换
↓
GATT服务可用
4.2 GATT服务架构
GATT定义了BLE数据传输的服务和特征值结构:
Service (服务)
└── Characteristic (特征值)
├── Value (值)
├── Descriptor (描述符)
│ ├── CCCD (客户端特征配置描述符)
│ └── CUDD (用户自定义描述符)
└── Properties (属性)
├── Read
├── Write
├── Notify
└── Indicate
4.2 数据路径服务定义
本系统实现了自定义的Datapath Service用于双向数据传输:
4.2.1 服务UUID定义
c
// ble_datapath_server.c:34-36
// 128-bit Service UUID
#define datapath_service_uuid_128_le \
0x12,0x34,0x56,0x78,0x90,0x00,0x00,0x80, \
0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x01
// TX Characteristic UUID (用于发送通知)
#define datapath_tx_character_uuid_128_le \
0x12,0x34,0x56,0x78,0x91,0x00,0x00,0x80, \
0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x02
// RX Characteristic UUID (用于接收写入)
#define datapath_rx_character_uuid_128_le \
0x12,0x34,0x56,0x78,0x92,0x00,0x00,0x80, \
0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x03
4.2.2 GATT服务声明
c
// ble_datapath_server.c:42-79
GATT_DECL_128_LE_PRI_SERVICE(g_ble_datapath_service,
datapath_service_uuid_128_le);
// RX特征值 - 接收数据(支持写入)
GATT_DECL_128_LE_CHAR(g_ble_datapath_rx_character,
datapath_rx_character_uuid_128_le,
GATT_WR_REQ|GATT_WR_CMD|GATT_RD_REQ, // 支持读写
ATT_SEC_NONE);
GATT_DECL_CUDD_DESCRIPTOR(g_ble_datapath_rx_cudd,
ATT_SEC_NONE);
// TX特征值 - 发送数据(支持通知)
GATT_DECL_128_LE_CHAR(g_ble_datapath_tx_character,
datapath_tx_character_uuid_128_le,
GATT_NTF_PROP, // 支持通知
ATT_SEC_NONE);
GATT_DECL_CCCD_DESCRIPTOR(g_ble_datapath_tx_cccd,
ATT_WR_ENC); // CCCD需要加密写入
GATT_DECL_CUDD_DESCRIPTOR(g_ble_datapath_tx_cudd,
ATT_SEC_NONE);
4.3 MTU (Maximum Transmission Unit) 交换
MTU决定了单次传输的最大数据量:
c
// 默认最小MTU
#define L2CAP_LE_MIN_MTU 23
// MTU交换事件处理 (app_ble.c:1523-1542)
case GAP_CONN_EVENT_MTU_EXCHANGED:
{
gap_conn_mtu_exchanged_t *p = param.mtu_exchanged;
CO_LOG_INFO("MTU Exchanged: conidx=%d mtu=%d",
p->con_idx, p->mtu);
// 更新MTU大小
g->curr_mtu_size[gap_zero_based_conidx(p->con_idx)] = p->mtu;
// 通知应用层
cb_event.evt_type = BLE_MTU_EXECHANGE_EVENT;
cb_event.p.mtu_exec_handled.conidx = gap_zero_based_conidx(p->con_idx);
cb_event.p.mtu_exec_handled.mtu = p->mtu;
app_ble_global_handle(&cb_event, NULL);
break;
}
MTU对数据传输的影响:
- MTU = 23: 每次最多传输 20字节 (23 - 3字节ATT头)
- MTU = 247: 每次最多传输 244字节
- MTU = 517: 每次最多传输 514字节 (最大值)
4.4 数据接收流程
4.4.1 GATT写入事件处理
c
// ble_datapath_server.c:404-417
case GATT_SERV_EVENT_CHAR_WRITE:
{
gatt_server_char_write_t *p = param.char_write;
// 验证写入参数
if (p->value_offset != 0 || p->value_len == 0 || p->value == NULL) {
return false;
}
// 调用数据接收处理
app_datapath_server_rx_data_received(svc->con_idx, svc->connhdl,
p->value, p->value_len);
// 异步发送写入响应
bt_thread_call_func_3(gatts_send_defer_write_rsp,
bt_fixed_param(svc->connhdl),
bt_fixed_param(p->ctx->token),
bt_fixed_param(0));
return true;
}
4.4.2 数据接收回调
c
// ble_datapath_server.c:353-383
static void app_datapath_server_rx_data_received(uint8_t con_idx,
uint16_t connhdl,
const uint8_t *data,
uint16_t len)
{
uint8_t conidx = gap_zero_based_conidx(con_idx);
TRACE("RX data length %d", len);
// 分配连接信息
app_datapath_server_alloc_con_info_with_conidx(conidx);
// 触发数据接收事件
if (dp_event_callback) {
app_dp_rec_data_msg_t data_msg;
data_msg.data = (uint8_t *)data;
data_msg.data_len = len;
data_msg.conidx = conidx;
dp_event_callback(DP_DATA_RECEIVED, (ble_if_app_dp_param_u *)&data_msg);
}
}
4.5 数据发送流程
4.5.1 通知(Notification)发送
c
// ble_datapath_server.c:225-234
void app_datapath_server_send_data_via_notification(uint8_t conidx,
uint8_t* data,
uint32_t len)
{
// 检查通知是否已使能
if (app_datapath_server_get_tx_ntf_en_by_conidx(conidx)) {
gatts_send_notification(gap_conn_bf(conidx),
g_ble_datapath_tx_character,
data,
(uint16_t)len);
}
}
4.5.2 指示(Indication)发送
c
// ble_datapath_server.c:236-245
void app_datapath_server_send_data_via_indication(uint8_t conidx,
uint8_t* data,
uint32_t len)
{
if (app_datapath_server_get_tx_ntf_en_by_conidx(conidx)) {
gatts_send_indication(gap_conn_bf(conidx),
g_ble_datapath_tx_character,
data,
(uint16_t)len);
}
}
Notification vs Indication:
| 特性 | Notification | Indication |
|---|---|---|
| 应答 | 无需应答 | 需要客户端确认 |
| 可靠性 | 不保证到达 | 保证到达 |
| 速度 | 快 | 慢 |
| 适用场景 | 传感器数据、音频流 | 重要控制命令 |
4.6 CCCD (Client Characteristic Configuration Descriptor)
CCCD用于客户端使能/禁用通知或指示:
c
// ble_datapath_server.c:438-453
case GATT_SERV_EVENT_DESC_WRITE:
{
gatt_server_desc_write_t *p = param.desc_write;
uint16_t config = CO_COMBINE_UINT16_LE(p->value);
bool notify_enabled = false;
// 检查是否使能通知
if (config & GATT_CCCD_SET_NOTIFICATION) {
notify_enabled = true;
}
// 更新CCC状态
app_datapath_server_tx_ccc_changed(svc->con_idx,
svc->connhdl,
notify_enabled);
// 发送写入响应
bt_thread_call_func_3(gatts_send_defer_write_rsp,
bt_fixed_param(svc->connhdl),
bt_fixed_param(p->ctx->token),
bt_fixed_param(0));
return true;
}
CCCD值定义:
0x0000: 禁用通知和指示0x0001: 使能通知0x0002: 使能指示0x0003: 使能通知和指示
5. 断连处理与恢复
5.1 断连原因分类
BLE断连原因定义在HCI规范中:
| 错误码 | 宏定义 | 说明 |
|---|---|---|
| 0x08 | CONNECTION_TIMEOUT | 连接超时 |
| 0x13 | REMOTE_USER_TERMINATED | 远端用户终止 |
| 0x16 | LOCAL_HOST_TERMINATED | 本地主机终止 |
| 0x3D | MIC_FAILURE | MIC校验失败 |
5.2 断连事件处理
5.2.1 连接关闭事件
c
// app_ble.c:1481-1501
case GAP_CONN_EVENT_CLOSED:
{
gap_conn_param_t *closed = param.closed;
CO_LOG_INFO("Connection Closed: idx=%d hdl=%04x reason=%02x",
closed->con_idx,
closed->connhdl,
closed->error_code);
// 重置连接参数
app_ble_reset_conn_param_mode(closed->con_idx);
// 通知应用层
cb_event.evt_type = BLE_DISCONNECT_EVENT;
cb_event.p.disconnect_handled.errCode = closed->error_code;
cb_event.p.disconnect_handled.conidx = gap_zero_based_conidx(closed->con_idx);
cb_event.p.disconnect_handled.connhdl = closed->connhdl;
cb_event.p.disconnect_handled.peer_bdaddr.addr_type = closed->peer_type & 0x01;
memcpy(cb_event.p.disconnect_handled.peer_bdaddr.addr,
closed->peer_addr.address,
sizeof(bt_bdaddr_t));
app_ble_global_handle(&cb_event, NULL);
break;
}
5.2.2 Datapath断连处理
c
// ble_datapath_server.c:300-324
static void app_datapath_server_disconnected(uint8_t con_idx, uint16_t connhdl)
{
uint8_t conidx = gap_zero_based_conidx(con_idx);
// 释放连接信息
if (app_datapath_server_free_con_info_by_conidx(conidx) == BT_STS_SUCCESS) {
TRACE("Datapath server disconnected");
tx_done_callback = NULL;
} else {
return;
}
// 调用断连回调
if (NULL != disconnected_done_callback) {
disconnected_done_callback(conidx);
}
// 触发断连事件
if (dp_event_callback) {
dp_event_callback(DP_DISCONN_DONE, (ble_if_app_dp_param_u *)&conidx);
}
}
5.2.3 应用层断连处理
c
// bes_ble.c:140-210 (平台接口层)
static void datapath_event_cb_box(DP_EVENT_TYPE_E event_type,
ble_if_app_dp_param_u *para_p)
{
ble_handle_t* ble_handle = &local_ble_callback[BLE_TYPE_CHARGEBOX];
switch (event_type) {
case DP_CONN_DONE: {
TRACE("DP_CONN_DONE: conidx=0x%x handle=%d",
para_p->connect_index,
gap_zero_based_ble_conidx_as_hdl(para_p->connect_index));
ble_handle->conidx = para_p->connect_index;
ble_handle->connected = true;
ble_handle->callback(BLE_EVENT_CONNECTED, NULL);
break;
}
case DP_DISCONN_DONE:
TRACE("DP_DISCONN_DONE");
if (ble_handle != NULL) {
ble_handle->connected = false;
ble_handle->callback(BLE_EVENT_DISCONNECTED, NULL);
}
ble_handle->conidx = 0xFF;
break;
// ... 其他事件处理
}
}
5.3 主动断连接口
c
// bes_ble.c:48-65
void ble_disconnect_box_conn(void)
{
ble_handle_t* ble_box_handle = &local_ble_callback[BLE_TYPE_CHARGEBOX];
bt_status_t ble_status = BT_STS_SUCCESS;
// 检查句柄有效性
if (ble_box_handle == NULL || ble_box_handle->is_initialized == false) {
TRACE("ble_box_handle is NULL");
return;
}
// 如果连接有效,则终止连接
if (ble_box_handle->conidx != 0xFF) {
ble_status = gap_terminate_connection(ble_box_handle->conidx, 0);
}
TRACE("Disconnect: conidx=%d status=%d",
ble_box_handle->conidx, ble_status);
}
5.4 断连后广播恢复
c
// bes_ui_ble_adv.c:299-313
void ui_ble_chargebox_refresh_adv(void)
{
LOG("Refresh ADV called");
// 提升CPU频率以快速处理
ui_freq_boost(BOOST_FREQ_208M, 5000);
// 检查是否需要停止广播
if (bt_tws_get_current_role() == BT_TWS_SLAVE_ROLE ||
ui_is_box_get_ble_connected() ||
ui_get_para()->ui_state == UI_STATE_IN_CASE_CLOSE ||
ble_is_box_connected()) {
// 从耳、已连接、已入盒时停止广播
ui_ble_chargebox_stop_adv();
} else {
// 否则启动快速广播
ui_ble_chargebox_start_fast_adv_10s();
}
}
6. 状态机设计
6.1 广播状态机
c
typedef struct {
bool adv_initialized; // 广播是否已初始化
box_adv_running_e adv_running; // 广播运行状态
bool ble_connected; // BLE连接状态
bool need_delay_adv; // 是否需要延迟广播
bool is_factory_reset; // 是否恢复出厂设置
uint8_t ble_mac[6]; // 缓存的BLE MAC地址
} box_adv_state_t;
状态转换图:
┌──────────────────────┐
│ ADV_RUNNING_NONE │
│ (未运行/已停止) │
└──────┬───────────────┘
│
│ start_fast_adv_10s()
↓
┌──────────────────────┐
│ ADV_RUNNING_FAST │
│ (快速广播30ms) │
└──────┬───────────────┘
│
│ 10秒定时器到期
↓
┌──────────────────────┐
│ ADV_RUNNING_SLOW │
│ (慢速广播480ms) │
└──────┬───────────────┘
│
│ 连接建立 / 入盒 / 恢复出厂
↓
┌──────────────────────┐
│ ADV_RUNNING_NONE │
│ (停止广播) │
└──────────────────────┘
6.2 连接状态机
c
typedef struct {
bes_ble_gatt_callback_t callback; // 回调函数
bes_cb_pri_e priority; // 回调优先级
bes_ble_type_t type; // BLE类型
uint8_t conidx; // 连接索引
bool connected; // 连接状态
bool is_initialized; // 初始化状态
} local_cb_info_t;
状态转换:
初始化
↓
┌────────────────┐
│ DISCONNECTED │ connected = false, conidx = 0xFF
└────────┬───────┘
│ GAP_CONN_EVENT_OPENED
↓
┌────────────────┐
│ CONNECTED │ connected = true, conidx = valid
└────────┬───────┘
│
│ ← MTU_EXCHANGED
│ ← CCC_CHANGED
│ ← DATA_RECEIVED/TX_DONE
│
│ GAP_CONN_EVENT_CLOSED
↓
┌────────────────┐
│ DISCONNECTED │ connected = false, conidx = 0xFF
└────────────────┘
6.3 连接参数动态调整
c
// app_ble.c:914-1014
void app_ble_update_conn_param_mode_of_specific_connection(uint8_t con_idx,
BLE_CONN_PARAM_MODE_E mode,
bool enable)
{
gap_update_params_t params = {0};
const BLE_CONN_PARAM_CONFIG_T *pConfig = &ble_conn_param_config[mode];
uint8_t conidx = gap_zero_based_conidx(con_idx);
if (conidx > BLE_CONNECTION_MAX) {
return;
}
CO_LOG_INFO("Update conn param: conidx=%d mode=%d enable=%d",
conidx, mode, enable);
if (enable) {
// 添加模式
if (existingBleConnParamModes[conidx] & (1 << mode)) {
return; // 已存在
}
existingBleConnParamModes[conidx] |= (1 << mode);
// 检查是否有更高优先级的模式
for (index = 0; index < ARRAY_SIZE(ble_conn_param_config); index++) {
if (((uint32_t)1 << ble_conn_param_config[index].ble_conn_param_mode) &
existingBleConnParamModes[conidx]) {
if (ble_conn_param_config[index].priority > pConfig->priority) {
return; // 有更高优先级,不更新
}
}
}
} else {
// 移除模式
if (!(existingBleConnParamModes[conidx] & (1 << mode))) {
return; // 不存在
}
existingBleConnParamModes[conidx] &= (~(1 << mode));
if (0 == existingBleConnParamModes[conidx]) {
// 恢复默认参数
pConfig = &ble_conn_param_config[0];
goto label_update;
}
// 找到剩余模式中优先级最高的
pConfig = NULL;
for (index = 0; index < ARRAY_SIZE(ble_conn_param_config); index++) {
if ((( uint32_t )1 << ( uint8_t )ble_conn_param_config[index].ble_conn_param_mode) &
existingBleConnParamModes[conidx]) {
if (NULL != pConfig) {
if (ble_conn_param_config[index].priority > pConfig->priority) {
pConfig = &ble_conn_param_config[index];
}
} else {
pConfig = &ble_conn_param_config[index];
}
}
}
}
label_update:
// 更新连接参数
params.conn_interval_min_1_25ms = pConfig->conn_interval_min;
params.conn_interval_max_1_25ms = pConfig->conn_interval_max;
params.max_peripheral_latency = pConfig->conn_slave_latency_cnt;
gap_update_le_conn_parameters(gap_zero_based_ble_conidx_as_hdl(conidx),
¶ms);
}
参数优先级:
PRIORITY_HIGH (OTA模式)
↑
PRIORITY_ABOVE_NORMAL2 (HFP/服务发现)
↑
PRIORITY_ABOVE_NORMAL1 (AI流)
↑
PRIORITY_ABOVE_NORMAL0 (A2DP)
↑
PRIORITY_NORMAL (默认/空闲)
7. 代码实现分析
7.1 代码架构层次
┌─────────────────────────────────────────┐
│ 应用层 (App Layer) │
│ - UI业务逻辑 │
│ - 充电盒通信 │
│ - 状态管理 │
└──────────────┬──────────────────────────┘
│
┌──────────────┴──────────────────────────┐
│ 平台接口层 (Platform Layer) │
│ bes_ble.c - BLE平台抽象接口 │
│ bes_ble_adv.c - 广播管理 │
└──────────────┬──────────────────────────┘
│
┌──────────────┴──────────────────────────┐
│ BLE核心层 (BLE Core Layer) │
│ app_ble.c - BLE核心管理 │
│ ble_datapath_server.c - GATT服务 │
└──────────────┬──────────────────────────┘
│
┌──────────────┴──────────────────────────┐
│ 协议栈层 (Stack Layer) │
│ GAP Service - 通用访问协议 │
│ GATT Service - 通用属性协议 │
│ L2CAP - 逻辑链路控制 │
│ HCI - 主机控制接口 │
└─────────────────────────────────────────┘
7.2 关键数据结构
7.2.1 全局BLE状态
c
// app_ble.c:104
static ble_global_t g_ble_global;
typedef struct {
APP_BLE_CORE_EVENT_CALLBACK ble_core_evt_cb; // 核心事件回调
APP_BLE_CORE_GLOBAL_HANDLER_FUNC ble_global_handler[BLE_MAX_CORE_EVT_CB]; // 全局处理器数组
set_rsp_dist_lk_bit_field_func dist_lk_set_cb; // 分发密钥回调
smp_identify_addr_exch_complete ble_smp_info_derived_from_bredr_complete; // SMP信息派生完成
uint8_t (*ble_resolving_list_fill_cb)(void); // 解析列表填充回调
void (*ble_smp_require_modify)(uint16_t, ble_smp_require_t *); // SMP需求修改
void (*ble_get_specific_irk_ia)(uint16_t, uint8_t **, bt_bdaddr_t **); // 获取特定IRK/IA
void (*ble_add_record_modify)(uint16_t, BleDevicePairingInfo *); // 添加记录修改
bool (*ble_get_specific_record)(uint16_t, const ble_bdaddr_t *, BleDevicePairingInfo *); // 获取特定记录
void (*ble_get_specific_hash)(uint16_t, uint8_t **); // 获取特定哈希
uint16_t curr_mtu_size[BLE_CONNECTION_MAX]; // 当前MTU大小
ble_smp_require_t *p_smp_req[BLE_CONNECTION_MAX]; // SMP需求指针
uint8_t local_database_hash[16]; // 本地数据库哈希
uint8_t default_tx_pref_phy_bits; // 默认TX首选PHY位
uint8_t default_rx_pref_phy_bits; // 默认RX首选PHY位
char peer_dev_name[BLE_CONNECTION_MAX][BLE_DEV_NAME_LEN]; // 对端设备名称
bool adv_force_disabled; // 广播强制禁用
} ble_global_t;
7.2.2 Datapath连接信息
c
// ble_datapath_server.c:123
struct app_datapath_server_env_tag {
struct {
uint8_t connectionIndex; // 连接索引
bool isNotificationEnabled; // 通知是否使能
uint32_t recv_cmd_latest; // 最新接收的命令
} con_info[BLE_CONNECTION_MAX];
} app_datapath_server_env;
7.3 MAC地址缓存优化
为避免频繁访问Flash,系统实现了MAC地址缓存:
c
// bes_ui_ble_adv.c:320-359
int adv_init(void)
{
g_box_adv_state.adv_initialized = false;
g_box_adv_state.ble_connected = false;
g_box_adv_state.adv_running = BOX_ADV_RUNNING_NONE;
// 一次性获取并缓存BLE MAC地址
bt_addr_t local_addr;
bt_get_local_mac(&local_addr);
memcpy(g_box_adv_state.ble_mac, local_addr.address, 6);
LOG("Cached MAC: %02X:%02X:%02X:%02X:%02X:%02X",
g_box_adv_state.ble_mac[5], g_box_adv_state.ble_mac[4],
g_box_adv_state.ble_mac[3], g_box_adv_state.ble_mac[2],
g_box_adv_state.ble_mac[1], g_box_adv_state.ble_mac[0]);
ble_adv_init();
// 创建慢速广播定时器
if (g_chargebox_adv_slow_timer == NULL) {
g_chargebox_adv_slow_timer = os_create_timer(chargebox_adv_slow_timer_cb,
os_timer_once);
}
// 配置充电盒广播
ui_ble_adv_chargebox();
g_box_adv_state.adv_initialized = true;
// 默认不启动广播,由应用层控制
ble_adv_enable(BLE_TYPE_CHARGEBOX, false);
return 0;
}
7.4 线程安全与异步调用
系统大量使用线程调用机制确保回调在正确的线程上下文执行:
c
// ble_datapath_server.c:413-415
// 异步发送写入响应
bt_thread_call_func_3(gatts_send_defer_write_rsp,
bt_fixed_param(svc->connhdl),
bt_fixed_param(p->ctx->token),
bt_fixed_param(0));
// app_ble.c:1203-1205
// 异步回复LTK请求
bt_thread_call_func_3(gap_reply_peer_ltk_request,
bt_fixed_param(conn->connhdl),
bt_fixed_param(positive_reply == false),
bt_alloc_param_size(ltk, GAP_KEY_LEN));
8. BLE协议层交互与时序详解
本章详细描述BLE各个关键流程在物理层、链路层和协议层之间的交互,基于Bluetooth Core Specification v5.3最新协议规范。
8.0 BLE协议栈层次交互总览
8.0.1 协议栈层次结构
┌──────────────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ (用户应用逻辑、业务处理) │
├──────────────────────────────────────────────────────────────┤
│ GAP & GATT 层 │
│ GAP: 设备发现、连接管理、安全 │
│ GATT: 服务发现、数据读写、特征值操作 │
├──────────────────────────────────────────────────────────────┤
│ L2CAP (逻辑链路控制) │
│ - 数据分段和重组 │
│ - 协议复用 (ATT, SMP, Signaling) │
│ - 流量控制 │
├──────────────────────────────────────────────────────────────┤
│ HCI (Host Controller Interface) │
│ - 命令接口 (Host → Controller) │
│ - 事件接口 (Controller → Host) │
│ - 数据接口 (双向) │
├══════════════════════════════════════════════════════════════┤
│ 链路层 (Link Layer - LL) │
│ - 状态机管理 (Standby/Advertising/Scanning/Initiating) │
│ - 连接管理 │
│ - 数据包格式化 │
│ - 加密 │
│ - 流量控制 │
├──────────────────────────────────────────────────────────────┤
│ 物理层 (Physical Layer - PHY) │
│ - RF射频收发 │
│ - 调制/解调 (GFSK) │
│ - 频率跳变 │
│ - 功率控制 │
└──────────────────────────────────────────────────────────────┘
2.4GHz ISM频段 (2400-2483.5 MHz)
40个RF通道 (3个广播通道 + 37个数据通道)
8.0.2 各层职责详解
| 层次 | 主要职责 | 关键PDU/消息 |
|---|---|---|
| 应用层 | 业务逻辑、用户交互 | 应用数据 |
| GATT | 属性协议、服务/特征值 | ATT PDU (Read/Write/Notify/Indicate) |
| GAP | 设备角色、连接管理 | 广播数据、连接参数 |
| L2CAP | 数据复用、分段重组 | L2CAP PDU |
| HCI | Host-Controller通信 | HCI Command/Event/Data |
| 链路层 | PDU收发、状态管理 | LL PDU (ADV/SCAN/CONN) |
| 物理层 | RF信号收发 | 无线电波 |
8.1 BLE广播流程详细时序图
8.1.1 广播启动完整时序 (基于Extended Advertising)
应用层 GAP HCI 链路层 物理层
| | | | |
|--开始广播-->| | | |
| | | | |
| |--设置广播参数----------->| |
| | HCI_LE_Set_Extended_ | |
| | Advertising_Parameters | |
| | | | |
| | |<--响应------| |
| | | Command | |
| | | Complete | |
| | | | |
| |--设置广播数据----------->| |
| | HCI_LE_Set_Extended_ | |
| | Advertising_Data | |
| | | | |
| | |<--响应------| |
| | | | |
| |--设置扫描响应数据------->| |
| | HCI_LE_Set_Extended_ | |
| | Scan_Response_Data | |
| | | | |
| | |<--响应------| |
| | | | |
| |--启动广播--------------->| |
| | HCI_LE_Set_Extended_ | |
| | Advertising_Enable | |
| | | | |
| | | |--进入广播状态-->|
| | | | |
| | | |--配置RF通道-->|
| | | | (Ch37,38,39)|
| | | | |
| | |<--响应------| |
| | | Command | |
| | | Complete | |
| | | | |
| |<-广播启动事件-----------| |
| | HCI_LE_Extended_ | |
| | Advertising_Set_ | |
| | Terminated (if needed) | |
|<--广播启动--| | | |
| 回调 | | | |
| | | | |
| | | |==周期性广播==>|
| | | | Adv Interval|
| | | | Ch37: ADV_PDU-->发射
| | | | ↓ (跳转) |
| | | | Ch38: ADV_PDU-->发射
| | | | ↓ (跳转) |
| | | | Ch39: ADV_PDU-->发射
| | | | ↓ |
| | | | (等待Adv Interval)
| | | | ↓ |
| | | | (重复) |
8.1.2 广播数据包在各层的封装
应用层数据: "MyDevice" + Service UUID + Manufacturer Data
↓ (GAP层封装)
广播数据结构 (AD Structure):
┌────────────────────────────────────────────────┐
│ Length │ Type │ Data │
├────────┼──────┼────────────────────────────────┤
│ 0x02 │ 0x01 │ 0x06 (Flags) │
│ 0x09 │ 0x09 │ "MyDevice" (Complete Name) │
│ 0x03 │ 0x03 │ 0x0D 0x18 (16-bit UUID) │
│ 0x08 │ 0xFF │ Company ID + Data │
└────────┴──────┴────────────────────────────────┘
↓ (HCI层传输)
HCI Command: HCI_LE_Set_Extended_Advertising_Data
┌────────────────────────────────────────────────┐
│ Opcode │ Length │ Handle │ Operation │ Data │
│ 0x2037 │ N │ 0x01 │ 0x03 │ ... │
└────────────────────────────────────────────────┘
↓ (链路层格式化)
LL ADV_PDU:
┌────────────────────────────────────────────────────────┐
│ Preamble │ Access Addr │ PDU Header │ Payload │ CRC │
│ 1 byte │ 4 bytes │ 2 bytes │ 6-255B │ 3B │
└────────────────────────────────────────────────────────┘
PDU Header:
- PDU Type: ADV_IND (0b0000) / ADV_EXT_IND (0b0111)
- TxAdd: Address Type
- RxAdd: Reserved
- Length: Payload Length
Payload:
- AdvA (6 bytes): Advertiser Address
- AdvData: Advertising Data
↓ (物理层调制)
RF信号 (GFSK调制):
- 频率: 2402 MHz (Ch37) / 2426 MHz (Ch38) / 2480 MHz (Ch39)
- 符号率: 1 Msym/s
- 调制指数: 0.5
- 发射功率: -20 dBm ~ +10 dBm
8.1.3 Legacy vs Extended Advertising对比
| 特性 | Legacy Advertising | Extended Advertising |
|---|---|---|
| 最大广播数据 | 31 bytes | 255 bytes (可分片至1650 bytes) |
| 广播PHY | 1M PHY only | 1M, 2M, Coded PHY |
| 广播集数量 | 1 | 最多16个同时广播 |
| 周期性广播 | 不支持 | 支持 Periodic Advertising |
| 功率控制 | 固定 | 支持动态调整 |
| HCI命令 | HCI_LE_Set_Advertising_* | HCI_LE_Set_Extended_Advertising_* |
8.2 BLE扫描流程详细时序图
8.2.1 扫描启动完整时序
应用层 GAP HCI 链路层 物理层
| | | | |
|--开始扫描-->| | | |
| | | | |
| |--设置扫描参数----------->| |
| | HCI_LE_Set_Extended_ | |
| | Scan_Parameters | |
| | - Scan Type (主动/被动)| |
| | - Scan Interval | |
| | - Scan Window | |
| | - Filter Policy | |
| | | | |
| | |<--响应------| |
| | | Command | |
| | | Complete | |
| | | | |
| |--启动扫描--------------->| |
| | HCI_LE_Set_Extended_ | |
| | Scan_Enable | |
| | | | |
| | | |--进入扫描状态-->|
| | | | |
| | | |--配置RF接收-->|
| | | | Ch37,38,39 |
| | | | |
| | |<--响应------| |
| | | | |
| | | |==扫描循环==>|
| | | | |
| | | | Ch37: 监听<--接收
| | | | ↓ |
| | | | 收到ADV_PDU|
| | | | ↓ |
| | | |<--解析PDU---|
| | | | (检查CRC) |
| | | | ↓ |
| | |<--广播报告--| (过滤) |
| | | HCI_LE_ | |
| | | Extended_ | |
| | | Advertising| |
| | | _Report | |
| | | | |
| |<-解析广播数据----------| |
| | - RSSI | | |
| | - Address| | |
| | - AD Data| | |
| | | | |
|<--广播回调--| | | |
| 上报 | | | |
| | | | |
| (如果是主动扫描) | | |
| | | | |
| | | |--发送SCAN_REQ-->发射
| | | | (150μs后) |
| | | | |
| | | |<--SCAN_RSP--接收
| | | | (广告商响应)|
| | | | ↓ |
| | |<--扫描响应--| |
| | | 报告 | |
| | | | |
| |<-扫描响应回调----------| |
|<--响应数据--| | | |
| | | | |
8.2.2 主动扫描vs被动扫描
被动扫描 (Passive Scan):
Scanner Advertiser
| |
| [监听广播通道] |
|<--------ADV_IND------------|
| (接收广播数据) |
| |
| [解析并上报] |
| |
| [继续监听下一个通道] |
主动扫描 (Active Scan):
Scanner Advertiser
| |
| [监听广播通道] |
|<--------ADV_IND------------|
| (接收广播数据) |
| |
|--------SCAN_REQ---------->|
| (150μs内发送) |
| |
|<------SCAN_RSP------------|
| (扫描响应数据) |
| |
| [解析并上报] |
8.2.3 扫描时间参数
扫描周期示意图:
|<------- Scan Interval -------->|<------- Scan Interval -------->|
| | |
|<-- Scan Window -->| (休眠) |<-- Scan Window -->| (休眠) |
| | | | |
| [监听并接收ADV] | [省电] | [监听并接收ADV] | [省电] |
|___________________|____________|___________________|____________|
占空比 = Scan Window / Scan Interval
示例配置:
- Scan Interval = 100ms (160 * 0.625ms)
- Scan Window = 50ms (80 * 0.625ms)
- 占空比 = 50%
- 功耗适中,发现速度快
8.3 BLE连接建立流程详细时序图
8.3.1 连接建立完整时序 (Initiator发起)
Central(主机) GAP HCI 链路层 物理层 | 物理层 链路层 HCI GAP Peripheral(从机)
| | | | | | | | | | |
|--发起连接->| | | | | | | | | |
| | | | | | | | | | |
| |--设置连接参数---------->| | | | | | | |
| | HCI_LE_Extended_ | | | | | | | |
| | Create_Connection | | | | | | | |
| | - Peer Address | | | | | | | |
| | - Conn Interval | | | | | | | |
| | - Conn Latency | | | | | | | |
| | - Supervision Timeout | | | | | | | |
| | | | | | | | | | |
| | |<-响应------| | | | | | | |
| | | Command | | | | | | | |
| | | Status | | | | | | | |
| | | | | | | | | |<==广播中==|
| | | | | | | | | | ADV_IND |
| | | |--进入发起状态--> | | | | | |
| | | | | | | | | | |
| | | |==扫描目标设备==> | | | | | |
| | | | | | | | | | |
| | | |<--------ADV_IND-----------|<========ADV_IND======| | |
| | | | (收到目标广播) | | 发射| | | |
| | | | | | | | | | |
| | | |-------CONNECT_IND-------->|发射 | | | |
| | | | (150μs内) | | | | | |
| | | | | | | 接收<-CONNECT_IND-| | |
| | | | | | | | | | |
| | | |--建立连接-->| | |<--建立连接-----------| | |
| | | | 状态 | | | 状态 | | | |
| | | | | | | | | | |
| CONNECT_IND PDU内容: | | | | | | |
| - InitA (发起者地址) | | | | | | |
| - AdvA (广播者地址) | | | | | | |
| - Access Address (连接使用) | | | | | | |
| - CRC Init | | | | | | |
| - WinSize, WinOffset | | | | | | |
| - Interval, Latency, Timeout | | | | | | |
| - Channel Map (37个数据通道) | | | | | | |
| - Hop Increment (跳频增量) | | | | | | |
| | | | | | | | | | |
| | | |==连接建立==>| | |<==连接建立== | | |
| | | | transmitWindowOffset | | | |
| | | | | | | | | | |
| | | |<--首个数据包->发射 | |接收<--首个数据包-----| | |
| | | | LL_DATA | | | LL_DATA | | | |
| | | | | | | | | | |
| | |<-连接完成事件----------| | |-连接完成事件--------->| | |
| | | HCI_LE_Connection_ | | | HCI_LE_ | | | |
| | | Complete | | | Connection | | |
| | | - Handle | | | _Complete| | | |
| | | - Role (Master) | | | - Handle | | | |
| | | - Peer Address | | | - Role (Slave) | | |
| | | - Conn Interval | | | - Peer | | | |
| | | | | | | Address | | | |
| |<-连接建立回调---------| | | | |-连接建立回调------>| |
|<-连接成功--| | | | | | | | |--连接成功->|
| | | | | | | | | | |
| | | |==数据通道通信==> | |<==数据通道通信== | | |
| | | | (频率跳变) | | | (频率跳变) | | |
8.3.2 CONNECT_IND PDU详细结构
CONNECT_IND / CONNECT_REQ PDU:
┌─────────────────────────────────────────────────────────────┐
│ PDU Header (2 bytes) │
├─────────────────────────────────────────────────────────────┤
│ PDU Type: 0b0101 (CONNECT_IND) / 0b1101 (AUX_CONNECT_REQ) │
│ TxAdd: Initiator地址类型 │
│ RxAdd: Advertiser地址类型 │
│ Length: 34 bytes │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Payload (34 bytes) │
├─────────────────────────────────────────────────────────────┤
│ InitA (6 bytes): 发起者蓝牙地址 │
│ AdvA (6 bytes): 广播者蓝牙地址 │
│ LLData (22 bytes): 链路层数据 │
│ ├─ Access Address (4 bytes): 连接使用的访问地址 │
│ ├─ CRC Init (3 bytes): CRC初始值 │
│ ├─ WinSize (1 byte): 发送窗口大小 (1.25ms单位) │
│ ├─ WinOffset (2 bytes): 发送窗口偏移 (1.25ms单位) │
│ ├─ Interval (2 bytes): 连接间隔 (1.25ms单位) │
│ ├─ Latency (2 bytes): 从设备延迟 │
│ ├─ Timeout (2 bytes): 监督超时 (10ms单位) │
│ ├─ ChM (5 bytes): 通道映射 (37个数据通道) │
│ └─ Hop & SCA (1 byte): │
│ - Hop Increment (5 bits): 跳频增量 (5-16) │
│ - SCA (3 bits): 睡眠时钟精度 │
└─────────────────────────────────────────────────────────────┘
连接参数示例:
- Access Address: 0x8E89BED6 (随机生成,符合规则)
- Interval: 24 (30ms)
- Latency: 0 (无延迟)
- Timeout: 500 (5000ms)
- Channel Map: 0x1FFFFFFFFF (全部37个通道启用)
- Hop: 7 (跳频增量)
8.3.3 连接建立时间线
时间轴 (Advertiser视角):
T0: 发送ADV_IND
|
├─ T0 + 150μs (最大): 接收窗口关闭
| (Initiator必须在此之前发送CONNECT_IND)
|
├─ T0 + 150μs ~ T0 + 10ms: 可能收到CONNECT_IND
| (实际取决于Initiator处理时间)
|
T1: 收到CONNECT_IND
|
├─ 立即停止广播
|
├─ 解析连接参数
| - Access Address
| - Channel Map
| - Hop Increment
| - Timing Parameters
|
T2: T1 + transmitWindowOffset
|
├─ 打开接收窗口 (transmitWindowSize)
|
├─ 等待首个数据包
|
T3: T2 + (0 ~ transmitWindowSize)
|
├─ 收到首个LL_DATA_PDU
|
├─ 连接同步完成
|
└─ 开始正常连接事件
连接建立总时间: T3 - T0
典型值: 6ms ~ 10ms (取决于transmitWindowOffset配置)
8.4 BLE配对鉴权流程详细时序图
8.4.1 LE Legacy Pairing (传统配对)
Central(主机) GAP/SMP HCI 链路层 | 链路层 HCI GAP/SMP Peripheral(从机)
| | | | | | | | |
| | | [已建立LE连接] | | | | | |
| | | | | | | | |
|--发起配对--->| | | | | | | |
| | | | | | | | |
| |--Pairing Request---->|--L2CAP(SMP)-->|-------->|----Pairing Request---->|
| | (SMP命令) | | | | | (SMP命令) | |
| | - IO Capability | | | | - IO Capability |
| | - OOB Flag | | | | - OOB Flag | |
| | - Auth Req | | | | - Auth Req | |
| | - Max Key Size | | | | - Max Key Size |
| | - Init Key Dist | | | | - Init Key Dist |
| | - Resp Key Dist | | | | - Resp Key Dist |
| | | | | | | | |
| | | | | | | |<--检查能力-|
| | | | | | | | 决定配对方法
| | | | | | | | |
| | | | | |<--------|<----Pairing Response---|
| |<---Pairing Response--|<--L2CAP(SMP)--| | (SMP命令) | |
| | | | | | | | |
|--选择配对方法| | | | | | 配对方法--| |
| (Just Works/Passkey/OOB) | | | | 选择 | |
| | | | | | | | |
| === Phase 1: Pairing Feature Exchange (完成) === | === Phase 1: 完成 === |
| | | | | | | | |
| === Phase 2: Short Term Key (STK) Generation === | === Phase 2: STK生成 === |
| | | | | | | | |
| (如果是Passkey Entry) | | | | | |
|<--显示/输入配对码--| | | | | --显示/输入配对码--> |
| "123456" | | | | | | "123456" | |
| | | | | | | | |
| |---Pairing Confirm--->|------->|----->|-------->|---Pairing Confirm----->|
| | Mconfirm = c1(TK, Sconfirm, | | | Sconfirm | |
| | Preq, Pres...) | | | | |
| | | | | | | | |
| | | | | |<--------|<----Pairing Random-----|
| |<---Pairing Random----|<-------|------| | Srand | |
| | Srand | | | | | | |
| | | | | | | | |
|--验证Sconfirm| | | | | |--验证Mconfirm--------->|
| | | | | | | | |
| |---Pairing Random---->|------->|----->|-------->|---Pairing Random----->|
| | Mrand | | | | | Mrand | |
| | | | | | | | |
| | | | | | | |--验证Mconfirm
| | | | | | | | |
|--生成STK-----| | | | | |--生成STK---| |
| STK = s1(TK, Srand, Mrand) | | | | STK = s1(TK, Srand, Mrand)
| | | | | | | | |
| === Phase 3: Link Encryption === | === Phase 3: 链路加密 === |
| | | | | | | | |
| |--HCI_LE_Start_Encryption----->| | | | |
| | (使用STK) | | | | | |
| | | | | | | | |
| | | |--LL_ENC_REQ-->发射 | | | |
| | | | | | 接收<-LL_ENC_REQ------------| |
| | | | | | | | |
| | | | | | 发射--LL_ENC_RSP---------->| |
| | | |<--LL_ENC_RSP--|接收 | | |
| | | | | | | | |
| | | |==开始加密通信==| |==开始加密通信== | |
| | | | | | | | |
| | |<--Encryption Changed-------| |--Encryption Changed---->| |
| | | Event | | | | Event | |
| | | | | | | | |
| === Phase 4: Key Distribution === | === Phase 4: 密钥分发 === |
| | | | | | | | |
| |---Encryption Information---->|----->|-------->|---Encryption Info----->|
| | LTK, EDIV, Rand | | | | (长期密钥) | |
| | | | | | | | |
| | | | | |<--------|<----Encryption Info----|
| |<---Encryption Information----|<-----|---------| LTK, EDIV, Rand |
| | | | | | | | |
| |---Identity Information------>|----->|-------->|---Identity Info------->|
| | IRK, Address | | | | (身份密钥) | |
| | | | | | | | |
| | | | | |<--------|<----Identity Info------|
| |<---Identity Information------|<-----|---------| IRK, Address |
| | | | | | | | |
| |---Signing Information------->|----->|-------->|---Signing Info-------->|
| | CSRK (连接签名密钥)| | | | | |
| | | | | | | | |
| | | | | |<--------|<----Signing Info------|
| |<---Signing Information-------|<-----|---------| CSRK | |
| | | | | | | | |
|--保存密钥到NV| | | | | |--保存密钥到NV-------->|
| | | | | | | | |
| | | | | | | | |
|<--配对完成---| | | | | |--配对完成--| |
| | | | | | | | |
8.4.2 LE Secure Connections (安全连接配对 - BLE 4.2+)
Central GAP/SMP 链路层 | 链路层 GAP/SMP Peripheral
| | | | | | |
| | [已建立LE连接] | | | | |
| | | | | | |
|--发起配对--->| | | | | |
| | | | | | |
| |--Pairing Request-->|----->|--------->|--Pairing Request-->|
| | SecureConn=1 | | | | 检查能力 |
| | | | | | |
| | | | |<---------|<--Pairing Response-|
| |<--Pairing Response|<------|----------| SecureConn=1 |
| | | | | | |
| === Phase 1: Feature Exchange (完成) === | === Phase 1完成 === |
| | | | | | |
| === Phase 2: Public Key Exchange === | === Phase 2: 公钥交换 === |
| | | | | | |
|--生成ECDH密钥对----------| | | |--生成ECDH密钥对--->|
| (P-256椭圆曲线) | | | | (P-256) | |
| PKa (公钥64字节) | | | | PKb (公钥64字节) |
| SKa (私钥32字节) | | | | SKb (私钥32字节) |
| | | | | | |
| |--Pairing Public Key------>|-------->|--Public Key-------->|
| | PKa (64 bytes) | | | | PKa | |
| | | | | | |
| | | | |<---------|<--Pairing Public Key|
| |<--Pairing Public Key------|----------| PKb (64 bytes) |
| | PKb | | | | |
| | | | | | |
|--验证公钥-----| | | | |--验证公钥--| |
| (检查点是否在曲线上) | | | | |
| | | | | | |
|--计算DHKey----| | | | |--计算DHKey-| |
| DHKey = P-256(SKa, PKb) | | | | DHKey = P-256(SKb, PKa)|
| (共享密钥) | | | | | (相同) | |
| | | | | | |
| === Phase 2.1: Authentication Stage 1 (Passkey Entry示例) === |
| | | | | | |
|<--输入Passkey-| | | | |--显示Passkey---------->|
| "123456" | | | | | "123456" | |
| | | | | | |
| (将Passkey转为ra[0..19]比特) | | | (转换) | |
| | | | | | |
| For i = 0 to 19: | | | For i = 0 to 19: |
| 生成Nai, 计算Cai = f4(PKax, PKbx, Nai, ra[i]) | 计算Cbi = f4(PKbx, PKax, Nbi, ra[i])
| | | | | | 生成Nbi | |
| | | | | | |
| |--Pairing Confirm-->|----->|--------->|--Confirm--->| |
| | Cai | | | | (20轮) | |
| | | | | | |
| | | | |<---------|<--Pairing Confirm------|
| |<--Pairing Confirm-|<------|----------| Cbi | |
| | | | | | |
| |--Pairing Random--->|----->|--------->|--Random---->| |
| | Nai | | | | 验证Cbi | |
| | | | | | |
| | | | |<---------|<--Pairing Random-------|
| |<--Pairing Random--|<------|----------| Nbi | |
|--验证Cai-----| | | | | |
| | | | | | |
| === Phase 2.2: Authentication Stage 2 === | === Stage 2 === |
| | | | | | |
|--计算MacKey和LTK---------| | | |--计算MacKey和LTK------>|
| MacKey = f5(DHKey, Na, Nb, ...) | | | (相同) | |
| LTK = f5(DHKey, Na, Nb, ...) | | | |
| | | | | | |
|--计算Ea-------| | | | |--计算Eb----| |
| Ea = f6(MacKey, Na, Nb, ...) | | | Eb = f6(MacKey, Nb, Na, ...)|
| | | | | | |
| |--DHKey Check------>|----->|--------->|--DHKey Check---------->|
| | Ea | | | | 验证Ea | |
| | | | | | |
| | | | |<---------|<--DHKey Check----------|
| |<--DHKey Check-----|<------|----------| Eb | |
|--验证Eb-------| | | | | |
| | | | | | |
| === Phase 3: Link Encryption (使用LTK) === | === Phase 3 === |
| | | | | | |
| |--Start Encryption->| | | |
| | (LTK) | | | | |
| | |--LL_ENC_REQ-->|--------->|----------->| |
| | | | |<---------|<--LL_ENC_RSP-----------|
| | |<--LL_ENC_RSP--| | |
| | | | | | |
| | |==加密连接== | |==加密连接== | |
| | | | | | |
| === Phase 4: Key Distribution === | === Phase 4 === |
| | | | | | |
| | (IRK, CSRK交换,同Legacy Pairing) |
| | | | | | |
|<--配对完成----| | | | |--配对完成->| |
8.4.3 配对方法选择决策表
根据双方的IO能力(IO Capability)和认证需求,选择配对方法:
| Initiator IO | Responder IO | 配对方法 | 说明 |
|---|---|---|---|
| DisplayOnly | DisplayOnly | Just Works | 无MITM保护 |
| DisplayOnly | DisplayYesNo | Just Works | 无MITM保护 |
| DisplayOnly | KeyboardOnly | Passkey Entry | Responder输入 |
| DisplayOnly | NoInputNoOutput | Just Works | 无MITM保护 |
| DisplayOnly | KeyboardDisplay | Passkey Entry | Responder输入 |
| DisplayYesNo | DisplayOnly | Just Works | 无MITM保护 |
| DisplayYesNo | DisplayYesNo | Numeric Comparison | 双方确认 |
| DisplayYesNo | KeyboardOnly | Passkey Entry | Responder输入 |
| DisplayYesNo | NoInputNoOutput | Just Works | 无MITM保护 |
| DisplayYesNo | KeyboardDisplay | Numeric Comparison | 双方确认 |
| KeyboardOnly | DisplayOnly | Passkey Entry | Initiator输入 |
| KeyboardOnly | DisplayYesNo | Passkey Entry | Initiator输入 |
| KeyboardOnly | KeyboardOnly | Passkey Entry | 双方输入 |
| KeyboardOnly | NoInputNoOutput | Just Works | 无MITM保护 |
| KeyboardOnly | KeyboardDisplay | Passkey Entry | Initiator输入 |
| NoInputNoOutput | * | Just Works | 无MITM保护 |
| KeyboardDisplay | DisplayOnly | Passkey Entry | Initiator输入 |
| KeyboardDisplay | DisplayYesNo | Numeric Comparison | 双方确认 |
| KeyboardDisplay | KeyboardOnly | Passkey Entry | Responder输入 |
| KeyboardDisplay | NoInputNoOutput | Just Works | 无MITM保护 |
| KeyboardDisplay | KeyboardDisplay | Numeric Comparison | 双方确认 |
8.5 BLE拒绝连接流程详细时序图
8.5.1 广播端拒绝连接 (使用白名单过滤)
Initiator 链路层 物理层 | 物理层 链路层 GAP Peripheral
| | | | | | | |
| | | | |<==广播中=| | |
| | | | | ADV_IND | | |
| | | | | (设置了白名单过滤) | |
| | | | | Filter Policy = 0x03 |
| | | | | (仅白名单连接) | |
| | | | | | | |
|--发起连接---| | | | | | |
| | | | | | | |
| |--扫描目标->| | | | | |
| | | | | | | |
| |<-----ADV_IND---------|接收<|发射 | | |
| | | | | | | |
| |--CONNECT_IND------->|发射 | | | |
| | InitA=AA:BB:CC:... | | 接收<-CONNECT_IND-----------||
| | (不在白名单中) | | | | 检查白名单-|
| | | | | | InitA不在白名单 |
| | | | | | ↓ |
| | | | | | **忽略CONNECT_IND** |
| | | | | | | |
| | | | |<==继续广播== | |
| | | | | ADV_IND | | |
| | | | | | | |
|--等待连接响应------------| | | | | |
| (transmitWindow) | | | | | |
| | | | | | | |
|--超时-------| | | | | | |
| (没有收到首个数据包) | | | | | |
| | | | | | | |
|<--连接失败事件----------| | | | | |
| HCI_LE_Connection_Complete | | | | |
| Status: 0x3E (Connection Failed| | | | |
| to be Established) | | | | | |
| | | | | | | |
原因分析:
1. Peripheral设置了白名单过滤策略
2. Initiator地址不在白名单中
3. Peripheral链路层直接丢弃CONNECT_IND
4. 不会通知上层应用
5. Initiator等待transmitWindow超时后报告连接失败
8.5.2 应用层主动拒绝连接请求
Initiator 链路层 | 链路层 HCI GAP 应用层 Peripheral
| | | | | | | |
|--发起连接---| | | | | | |
| | | | | | | |
| |<====CONNECT_IND接收====| | | |
| | | | | | | |
| | | |--连接请求事件----->| | |
| | | | HCI_LE_Connection_Complete | |
| | | | Status: 0x00 (成功) | |
| | | | Handle: 0x0040 | | |
| | | | | | | |
| | | | |--连接回调---------->| |
| | | | | peer_addr | |
| | | | | role: Slave | |
| | | | | | | |
| | | | | | |--检查连接条件
| | | | | | | (黑名单/白名单)
| | | | | | | (连接数限制)
| | | | | | | (电池电量)
| | | | | | | (其他业务逻辑)
| | | | | | | |
| | | | | | |<--决策:拒绝|
| | | | | | | |
| | | | | |<--断开连接-------------|
| | | | | | Reason: 拒绝连接 |
| | | | | | | |
| | | | |<-HCI_Disconnect-----| |
| | | | | Handle: 0x0040 | |
| | | | | Reason: 0x13 | |
| | | | | (Remote User | |
| | | | | Terminated) | |
| | | | | | | |
| | | |<--LL_TERMINATE_IND-----------| |
| | | | ErrorCode: 0x13| | |
| |<====LL_TERMINATE_IND接收==| | | |
| | | | | | | |
|<--断开连接事件------| | | | | | |
| HCI_Disconnection_ | | | | | | |
| Complete | | | | | | |
| Reason: 0x13 | | | | | | |
| | | | | | | |
拒绝原因码 (BLE常用):
- 0x05: Authentication Failure
- 0x0D: Connection Rejected due to Limited Resources
- 0x0E: Connection Rejected Due to Security Reasons
- 0x0F: Connection Rejected due to Unacceptable BD_ADDR
- 0x13: Remote User Terminated Connection
- 0x16: Connection Terminated by Local Host
8.5.3 链路层拒绝连接 (无资源)
Initiator 链路层 | 链路层 HCI GAP Peripheral
| | | | | | |
| | | | [已有最大连接数] | |
| | | | | | |
|--发起连接---| | | | | |
| | | | | | |
| |<====CONNECT_IND接收====| | |
| | | | | | |
| | | |--检查连接资源------| |
| | | | 可用连接槽: 0 | |
| | | | (BLE_CONNECTION_MAX已满) |
| | | | ↓ | |
| | | | **无法接受新连接**| |
| | | | | | |
| | | |--立即发送LL_TERMINATE_IND----->|
| | | | ErrorCode: 0x0D | |
| | | | (Connection Rejected |
| | | | Limited Resources) |
| | | | | | |
| |<====LL_TERMINATE_IND接收== | |
| | | | | | |
|<--连接完成事件------| | | | | |
| HCI_LE_Connection_ | | | | | |
| Complete | | | | | |
| Status: 0x0D | | | | | |
| (Limited Resources)| | | | | |
| | | | | | |
8.6 BLE断开连接流程详细时序图
8.6.1 主动断开连接 (Host发起)
应用层 GAP HCI 链路层 物理层 | 物理层 链路层 HCI GAP 应用层
| | | | | | | | | | |
|--断开连接->| | | | | | | | | |
| (用户操作/超时/其他) | | | | | | | | |
| | | | | | | | | | |
| |--HCI_Disconnect------>| | | | | | | |
| | Handle: 0x0040 | | | | | | | |
| | Reason: 0x13 | | | | | | | |
| | (Remote User | | | | | | | |
| | Terminated) | | | | | | | |
| | | | | | | | | | |
| | |<--Command Status-----| | | | | | |
| | | Status: 0x00 | | | | | | |
| | | | | | | | | | |
| | | |--LL_TERMINATE_IND->发射 | | | | |
| | | | ErrorCode: 0x13 | | 接收<-LL_TERMINATE_IND----| |
| | | | | | | | | | |
| | | |==停止数据传输==> | |<==停止数据传输== | | |
| | | | | | | | | | |
| | | |--等待6个连接事件->| | |<-等待确认--| | | |
| | | | (确保对方收到) | | | | | | |
| | | | | | | | | | |
| | | |<--关闭连接--------| | |--关闭连接->| | | |
| | | | 释放资源 | | | 释放资源 | | | |
| | | | | | | | | | |
| | |<--Disconnection Complete-----| |--Disconnection Complete--->| |
| | | Handle: 0x0040 | | | Handle | | | |
| | | Reason: 0x13 | | | Reason | | | |
| | | | | | | | | | |
| |<--断开回调-----------| | | | |--断开回调-------->| |
|<--断开通知-| | | | | | | | |--断开通知>|
| | | | | | | | | | |
8.6.2 监督超时断开 (Supervision Timeout)
Central 链路层 HCI | HCI 链路层 Peripheral
| | | | | | |
| [正常连接通信] | | | [正常连接通信] |
| | | | | | |
|==连接事件==>| | | |<==连接事件== |
| |<------数据包--------|接收 |发射--数据包--------->|
| | | | | | |
| |-------数据包------->|发射 |接收<--数据包---------|
| | | | | | |
| ... (多个连接事件) ... | | | |
| | | | | | |
| | | | | [出现干扰/距离过远]
| | | | | | |
|==连接事件==>| | | |<==连接事件== |
| |-------数据包------->|发射 | | |
| | | | | **未收到** (丢包) |
| | | | | | |
| |<------未收到ACK-----| | | |
| | | | | | |
|==连接事件==>| | | |<==连接事件== |
| |-------数据包------->|发射 | | |
| | (重传) | | | **未收到** (丢包) |
| | | | | | |
| ... (持续丢包) ... | | | |
| | | | | | |
| |--监督定时器计数-----| | |--监督定时器计数----|
| | timeout_counter++ | | | timeout_counter++ |
| | | | | | |
| ... (继续尝试通信) ... | | | |
| | | | | | |
| |--超时检查-----------| | |--超时检查--| |
| | if (time_elapsed | | | if (time_elapsed) |
| | >= Supervision | | | >= Supervision |
| | Timeout) | | | Timeout) |
| | | | | | |
| | **超时触发** | | | **超时触发** |
| | | | | | |
| |<--关闭连接----------| | |--关闭连接->| |
| | 释放资源 | | | 释放资源 | |
| | | | | | |
| |<--Disconnection Complete-------|--Disconnection Complete----->|
| | Status: 0x00 | | | Status | |
| | Reason: 0x08 | | | Reason: 0x08 |
| | (Connection Timeout) | | (Connection Timeout) |
| | | | | | |
监督超时参数:
- 范围: 100ms ~ 32s (0x000A ~ 0x0C80 * 10ms)
- 典型值: 5s (500 * 10ms)
- 必须满足: Timeout > (1 + Latency) * Interval * 2
- 超时后双方独立关闭连接,不交换LL_TERMINATE_IND
8.6.3 链路层即时断开 (LL_TERMINATE_IND)
Device A 链路层 | 链路层 Device B
| | | | |
| [连接中] | | [连接中]
| | | | |
|--检测到错误-| | | |
| (MIC失败/ | | | |
| 版本不兼容) | | |
| | | | |
| |--发送LL_TERMINATE_IND-->|发射
| | ErrorCode: 0x3D |
| | (MIC Failure) | | 接收<-LL_TERMINATE_IND
| | | | |
| |--立即关闭连接->| |<--立即关闭连接---
| | (无需等待确认)| | (收到即关闭)
| | | | |
| |<--Disconnection Complete|--Disconnection Complete-->
| | | | |
|<--上报断开--| | | |--上报断开->
| | | | |
LL_TERMINATE_IND PDU格式:
┌────────────────────────────┐
│ LLID: 0b11 (LL Control PDU)│
│ NESN, SN, MD │
│ Length: 2 │
├────────────────────────────┤
│ Opcode: 0x02 │
│ ErrorCode: 1 byte │
└────────────────────────────┘
常见ErrorCode:
- 0x05: Authentication Failure
- 0x13: Remote User Terminated Connection
- 0x16: Connection Terminated by Local Host
- 0x3B: Unacceptable Connection Parameters
- 0x3D: Connection Terminated Due to MIC Failure
- 0x3E: Connection Failed to be Established
8.7 HCI日志分析实例
8.7.1 广播启动日志解析
从日志分析广播启动的完整流程:
时间戳: 15:49:47:495
1. 广播数据刷新触发
[I] BLE : 00ad #3335 FADV 2c244a9b
→ 准备刷新广播数据
2. 禁用旧广播
[I] BLE : 00a5 #3310 FDIS 01
→ 禁用广播句柄0x01
3. 设置广播参数
[TX]: 01 36 20 19 02 13 00 40 00 00 60 00 00 07 02 00 44 b1 49 13 e9 7c 00 fb 01 00 01 01 00
解析:
- 01: HCI Command
- 36 20: Set Extended Advertising Parameters (0x2036)
- 02: Advertising Handle
- 间隔参数: 0x0040 = 64 * 0.625ms = 40ms
[RX]: 04 0e 05 05 36 20 00 fb
→ 控制器响应: 状态0x00(成功), Selected TX Power
4. 设置广播数据
[TX]: 01 37 20 0e 02 03 00 0a 06 16 2c fe 99 f5 0e 02 0a f5
解析:
- 37 20: Set Extended Advertising Data (0x2037)
- 02: Handle
- 03 00: Operation (Complete)
- 0a: Length
- 广播数据: 06 16 2c fe 99 f5 0e 02 0a f5
[RX]: 04 0e 04 05 37 20 00
→ 控制器响应: 成功
5. 设置扫描响应数据(如果需要)
[TX]: 01 38 20 24 02 03 00 20 1f 09 73 6f 75 6e 64 63 6f 72 65 ...
解析:
- 38 20: Set Extended Scan Response Data (0x2038)
- 包含完整本地名称
6. 启动广播
[TX]: 01 39 20 06 01 01 02 00 00 00
解析:
- 39 20: LE Set Extended Advertising Enable (0x2039)
- 01: Enable
- 01: Number of sets
- 02: Advertising Handle
- 00 00 00: Duration, Max Events
[RX]: 04 0e 04 05 39 20 0c
→ 控制器响应: 错误码0x0c (Command Disallowed)
重试:
[TX]: 01 39 20 06 01 01 08 00 00 00
使用不同的Handle(0x08)
[RX]: 04 0e 04 05 39 20 0b
→ 错误码0x0b (Command Disallowed - 可能是快速重复命令)
时序图:
主机 控制器
│ │
├──── Set Adv Params (0x2036) ─────>│
│<───── Command Complete ───────────┤
│ │
├──── Set Adv Data (0x2037) ───────>│
│<───── Command Complete ───────────┤
│ │
├──── Set Scan Rsp (0x2038) ───────>│
│<───── Command Complete ───────────┤
│ │
├──── Enable Adv (0x2039) ─────────>│
│<───── Command Complete ───────────┤
│ │
│ [开始广播]
│ │
8.2 连接建立时序
时间戳: 15:52:30 (示例)
1. 中心设备发起连接
控制器收到连接请求
2. LE Meta Event - Connection Complete
[RX]: 04 3e 13 01 00 XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
解析:
- 04: HCI Event Packet
- 3e: LE Meta Event
- 13: Parameter Length (19 bytes)
- 01: Subevent Code (LE Connection Complete)
- 00: Status (Success)
- XX XX: Connection Handle
- 01: Role (0=Master, 1=Slave)
- XX: Peer Address Type
- XX XX XX XX XX XX: Peer Address
- XX XX: Conn Interval
- XX XX: Conn Latency
- XX XX: Supervision Timeout
- XX: Master Clock Accuracy
3. GAP层处理
[I] BLE : CNNS idx=0 role=1 hdl=0001
→ 连接建立: 索引0, 角色为Slave, 句柄0x0001
4. 加载GATT缓存
app_ble_check_load_server_cache()
app_ble_check_load_client_cache()
5. 应用层通知
BLE_LINK_CONNECTED_EVENT
DP_CONN_DONE: conidx=0x0
6. MTU交换
客户端发起MTU Exchange Request
[RX]: ATT_EXCHANGE_MTU_REQ mtu=XXX
服务端响应
[TX]: ATT_EXCHANGE_MTU_RSP mtu=247
MTU Exchange Complete
[I] BLE : MTU Exchanged: conidx=0 mtu=247
7. 服务发现(可选)
客户端读取Primary Service
客户端读取Characteristic
8. CCCD写入
客户端使能通知
[RX]: ATT_WRITE_REQ handle=CCCD value=0x0001
[I] BLE : DP_CCC_CHANGED conidx=0
连接就绪,可以开始数据传输
完整时序图:
外设(从设备) 中心设备(主设备)
│ │
├──────── Advertising ──────────────────>│
│ │
│<──────── Scan Request ─────────────────┤
├──────── Scan Response ────────────────>│
│ │
│<──────── Connect Request ──────────────┤
│ │
[连接建立] [连接建立]
│ │
│<──────── MTU Exchange Req ─────────────┤
├──────── MTU Exchange Rsp ─────────────>│
│ │
│<──────── Discover Services ────────────┤
├──────── Service List ─────────────────>│
│ │
│<──────── Discover Chars ───────────────┤
├──────── Char List ────────────────────>│
│ │
│<──────── Write CCCD(0x0001) ───────────┤
├──────── Write Response ───────────────>│
│ │
[就绪状态] [就绪状态]
│ │
├──────── Notification ─────────────────>│
│ │
│<──────── Write Request ────────────────┤
├──────── Write Response ───────────────>│
│ │
8.3 数据传输时序
8.3.1 通知(Notification)传输
外设 中心设备
│ │
├────── ATT Handle Value Ntf ────────>│
│ (无需响应) │
│ │
├────── ATT Handle Value Ntf ────────>│
│ │
├────── ATT Handle Value Ntf ────────>│
│ │
数据包格式:
┌──────┬────────┬─────────┬──────────┐
│ 0x1B │ Handle │ Value │ │
│ (Ntf)│(2 byte)│(N bytes)│ │
└──────┴────────┴─────────┴──────────┘
8.3.2 写入(Write)传输
外设 中心设备
│ │
│<────── ATT Write Request ───────────┤
│ Handle + Value │
├────── ATT Write Response ──────────>│
│ (0x13) │
│ │
│<────── ATT Write Command ───────────┤
│ (无需响应) │
│ │
8.4 断连时序
时间戳: 15:49:51:599 (从日志分析)
1. 上层协议断连
HFP断连:
[RX]: 02 82 20 08 00 04 00 40 30 21 53 01 49
→ RFCOMM DISC命令 (dlci=8)
[TX]: 02 82 20 08 00 04 00 55 00 21 73 01 63
→ RFCOMM UA响应
2. AVRCP断连
[I] BLE : AVRCP_EVENT_DISCONNECT
3. A2DP断连
[I] BLE : A2DP_EVENT_STREAM_CLOSED
4. L2CAP通道关闭
各个PSM通道依次关闭
5. ACL链路断开(如果完全断连)
[RX]: 04 05 04 XX XX XX XX
→ HCI Disconnection Complete Event
6. BLE连接断开(如果有)
[I] BLE : GAP_CONN_EVENT_CLOSED
conidx=X reason=0x13 (Remote User Terminated)
7. 清理资源
- 释放连接信息
- 重置状态变量
- 通知应用层
8. 恢复广播(如果需要)
ui_ble_chargebox_refresh_adv()
→ 启动快速广播(30ms间隔)
8.8 CTKD跨传输密钥派生机制
8.8.1 CTKD定义与应用场景
CTKD (Cross-Transport Key Derivation) 是蓝牙核心规范v4.1引入的一种跨传输密钥派生机制,允许双模设备(同时支持BR/EDR和BLE)在一个传输层(如BLE)上建立的安全密钥派生出另一个传输层(如BR/EDR)的密钥,从而实现:
- 无缝跨传输切换: 用户只需配对一次,即可在BR/EDR和BLE之间自由切换
- 简化配对流程: 避免在两个传输层分别进行配对
- 增强用户体验: 支持统一的设备身份标识和安全上下文
典型应用场景:
- 耳机设备通过BLE完成快速配对,然后自动建立BR/EDR连接用于音频传输
- 智能手表通过BLE进行数据同步,通过BR/EDR进行音频通话
- 键盘/鼠标等输入设备在BLE低功耗模式和BR/EDR高速模式间切换
8.8.2 CTKD密钥派生原理
CTKD基于蓝牙安全管理器(SMP)的密钥分发机制,使用h4函数进行密钥派生:
密钥派生公式 (Bluetooth Core Spec v5.3, Vol 3, Part H, Section 2.4.2.5):
BR/EDR Link Key = h4(LTK, "lebr")
其中:
- h4: CMAC-AES-128加密函数
- LTK: BLE配对时生成的Long Term Key (128位)
- "lebr": 4字节标识符,表示从LE到BR/EDR的派生
派生流程:
┌──────────────────────────────────────────────────────────────┐
│ CTKD密钥派生流程 │
└──────────────────────────────────────────────────────────────┘
阶段1: BLE配对与密钥交换
┌────────────┐ ┌────────────┐
│ BLE主设备 │ │ BLE从设备 │
└────────────┘ └────────────┘
│ │
├───── SMP Pairing Request ───────────────────>│
│ (init_key_dist |= GAP_KDIST_LINKKEY) │
│ │
│<───── SMP Pairing Response ──────────────────┤
│ (resp_key_dist |= GAP_KDIST_LINKKEY) │
│ │
├───── SMP Pairing Confirm ───────────────────>│
│ │
│<───── SMP Pairing Random ────────────────────┤
│ │
├───── SMP Encryption Information ────────────>│
│ (分发LTK) │
│ │
阶段2: Link Key派生与分发
│ │
│ 本地计算: │
│ BR_EDR_LK = h4(LTK, "lebr") │
│ │
├───── SMP Link Key Distribution ─────────────>│
│ (分发派生的BR/EDR Link Key) │
│ │
│<───── SMP Link Key Distribution ─────────────┤
│ (对端也分发其派生的Link Key) │
│ │
阶段3: BR/EDR连接复用派生密钥
│ │
│ 使用派生的Link Key建立BR/EDR安全连接 │
│ 无需重新配对 │
│ │
├═══ BR/EDR ACL Connection (with Link Key) ═══>│
│ │
h4函数实现 (CMAC-AES-128):
c
/**
* @brief CTKD h4函数 - 派生BR/EDR Link Key
*
* @param ltk 输入的BLE LTK (128位)
* @param keyID 标识符 "lebr" (4字节)
* @param link_key 输出的BR/EDR Link Key (128位)
*
* 算法: link_key = AES-CMAC(ltk, keyID)
*/
static void ctkd_h4_derive_link_key(const uint8_t *ltk,
const char *keyID,
uint8_t *link_key)
{
// keyID = "lebr" for LE to BR/EDR
// keyID = "brle" for BR/EDR to LE
// CMAC-AES-128计算
aes_cmac_calculate(ltk, 16, keyID, 4, link_key);
}
8.8.3 CTKD密钥分发标志
在SMP配对过程中,通过设置GAP_KDIST_LINKKEY标志来指示支持CTKD:
c
/// bthost/adapter/inc/ble/common/ble_core_common.h:381-396
/// Key Distribution Flags
enum gap_kdist
{
/// No Keys to distribute
GAP_KDIST_NONE = 0x00,
/// Encryption key (LTK) in distribution
GAP_KDIST_ENCKEY = (1 << 0), // 0x01
/// IRK (Identity Resolving Key) in distribution
GAP_KDIST_IDKEY = (1 << 1), // 0x02
/// CSRK (Connection Signature Resolving Key) in distribution
GAP_KDIST_SIGNKEY = (1 << 2), // 0x04
/// Link Key (BR/EDR Link Key) in distribution - CTKD标志
GAP_KDIST_LINKKEY = (1 << 3), // 0x08
GAP_KDIST_LAST = (1 << 4) // 0x10
};
密钥分发组合示例:
| 场景 | init_key_dist | resp_key_dist | 说明 |
|---|---|---|---|
| 标准BLE配对 | `ENCKEY | IDKEY` | `ENCKEY |
| CTKD完整支持 | `ENCKEY | IDKEY | LINKKEY` |
| 仅初始方分发 | `ENCKEY | LINKKEY` | ENCKEY |
8.8.4 CTKD代码实现示例
8.8.4.1 CTKD使能配置
编译时配置 (bthost/adapter/inc/bt/common/bt_sys_config.h:109-111):
c
#ifdef BLE_ONLY_ENABLED
#undef __GATT_OVER_BR_EDR__
#undef CTKD_ENABLE // BLE-only模式禁用CTKD
#undef IS_CTKD_OVER_BR_EDR_ENABLED
#endif
运行时检查宏 (bthost/service/ble_app_new/src/ble_demo_app.c:41):
c
// 检查设备是否支持CTKD: 比较BLE地址与BR/EDR地址是否相同
#define IS_CTKD_SUPPORT() \
(memcmp(p_demo_app_info->app_demo_addr.address, \
gap_hci_bt_address(), \
sizeof(bt_bdaddr_t)) == 0)
8.8.4.2 SMP配对需求设置
在配对过程中动态设置CTKD标志 (bthost/service/ble_app_new/src/app_ble.c:3785-3807):
c
static void app_ble_smp_get_requirements(uint16_t connhdl,
smp_requirements_t *p_requirements)
{
ble_global_t *g = ble_get_global();
#if defined(CTKD_ENABLE)
uint8_t key_dist = 0;
// 1. 检查是否有回调函数自定义密钥分发
if (g->dist_lk_set_cb == NULL)
{
// 默认启用CTKD: 分发Link Key
key_dist = GAP_KDIST_LINKKEY;
}
else
{
// 应用层回调决定是否分发Link Key
key_dist |= g->dist_lk_set_cb();
}
// 2. 根据key_dist设置init和resp的分发标志
if (key_dist & GAP_KDIST_LINKKEY)
{
// 主设备和从设备都分发Link Key
p_requirements->init_key_dist |= GAP_KDIST_LINKKEY;
p_requirements->resp_key_dist |= GAP_KDIST_LINKKEY;
}
else
{
// 移除Link Key分发标志
p_requirements->init_key_dist &= ~GAP_KDIST_LINKKEY;
p_requirements->resp_key_dist &= ~GAP_KDIST_LINKKEY;
}
#endif
// 3. 应用层可进一步修改SMP需求
if (g->ble_smp_require_modify != NULL)
{
g->ble_smp_require_modify(conn->connhdl,
(ble_smp_require_t *)p_requirements);
}
}
8.8.4.3 针对iOS设备的CTKD处理
iOS设备对CTKD有特殊要求,需要动态调整 (bthost/service/ble_app_new/src/ble_demo_app.c:387-396):
c
static void ble_demo_app_smp_requirement_modify_handler(uint16_t connhdl,
ble_smp_require_t *p_requirements)
{
gap_conn_item_t *conn = gap_get_conn_item(connhdl);
if (conn == NULL) return;
// 如果设备不是iOS设备,且不支持CTKD over BR/EDR,移除Link Key分发
// 这样可以确保LE Audio设备能通过其他方法进行CTKD
if ((conn->adv_handle == p_demo_app_info->adv_hdl) &&
(IS_IOS_DEV(conn->con_idx) == 0) && // 非iOS设备
(IS_CTKD_SUPPORT() == 0)) // 不支持CTKD
{
TRACE(1, "adv hdl:%d smp requirements modify", conn->adv_handle);
// 移除Link Key分发标志
p_requirements->init_key_dist &= ~GAP_KDIST_LINKKEY;
p_requirements->resp_key_dist &= ~GAP_KDIST_LINKKEY;
}
}
8.8.4.4 CTKD密钥存储结构
密钥安全存储结构 (bthost/adapter/inc/adapter_service/gap_service.h:899-922):
c
/// BLE配对安全信息结构
typedef struct
{
// 配对级别 (Unauthenticated/Authenticated/Secure Connection)
uint8_t pairing_lvl;
// 密钥分发标志位
uint8_t local_ltk_distributed: 1; // 本地LTK已分发
uint8_t local_irk_distributed: 1; // 本地IRK已分发
uint8_t local_csrk_distributed: 1; // 本地CSRK已分发
uint8_t peer_ltk_distributed: 1; // 对端LTK已分发
uint8_t peer_irk_distributed: 1; // 对端IRK已分发
uint8_t peer_csrk_distributed: 1; // 对端CSRK已分发
// 对端密钥
uint8_t peer_irk[GAP_KEY_LEN]; // 16字节IRK
uint8_t peer_csrk[GAP_KEY_LEN]; // 16字节CSRK
// 加密密钥
uint8_t ltk[GAP_KEY_LEN]; // 16字节LTK (用于派生Link Key)
uint8_t local_ltk[GAP_KEY_LEN]; // 16字节本地LTK
// 随机数和EDIV (Legacy Pairing使用)
uint8_t rand[GAP_RAND_LEN]; // 8字节随机数
uint8_t local_rand[GAP_RAND_LEN]; // 8字节本地随机数
uint8_t ediv[GAP_EDIV_LEN]; // 2字节EDIV
uint8_t local_ediv[GAP_EDIV_LEN]; // 2字节本地EDIV
// 加密密钥长度 (7-16字节)
uint8_t enc_key_size;
} gap_bond_sec_t;
/// 连接信息结构
typedef struct
{
// ... 其他连接信息 ...
// CTKD相关
uint8_t ctkd_enc_key_size; // CTKD加密密钥长度
uint8_t *link_key; // 指向派生的BR/EDR Link Key (16字节)
gap_bond_sec_t sec; // 绑定安全信息
} gap_conn_item_t;
8.8.4.5 CTKD与GATT over BR/EDR集成
CTKD常与GATT over BR/EDR结合使用 (bthost/service/common/besmain.cpp:769-982):
c
static void bt_stack_config(void)
{
// 1. 使能LE主机支持
uint8_t le_host_support[] = {
#ifdef BLE_HOST_SUPPORT
1, // LE Host Support
#else
0,
#endif
0 // Simultaneous LE and BR/EDR to Same Device Capable
};
// 2. 使能Secure Connections (CTKD需要)
uint8_t sec_conn_host_supp[] = {
#if BLE_AUDIO_ENABLED || (defined(CTKD_ENABLE) && defined(IS_CTKD_OVER_BR_EDR_ENABLED))
1, // Secure Connections Host Support
#else
0,
#endif
};
// 3. L2CAP配置 - 使能SMP over BR/EDR
bt_l2cap_config_t l2cap_cfg = {
#if defined(__GATT_OVER_BR_EDR__)
.gatt_over_br_edr = true, // GATT over BR/EDR
#endif
#if defined(__HFP_ACS_BV17_I__) || \
(defined(CTKD_ENABLE) && defined(IS_CTKD_OVER_BR_EDR_ENABLED))
.smp_over_br_edr = true, // SMP over BR/EDR (CTKD需要)
#endif
};
l2cap_config_set(&l2cap_cfg);
}
8.8.5 CTKD完整时序图
以下时序图展示了CTKD从BLE配对到BR/EDR连接建立的完整过程:
┌──────────────────────────────────────────────────────────────────────────┐
│ CTKD完整流程: BLE配对 → 密钥派生 → BR/EDR连接 │
└──────────────────────────────────────────────────────────────────────────┘
设备A (Central/Master) 设备B (Peripheral/Slave)
BLE PHY BLE PHY
│ │
│ │
════════════════════ 阶段1: BLE广播与连接建立 ═══════════════════
│ │
│ <──── ADV_IND ──── │
│ (BLE可连接广播) │
│ │
├──────── CONNECT_IND ────────────────────────────────>│
│ (发起BLE连接) │
│ │
│<──────── LE Connection Complete ────────────────────┤
│ (connHandle=0x0040, interval=24, latency=0)│
│ │
════════════════════ 阶段2: SMP配对与密钥协商 ═══════════════════
│ │
│── SMP: Pairing Request ──────────────────────────────>│
│ IO Capability: NoInputNoOutput │
│ Auth Req: Bonding, MITM=0, SC=1 │
│ init_key_dist: ENCKEY|IDKEY|LINKKEY (0x0B) │
│ │
│<─ SMP: Pairing Response ─────────────────────────────┤
│ IO Capability: DisplayYesNo │
│ Auth Req: Bonding, MITM=1, SC=1 │
│ resp_key_dist: ENCKEY|IDKEY|LINKKEY (0x0B) │
│ │
├─ SMP: Pairing Public Key (P-256) ───────────────────>│
│ │
│<─ SMP: Pairing Public Key (P-256) ───────────────────┤
│ │
│ [双方计算DHKey = P-256(local_private, peer_public)]│
│ │
├─ SMP: Pairing Confirm ──────────────────────────────>│
│ Confirm = f4(PKa, PKb, Na, 0) │
│ │
│<─ SMP: Pairing Confirm ──────────────────────────────┤
│ Confirm = f4(PKa, PKb, Nb, 0) │
│ │
├─ SMP: Pairing Random ───────────────────────────────>│
│ Random Value = Na │
│ │
│<─ SMP: Pairing Random ───────────────────────────────┤
│ Random Value = Nb │
│ │
│ [验证Confirm值是否匹配] │
│ │
├─ SMP: Pairing DHKey Check ──────────────────────────>│
│ Ea = f6(MacKey, Na, Nb, rb, IOcapA, A, B) │
│ │
│<─ SMP: Pairing DHKey Check ──────────────────────────┤
│ Eb = f6(MacKey, Nb, Na, ra, IOcapB, B, A) │
│ │
│ [配对成功, 生成LTK = f5(DHKey, Na, Nb, A, B)] │
│ │
════════════════════ 阶段3: 密钥分发 (含CTKD) ═══════════════════
│ │
├─ SMP: Encryption Information ───────────────────────>│
│ LTK (128-bit Long Term Key) │
│ │
├─ SMP: Master Identification ────────────────────────>│
│ EDIV=0x0000, Rand=0x0000000000000000 │
│ (SC配对下EDIV和Rand均为0) │
│ │
├─ SMP: Identity Information ─────────────────────────>│
│ IRK (128-bit Identity Resolving Key) │
│ │
├─ SMP: Identity Address Information ─────────────────>│
│ Address Type: Public/Random Static │
│ Address: XX:XX:XX:XX:XX:XX │
│ │
├─ SMP: Signing Information ──────────────────────────>│
│ CSRK (128-bit Connection Signature Resolving Key) │
│ │
│ *** CTKD密钥派生 *** │
│ [本地计算: BR_Link_Key = h4(LTK, "lebr")] │
│ │
├─ SMP: Link Key (CTKD) ──────────────────────────────>│
│ 派生的BR/EDR Link Key (128-bit) │
│ 用于后续BR/EDR连接认证 │
│ │
│<─ SMP: Encryption Information ───────────────────────┤
│ 对端的LTK │
│ │
│<─ SMP: Master Identification ────────────────────────┤
│ EDIV=0x0000, Rand=0x0000000000000000 │
│ │
│<─ SMP: Identity Information ─────────────────────────┤
│ 对端的IRK │
│ │
│<─ SMP: Identity Address Information ─────────────────┤
│ 对端的Identity Address │
│ │
│<─ SMP: Signing Information ──────────────────────────┤
│ 对端的CSRK │
│ │
│ [本地计算: BR_Link_Key = h4(peer_LTK, "lebr")] │
│ │
│<─ SMP: Link Key (CTKD) ──────────────────────────────┤
│ 对端派生的BR/EDR Link Key │
│ │
│ [存储所有密钥到NV存储] │
│ gap_nv_add_ble_device(conn, &bond_sec); │
│ │
════════════════════ 阶段4: BR/EDR连接建立 (使用CTKD密钥) ═══════
│ │
BR/EDR PHY BR/EDR PHY
│ │
├──── Page (使用相同的BD Address) ────────────────────>│
│ │
│<──── Page Response ──────────────────────────────────┤
│ │
├──── LMP Authentication Request ─────────────────────>│
│ (使用CTKD派生的Link Key进行认证) │
│ │
│<──── LMP Authentication Response ────────────────────┤
│ │
│ [Challenge-Response认证成功] │
│ [无需重新配对!] │
│ │
├══ BR/EDR ACL Connection Established ════════════════>│
│ Connection Handle: 0x0001 │
│ Encryption Enabled: Yes (使用派生的Link Key) │
│ │
════════════════════ 阶段5: GATT over BR/EDR (可选) ═════════════
│ │
│── L2CAP: Connection Request (PSM=0x001F) ───────────>│
│ (ATT over BR/EDR) │
│ │
│<─ L2CAP: Connection Response ────────────────────────┤
│ CID=0x0040 Allocated │
│ │
│── ATT: Exchange MTU Request ─────────────────────────>│
│ │
│<─ ATT: Exchange MTU Response ────────────────────────┤
│ │
│ [GATT服务发现和数据传输...] │
│ │
时序关键点说明:
- init_key_dist和resp_key_dist包含LINKKEY标志 (0x0B = 0x01|0x02|0x08)
- h4派生函数 :
BR_Link_Key = AES-CMAC(LTK, "lebr") - 双向密钥分发: 主从设备都分发各自派生的Link Key
- BR/EDR认证无需用户交互: 直接使用派生的Link Key进行Challenge-Response认证
- 密钥长度: 所有密钥均为128位 (16字节)
8.8.6 CTKD实际操作例程
例程1: 使能CTKD并设置SMP参数
c
// 1. 在应用层注册CTKD使能回调
uint8_t app_ctkd_link_key_dist_callback(void)
{
// 检查设备是否支持双模
if (bt_is_dual_mode_device())
{
return GAP_KDIST_LINKKEY; // 返回Link Key分发标志
}
return 0; // 单模设备不分发Link Key
}
void app_ble_init(void)
{
ble_global_t *g = ble_get_global();
// 注册CTKD密钥分发回调
g->dist_lk_set_cb = app_ctkd_link_key_dist_callback;
// 设置Secure Connections要求 (CTKD需要)
g->default_smp_requirements.auth_req |= SMP_AUTH_SEC_CON;
// 设置默认密钥分发 (含CTKD)
g->default_smp_requirements.init_key_dist =
GAP_KDIST_ENCKEY | // LTK
GAP_KDIST_IDKEY | // IRK
GAP_KDIST_LINKKEY; // BR/EDR Link Key (CTKD)
g->default_smp_requirements.resp_key_dist =
GAP_KDIST_ENCKEY |
GAP_KDIST_IDKEY |
GAP_KDIST_LINKKEY;
}
例程2: 处理CTKD密钥接收事件
c
static void app_ble_handle_link_key_received(uint16_t connhdl,
const uint8_t *link_key)
{
gap_conn_item_t *conn = gap_get_conn_item(connhdl);
if (conn == NULL) return;
// 1. 分配Link Key存储空间
if (conn->link_key == NULL)
{
conn->link_key = (uint8_t *)malloc(GAP_KEY_LEN);
}
// 2. 保存接收到的Link Key
memcpy(conn->link_key, link_key, GAP_KEY_LEN);
// 3. 记录CTKD密钥长度
conn->ctkd_enc_key_size = conn->sec.enc_key_size;
TRACE(2, "[CTKD] Link Key received for connhdl=0x%04x, key_size=%d",
connhdl, conn->ctkd_enc_key_size);
// 4. 可选: 触发BR/EDR连接
if (app_should_establish_bredr_connection())
{
bt_bdaddr_t *peer_addr = &conn->peer.peer_addr.addr;
app_bt_start_bredr_connection(peer_addr);
}
}
例程3: 使用CTKD密钥建立BR/EDR连接
c
void app_bt_establish_bredr_with_ctkd(const bt_bdaddr_t *peer_addr)
{
// 1. 从NV存储中获取CTKD密钥
gap_bredr_sec_t bredr_sec;
if (!nv_get_bt_device_by_addr(peer_addr, &bredr_sec))
{
TRACE(0, "[CTKD] No Link Key found, pairing required");
return;
}
// 2. 验证Link Key是否有效
if (bredr_sec.link_key_present)
{
TRACE(1, "[CTKD] Using derived Link Key, key_size=%d",
bredr_sec.link_key_size);
// 3. 使用存储的Link Key建立BR/EDR连接
bt_create_connection_param_t param = {
.bd_addr = *peer_addr,
.use_stored_link_key = true,
.link_key = bredr_sec.link_key,
.link_key_size = bredr_sec.link_key_size,
};
btif_me_create_acl_connection(¶m);
}
else
{
TRACE(0, "[CTKD] Link Key not present");
}
}
例程4: CTKD日志分析示例
以下是一次完整的CTKD配对日志:
[15:49:50.123] [I] BLE: SMP Pairing Request sent
[15:49:50.125] IO Cap: NoInputNoOutput
[15:49:50.126] Auth Req: Bonding|SC (0x0D)
[15:49:50.127] Init Key Dist: ENCKEY|IDKEY|LINKKEY (0x0B)
[15:49:50.128] Resp Key Dist: ENCKEY|IDKEY|LINKKEY (0x0B)
[15:49:50.234] [I] BLE: SMP Pairing Response received
[15:49:50.235] IO Cap: DisplayYesNo
[15:49:50.236] Auth Req: Bonding|MITM|SC (0x0D)
[15:49:50.456] [I] BLE: SMP Public Key Exchange Complete
[15:49:50.457] DHKey Calculated
[15:49:50.678] [I] BLE: SMP Pairing Confirm OK
[15:49:50.789] [I] BLE: SMP Pairing Random OK
[15:49:50.890] [I] BLE: SMP DHKey Check OK
[15:49:50.991] [I] BLE: SMP LTK Generated
[15:49:50.992] LTK: A1 B2 C3 D4 E5 F6 G7 H8 I9 J0 K1 L2 M3 N4 O5 P6
[15:49:51.100] [I] BLE: SMP Key Distribution Phase
[15:49:51.102] [I] BLE: Encryption Information Sent (LTK)
[15:49:51.105] [I] BLE: Identity Information Sent (IRK)
[15:49:51.108] [I] BLE: Identity Address: Public 11:22:33:44:55:66
[15:49:51.200] [I] BLE: **CTKD Key Derivation**
[15:49:51.201] Input LTK: A1 B2 C3 D4 E5 F6 ...
[15:49:51.202] h4("lebr"): ...
[15:49:51.203] BR Link Key: 12 34 56 78 9A BC DE F0 ...
[15:49:51.250] [I] BLE: Link Key Sent (CTKD)
[15:49:51.251] Link Key: 12 34 56 78 9A BC DE F0 ...
[15:49:51.350] [I] BLE: Pairing Complete
[15:49:51.351] Pairing Level: Secure Connection (0x0C)
[15:49:51.352] Bonded: Yes
[15:49:51.353] CTKD Enabled: Yes
[15:49:52.000] [I] BT: BR/EDR Connection Request
[15:49:52.001] Peer Address: 11:22:33:44:55:66
[15:49:52.002] Using CTKD Link Key
[15:49:52.100] [I] BT: Authentication Request
[15:49:52.101] Challenge: XX XX XX XX ...
[15:49:52.102] Response: YY YY YY YY ...
[15:49:52.103] Result: Success (无需用户交互!)
[15:49:52.200] [I] BT: BR/EDR Connection Established
[15:49:52.201] Handle: 0x0001
[15:49:52.202] Encryption: Enabled
[15:49:52.203] Link Key Type: CTKD Derived
8.8.7 CTKD常见问题排查
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| SMP配对时未分发Link Key | GAP_KDIST_LINKKEY标志未设置 |
检查init_key_dist和resp_key_dist |
| BR/EDR连接需要重新配对 | Link Key未正确存储或派生失败 | 检查NV存储中的Link Key字段 |
| iOS设备CTKD失败 | iOS对CTKD有特殊要求 | 使用IS_IOS_DEV()判断并特殊处理 |
| 双模地址不一致 | BLE和BR/EDR使用不同地址 | 确保IS_CTKD_SUPPORT()返回true |
| h4派生失败 | LTK无效或加密库未正确初始化 | 检查LTK值和AES-CMAC实现 |
调试命令:
c
// 打印CTKD状态
void app_ctkd_dump_status(uint16_t connhdl)
{
gap_conn_item_t *conn = gap_get_conn_item(connhdl);
TRACE(0, "=== CTKD Status ===");
TRACE(1, "CTKD Enabled: %d", IS_CTKD_SUPPORT());
TRACE(1, "Link Key Present: %d", (conn->link_key != NULL));
TRACE(1, "Key Size: %d", conn->ctkd_enc_key_size);
TRACE(1, "Pairing Level: 0x%02x", conn->sec.pairing_lvl);
if (conn->link_key)
{
DUMP8("Link Key: ", conn->link_key, GAP_KEY_LEN);
}
}
9. 常见问题与调试
9.1 广播问题
问题1: 广播无法启动
可能原因:
adv_force_disabled标志被设置- BLE连接数已达上限
- 处于从耳角色
- HCI命令返回错误
调试方法:
c
// 检查广播是否被允许
bool ble_adv_is_allowed(void)
{
if (!gap_stack_is_ready()) {
return false;
}
if (ble_get_global()->adv_force_disabled) {
return false;
}
if (app_is_arrive_at_max_ble_connections()) {
return false;
}
return true;
}
问题2: 广播间隔不正确
检查:
- 是否设置了正确的间隔参数
- 定时器是否正常工作
- 状态机是否正确切换
9.2 连接问题
问题1: 连接后立即断开
可能原因:
- 连接参数不兼容
- MTU交换失败
- 安全要求不匹配
- 控制器资源不足
问题2: 无法收到数据
检查:
- CCCD是否正确配置
- MTU大小是否足够
- 回调函数是否正确注册
- 数据包格式是否正确
9.3 日志分析技巧
关键日志标记:
CNNS: Connection StartedCNNE: Connection EndedFADV: Force ADV (广播强制操作)FDIS: Force DisableMTUE: MTU ExchangedDP_CONN_DONE: Datapath连接完成DP_DISCONN_DONE: Datapath断连完成
HCI事件码:
04 0e: HCI Command Complete04 3e: HCI LE Meta Event04 05: HCI Disconnection Complete04 13: HCI Number of Completed Packets
10. 性能优化建议
10.1 功耗优化
-
广播间隔优化
- 连接前: 30ms快速广播(10秒) → 480ms慢速广播
- 已连接: 停止广播
- 建议根据实际场景调整时长
-
连接参数优化
- 空闲时使用大连接间隔(400×1.25ms = 500ms)
- 数据传输时使用小连接间隔(12×1.25ms = 15ms)
- 根据场景动态调整
-
PHY选择
- LE 1M: 平衡功耗和速率
- LE 2M: 高速传输,略高功耗
- LE Coded: 长距离,高功耗
10.2 吞吐量优化
-
增大MTU
c// 请求最大MTU gap_request_mtu_exchange(connhdl, 247); // 最大247字节 -
使用连接事件长度扩展
c// 增加连接事件长度以传输更多数据包 gap_set_conn_event_length(connhdl, max_ce_length); -
批量传输
c// 在单个连接事件中发送多个通知 for (int i = 0; i < batch_count; i++) { gatts_send_notification(connhdl, char_handle, data[i], len[i]); }
10.3 稳定性优化
-
错误处理
c// 所有HCI命令都应检查返回状态 bt_status_t status = gap_start_advertising(¶ms); if (status != BT_STS_SUCCESS) { LOG("Adv start failed: %d", status); // 错误恢复逻辑 } -
超时机制
c// 连接超时检测 os_timer_start(conn_timeout_timer, CONN_TIMEOUT_MS); -
状态同步
c// 确保状态一致性 if (ble_handle->connected != gap_is_connected(conidx)) { LOG("State mismatch detected, syncing..."); ble_handle->connected = gap_is_connected(conidx); }
11. 硬件RF层与物理层深度解析
11.1 RF配置和功率控制
BES芯片支持精细的RF功率控制,用于优化功耗和通信距离:
RF配置结构 (bt_drv_interface.h:250-275):
c
/// BT driver customer RF config structure
struct btdrv_customer_rf_config_t
{
bool config_xtal_en; // 晶振配置使能
bool config_tx_pwr_en; // 发送功率配置使能
uint16_t xtal_cap_val; // 晶振电容值
// BT Classic功率索引 (8级功率)
int8_t bt_tx_idx7_pwr; // BT TX 最高功率 (index 7)
int8_t bt_tx_idx6_pwr; // BT TX index 6
int8_t bt_tx_idx5_pwr; // BT TX index 5
int8_t bt_tx_idx4_pwr; // BT TX index 4
int8_t bt_tx_idx3_pwr; // BT TX index 3
int8_t bt_tx_idx2_pwr; // BT TX index 2
int8_t bt_tx_idx1_pwr; // BT TX index 1
int8_t bt_tx_idx0_pwr; // BT TX 最低功率 (index 0)
// BLE功率索引 (8级功率)
int8_t le_tx_idx7_pwr; // LE TX 最高功率
int8_t le_tx_idx6_pwr;
int8_t le_tx_idx5_pwr;
int8_t le_tx_idx4_pwr;
int8_t le_tx_idx3_pwr;
int8_t le_tx_idx2_pwr;
int8_t le_tx_idx1_pwr;
int8_t le_tx_idx0_pwr; // LE TX 最低功率
// 特殊场景功率
int8_t bt_tx_page_pwr; // Page扫描功率
int8_t bt_tx_page_high_pwr; // Page高功率模式
int8_t bt_tx_max_pwr; // BT最大功率
int8_t le_tx_max_pwr; // LE最大功率
};
功率控制接口:
c
// 设置BDR/BLE发送功率
void btdrv_set_bdr_ble_txpower(uint8_t txpwr_idx, uint16_t n);
// 设置EDR发送功率
void btdrv_set_edr_txpower(uint8_t div, uint8_t power_level);
// 更新固定发送功率
void bt_drv_rf_update_fixed_tx_pwr(uint8_t Bt_Tx_Pwr_In_dB);
void ble_drv_rf_update_fixed_tx_pwr(uint8_t Ble_Tx_Pwr_In_dB);
// 高效功率控制
void bt_drv_rf_high_efficency_tx_pwr_ctrl(bool en, bool reset);
11.2 BT_CLK时钟单位和Slot机制
BT_CLK是蓝牙系统的核心时间基准:
时钟单位定义 (bt_drv_interface.h:116-122):
c
// BT_CLK单位: 312.5us (Bluetooth Clock Unit)
#define BT_CLK_UNIT 312.5 // us
#define BT_CLK_UNIT_2X 625 // us (2倍时钟单位)
#define BT_CLK_UNIT_10X 3125 // us (10倍时钟单位)
// 时间单位转换宏
#define US_TO_BTCLKS(us) ((uint64_t)(us) * 2 / BT_CLK_UNIT_2X)
#define BTCLKS_TO_US(n) ((uint64_t)(n) * BT_CLK_UNIT_2X / 2)
#define HALF_SLOT_INV(x) (HALF_SLOT_SIZE - (x) - 1)
Slot时间片概念:
1 Slot = 625us = 2个BT_CLK = 1个时间单位
1 BT_CLK = 312.5us = 半个Slot
时间轴:
|--Slot 0--|--Slot 1--|--Slot 2--|--Slot 3--|
0 625 1250 1875 2500 (us)
| TX | RX | TX | RX |
Master Slave Master Slave
时钟操作接口:
c
// 获取当前BT时钟
uint32_t btdrv_syn_get_curr_ticks(void);
// 获取连接的BT时间
uint32_t bt_syn_get_curr_ticks(uint16_t conhdl);
// 获取时间偏移
int32_t bt_syn_get_offset_ticks(uint16_t conhdl);
// 时钟计数转换为微秒
int64_t btdrv_clkcnt_to_us(uint32_t clk, uint16_t cnt);
// 从从设备时钟转换为主设备时钟
int btdrv_slave2master_clkcnt_convert(uint32_t local_clk, uint16_t local_cnt,
int32_t clk_offset, uint16_t bit_offset,
uint32_t *master_clk, uint16_t *master_cnt);
11.3 物理层Packet类型
BT Classic支持多种数据包类型,用于不同的传输速率和可靠性需求:
Packet类型定义 (bt_drv_interface.h:174-201):
c
/// Packet type code interpretation - BR (Basic Rate)
#define ID_NUL_TYPE 0x0 // NULL packet (无数据)
#define POLL_TYPE 0x1 // POLL packet (轮询)
#define FHS_TYPE 0x2 // Frequency Hop Synchronization (跳频同步)
// BR数据包 (1 Mbps)
#define DM1_TYPE 0x3 // Data Medium rate, 1-slot, FEC(2/3)
#define DH1_TYPE 0x4 // Data High rate, 1-slot, No FEC
#define DM3_TYPE 0xA // Data Medium rate, 3-slot, FEC(2/3)
#define DH3_TYPE 0xB // Data High rate, 3-slot, No FEC
#define DM5_TYPE 0xE // Data Medium rate, 5-slot, FEC(2/3)
#define DH5_TYPE 0xF // Data High rate, 5-slot, No FEC
// EDR数据包 (2/3 Mbps)
#define DH1_2_TYPE 0x4 // 2Mbps, 1-slot
#define DH1_3_TYPE 0x8 // 3Mbps, 1-slot
#define DH3_2_TYPE 0xA // 2Mbps, 3-slot
#define DH3_3_TYPE 0xB // 3Mbps, 3-slot
#define DH5_2_TYPE 0xE // 2Mbps, 5-slot
#define DH5_3_TYPE 0xF // 3Mbps, 5-slot
// SCO/eSCO数据包 (语音)
#define HV1_TYPE 0x5 // High quality Voice, 1-slot
#define HV2_TYPE 0x6 // High quality Voice, 2-slot
#define HV3_TYPE 0x7 // High quality Voice, 3-slot
#define EV3_TYPE 0x7 // Enhanced Voice, 3-slot
#define EV4_TYPE 0xC // Enhanced Voice, 4-slot
#define EV5_TYPE 0xD // Enhanced Voice, 5-slot
#define EV3_2_TYPE 0x6 // EV3 @ 2Mbps
#define EV3_3_TYPE 0x7 // EV3 @ 3Mbps
#define EV5_2_TYPE 0xC // EV5 @ 2Mbps
#define EV5_3_TYPE 0xD // EV5 @ 3Mbps
Packet有效载荷大小 (bt_drv_interface.h:203-228):
c
// BR数据包大小 (字节)
#define DM1_PACKET_SIZE 17 // DM1: 17 bytes
#define DH1_PACKET_SIZE 27 // DH1: 27 bytes
#define DM3_PACKET_SIZE 121 // DM3: 121 bytes
#define DH3_PACKET_SIZE 183 // DH3: 183 bytes
#define DM5_PACKET_SIZE 224 // DM5: 224 bytes
#define DH5_PACKET_SIZE 339 // DH5: 339 bytes
// EDR数据包大小 (字节)
#define DH1_2_PACKET_SIZE 54 // DH1-2: 54 bytes
#define DH1_3_PACKET_SIZE 83 // DH1-3: 83 bytes
#define DH3_2_PACKET_SIZE 367 // DH3-2: 367 bytes
#define DH3_3_PACKET_SIZE 552 // DH3-3: 552 bytes
#define DH5_2_PACKET_SIZE 679 // DH5-2: 679 bytes
#define DH5_3_PACKET_SIZE 1021 // DH5-3: 1021 bytes (最大)
// SCO数据包大小 (字节)
#define HV1_PACKET_SIZE 10
#define HV2_PACKET_SIZE 20
#define HV3_PACKET_SIZE 30
#define EV3_PACKET_SIZE 30
#define EV4_PACKET_SIZE 120
#define EV5_PACKET_SIZE 180
Packet类型选择策略:
吞吐量优先: DH5 > DH3 > DH1
可靠性优先: DM5 > DM3 > DM1 (带FEC纠错)
延迟优先: DH1 (单slot, 625us)
平衡模式: DH3 (3-slot, 1.875ms)
EDR模式:
- DH5_3: 1021字节/3.125ms = 326 KB/s (最大吞吐)
- DH3_2: 367字节/1.875ms = 196 KB/s
11.4 AFH自适应跳频
AFH (Adaptive Frequency Hopping) 用于避开干扰信道,提高通信质量:
LMP AFH命令 (bt_drv_interface.h:80-100):
c
// LMP AFH相关Opcode
#define LMP_SET_AFH_OPCODE 60 // 设置AFH信道映射
#define LMP_CH_CLASS_REQ_EXTOPCODE 16 // 信道分类请求
#define LMP_CH_CLASS_EXTOPCODE 17 // 信道分类
// AFH信道映射
#define CHNL_MAP_LEN 0x0A // 10字节信道映射
#define LE_CHNL_MAP_LEN 0x05 // LE: 5字节信道映射
AFH工作原理:
BT Classic使用79个信道 (2.402 - 2.480 GHz)
信道间隔: 1 MHz
跳频速率: 1600 hops/sec
AFH机制:
1. 监测每个信道的质量 (RSSI, BER)
2. 将质量差的信道标记为"坏信道"
3. 更新AFH信道映射 (bitmap, 79 bits)
4. 仅在"好信道"中跳频
5. 至少保留20个可用信道
信道映射格式 (10字节):
Byte 0: Channel 0-7
Byte 1: Channel 8-15
...
Byte 9: Channel 72-78, Reserved[79-79]
示例:
0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x7F
= 全部79个信道可用
BLE信道映射 (co_bt_defines.h:99-180):
c
// BLE使用40个信道 (0-39)
// 数据信道: 0-36 (37个)
// 广播信道: 37, 38, 39 (3个)
#define DATA_CHANNEL_MIN 0
#define DATA_CHANNEL_MAX 36
#define DATA_CHANNEL_NB 37
#define ADV_CHANNEL_37 37 // 2402 MHz
#define ADV_CHANNEL_38 38 // 2426 MHz
#define ADV_CHANNEL_39 39 // 2480 MHz
// 最少使用信道数
#define DATA_CHANNEL_USED_NB_MIN 2
// 信道映射长度
#define LE_CHNL_MAP_LEN 0x05 // 5字节 (40 bits)
12. SMP安全管理与配对鉴权
12.1 SMP状态机 (26个Phase)
SMP (Security Manager Protocol) 状态机控制配对流程:
SMP Phase定义 (gap_i.h:232-260):
c
typedef enum {
SMP_PHASE_IDLE = 0x00, // 空闲
SMP_PHASE_PAIRING_START = 0x01, // 配对开始
SMP_PHASE_GEN_TK_RAND = 0x02, // 生成TK/随机数
SMP_PHASE_GEN_CFM_VALUE = 0x03, // 生成Confirm值
SMP_PHASE_WAIT_PAIRING_CONFIRM = 0x04, // 等待配对Confirm
SMP_PHASE_WAIT_PAIRING_RANDOM = 0x05, // 等待配对Random
SMP_PHASE_VERIFY_CONFIRM = 0x06, // 验证Confirm
SMP_PHASE_GEN_STK = 0x07, // 生成STK (Legacy)
SMP_PHASE_GEN_PUB_KEY = 0x08, // 生成公钥 (SC)
SMP_PHASE_WAIT_PAIRING_PUBLIC_KEY = 0x09, // 等待对方公钥
SMP_PHASE_GEN_DHKEY = 0x0a, // 生成DHKey
SMP_PHASE_GET_PASSKEY = 0x0b, // 获取Passkey
SMP_PHASE_GET_L_OOB_AUTH_DATA = 0x0c, // 获取本地OOB数据
SMP_PHASE_GET_P_OOB_AUTH_DATA = 0x0d, // 获取对方OOB数据
SMP_PHASE_GEN_RANDOM = 0x0e, // 生成随机数
SMP_PHASE_GEN_USER_VALUE = 0x0f, // 生成用户确认值
SMP_PHASE_START_STAGE_2 = 0x10, // 开始阶段2
SMP_PHASE_GEN_LTK = 0x11, // 生成LTK
SMP_PHASE_GEN_CHECK_VALUE = 0x12, // 生成Check值
SMP_PHASE_WAIT_PAIRING_DHKEY_CHECK = 0x13, // 等待DHKey Check
SMP_PHASE_VERIFY_CHECK = 0x14, // 验证Check
SMP_PHASE_WAIT_PEER_LTK_REQ = 0x15, // 等待LTK请求
SMP_PHASE_WAIT_ENC_CHANGE = 0x16, // 等待加密变更
SMP_PHASE_START_PHASE_3 = 0x17, // 开始阶段3 (密钥分发)
SMP_PHASE_WAIT_DIST_KEY = 0x18, // 等待密钥分发
SMP_PHASE_FINISHED = 0x19, // 配对完成
SMP_PHASE_MAX_NUM = 0x1a,
} smp_phase_t;
SMP Opcode (gap_i.h:213-228):
c
typedef enum {
SMP_PAIRING_REQ = 0x01, // 配对请求
SMP_PAIRING_RSP = 0x02, // 配对响应
SMP_PAIRING_CONFIRM = 0x03, // 配对Confirm
SMP_PAIRING_RANDOM = 0x04, // 配对Random
SMP_PAIRING_FAILED = 0x05, // 配对失败
SMP_ENCRYPTION_INFO = 0x06, // 加密信息 (LTK)
SMP_CENTRAL_IDENTIFICATION = 0x07, // 中心设备标识 (EDIV, Rand)
SMP_IDENTITY_INFO = 0x08, // 身份信息 (IRK)
SMP_IDENTITY_ADDR_INFO = 0x09, // 身份地址信息
SMP_SIGNING_INFO = 0x0a, // 签名信息 (CSRK)
SMP_SECURITY_REQ = 0x0b, // 安全请求
SMP_PAIRING_PUBLIC_KEY = 0x0c, // 配对公钥 (SC)
SMP_PAIRING_DHKEY_CHECK = 0x0d, // DHKey Check (SC)
SMP_PAIRING_KEYPRESS_NOTIFY = 0x0e, // 按键通知
} smp_opcode_t;
SMP配对流程示例 (Legacy Pairing - Just Works):
Initiator (Central) Responder (Peripheral)
| |
|------ Pairing Request --------> | Phase 1: 特征交换
| |
|<----- Pairing Response -------- |
| |
| 生成TK=0 (Just Works) | Phase 2: 认证
| |
| 生成Mrand, 计算Mconfirm |
| |
|------ Pairing Confirm --------> |
| |
| 生成Srand, 计算Sconfirm |
| |
|<----- Pairing Confirm --------- |
| |
|------ Pairing Random ---------> | 发送Mrand
| |
| 验证Mconfirm = c1(TK, Mrand, ...)|
| |
|<----- Pairing Random ---------- | 发送Srand
| |
| 验证Sconfirm = c1(TK, Srand, ...) |
| |
| 生成STK = s1(TK, Srand, Mrand) |
| |
|------ Start Encryption --------> | Phase 3: 加密
| |
|<----- Encryption Change -------- |
| |
|------ Encryption Info ---------> | Phase 4: 密钥分发
|------ Master Identification ---> | (LTK, EDIV, Rand)
|------ Identity Info -----------> | (IRK)
|------ Identity Address Info ---> | (Identity Address)
|------ Signing Info ------------> | (CSRK)
| |
|<----- Identity Info ----------- |
|<----- Identity Address Info --- |
|<----- Signing Info ------------ |
| |
配对完成
12.2 配对方法 (4种)
配对方法枚举:
c
typedef enum {
PAIRING_METH_JUST_WORKS = 0, // Just Works (无需用户交互)
PAIRING_METH_PASSKEY_ENTRY = 1, // Passkey Entry (6位数字)
PAIRING_METH_OOB = 2, // Out of Band (带外数据)
PAIRING_METH_NUMERIC_COMP = 3, // Numeric Comparison (数字比较, SC only)
} smp_pairing_method_t;
IO能力与配对方法映射表:
IO能力定义 (bt_common_define.h:1394-1397):
#define BTM_IO_DISPLAY_ONLY 0 // 仅显示
#define BTM_IO_DISPLAY_YESNO 1 // 显示+Yes/No
#define BTM_IO_KEYBOARD_ONLY 2 // 仅键盘
#define BTM_IO_NO_IO 3 // 无IO
Legacy Pairing方法选择:
| Display | Display | Keyboard | No IO
| Only | Yes/No | Only |
--------------------+---------+---------+----------+-------
Display Only | JW | JW | PE(I) | JW
Display Yes/No | JW | JW | PE(I) | JW
Keyboard Only | PE(R) | PE(R) | PE(Both) | JW
No IO | JW | JW | JW | JW
OOB Available | OOB | OOB | OOB | OOB
Secure Connections方法选择:
| Display | Display | Keyboard | No IO
| Only | Yes/No | Only |
--------------------+---------+---------+----------+-------
Display Only | JW | NC | PE(I) | JW
Display Yes/No | NC | NC | PE(I) | JW
Keyboard Only | PE(R) | PE(R) | PE(Both) | JW
No IO | JW | JW | JW | JW
OOB Available | OOB | OOB | OOB | OOB
JW: Just Works
PE: Passkey Entry (I=Initiator输入, R=Responder输入, Both=双向)
NC: Numeric Comparison (数字比较)
12.3 LE Legacy Pairing vs LE Secure Connections
Legacy Pairing特点:
c
// 使用STK (Short Term Key) 作为临时密钥
// 密钥长度: 128 bits
// 加密算法: AES-128-CCM
// 弱点: 易受MITM攻击 (被动窃听)
// Legacy配对密钥生成:
// 1. TK (Temporary Key): 根据配对方法生成
// - Just Works: TK = 0
// - Passkey Entry: TK = 6位数字 (000000-999999)
// - OOB: TK = 128-bit random number
//
// 2. Confirm值计算:
// Mconfirm = c1(TK, Mrand, Pairing Req, Pairing Rsp, Initiating Device, Responding Device)
// Sconfirm = c1(TK, Srand, ...)
//
// 3. STK生成:
// STK = s1(TK, Srand, Mrand)
//
// 4. LTK生成: 在加密链路上随机生成
Secure Connections特点:
c
// 使用ECDH (Elliptic Curve Diffie-Hellman) 公钥密码
// 密钥长度: 256 bits (P-256 curve)
// 抗MITM: 提供主动攻击防护
// 支持Numeric Comparison方法
// SC配对密钥生成:
// 1. 生成P-256公私钥对:
// Local: PKa (public), SKa (private)
// Peer: PKb (public), SKb (private)
//
// 2. 计算DHKey:
// DHKey = P256(SKa, PKb) = P256(SKb, PKa)
//
// 3. 生成Nonce: Na, Nb (128-bit random)
//
// 4. 计算Confirm值 (Numeric Comparison):
// Va = g2(PKax, PKbx, Na, Nb) // 6位数字
// 双方显示Va进行比较
//
// 5. 计算Check值:
// Ea = f5(DHKey, Na, Nb, ...) -> MacKey, LTK
// Ca = f6(MacKey, Na, Nb, ...)
//
// 6. 验证Check:
// 接收对方的Cb, 验证匹配
加密函数 (gap_i.h:166-192):
c
// Legacy Pairing函数
bool smp_e(const uint8_t *key_128_le, const uint8_t *plain_128_le,
gap_key_callback_t cmpl, void *priv); // AES-128加密
// Secure Connections函数
bool smp_f5_gen_key_T(const uint8_t *DHKey_256_le,
gap_key_callback_t func, void *priv); // 生成T
bool smp_f5_gen_mackey_ltk(const uint8_t *key_T_128_le, ...); // 生成MacKey和LTK
bool smp_f6(const uint8_t *W_128_le, ...); // 生成Check值
bool smp_g2(const uint8_t *PKax_U_256_le, ...); // 生成6位数字
bool smp_h6(const uint8_t *W_128_le, ...); // BR/EDR密钥派生
bool smp_h7(const uint8_t *salt_128_le, ...); // BR/EDR密钥派生
12.4 密钥分发 (LTK/IRK/CSRK/EDIV/RAND)
密钥类型:
c
// SMP密钥分发Opcode (gap_i.h:219-223)
SMP_ENCRYPTION_INFO = 0x06, // LTK (Long Term Key)
SMP_CENTRAL_IDENTIFICATION = 0x07, // EDIV + Rand (Legacy only)
SMP_IDENTITY_INFO = 0x08, // IRK (Identity Resolving Key)
SMP_IDENTITY_ADDR_INFO = 0x09, // Identity Address
SMP_SIGNING_INFO = 0x0a, // CSRK (Connection Signature Resolving Key)
密钥用途:
1. LTK (Long Term Key) - 128/256 bits
用途: 链路层加密
分发: Phase 3密钥分发阶段
存储: 用于重连时快速加密
Legacy: 需要EDIV + Rand标识
SC: LTK直接由f5生成,无需EDIV/Rand
2. EDIV (Encrypted Diversifier) - 16 bits
RAND (Random Number) - 64 bits
用途: 标识Legacy Pairing的LTK
流程: 重连时,Slave发送LTK Request(EDIV, Rand)
Master根据EDIV+Rand查找对应的LTK
3. IRK (Identity Resolving Key) - 128 bits
用途: 解析RPA (Resolvable Private Address)
算法: hash = ah(IRK, prand[23:0])
if (hash == rpa[47:24]) -> 地址匹配
隐私保护: 设备使用RPA后,仅持有IRK的设备能识别
4. CSRK (Connection Signature Resolving Key) - 128 bits
用途: 数据签名 (无需加密连接)
算法: Signature = AES-CMAC(CSRK, data)
应用: 无连接数据完整性验证
5. Identity Address - 6 bytes + 1 byte type
用途: 设备的真实身份地址 (Public or Static Random)
作用: 与IRK配对使用,用于地址解析后的设备标识
密钥分发流程:
c
// Initiator Key Distribution (gap_i.h:329-331)
uint8_t init_key_dist; // Bit mask of keys to distribute
#define KEY_DIST_ENC_KEY 0x01 // LTK (+ EDIV/Rand for Legacy)
#define KEY_DIST_ID_KEY 0x02 // IRK + Identity Address
#define KEY_DIST_SIGN_KEY 0x04 // CSRK
#define KEY_DIST_LINK_KEY 0x08 // Link Key (BR/EDR CTKD)
// Responder Key Distribution
uint8_t resp_key_dist; // Bit mask of keys to distribute
// 示例: 双向分发所有密钥
init_key_dist = 0x07; // LTK + IRK + CSRK
resp_key_dist = 0x07; // LTK + IRK + CSRK
12.5 CTKD跨传输密钥派生
CTKD (Cross-Transport Key Derivation) 允许BR/EDR和LE共享安全密钥:
CTKD函数 (gap_i.h:125-128, 186-189):
c
// 通知LTK从Link Key派生
void gap_ctkd_notify_ltk_derived(gap_conn_item_t *bredr_conn,
bt_addr_type_t peer_type,
const bt_bdaddr_t *peer_addr,
bool wait_peer_kdist);
// 通知Link Key从LTK派生
void gap_ctkd_notify_link_key_derived(gap_conn_item_t *conn,
const bt_bdaddr_t *bt_addr,
const uint8_t *link_key,
bool wait_peer_ia);
// Link Key -> LTK派生
bool smp_linkkey_to_iltk(const uint8_t *linkkey, bool ct2,
gap_key_callback_t func, void *priv);
bool smp_iltk_to_ltk(const uint8_t *iltk,
gap_key_callback_t func, void *priv);
// LTK -> Link Key派生
bool smp_ltk_to_ilk(const uint8_t *ltk, bool ct2,
gap_key_callback_t func, void *priv);
bool smp_ilk_to_linkkey(const uint8_t *ilk,
gap_key_callback_t func, void *priv);
CTKD工作流程:
场景1: BR/EDR已配对,LE首次连接
Step 1: BR/EDR已有Link Key
Step 2: LE连接时,使用h6/h7从Link Key派生LTK
ilk = h6(Link_Key, "tmp1")
ltk = h7(ilk, "lebr")
Step 3: 使用派生的LTK加密LE连接
Step 4: 交换IRK (可选)
场景2: LE已配对,BR/EDR首次连接
Step 1: LE已有LTK
Step 2: BR/EDR连接时,使用h6/h7从LTK派生Link Key
iltk = h7(LTK, "brle")
link_key = h6(iltk, "tmp2")
Step 3: 使用派生的Link Key进行BR/EDR配对
Step 4: 交换必要的密钥
优势:
- 避免重复配对
- 提升用户体验
- 维护统一的安全上下文
13. 错误码体系
13.1 HCI错误码 (bt_common_define.h:829-889)
c
typedef uint8_t btif_error_code_t;
// 常见HCI错误码
#define BTIF_BEC_NO_ERROR 0x00 // 成功
#define BTIF_BEC_UNKNOWN_HCI_CMD 0x01 // 未知HCI命令
#define BTIF_BEC_NO_CONNECTION 0x02 // 无连接
#define BTIF_BEC_HARDWARE_FAILURE 0x03 // 硬件故障
#define BTIF_BEC_PAGE_TIMEOUT 0x04 // Page超时
#define BTIF_BEC_AUTHENTICATE_FAILURE 0x05 // 认证失败
#define BTIF_BEC_MISSING_KEY 0x06 // 密钥丢失
#define BTIF_BEC_MEMORY_FULL 0x07 // 内存满
#define BTIF_BEC_CONNECTION_TIMEOUT 0x08 // 连接超时
#define BTIF_BEC_MAX_CONNECTIONS 0x09 // 达到最大连接数
#define BTIF_BEC_MAX_SCO_CONNECTIONS 0x0a // 达到最大SCO连接数
#define BTIF_BEC_ACL_ALREADY_EXISTS 0x0b // ACL连接已存在
#define BTIF_BEC_COMMAND_DISALLOWED 0x0c // 命令不允许
#define BTIF_BEC_LIMITED_RESOURCE 0x0d // 资源受限
#define BTIF_BEC_SECURITY_ERROR 0x0e // 安全错误
#define BTIF_BEC_PERSONAL_DEVICE 0x0f // 个人设备拒绝
#define BTIF_BEC_HOST_TIMEOUT 0x10 // 主机超时
#define BTIF_BEC_UNSUPPORTED_FEATURE 0x11 // 不支持的特性
#define BTIF_BEC_INVALID_HCI_PARM 0x12 // 无效HCI参数
#define BTIF_BEC_USER_TERMINATED 0x13 // 用户终止 (常见断连原因)
#define BTIF_BEC_LOW_RESOURCES 0x14 // 资源不足
#define BTIF_BEC_POWER_OFF 0x15 // 关机
#define BTIF_BEC_LOCAL_TERMINATED 0x16 // 本地终止
#define BTIF_BEC_REPEATED_ATTEMPTS 0x17 // 重复尝试
#define BTIF_BEC_PAIRING_NOT_ALLOWED 0x18 // 不允许配对
#define BTIF_BEC_UNKNOWN_LMP_PDU 0x19 // 未知LMP PDU
#define BTIF_BEC_UNSUPPORTED_REMOTE 0x1a // 远端不支持
#define BTIF_BEC_SCO_OFFSET_REJECT 0x1b // SCO偏移拒绝
#define BTIF_BEC_SCO_INTERVAL_REJECT 0x1c // SCO间隔拒绝
#define BTIF_BEC_SCO_AIR_MODE_REJECT 0x1d // SCO空口模式拒绝
#define BTIF_BEC_INVALID_LMP_PARM 0x1e // 无效LMP参数
#define BTIF_BEC_UNSPECIFIED_ERR 0x1f // 未指定错误
#define BTIF_BEC_UNSUPPORTED_LMP_PARM 0x20 // 不支持的LMP参数
#define BTIF_BEC_ROLE_CHG_NOT_ALLOWED 0x21 // 不允许角色切换
#define BTIF_BEC_LMP_RESPONSE_TIMEOUT 0x22 // LMP响应超时
#define BTIF_BEC_LMP_TRANS_COLLISION 0x23 // LMP传输冲突
#define BTIF_BEC_LMP_PDU_NOT_ALLOWED 0x24 // LMP PDU不允许
#define BTIF_BEC_ENCRYP_MODE_NOT_ACC 0x25 // 加密模式不接受
#define BTIF_BEC_UNIT_KEY_USED 0x26 // Unit Key使用 (不安全)
#define BTIF_BEC_QOS_NOT_SUPPORTED 0x27 // QoS不支持
#define BTIF_BEC_INSTANT_PASSED 0x28 // Instant已过
#define BTIF_BEC_PAIR_UNITKEY_NO_SUPP 0x29 // 配对Unit Key不支持
#define BTIF_BEC_NOT_FOUND 0xf1 // 未找到
#define BTIF_BEC_REQUEST_CANCELLED 0xf2 // 请求取消
// SDP错误码
#define BTIF_BEC_INVALID_SDP_PDU 0xd1 // 无效SDP PDU
#define BTIF_BEC_SDP_DISCONNECT 0xd2 // SDP断连
#define BTIF_BEC_SDP_NO_RESOURCES 0xd3 // SDP资源不足
#define BTIF_BEC_SDP_INTERNAL_ERR 0xd4 // SDP内部错误
#define BTIF_BEC_STORE_LINK_KEY_ERR 0xe0 // 存储Link Key错误
// BES厂商错误码
#define BT_ECODE_DISCONNECT_ITSELF 0xba // 主动断连
#define BT_ECODE_IBRT_SLAVE_CLEANUP 0xbb // IBRT从设备清理
#define BT_ECODE_SDP_OPEN_TIMEOUT 0xbc // SDP打开超时
#define BT_ECODE_SDP_ClIENT_TX_TIMEOUT 0xbd // SDP客户端发送超时
#define BTIF_BEC_BT_LINK_REAL_DISCONNECTED 0xb8 // BT链路真正断连
#define BTIF_BEC_BT_CANCEL_PAGE 0xb9 // 取消Page
13.2 错误码分类与处理
按严重程度分类:
c
// 1. 正常断连 (可忽略)
case BTIF_BEC_USER_TERMINATED: // 0x13 - 用户主动断连
case BTIF_BEC_LOCAL_TERMINATED: // 0x16 - 本地主动断连
case BTIF_BEC_POWER_OFF: // 0x15 - 关机
// 正常流程,无需告警
// 2. 超时类错误 (可能是信号问题)
case BTIF_BEC_PAGE_TIMEOUT: // 0x04 - Page超时
case BTIF_BEC_CONNECTION_TIMEOUT: // 0x08 - 连接超时
case BTIF_BEC_HOST_TIMEOUT: // 0x10 - 主机超时
case BTIF_BEC_LMP_RESPONSE_TIMEOUT: // 0x22 - LMP超时
// 检查信号强度、距离、干扰
// 3. 安全类错误 (需要重新配对)
case BTIF_BEC_AUTHENTICATE_FAILURE: // 0x05 - 认证失败
case BTIF_BEC_MISSING_KEY: // 0x06 - 密钥丢失
case BTIF_BEC_SECURITY_ERROR: // 0x0e - 安全错误
case BTIF_BEC_PAIRING_NOT_ALLOWED: // 0x18 - 不允许配对
// 清除配对记录,重新配对
// 4. 资源类错误 (系统问题)
case BTIF_BEC_MEMORY_FULL: // 0x07 - 内存满
case BTIF_BEC_LIMITED_RESOURCE: // 0x0d - 资源受限
case BTIF_BEC_LOW_RESOURCES: // 0x14 - 资源不足
case BTIF_BEC_MAX_CONNECTIONS: // 0x09 - 达到最大连接数
// 释放资源,检查内存泄漏
// 5. 硬件类错误 (严重)
case BTIF_BEC_HARDWARE_FAILURE: // 0x03 - 硬件故障
// 重启设备,检查硬件
错误处理示例:
c
void app_bt_handle_disconnect(uint8_t device_id, uint8_t error_code)
{
LOG("Disconnect: device=%d reason=0x%02x", device_id, error_code);
switch (error_code) {
case BTIF_BEC_CONNECTION_TIMEOUT:
// 连接超时: 可能是距离太远或信号干扰
LOG("Connection timeout - check signal quality");
// 可以尝试重连
app_bt_retry_connection(device_id);
break;
case BTIF_BEC_AUTHENTICATE_FAILURE:
// 认证失败: 清除配对信息
LOG("Authentication failed - clear pairing");
nv_record_delete_ble_pairing_info(device_id);
break;
case BTIF_BEC_USER_TERMINATED:
case BTIF_BEC_LOCAL_TERMINATED:
// 正常断连: 无需特殊处理
LOG("Normal disconnection");
break;
default:
// 其他错误: 记录日志
LOG("Unexpected error: 0x%02x", error_code);
break;
}
}
14. LMP命令与扩展Opcode
14.1 LMP Opcode定义
LMP (Link Manager Protocol) 用于控制链路层行为:
基本LMP Opcode (bt_drv_interface.h:22-86):
c
// LMP基础命令 (Opcode 1-66)
#define LMP_NAME_REQ_OPCODE 1 // 名称请求
#define LMP_NAME_RES_OPCODE 2 // 名称响应
#define LMP_ACCEPTED_OPCODE 3 // 接受
#define LMP_NOT_ACCEPTED_OPCODE 4 // 不接受
#define LMP_CLK_OFF_REQ_OPCODE 5 // 时钟偏移请求
#define LMP_CLK_OFF_RES_OPCODE 6 // 时钟偏移响应
#define LMP_DETACH_OPCODE 7 // 分离
#define LMP_INRAND_OPCODE 8 // 输入随机数
#define LMP_COMBKEY_OPCODE 9 // 组合密钥
#define LMP_UNITKEY_OPCODE 10 // Unit密钥
#define LMP_AURAND_OPCODE 11 // 认证随机数
#define LMP_SRES_OPCODE 12 // 认证响应
#define LMP_TEMPRAND_OPCODE 13 // 临时随机数
#define LMP_TEMPKEY_OPCODE 14 // 临时密钥
#define LMP_ENC_MODE_REQ_OPCODE 15 // 加密模式请求
#define LMP_ENC_KEY_SIZE_REQ_OPCODE 16 // 加密密钥大小请求
#define LMP_START_ENC_REQ_OPCODE 17 // 开始加密请求
#define LMP_STOP_ENC_REQ_OPCODE 18 // 停止加密请求
#define LMP_SWITCH_REQ_OPCODE 19 // 主从切换请求
#define LMP_HOLD_OPCODE 20 // Hold模式
#define LMP_HOLD_REQ_OPCODE 21 // Hold请求
#define LMP_SNIFF_REQ_OPCODE 23 // Sniff请求
#define LMP_UNSNIFF_REQ_OPCODE 24 // 退出Sniff
#define LMP_PARK_REQ_OPCODE 25 // Park请求
#define LMP_INCR_PWR_REQ_OPCODE 31 // 增加功率请求
#define LMP_DECR_PWR_REQ_OPCODE 32 // 降低功率请求
#define LMP_MAX_PWR_OPCODE 33 // 最大功率
#define LMP_MIN_PWR_OPCODE 34 // 最小功率
#define LMP_AUTO_RATE_OPCODE 35 // 自动速率
#define LMP_PREF_RATE_OPCODE 36 // 首选速率
#define LMP_VER_REQ_OPCODE 37 // 版本请求
#define LMP_VER_RES_OPCODE 38 // 版本响应
#define LMP_FEATS_REQ_OPCODE 39 // 特性请求
#define LMP_FEATS_RES_OPCODE 40 // 特性响应
#define LMP_QOS_OPCODE 41 // QoS
#define LMP_QOS_REQ_OPCODE 42 // QoS请求
#define LMP_SCO_LINK_REQ_OPCODE 43 // SCO链路请求
#define LMP_RMV_SCO_LINK_REQ_OPCODE 44 // 移除SCO链路请求
#define LMP_MAX_SLOT_OPCODE 45 // 最大Slot
#define LMP_MAX_SLOT_REQ_OPCODE 46 // 最大Slot请求
#define LMP_TIMING_ACCU_REQ_OPCODE 47 // 时序精度请求
#define LMP_TIMING_ACCU_RES_OPCODE 48 // 时序精度响应
#define LMP_SETUP_CMP_OPCODE 49 // 设置完成
#define LMP_HOST_CON_REQ_OPCODE 51 // 主机连接请求
#define LMP_SLOT_OFF_OPCODE 52 // Slot偏移
#define LMP_SUPV_TO_OPCODE 55 // 监督超时
#define LMP_TEST_ACTIVATE_OPCODE 56 // 测试激活
#define LMP_TEST_CTRL_OPCODE 57 // 测试控制
#define LMP_ENC_KEY_SIZE_MASK_REQ_OPCODE 58 // 加密密钥大小掩码请求
#define LMP_ENC_KEY_SIZE_MASK_RES_OPCODE 59 // 加密密钥大小掩码响应
#define LMP_SET_AFH_OPCODE 60 // 设置AFH (自适应跳频)
#define LMP_ENCAPS_HDR_OPCODE 61 // 封装头
#define LMP_ENCAPS_PAYL_OPCODE 62 // 封装载荷
#define LMP_SP_CFM_OPCODE 63 // 简单配对Confirm
#define LMP_SP_NB_OPCODE 64 // 简单配对Number
#define LMP_DHKEY_CHK_OPCODE 65 // DHKey Check
#define LMP_PAUSE_ENC_AES_REQ_OPCODE 66 // 暂停AES加密请求
LMP扩展Opcode (Escape 4) (bt_drv_interface.h:88-114):
c
// 使用Escape 4 (Opcode 127, ExtOpcode 1-34)
#define LMP_ACCEPTED_EXT_EXTOPCODE 1 // 接受 (扩展)
#define LMP_NOT_ACCEPTED_EXT_EXTOPCODE 2 // 不接受 (扩展)
#define LMP_FEATS_REQ_EXT_EXTOPCODE 3 // 特性请求 (扩展)
#define LMP_FEATS_RES_EXT_EXTOPCODE 4 // 特性响应 (扩展)
#define LMP_CLK_ADJ_EXTOPCODE 5 // 时钟调整
#define LMP_CLK_ADJ_ACK_EXTOPCODE 6 // 时钟调整确认
#define LMP_CLK_ADJ_REQ_EXTOPCODE 7 // 时钟调整请求
#define LMP_PKT_TYPE_TBL_REQ_EXTOPCODE 11 // 数据包类型表请求
#define LMP_ESCO_LINK_REQ_EXTOPCODE 12 // eSCO链路请求
#define LMP_RMV_ESCO_LINK_REQ_EXTOPCODE 13 // 移除eSCO链路请求
#define LMP_CH_CLASS_REQ_EXTOPCODE 16 // 信道分类请求
#define LMP_CH_CLASS_EXTOPCODE 17 // 信道分类
#define LMP_SSR_REQ_EXTOPCODE 21 // Sniff Subrating请求
#define LMP_SSR_RES_EXTOPCODE 22 // Sniff Subrating响应
#define LMP_PAUSE_ENC_REQ_EXTOPCODE 23 // 暂停加密请求
#define LMP_RESUME_ENC_REQ_EXTOPCODE 24 // 恢复加密请求
#define LMP_IO_CAP_REQ_EXTOPCODE 25 // IO能力请求 (SSP)
#define LMP_IO_CAP_RES_EXTOPCODE 26 // IO能力响应 (SSP)
#define LMP_NUM_COMPARISON_FAIL_EXTOPCODE 27 // 数字比较失败
#define LMP_PASSKEY_FAIL_EXTOPCODE 28 // Passkey失败
#define LMP_OOB_FAIL_EXTOPCODE 29 // OOB失败
#define LMP_KEYPRESS_NOTIF_EXTOPCODE 30 // 按键通知
#define LMP_PWR_CTRL_REQ_EXTOPCODE 31 // 功率控制请求
#define LMP_PWR_CTRL_RES_EXTOPCODE 32 // 功率控制响应
#define LMP_PING_REQ_EXTOPCODE 33 // Ping请求
#define LMP_PING_RES_EXTOPCODE 34 // Ping响应
LMP命令使用场景:
连接建立:
1. LMP_VER_REQ/RES - 交换版本信息
2. LMP_FEATS_REQ/RES - 交换特性支持
3. LMP_NAME_REQ/RES - 获取设备名称 (可选)
4. LMP_CLK_OFF_REQ/RES - 时钟偏移同步
配对认证 (Simple Secure Pairing):
1. LMP_IO_CAP_REQ/RES - 交换IO能力
2. LMP_SP_CFM - 简单配对Confirm (Passkey Entry)
3. LMP_SP_NB - 简单配对Number (Numeric Comparison)
4. LMP_DHKEY_CHK - DHKey Check (SC)
加密:
1. LMP_ENC_MODE_REQ - 请求加密模式
2. LMP_ENC_KEY_SIZE_REQ - 协商密钥大小
3. LMP_START_ENC_REQ - 开始加密
4. LMP_PAUSE_ENC_REQ - 暂停加密 (密钥刷新)
5. LMP_RESUME_ENC_REQ - 恢复加密
功率控制:
1. LMP_INCR_PWR_REQ - 请求增加功率
2. LMP_DECR_PWR_REQ - 请求降低功率
3. LMP_MAX_PWR - 使用最大功率
4. LMP_MIN_PWR - 使用最小功率
低功耗模式:
1. LMP_SNIFF_REQ - 进入Sniff模式
2. LMP_UNSNIFF_REQ - 退出Sniff模式
3. LMP_SSR_REQ/RES - Sniff Subrating (降低Sniff功耗)
4. LMP_HOLD_REQ - 进入Hold模式
5. LMP_PARK_REQ - 进入Park模式
链路质量:
1. LMP_SET_AFH - 设置自适应跳频
2. LMP_CH_CLASS_REQ - 信道分类请求
3. LMP_AUTO_RATE - 自动速率调整
4. LMP_MAX_SLOT_REQ - 协商最大Slot数
15. Slot时间片调度机制深度解析
15.1 BLE Connection Event Slot分配
BLE连接事件占用特定的时间片进行数据交换:
连接间隔与Slot关系:
BLE连接间隔 = N × 1.25ms (N: 6-3200)
最小间隔: 7.5ms (N=6)
最大间隔: 4000ms (N=3200)
每个Connection Event:
|<------- Connection Interval -------->|
| CE_0 | CE_1 | CE_2 | ...
|--1.25ms--|--1.25ms--|--1.25ms--|
Connection Event内部:
| AnchorPoint | Tx | Rx | Tx | Rx | ... |
|<----------- Connection Event -------->|
最长持续时间受限于间隔
Connection Event Slot分配:
- 每个LL PDU传输占用一个Slot
- Slot长度取决于PDU大小和PHY速率
- Master先发送 (TX slot)
- Slave响应 (RX slot)
- 交替进行直到事件结束或无数据
BLE Slot时长计算:
c
// 1M PHY:
// T_IFS (Inter Frame Space) = 150us
// Preamble = 8us (1 byte @ 1Mbps)
// Access Address = 32us (4 bytes)
// PDU Header = 16us (2 bytes)
// Payload = N × 8us
// CRC = 24us (3 bytes)
// Total = 8 + 32 + 16 + N×8 + 24 + 150 = 230 + N×8 (us)
// 最小Slot (空PDU): 230us
// 最大Slot (251字节): 230 + 251×8 = 2238us ≈ 2.24ms
// 2M PHY: 时间减半
// Coded PHY (S=8): 时间×8 (用于长距离)
15.2 TWS私有Slot调度优化
TWS (True Wireless Stereo) 场景下的特殊Slot调度:
TWS Slot优化策略:
c
// TWS场景: 主耳+从耳+手机
// 需要在同一个Connection Interval内完成:
// 1. 手机 -> 主耳 (音频数据)
// 2. 主耳 -> 从耳 (音频数据+控制信令)
// 3. 从耳 -> 主耳 (状态反馈)
// 时序优化 (假设15ms连接间隔):
// |<-------- 15ms Connection Interval -------->|
// | Phone->Master | Master->Slave | Slave->Master |
// | 5ms | 5ms | 5ms |
// CE0 CE1 (TWS私有) CE2
// Slot对齐策略:
void bt_drv_reg_op_music_link_config(uint16_t active_link,
uint8_t active_role,
uint16_t inactive_link,
uint8_t inactive_role);
// TWS Slot调度函数
uint32_t bt_drv_reg_op_get_multi_ibrt_slice(uint8_t active);
TWS时间片分配示例:
标准A2DP场景 (非TWS):
Connection Interval = 15ms
|<----- 15ms ----->|<----- 15ms ----->|
| Phone -> Earbud | Phone -> Earbud |
| 数据传输 | 数据传输 |
TWS场景 (Enhanced IBRT):
|<---------- 15ms Connection Interval ---------->|
| Phone | Master | Slave | Reserved |
| -> M | -> S | -> M | |
| 5ms 3slots| 5ms 3slots | 5ms 3slots| |
\ \ \
\__音频 \__音频转发 \__状态同步
下发
优化点:
1. Slot精确对齐: 避免冲突
2. 优先级管理: A2DP > BLE > SCO
3. Sniff协调: TWS链路同步进入Sniff
4. 延迟补偿: 主从耳音频同步
15.3 A2DP/SCO/BLE多链路Slot协调
多链路并发时的Slot仲裁机制:
Slot优先级定义:
c
// BLE连接参数优先级 (app_ble.c:315-334)
enum {
PRIORITY_NORMAL = 0, // 默认/空闲
PRIORITY_ABOVE_NORMAL0, // A2DP
PRIORITY_ABOVE_NORMAL1, // AI流
PRIORITY_ABOVE_NORMAL2, // HFP/服务发现
PRIORITY_HIGH, // OTA
};
// Slot仲裁优先级 (由低到高):
// 1. BLE Connection (可延迟)
// 2. BLE Advertising (可跳过)
// 3. A2DP Streaming (需要稳定)
// 4. SCO Voice (最高优先级,实时)
多链路Slot分配策略:
场景1: A2DP + BLE同时活跃
A2DP Slot: 每15ms一次 (retransmission window)
BLE Slot: 每45ms一次 (可调整)
时间轴:
|<-15ms->|<-15ms->|<-15ms->|<-15ms->|
| A2DP |BLE+A2DP| A2DP |BLE+A2DP|
3 slots 1+2slots 3 slots 1+2slots
场景2: SCO + A2DP + BLE
SCO Slot: 每7.5ms一次 (HV3: 3 slots)
A2DP Slot: 每15ms一次 (降低为2 slots)
BLE Slot: 每60ms一次 (降低频率)
时间轴:
|<-7.5ms->|<-7.5ms->|<-7.5ms->|<-7.5ms->|
| SCO | SCO+A2DP| SCO | SCO+BLE |
3 slots 2+1 slot 3 slots 2+1 slot
冲突解决:
1. SCO优先: 强制占用Slot
2. A2DP降级: 减少packet size或retransmission
3. BLE延迟: 增加connection interval
4. 动态调整: 根据实时负载
Slot冲突检测与协调:
c
// 检查Slot冲突
void bt_drv_reg_op_cs_monitor(void);
// Slot触发器设置
void bt_syn_set_tg_ticks(uint32_t val, uint16_t conhdl,
uint8_t mode, uint8_t trig_route,
bool no_link_trig);
// 取消Slot触发
void bt_syn_cancel_tg_ticks(uint8_t trig_route);
// Slot触发模式
#define ACL_TRIGGLE_MODE 1 // ACL连接触发
#define SCO_TRIGGLE_MODE 2 // SCO连接触发
15.3.1 SCO与BLE物理层时间片冲突深度分析
问题背景: SCO (Synchronous Connection-Oriented) 是蓝牙BR/EDR用于实时语音传输的链路,具有最高的时间片优先级。当SCO与BLE同时激活时,在物理层(RF射频层)会产生时间片竞争冲突。
物理层时间片竞争:
物理层资源冲突示意图:
单个蓝牙控制器(BES芯片)只有一个射频收发器
├─ BR/EDR (Classic Bluetooth)
│ ├─ ACL (异步数据链路)
│ └─ SCO/eSCO (同步语音链路) ← 最高优先级
│
└─ BLE (Low Energy)
├─ Advertising (广播)
├─ Scanning (扫描)
└─ Connection (连接)
时间轴上的Slot竞争:
|<---- BT_CLK = 312.5us ---->|<---- BT_CLK = 312.5us ---->|
| SCO Slot (reserved) | BLE Connection Event |
↑ ↑
强制占用 可能被打断或延迟
SCO周期性占用:
HV3: 每7.5ms占用3个Slot (1.875ms)
EV3: 每7.5ms占用3个Slot (1.875ms)
mSBC: 每7.5ms占用3个Slot (eSCO窗口)
BLE连接事件:
Connection Interval: 可配置 (7.5ms - 4000ms)
Connection Event长度: 取决于数据量 (最长可达Connection Interval)
时间片冲突类型:
c
// 类型1: SCO Slot完全覆盖BLE Connection Event
// 现象: BLE连接事件被跳过,导致supervision timeout或丢包
时间轴:
|<------- 7.5ms ------->|<------- 7.5ms ------->|
| SCO | ... | SCO | ... | SCO | ... | SCO | ... |
^BLE CE(被打断) ^BLE CE(被跳过)
// 类型2: BLE连接间隔与SCO周期不同步
// 现象: 周期性地发生冲突,导致音质抖动或BLE延迟增加
SCO周期: |----7.5ms----|----7.5ms----|----7.5ms----|
BLE间隔: |------15ms------|------15ms------|
冲突点: X X X
(每次都碰撞) (每次都碰撞) (持续碰撞)
// 类型3: 多链路并发场景
// SCO + A2DP + BLE同时活跃
时间轴 (最坏情况):
|<----- 7.5ms ----->|<----- 7.5ms ----->|
| SCO | A2DP | BLE | SCO | A2DP | BLE |
3slot 2slot ? 3slot 2slot ?
^ ^
BLE被挤压 BLE被延迟
系统应对策略:
策略1: BLE连接参数自适应调整 (app_ble.c:1024-1039):
c
// 检测SCO状态并动态调整BLE连接间隔
static void fp_update_ble_connect_param_timer_handler(void const *param)
{
if (delay_update_conidx != GAP_INVALID_CONIDX) {
// 关键判断: 检测SCO是否激活
if (amgr_is_bluetooth_sco_on()) {
// ✅ SCO激活 -> 强制切换到HFP_ON模式
// HFP_ON: 60ms连接间隔 (48 × 1.25ms)
// 目的: 减少BLE占用时间片的频率,为SCO让路
app_ble_update_conn_param_mode_of_specific_connection(
delay_update_conidx, BLE_CONN_PARAM_MODE_HFP_ON, true);
} else {
// ❌ SCO未激活 -> 恢复到默认模式
// DEFAULT: 45ms连接间隔 (36 × 1.25ms)
app_ble_update_conn_param_mode_of_specific_connection(
delay_update_conidx, BLE_CONN_PARAM_MODE_DEFAULT, true);
}
delay_update_conidx = GAP_INVALID_CONIDX;
}
}
// 连接参数对比:
// DEFAULT模式: 45ms间隔 -> 每秒请求22次BLE CE
// HFP_ON模式: 60ms间隔 -> 每秒请求16次BLE CE
// 降低频率: 减少27% -> 更多时间片留给SCO
策略2: TWS Poll间隔在SCO场景下的动态调整 (bt_drv_reg_op.cpp:1514-1522):
c
// TWS私有链路在SCO场景下的Slot调整
void btdrv_reg_op_set_private_tws_poll_interval(
uint16_t poll_interval, // 正常场景下的poll间隔
uint16_t poll_interval_in_sco) // SCO场景下的poll间隔
{
// poll_interval必须是4的倍数
ASSERT_ERR((poll_interval % 4 == 0));
// 在SCO激活时,TWS链路会自动切换到poll_interval_in_sco
// 目的: 降低主从耳之间的同步频率,减少与SCO的冲突
// 示例配置:
// 正常模式: poll_interval = 20ms (32 slots)
// SCO模式: poll_interval_in_sco = 40ms (64 slots)
// 效果: TWS同步频率降低50%,为SCO腾出更多Slot
}
策略3: 物理层Slot仲裁优先级 (硬件控制器层):
c
// BES蓝牙控制器的Slot仲裁优先级(固定):
// Priority Level (由高到低):
//
// 1. SCO/eSCO (实时语音)
// - 周期性强制占用,不可被打断
// - HV3: 每7.5ms占用3个Slot
// - Latency: 0 (零延迟)
//
// 2. ACL Sniff Anchor Point (ACL同步点)
// - Sniff模式下的必须监听点
// - 丢失会导致连接超时
//
// 3. BLE Connection Event (BLE连接事件)
// - 可以被SCO打断
// - 可以延迟到下一个Connection Interval
// - Supervision Timeout保护: 2秒
//
// 4. ACL Active Mode (ACL活跃传输)
// - A2DP音频流传输
// - 可以降低packet type (DH5->DH3->DM3)
//
// 5. BLE Advertising (BLE广播)
// - 优先级最低
// - 可以跳过广播事件
//
// 硬件实现:
// - Slot Arbiter硬件模块自动处理冲突
// - SCO Slot预留: 在SCO建立时预留固定时间片
// - BLE CE延迟: 如果检测到冲突,自动推迟到下一个窗口
策略4: 15ms最小间隔保护机制 (app_ble.c:1094-1099):
c
// 防止BLE连接间隔过短导致频繁冲突
static void app_ble_conn_param_update_req(gap_conn_update_req_t *update_req)
{
uint16_t conn_interval_min_1_25ms = update_req->params_req.conn_interval_min_1_25ms;
uint32_t conn_interval_min_us = conn_interval_min_1_25ms * 1250;
// ⚠️ 如果BLE连接间隔 < 15ms
// 问题: BLE每15ms就会请求时间片,与SCO (7.5ms周期) 产生频繁碰撞
//
// SCO周期: |--7.5ms--|--7.5ms--|--7.5ms--|
// BLE (10ms): |----10ms----|----10ms----|
// 冲突点: X X
// (每次都撞) (每次都撞)
//
// 如果允许< 15ms间隔:
// - BLE占用过于频繁,SCO语音可能出现卡顿
// - 控制器Slot仲裁压力增大
// - 系统功耗上升
if (conn_interval_min_us < 15000) {
// 拒绝过短的间隔,延迟10秒后重新评估
fp_update_ble_connect_param_start(gap_zero_based_conidx(update_req->con_idx));
accept = true; // 暂时接受,但稍后会强制调整
}
gap_update_le_conn_parameters_rsp(update_req->con_idx,
&update_req->params_req,
accept);
}
// 15ms的选择原因:
// - SCO周期: 7.5ms (HV3/EV3)
// - 15ms = 2 × SCO周期
// - BLE每隔2个SCO周期请求一次时间片,避免每次都冲突
实际场景时序分析:
场景: 通话中 + BLE连接 + A2DP暂停
时间轴 (以Slot为单位, 1 Slot = 0.625ms):
|<------------- 7.5ms (12 slots) ------------>|<------------- 7.5ms (12 slots) ------------>|
| SCO | SCO | SCO | ... | ... | BLE | ... | SCO | SCO | SCO | ... | ... | BLE | ... |
TX/RX TX/RX TX/RX CE TX/RX TX/RX TX/RX CE
0-1.875ms: SCO强制占用 (3 slots)
1.875-7.5ms: 空闲/其他链路
5-6ms: BLE Connection Event (如果没有与SCO冲突)
7.5-9.375ms: 下一个SCO周期开始
BLE Connection Interval = 60ms (HFP_ON模式)
每60ms只请求一次BLE CE,大部分时间片留给SCO
对比: 如果BLE Interval = 10ms
|<-- 10ms -->|<-- 10ms -->|<-- 10ms -->|
每10ms请求一次,几乎每次都与SCO碰撞
SCO: X X X X X
碰撞 碰撞 碰撞 碰撞 碰撞
日志分析示例 (R_COM4_time_data@01-20_15-49-45.log):
# 场景1: 正常BLE连接 (无SCO)
[18:30:45.123] [BLE] Connection interval: 36 (45ms) - DEFAULT模式
[18:30:45.168] [BLE] Connection Event: CE_0, latency=0ms
[18:30:45.213] [BLE] Connection Event: CE_1, latency=0ms
# 场景2: 来电,SCO建立
[18:31:20.456] [SCO] SCO connection setup: codec=mSBC, interval=7.5ms
[18:31:20.458] [BLE] Detect SCO active, update conn param to HFP_ON
[18:31:20.460] [BLE] Update interval: 48 (60ms) ← 自动延长间隔
[18:31:20.520] [BLE] Connection Event: CE_5, latency=2ms ← 被SCO延迟了2ms
[18:31:20.580] [BLE] Connection Event: CE_6, latency=0ms
# 场景3: 通话结束,SCO断开
[18:32:50.789] [SCO] SCO disconnected
[18:32:50.800] [BLE] Restore conn param to DEFAULT
[18:32:50.802] [BLE] Update interval: 36 (45ms) ← 恢复到默认间隔
性能指标对比:
| 场景 | BLE间隔 | SCO状态 | BLE丢包率 | SCO音质 | 系统负载 |
|---|---|---|---|---|---|
| 仅BLE | 45ms | 无 | 0% | N/A | 低 |
| BLE+SCO (无优化) | 45ms | 7.5ms周期 | 5-10% | 偶尔卡顿 | 高 |
| BLE+SCO (优化后) | 60ms | 7.5ms周期 | <1% | 稳定 | 中 |
| BLE+SCO (激进) | 15ms | 7.5ms周期 | 15-20% | 严重卡顿 | 很高 |
小结:
- 根本原因: 单射频控制器时间片竞争,SCO具有最高优先级
- 核心策略: 检测SCO状态,自动延长BLE连接间隔(45ms→60ms)
- 协同优化: TWS Poll间隔同步调整,减少主从耳同步对SCO的影响
- 保护机制: 拒绝<15ms的BLE间隔请求,避免频繁冲突
- 硬件支持: Slot Arbiter自动仲裁,SCO预留时间片
15.4 Sniff模式的Slot管理
Sniff模式通过减少活跃Slot降低功耗:
Sniff参数:
c
typedef struct {
uint16_t sniff_max_interval; // 最大Sniff间隔 (slots, 0.625ms)
uint16_t sniff_min_interval; // 最小Sniff间隔
uint16_t sniff_attempt; // Sniff尝试次数
uint16_t sniff_timeout; // Sniff超时 (slots)
} sniff_params_t;
// 示例: 轻度Sniff (音乐播放)
// Interval: 800 slots (500ms)
// Attempt: 4 slots (2.5ms)
// Timeout: 1 slots (0.625ms)
// 示例: 深度Sniff (空闲状态)
// Interval: 2000 slots (1.25s)
// Attempt: 2 slots (1.25ms)
// Timeout: 1 slots (0.625ms)
Sniff Slot时序:
Normal模式 (Active所有Slot):
|-----|-----|-----|-----|-----|-----|-----|-----|
| TX | RX | TX | RX | TX | RX | TX | RX |
连续活跃,功耗高
Sniff模式 (周期性唤醒):
|<------- Sniff Interval = 500ms -------->|
|-----| |-----|
| TX/RX| | TX/RX|
Attempt Attempt
2.5ms 2.5ms
Sniff Subrating (SSR) - 进一步优化:
|<- Sniff Int ->|<- Sniff Int ->|<- Sniff Int ->|
|-----| | |-----| |
| TX/RX| | | TX/RX| |
实际监听 跳过(无数据) 实际监听
功耗对比:
Active: 100% duty cycle
Sniff: 0.5% duty cycle (500ms间隔,2.5ms attempt)
Sniff+SSR: 0.25% duty cycle (每两个interval监听一次)
Sniff Subrating (SSR):
c
// LMP SSR协商 (bt_drv_interface.h:101-102)
#define LMP_SSR_REQ_EXTOPCODE 21 // SSR请求
#define LMP_SSR_RES_EXTOPCODE 22 // SSR响应
// SSR工作原理:
// 1. 在Sniff基础上进一步降低监听频率
// 2. Subrate: 每N个Sniff Interval监听一次
// 3. Latency: 允许的最大延迟 (用于数据传输)
// 示例: Subrate = 3
// Sniff Interval = 500ms
// 实际监听周期 = 500ms × 3 = 1.5s
// 适用场景:
// - 长时间空闲 (无按键、无音频)
// - 仅保持连接 (心跳)
// - 快速恢复: 检测到数据后立即退出SSR
16. 总结
本文档详细介绍了BLE低功耗蓝牙的完整技术实现,包括:
- 协议基础: BLE协议栈层次、角色定义、地址类型
- 广播机制: 快慢广播切换、低功耗优化策略
- 连接管理: 连接建立流程、参数动态调整
- 数据传输: GATT服务架构、MTU交换、双向数据传输
- 状态管理: 完整的状态机设计和转换逻辑
- 代码实现: 详细的代码分析和实现细节
- 协议时序: HCI层面的完整时序分析
- 硬件RF层: RF配置、功率控制、时钟机制
- 物理层: Packet类型、AFH自适应跳频
- SMP安全: 26状态机、4种配对方法、密钥分发、CTKD
- 错误码: 完整的HCI错误码体系与处理策略
- LMP命令: 基本和扩展Opcode详解
- Slot调度: BLE/TWS/多链路/Sniff模式时间片管理
通过本文档,开发者可以:
- 理解BLE的工作原理和协议栈结构
- 掌握广播、连接、数据传输的完整流程
- 深入了解底层RF、Slot调度、SMP安全机制
- 学习低功耗优化和性能调优技巧
- 快速定位和解决BLE相关问题
- 掌握TWS和多链路并发的优化策略
建议新手按照文档顺序学习,从基础协议开始,逐步深入到底层实现和高级优化技巧。
附录
A. 术语表
| 术语 | 全称 | 说明 |
|---|---|---|
| BLE | Bluetooth Low Energy | 低功耗蓝牙 |
| GAP | Generic Access Profile | 通用访问配置文件 |
| GATT | Generic Attribute Profile | 通用属性配置文件 |
| ATT | Attribute Protocol | 属性协议 |
| L2CAP | Logical Link Control and Adaptation Protocol | 逻辑链路控制和适配协议 |
| HCI | Host Controller Interface | 主机控制器接口 |
| MTU | Maximum Transmission Unit | 最大传输单元 |
| CCCD | Client Characteristic Configuration Descriptor | 客户端特征配置描述符 |
| UUID | Universally Unique Identifier | 通用唯一识别码 |
| RPA | Resolvable Private Address | 可解析私有地址 |
| IRK | Identity Resolving Key | 身份解析密钥 |
| LTK | Long Term Key | 长期密钥 |
| PHY | Physical Layer | 物理层 |
附录C. API快速参考
本章节提供了文档中新增的所有重要API的快速查找参考。
C.1 地址管理API
| API | 功能 | 文件位置 |
|---|---|---|
bes_ble_gap_set_local_irk() |
设置本地IRK | bthost/adapter/inc/ble/gap/bes_gap_api.h:961 |
bes_ble_gap_get_current_ble_addr() |
获取本地Identity Address | bthost/adapter/inc/ble/gap/bes_gap_api.h:1123 |
bes_ble_gap_get_local_rpa_addr() |
获取本地RPA地址 | bthost/adapter/inc/ble/gap/bes_gap_api.h:1138 |
bes_ble_gap_read_local_rpa_by_adv_hdl() |
通过广播handle读取RPA | bthost/adapter/inc/ble/gap/bes_gap_api.h:1146 |
bes_ble_gap_set_rpa_timeout() |
设置RPA超时时间 | bthost/adapter/inc/ble/gap/bes_gap_api.h:641 |
bes_ble_gap_set_rpa_list() |
设置RPA解析列表 | bthost/adapter/inc/ble/gap/bes_gap_api.h:629 |
bes_ble_gap_set_bonded_devs_rpa_list() |
添加已配对设备到解析列表 | bthost/adapter/inc/ble/gap/bes_gap_api.h:636 |
C.2 广播数据构建API
| API | 功能 | 文件位置 |
|---|---|---|
gap_dt_add_raw_data() |
添加原始广播数据 | bthost/adapter/inc/adapter_service/gap_service.h:3107 |
gap_dt_add_flags() |
添加Flags字段 | bthost/adapter/inc/adapter_service/gap_service.h |
app_ble_dt_set_flags() |
设置广播Flags | bthost/service/ble_app_new/inc/app_ble.h:451 |
app_ble_dt_set_local_name() |
设置本地名称 | bthost/service/ble_app_new/inc/app_ble.h:452 |
gap_dt_add_service_uuid_16() |
添加16-bit UUID | bthost/adapter/inc/adapter_service/gap_service.h |
gap_dt_add_service_uuid_128() |
添加128-bit UUID | bthost/adapter/inc/adapter_service/gap_service.h |
C.3 广播控制API
| API | 功能 | 文件位置 |
|---|---|---|
bes_ble_gap_start_adv() |
启动所有已启用的广播 | bthost/adapter/inc/ble/gap/bes_gap_api.h:606 |
bes_ble_gap_start_connectable_adv() |
启动可连接广播 | bthost/adapter/inc/ble/gap/bes_gap_api.h:599 |
bes_ble_gap_stop_adv_all() |
停止所有广播 | bthost/adapter/inc/ble/gap/bes_gap_api.h:580 |
bes_ble_gap_custom_adv_stop() |
停止指定广播 | bthost/adapter/inc/ble/gap/bes_gap_api.h:688 |
bes_ble_gap_force_switch_adv() |
强制切换广播状态 | bthost/adapter/inc/ble/gap/bes_gap_api.h:572 |
bes_ble_gap_refresh_adv_state() |
刷新广播状态 | bthost/adapter/inc/ble/gap/bes_gap_api.h:803 |
bes_ble_gap_is_in_advertising_state() |
检查广播状态 | bthost/adapter/inc/ble/gap/bes_gap_api.h:796 |
app_ble_refresh_adv_state_generic() |
应用层刷新广播 | bthost/service/ble_app_new/inc/app_ble.h:470 |
C.4 广播参数设置API
| API | 功能 | 文件位置 |
|---|---|---|
bes_ble_gap_param_set_adv_interval() |
设置广播间隔 | bthost/adapter/inc/ble/gap/bes_gap_api.h:780 |
bes_ble_set_all_adv_txpwr() |
设置广播发射功率 | bthost/adapter/inc/ble/gap/bes_gap_api.h:789 |
bes_ble_gap_set_adv_param() |
设置广播参数结构 | bthost/adapter/inc/ble/gap/bes_gap_api.h:651 |
bes_ble_gap_custom_adv_write_data() |
写入自定义广播数据 | bthost/adapter/inc/ble/gap/bes_gap_api.h:682 |
bes_ble_gap_custom_adv_start() |
启动自定义广播 | bthost/adapter/inc/ble/gap/bes_gap_api.h:667 |
C.5 白名单与过滤API
| API | 功能 | 文件位置 |
|---|---|---|
bes_ble_gap_set_white_list() |
设置白名单 | bthost/adapter/inc/ble/gap/bes_gap_api.h:612 |
bes_ble_gap_remove_white_list_user_item() |
移除白名单 | bthost/adapter/inc/ble/gap/bes_gap_api.h:621 |
bes_ble_gap_clear_white_list_for_mobile() |
清除移动设备白名单 | bthost/adapter/inc/ble/gap/bes_gap_api.h:869 |
app_ble_set_white_list() |
应用层设置白名单 | bthost/service/ble_app_new/inc/app_ble.h:435 |
app_ble_clear_white_list() |
应用层清除白名单 | bthost/service/ble_app_new/inc/app_ble.h:436 |
C.6 关键枚举类型
地址类型
c
// bthost/adapter/inc/ble/gap/bes_gap_api.h:211-233
BES_ADDR_PUBLIC // 公共地址
BES_ADDR_RAND // 随机地址
BES_ADDR_RPA_OR_PUBLIC // RPA或公共地址
BES_ADDR_RPA_OR_RAND // RPA或随机地址
BES_ADDR_NONE // 匿名广播
广播通道
c
// bthost/adapter/inc/ble/gap/bes_gap_api.h:235-247
BES_ADV_CHNL_37_EN // 通道37 (2402 MHz)
BES_ADV_CHNL_38_EN // 通道38 (2426 MHz)
BES_ADV_CHNL_39_EN // 通道39 (2480 MHz)
BES_ADV_ALL_CHNLS_EN // 全部3个通道
广播Flags
c
// bthost/adapter/inc/ble/gap/bes_gap_api.h:188-207
BES_GAP_LE_LIM_DISCOVERABLE_FLG_BIT // 限时可发现
BES_GAP_LE_GEN_DISCOVERABLE_FLG_BIT // 通用可发现
BES_GAP_BR_EDR_NOT_SUPPORTED_BIT // 不支持BR/EDR
BES_GAP_SIMUL_BR_EDR_LE_CONTROLLER_BIT // 双模支持
广播过滤策略
c
// bthost/adapter/inc/ble/gap/bes_gap_api.h:249-261
BES_ADV_ALLOW_SCAN_ANY_CON_ANY // 允许任何设备
BES_ADV_ALLOW_SCAN_WLST_CON_ANY // 白名单扫描,任何连接
BES_ADV_ALLOW_SCAN_ANY_CON_WLST // 任何扫描,白名单连接
BES_ADV_ALLOW_SCAN_WLST_CON_WLST // 仅白名单设备
广播类型
c
// bthost/adapter/inc/ble/gap/bes_gap_api.h:263-276
BES_ADV_CONN_UNDIR // 可连接非定向广播
BES_ADV_CONN_DIR // 可连接高占空比定向广播
BES_ADV_DISC_UNDIR // 可发现非定向广播
BES_ADV_NONCONN_UNDIR // 不可连接非定向广播
BES_ADV_CONN_DIR_LDC // 可连接低占空比定向广播
C.7 常用数据类型
地址结构
c
// bthost/adapter/inc/ble/gap/bes_gap_api.h:413-419
typedef struct bes_ble_bdaddr
{
uint8_t addr[6]; // BD Address (LSB first)
uint8_t addr_type; // Address type
} bes_ble_bdaddr_t;
广播参数结构
c
// bthost/adapter/inc/ble/gap/bes_gap_api.h:421-436
typedef struct
{
BLE_ADV_ACTIVITY_USER_E actv_user;
bool is_custom_adv_flags;
BLE_ADV_ADDR_TYPE_E type;
uint8_t *local_addr;
bes_ble_bdaddr_t *peer_addr;
uint32_t adv_interval;
BLE_ADV_TYPE_E adv_type;
ADV_MODE_E adv_mode;
int8_t tx_power_dbm;
uint8_t *adv_data;
uint8_t adv_data_size;
uint8_t *scan_rsp_data;
uint8_t scan_rsp_data_size;
} bes_ble_gap_cus_adv_param_t;
C.8 文档新增章节索引
本次更新新增了以下章节:
| 章节 | 标题 | 页码/行号 |
|---|---|---|
| 1.3 | BLE地址类型详解 | 行44-200 |
| 1.3.1 | 地址类型枚举定义 | 行55-89 |
| 1.3.2 | Own Address Type | 行91-111 |
| 1.3.3 | 地址相关API | 行113-154 |
| 1.3.4 | 地址解析与隐私保护 | 行156-190 |
| 1.3.5 | 地址使用建议 | 行192-200 |
| 2.1.1 | BLE广播通道与频率 | 行237-285 |
| 2.1.2 | 广播Flags设置 | 行287-341 |
| 2.1.3 | 广播数据构建API | 行343-453 |
| 2.8 | 广播控制API | 行887-1041 |
| 2.8.1 | 广播启动与停止 | 行891-938 |
| 2.8.2 | 广播刷新机制 | 行940-1005 |
| 2.8.3 | 广播参数设置 | 行1007-1041 |
| 2.9 | 白名单与过滤策略 | 行1043-1181 |
| 2.9.1 | 白名单API | 行1047-1086 |
| 2.9.2 | 广播过滤策略 | 行1088-1148 |
| 2.9.3 | 扫描过滤策略 | 行1150-1171 |
| 2.9.4 | 白名单最佳实践 | 行1173-1181 |
| 2.10 | 连接发现性模式 | 行1182-1218 |
| 4.1.2 | GATT over BR/EDR | 行1623-1658 |
C.9 快速查找索引
按功能查找:
| 功能 | 章节 | 关键API |
|---|---|---|
| 设置地址类型 | 1.3 | bes_ble_gap_set_local_irk() |
| 构建广播数据 | 2.1.3 | gap_dt_add_raw_data(), app_ble_dt_set_flags() |
| 启动/停止广播 | 2.8.1 | bes_ble_gap_start_adv(), bes_ble_gap_stop_adv_all() |
| 刷新广播 | 2.8.2 | bes_ble_gap_refresh_adv_state() |
| 白名单管理 | 2.9 | bes_ble_gap_set_white_list() |
| RPA管理 | 1.3.4 | bes_ble_gap_set_rpa_list() |
| 广播通道设置 | 2.1.1 | BES_ADV_ALL_CHNLS_EN |
| GATT over BR/EDR | 4.1.2 | 启用宏定义 |
B. 参考资料
- Bluetooth Core Specification v5.3
- Bluetooth SIG - Generic Access Profile
- Bluetooth SIG - Generic Attribute Profile
- HCI Reference Manual
文档版本 : v2.1
最后更新 : 2026-01-22
维护者: sanke
版本更新记录
v2.1 (2026-01-22)
- ✅ 新增 8.8 CTKD跨传输密钥派生机制完整文档
- CTKD定义、应用场景与价值
- CTKD密钥派生原理(h4函数、AES-CMAC算法)
- GAP_KDIST_LINKKEY密钥分发标志详解
- CTKD代码实现示例(使能配置、SMP设置、iOS兼容处理)
- CTKD密钥存储结构(gap_bond_sec_t、gap_conn_item_t)
- CTKD与GATT over BR/EDR集成
- CTKD完整时序图(BLE配对→密钥派生→BR/EDR连接)
- CTKD实际操作例程(4个完整例程含日志分析)
- CTKD常见问题排查与调试命令
v2.0 (2026-01-22)
- ✅ 新增 1.3 BLE地址类型详解章节(Public, Random Static, RPA等)
- ✅ 新增地址管理API文档(IRK设置、RPA获取、解析列表管理)
- ✅ 新增 2.1.1 BLE广播通道与频率详细说明(37/38/39通道特性)
- ✅ 新增 2.1.2 广播Flags设置文档
- ✅ 新增 2.1.3 广播数据构建API(gap_dt_add_raw_data等)
- ✅ 新增 2.8 广播控制API完整文档(启动/停止/刷新机制)
- ✅ 新增 2.9 白名单与过滤策略详解
- ✅ 新增 2.10 连接发现性模式说明
- ✅ 新增 4.1.2 GATT over BR/EDR特性文档
- ✅ 新增附录C API快速参考(包含所有新增API索引)
- ✅ 补充完善了所有用户提到的缺失功能文档
v1.0 (初始版本)
- 基础BLE技术文档框架