CH58X BLE 配对流程详解
本文基于 CH584 MCU 的 BLE 心率传感器项目,逐层拆解 BLE 外设的配对流程。文中的代码注释和配置均来自项目实际源码。
系统架构
整个 BLE 协议栈运行在沁恒 CH58x 系列的 TMOS 任务调度系统之上。调用链路如下:
Main.c 主循环
└─ ble_peripheral_process() → TMOS_SystemProcess()
└─ 事件分发 ble_peripheral_process_event()
└─ GAPRole_PeripheralStartDevice() → 启动设备
└─ GAP → GATT → SM(安全管理器)
└─ CH58x BLE Controller(硬件链路层)Main.c 在主循环中交替执行 ble_peripheral_process() 和应用调度器。协议栈事件通过 TMOS 消息机制异步回调到应用层。
编译期 BLE 配置
项目在 src/app_project_config.h 中覆盖了 SDK 的默认 BLE 参数:
| 参数 | 值 | 说明 |
|---|---|---|
BLE_MEMHEAP_SIZE |
10240 (10 KB) | 协议栈内存池 |
BLE_BUFF_MAX_LEN |
251 (ATT_MTU = 247) | 最大应用数据包长度 |
BLE_BUFF_NUM |
8 | 收发缓冲区数量 |
BLE_TX_NUM_EVENT |
6 | 单连接事件最大发包数 |
BLE_TX_POWER |
LL_TX_POWEER_MINUS_10_DBM |
发射功率 -10 dBm |
SDK 层 CONFIG.h 还控制了绑定存储的硬件参数:
| 参数 | 值 | 说明 |
|---|---|---|
BLE_SNV |
TRUE | 启用 SNV 绑定存储 |
BLE_SNV_ADDR |
0x77000 - FLASH_ROM_MAX_SIZE |
存储起始地址 |
BLE_SNV_BLOCK |
256 bytes | 块大小 |
BLE_SNV_NUM |
1 | 块数量 |
PERIPHERAL_MAX_CONNECTION |
1 | 最大同时连接数 |
配对初始化
入口函数 ble_peripheral_init() 位于 src/ble_peripheral.c:103,分三步完成初始化。
第一步:底层初始化
CH58x_BLEInit(); // BLE 协议栈库初始化
HAL_Init(); // 硬件抽象层初始化
GAPRole_PeripheralInit(); // 配置为 Peripheral 角色CH58x_BLEInit() 内部配置 SNV 的 Flash 读写回调,为绑定数据持久化准备硬件通路。
第二步:广播与连接参数
#define BLE_PERIPHERAL_ADV_INTERVAL 160 // 160 × 0.625 ms = 100 ms
#define BLE_PERIPHERAL_MIN_CONN_INTERVAL 6 // 6 × 1.25 ms = 7.5 ms
#define BLE_PERIPHERAL_MAX_CONN_INTERVAL 6 // 同上,固定连接间隔
#define BLE_PERIPHERAL_SLAVE_LATENCY 0 // 无延迟
#define BLE_PERIPHERAL_CONN_TIMEOUT 100 // 100 × 10 ms = 1 s广播间隔 100 ms,连接间隔固定 7.5 ms,适合低延迟数据传输。
第三步:安全参数——核心决策
uint32_t passkey = 0u;
uint8_t pair_mode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ; // 等主机发起
uint8_t mitm = FALSE; // 无 MITM 保护
uint8_t bonding = FALSE; // 不保存绑定
uint8_t io_cap = GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT; // 无输入输出
GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(passkey), &passkey);
GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(pair_mode), &pair_mode);
GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(mitm), &mitm);
GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(io_cap), &io_cap);
GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(bonding), &bonding);Bonding 关闭意味着每次断开后不会保存对端设备的密钥信息,重连时需要重新完成配对流程。
配对方式:Just Works
当前配置组合 (NoInputNoOutput + MITM=FALSE) 决定了采用 BLE 的 Just Works 配对:
Central(手机) Peripheral(CH58x)
│ │
│──── Pairing Request ─────────────────→│ 主机发起配对
│←─── Pairing Response ────────────────│ IO=NoInputNoOutput, Bonding=0
│ │
│ ← 双方用 TK=0 计算确认值和 STK → │ 无用户交互
│ │
│←── Encryption Start ─────────────────→│ 链路加密建立- 临时密钥 TK 固定为 0,无用户交互
- 无 MITM 保护,链路上的中间人攻击无法被检测
- 不存储 LTK / IRK / CSRK,设备断电或断开后密钥全部丢失
广播与扫描响应
设备启动后发出两类广播数据:
广播包(31 字节上限):
Flags: LE General Discoverable + BR/EDR Not Supported
Service UUID: 0xFFF0(自定义服务)扫描响应(在主机主动扫描时返回):
设备名称: "CH584F_HR_PROTO"(15 字符完整名称)
连接间隔范围: 最小 6,最大 6(固定 7.5 ms)
TX Power: 0(未显式指定功率值)广播过滤策略使用默认值 GAP_FILTER_POLICY_ALL,未启用白名单过滤。
连接与断开事件流
应用层通过 ble_peripheral_state_changed() 回调处理状态迁移。该函数在 ble_peripheral.c:330 实现:
状态机转换:
[空闲/广播中]
│ GAP_LINK_ESTABLISHED_EVENT
▼
[已连接]
├─ 记录 conn_handle
├─ 80 TU 后发起连接参数更新请求
└─ 激活 TinyFrame 协议层
│
│ GAP_LINK_TERMINATED_EVENT
▼
[等待/广播中]
├─ 清除连接句柄
├─ 清除协议层连接状态
└─ 若之前广播已开启 → 自动恢复广播连接参数更新请求始终使用相同的 min/max 间隔(6),本质上是在确认而非协商。断开后广播恢复逻辑由 ble_peripheral_set_advertising() 的状态变量 g_ble_peripheral_advertising_enabled 控制——如果应用层此前开启了广播,断开后自动延续。
绑定回调:当前全部为空
协议栈通过 gapBondCBs_t 结构体向应用层通知配对事件:
static gapBondCBs_t g_ble_peripheral_bond_callbacks = {
NULL, // passcodeCB — 动态密码请求
NULL, // pairStateCB — 配对状态变化
NULL, // oobCB — OOB 安全配对
};三个回调全部为 NULL 的影响:
- passcodeCB = NULL:使用静态 Passkey=0,无法动态生成或向用户展示密码
- pairStateCB = NULL:不感知配对进度,出现失败时无日志也无重试
- oobCB = NULL:OOB 安全配对不支持
SNV 绑定存储:硬件已就绪,尚未启用
BLE_SNV = TRUE 已在 CONFIG.h 中开启,Flash 读写回调已在 CH58x_BLEInit() 中注册。但由于 bonding = FALSE,协议栈不会实际写入任何绑定数据。
如果将来启用 bonding,每个绑定设备将占用 6 条 NV 记录,NV ID 计算公式为:
nvID = (bondIdx * 6 + offset) + 0x0200
offset 0: gapBondRec_t — 设备地址、地址类型、状态标志
offset 1: gapBondLTK_t — 本地 LTK(16 bytes)、ediv、rand、keySize
offset 2: gapBondLTK_t — 对端 LTK
offset 3: IRK (16 bytes) — 身份解析密钥
offset 4: CSRK (16 bytes) — 签名解析密钥
offset 5: SignCounter — 签名计数器(uint32_t)存储区域位于 Flash 末尾(0x77000 - FLASH_ROM_MAX_SIZE),单块 256 bytes。如果设备需要绑定多台主机,需要调整 BLE_SNV_NUM 扩大存储空间。
启用安全配对的方法
如果需要提升安全性,按以下顺序修改:
1. 启用 Bonding
uint8_t bonding = TRUE;
GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(bonding), &bonding);2. 实现绑定回调
至少实现 pairStateCB,在 GAPBOND_PAIRING_STATE_BOND_SAVED 状态时记录绑定完成。如果需要动态密码,则实现 passcodeCB。
3. 为 GATT 特征添加加密权限
当前 BLE UART 服务的读写权限不加加密限制(ble_uart_service_16bit.c)。需要修改两处:
RX 特征值(第 138 行),将 GATT_PERMIT_WRITE 替换为:
GATT_PERMIT_ENCRYPT_WRITE,TX 特征 CCCD(第 124 行),将 GATT_PERMIT_READ | GATT_PERMIT_WRITE 替换为:
GATT_PERMIT_ENCRYPT_READ | GATT_PERMIT_ENCRYPT_WRITE,加密后,主机必须先完成配对建立加密链路,才能读写这些特征。GATT_PERMIT_ENCRYPT_READ 和 GATT_PERMIT_ENCRYPT_WRITE 定义在 CH58xBLE_LIB.h 中,分别为 0x40 和 0x80。
4. 按需启用 MITM
如果设备有显示或输入能力,可以启用 MITM 保护:
uint8_t mitm = TRUE;
uint8_t io_cap = GAPBOND_IO_CAP_DISPLAY_YES_NO;注意:启用 MITM 后配对需要用户交互(确认数字或输入密码),对无屏设备不适用。
总结
当前固件的 BLE 配对策略可以归纳为几条关键事实:
- 配对模式为 Just Works,无认证、无 MITM 保护
- Bonding 关闭,断开后不记忆设备,每次重连都需要重新配对
- SNV Flash 存储已配置但未使用
- 广播在断开后自动恢复,遵循应用层的开关状态
- 连接间隔固定 7.5 ms,延迟极低,适合心率数据实时传输
- 升级到安全配对只需修改 bonding 标志、实现回调、添加加密权限三步