蓝牙协议栈与 Profile(btstack)
杰理 AC63 系列蓝牙 SoC 的 BTstack 协议栈集成说明:本页介绍 btstack 在 fw-AC63_BT_SDK 中的组织方式、编译期裁剪配置、内存池划分、事件分发机制以及经典蓝牙(BR/EDR)与低功耗蓝牙(BLE)各 Profile 的实现边界。btstack 以预编译静态库(btstack.a)形式随 SDK 发布,SDK 提供配套头文件与配置源文件,应用层通过 lib_btstack_config.c 与 bt_profile_config.h 决定协议栈的编译形态。
Purpose and Scope
本页覆盖与 btstack 直接相关的全部集成面:
- 协议栈库在仓库中的存放位置(
include_lib/btstack/头文件、各 CPU 目录下的btstack.a预编译库、链接脚本)。 - 协议栈的编译期配置机制(
lib_btstack_config.c)与模块裁剪(bt_profile_config.h中的config_stack_modules)。 - 协议栈内存池(
app_bredr_pool、app_le_pool、app_l2cap_pool、app_bredr_profile)的划分与容量获取接口。 - 事件回调机制:
btstack_event.h提供的 HCI 事件包解析辅助函数(A2DP/AVRCP/HFP/HSP/ANCS/GOEP 等 meta-event 子事件码提取)。 - 经典 Profile(A2DP/AVRCP/HFP/HSP/SPP/GOEP)与 LE Profile(ATT/GATT/SM)在头文件层面的接口边界。
以下内容属于兄弟页面,不在本页展开:具体应用示例(SPP、HID、Mesh 等)的 App 层实现、蓝牙控制器(controller)固件与射频驱动、协议栈之上的应用业务逻辑(如音视频流播放、配网逻辑)。
Overview
btstack 是一个运行于嵌入式系统上的开源蓝牙协议栈实现,杰理(Jieli)将其移植并适配到 AC63 系列芯片,编译为与具体 CPU 型号对应的 btstack.a 静态库。SDK 中该库通过 配置源文件 + 头文件接口 两层方式暴露给应用:
- 配置层:每个应用工程(如
apps/spp_and_le、apps/hid、apps/mesh)各自携带一份lib_btstack_config.c,通过一组const int全局变量告诉预编译库当前固件需要哪些功能(AAC 编解码、RCSP 智能音箱协议、LE 连接数、GATT server/client 数量、SM 加密等)。因为库是预编译的,所以功能裁剪发生在编译链接期而非库编译期——未启用的模块在链接时不会引入相关代码/数据段。 - 接口层:
include_lib/btstack/下的头文件定义了应用可见的全部 API:任务生命周期(btstack_task.h)、事件解析(btstack_event.h)、经典蓝牙基础(bluetooth.h)、A2DP 编解码(a2dp_media_codec.h)、AVCTP 控制(avctp_user.h)、LE 协议族(le/下的 ATT/GATT/SM/BLE API)以及第三方协议(third_party/)。
典型使用场景:音箱(soundbox)同时启用经典蓝牙(A2DP 播放 + HFP 通话 + AVRCP 控制)与 BLE(手机 App 配网/RCSP 控制);耳机/头戴设备只启用部分 Profile;Beacon/24G 无连接场景则完全关闭 GATT 与加密以省内存。
Architecture
flowchart TD
subgraph sg_App["应用层 apps/"]
APP["app 工程<br/>(spp_and_le / hid / mesh)"]
CFG["lib_btstack_config.c<br/>编译期配置变量"]
PROFILE_H["bt_profile_config.h<br/>模块掩码 + 内存池声明"]
end
subgraph sg_Btstack["btstack 协议栈 (预编译 btstack.a)"]
TASK["btstack_task<br/>btstack_init / btstack_exit"]
EVT["btstack_event.h<br/>HCI 事件包解析"]
CLASSIC["经典 Profile<br/>A2DP / AVRCP / HFP / HSP<br/>SPP / GOEP / AVDTP"]
LE["LE 协议族<br/>ATT / GATT / SM / L2CAP"]
THIRD["第三方协议<br/>ANCS / RCSP / app_protocol"]
end
subgraph sg_Hw["硬件平台"]
CPU["cpu/bd19|bd29|br23|br25|br30|br34<br/>btstack.a 静态库"]
CTRL["蓝牙控制器<br/>HCI 传输"]
end
subgraph sg_Mem["内存资源"]
POOL["app_bredr_pool<br/>app_le_pool<br/>app_l2cap_pool<br/>app_bredr_profile"]
end
APP --> CFG
APP --> PROFILE_H
CFG -->|"const int 配置符号"| TASK
PROFILE_H -->|"STACK_MODULES_IS_SUPPORT"| TASK
TASK --> EVT
TASK --> CLASSIC
TASK --> LE
TASK --> THIRD
EVT --> APP
CLASSIC --> CPU
LE --> CPU
CPU --> CTRL
TASK --> POOL
架构说明:
- 应用层通过
lib_btstack_config.c中的const int符号(如CONFIG_BTSTACK_SUPPORT_AAC、config_le_gatt_server_num)和bt_profile_config.h中的config_stack_modules模块掩码,在链接期决定协议栈最终形态。这是预编译库方案的关键设计:库代码全量存在,但未启用模块对应的符号引用不成立,链接器将其丢弃,从而节省 Flash 与 RAM。 - btstack 库内部分三层:任务与事件基础设施(
btstack_task.h、btstack_event.h)、经典 Profile 层、LE 协议族层。btstack_event.h中所有事件解析函数都基于统一的 HCI 事件包格式(event[0]为事件类型,event[2]为 meta 子事件码),说明库通过单一事件管道向应用分发所有 Profile 的通知。 - 硬件层:
cpu/目录下每个 CPU 型号(bd19/bd29/br23/br25/br30/br34)各有一份btstack.a,因为协议栈与芯片的 HCI 传输、定时器、内存模型深度耦合;应用链接时选择与目标芯片匹配的库文件。 - 内存资源:协议栈工作区由应用侧提供的静态字节池支撑,
bt_profile_config.h声明的四个池(BR/EDR、LE、L2CAP、Profile)由应用分配,库通过get_*_pool_len()接口查询容量。
协议栈源码布局与构建产物
头文件目录 include_lib/btstack/
仓库以 include_lib/btstack/ 作为协议栈的唯一公开接口面,头文件按功能分组:
| 头文件 | 职责 |
|---|---|
btstack_task.h | 协议栈任务生命周期:btstack_init() / btstack_exit(),以及产线测试用 ble_bqb_test_thread_init() |
btstack_event.h | HCI 事件包解析:从统一事件包中提取事件类型与各 Profile 的 meta 子事件码 |
btstack_typedef.h | 基础类型与通用宏定义 |
bluetooth.h | 经典蓝牙(BR/EDR)基础 API 与常量 |
bt_profile_config.h | 模块选择掩码(config_stack_modules)与四个内存池的声明、容量接口 |
a2dp_media_codec.h | A2DP 媒体编解码相关定义(SBC/AAC 等) |
avctp_user.h | AVCTP 传输层用户接口(AVRCP 依赖) |
le/att.h、le/gatt.h | LE 属性协议与通用属性规范接口 |
le/ble_api.h、le/le_user.h | BLE 用户层 API 与配置 |
le/sm.h | LE 安全管理(配对、加密) |
le/le_common_define.h、le/ble_data_types.h | LE 公共常量与数据类型 |
third_party/app_protocol_event.h | 第三方/App 私有协议事件 |
third_party/common/btstack_3th_protocol_user.h | 第三方协议(如 RCSP)用户接口 |
third_party/common/ble_config.h | BLE 配置项 |
btstack_lib*.ld | 库的链接脚本(text/data/bss 段布局) |
说明:头文件只声明接口,实现全部封装在
btstack.a中。这使应用层与协议栈实现解耦,也意味着无法通过阅读源码了解协议栈内部算法——可观测的只有配置符号、事件包格式与回调接口。
预编译库 btstack.a
cpu/ 下每个芯片目录的 liba/ 中存放对应型号的协议栈静态库:
cpu/bd19/liba/btstack.acpu/bd29/liba/btstack.acpu/br23/liba/btstack.acpu/br25/liba/btstack.acpu/br30/liba/btstack.acpu/br34/liba/btstack.a
同一份协议栈源码针对不同 CPU 编译出不同库,原因在于各芯片的 HCI 控制器接口、中断/定时器实现与内存访问方式不同。应用工程通过链接脚本与 Makefile 选择与目标芯片匹配的库,避免协议栈直接依赖硬件抽象层(HAL),而是把差异封装进库内。
编译期配置机制:lib_btstack_config.c
每个应用工程都有一份自己的 lib_btstack_config.c(例如 apps/spp_and_le/config/lib_btstack_config.c)。其核心思想是:用一组 const int 全局符号作为预编译库的“配置开关”,应用在链接期提供这些符号的定义,库内代码引用它们来决定行为。由于是常量,编译器可做常量传播与死代码消除,未启用功能即被剔除。
配置项分为四类:
1. Flash 容量与编解码
#ifdef CONFIG_SOUNDBOX_FLASH_256K
const int CONFIG_BTSTACK_BIG_FLASH_ENABLE = 0;
#else
const int CONFIG_BTSTACK_BIG_FLASH_ENABLE = 1;
#endif
#if TCFG_BT_SUPPORT_AAC
const int CONFIG_BTSTACK_SUPPORT_AAC = 1;
#else
const int CONFIG_BTSTACK_SUPPORT_AAC = 0;
#endif
CONFIG_BTSTACK_BIG_FLASH_ENABLE:小 Flash(256K 音箱)固件关闭“大 Flash”优化路径,以节省代码体积;普通固件开启,可启用更多 Flash 驻留数据。CONFIG_BTSTACK_SUPPORT_AAC:是否编译 A2DP AAC 解码路径,直接跟随应用宏TCFG_BT_SUPPORT_AAC。此开关影响协议栈与媒体流两侧的编解码资源占用。
2. Sniff 模式行为
#if SNIFF_MODE_RESET_ANCHOR
//协议栈接收到命令是否自动退出sniff
const int config_btstask_auto_exit_sniff = 0;
#else
const int config_btstask_auto_exit_sniff = 1;
#endif
config_btstask_auto_exit_sniff 控制协议栈在收到主机命令时是否自动退出 sniff 低功耗模式。当 SNIFF_MODE_RESET_ANCHOR 定义时置 0(不自动退出,保持锚点),否则置 1(自动退出以便快速响应)。这是功耗与响应延迟之间的权衡:自动退出能降低命令延迟,但会破坏 sniff 锚点的低功耗收益。
3. 智能音箱/RCSP 协议
#if SMART_BOX_EN
const int config_rcsp_stack_enable = 1;
#else
const int config_rcsp_stack_enable = 0;
#endif
config_rcsp_stack_enable 启用杰理 RCSP(Remote Control & Smart Protocol,用于智能音箱与手机 App 联动的自定义协议)栈,跟随应用宏 SMART_BOX_EN。启用后协议栈会包含 third_party 相关的协议解析与事件处理代码。
4. LE 协议栈资源配额
#if TCFG_USER_BLE_ENABLE
#if CONFIG_APP_NONCONN_24G || CONFIG_APP_BEACON
//无链接,不需要gatt功能
const int config_le_hci_connection_num = 0;//支持同时连接个数
const int config_le_sm_support_enable = 0; //是否支持加密配对
const int config_le_gatt_server_num = 0; //支持server角色个数
const int config_le_gatt_client_num = 0; //支持client角色个数
#else
const int config_le_hci_connection_num = CONFIG_BT_GATT_CONNECTION_NUM;//支持同时连接个数
const int config_le_sm_support_enable = CONFIG_BT_SM_SUPPORT_ENABLE; //是否支持加密配对
const int config_le_gatt_server_num = CONFIG_BT_GATT_SERVER_NUM; //支持server角色个数
const int config_le_gatt_client_num = CONFIG_BT_GATT_CLIENT_NUM; //支持client角色个数
#endif
#else
const int config_le_hci_connection_num = 0;//支持同时连接个数
const int config_le_sm_support_enable = 0; //是否支持加密配对
const int config_le_gatt_server_num = 0; //支持server角色个数
const int config_le_gatt_client_num = 0; //支持client角色个数
#endif
/*config_le_sm_sub_sc_enable: SC加密模式使能,need config_le_sm_support_enable = 1*/
const int config_le_sm_sub_sc_enable = CONFIG_BT_SM_SUPPORT_ENABLE & 0;//
const int config_le_sm_sub_sc_bridge_edr_enable = 0; /*default 0*/
LE 侧共五个配额变量:
config_le_hci_connection_num:LE 同时连接数上限(多连接时分配对应的链路资源)。config_le_sm_support_enable:是否支持 SM 加密配对。config_le_gatt_server_num/config_le_gatt_client_num:GATT Server / Client 角色实例数。config_le_sm_sub_sc_enable:LE Secure Connections(SC)加密模式,当前源码中硬编码为CONFIG_BT_SM_SUPPORT_ENABLE & 0,即总是 0(关闭);注释明确指出其依赖config_le_sm_support_enable = 1。config_le_sm_sub_sc_bridge_edr_enable:SC-over-BREDR 桥接使能,默认 0。
关键设计意图:非连接型应用(24G 遥控、Beacon)直接把所有 LE 配额置 0,从而在链接期剥离整个 ATT/GATT/SM 模块,最大程度节省 RAM/Flash;普通 BLE 应用则从 CONFIG_BT_* 系列应用宏映射配额。配额不是运行时动态分配,而是编译期常量,因此内存池大小可以静态确定。
文件末尾还保留了调试开关的注释示例(l2cap_debug_enable = 0xf0、rfcomm_debug_enable = 0xf、profile_debug_enable = 0xff、ble_debug_enable = 0xff),表明协议栈内部支持按模块打开调试日志,但默认不启用。
模块选择与内存池:bt_profile_config.h
bt_profile_config.h 定义了协议栈顶层功能域的模块掩码:
#define BT_BTSTACK_CLASSIC BIT(0)
#define BT_BTSTACK_LE_ADV BIT(1)
#define BT_BTSTACK_LE BIT(2)
extern const int config_stack_modules;
#define STACK_MODULES_IS_SUPPORT(x) (config_stack_modules & (x))
BT_BTSTACK_CLASSIC:经典蓝牙(BR/EDR,A2DP/HFP/SPP 等)。BT_BTSTACK_LE_ADV:仅广播(Adv-only,无连接,对应 Beacon/24G 场景)。BT_BTSTACK_LE:完整 LE(含连接、GATT、SM)。
config_stack_modules 由应用定义(在某个 lib_btstack_config.c 或应用配置中),库内所有模块代码通过 STACK_MODULES_IS_SUPPORT(x) 判断自己是否应生效。这是一个典型的编译期特性开关 + 位掩码设计:单个 int 即可描述协议栈形态,且与 LE 配额变量(上一节)互为补充——掩码决定“有/无”,配额决定“多少个”。
同一头文件还声明了协议栈运行所需的四个静态内存池:
extern u8 app_bredr_pool[];
extern u8 app_le_pool[];
extern u8 app_l2cap_pool[];
extern u8 app_bredr_profile[];
extern u16 get_bredr_pool_len(void);
extern u16 get_le_pool_len(void);
extern u16 get_l2cap_stack_len(void);
extern u16 get_profile_pool_len(void);
池的划分遵循协议栈的资源域边界:
| 池 | 用途域 | 容量接口 |
|---|---|---|
app_bredr_pool | 经典蓝牙链路与控制 | get_bredr_pool_len() |
app_le_pool | LE 链路、GATT、SM | get_le_pool_len() |
app_l2cap_pool | L2CAP 通道(BR/EDR 与 LE 共用) | get_l2cap_stack_len() |
app_bredr_profile | 经典 Profile 实例(A2DP/AVRCP/HFP 等) | get_profile_pool_len() |
池内存由应用提供(静态数组),协议栈只消费;容量通过 getter 暴露给应用,便于在启动日志中核对内存占用。这种“库不持有全局大缓冲、工作区由宿主注入”的模式,是嵌入式协议栈常见的资源所有权划分:Flash 里库代码可全量存在,但 RAM 只按需分配。
核心流程:初始化与事件分发
初始化流程
协议栈的入口在 btstack_task.h,仅暴露三个函数:
int btstack_init();
int btstack_exit();
void ble_bqb_test_thread_init(void);
应用在系统启动阶段调用 btstack_init()。从配置符号可以还原其内部大致流程:
flowchart TD
Start([应用启动]) --> Init["btstack_init()"]
Init --> Check{"config_stack_modules<br/>模块掩码检查"}
Check -->|"BT_BTSTACK_CLASSIC"| CPool["使用 app_bredr_pool<br/>+ app_l2cap_pool<br/>初始化经典协议栈"]
Check -->|"BT_BTSTACK_LE_ADV / LE"| LPool["使用 app_le_pool<br/>初始化 LE 栈"]
Check -->|"全 0"| Lite["最小化启动<br/>仅任务与 HCI"]
CPool --> EvtLoop["事件循环启动<br/>等待 HCI 事件"]
LPool --> EvtLoop
Lite --> EvtLoop
EvtLoop -->|"事件到达"| Parse["btstack_event.h<br/>解析事件包"]
Parse --> Dispatch["按子事件码分发<br/>给各 Profile 回调"]
Dispatch --> AppCB["应用回调处理"]
btstack_exit() 用于反向拆除协议栈任务、释放资源;ble_bqb_test_thread_init() 是产测(BQB 认证测试)专用线程入口,仅测试固件使用。
统一事件管道与解析辅助函数
btstack_event.h 的核心设计是所有 Profile 的通知都通过同一种 HCI 事件包格式上抛,应用通过内联辅助函数解析。文件开头即定义:
static inline uint8_t hci_event_packet_get_type(const uint8_t *event)
{
return event[0];
}
static inline uint8_t hci_event_ancs_meta_get_subevent_code(const uint8_t *event)
{
return event[2];
}
static inline uint8_t hci_event_avdtp_meta_get_subevent_code(const uint8_t *event)
{
return event[2];
}
static inline uint8_t hci_event_a2dp_meta_get_subevent_code(const uint8_t *event)
{
return event[2];
}
static inline uint8_t hci_event_avrcp_meta_get_subevent_code(const uint8_t *event)
{
return event[2];
}
static inline uint8_t hci_event_goep_meta_get_subevent_code(const uint8_t *event)
{
return event[2];
}
static inline uint8_t hci_event_hfp_meta_get_subevent_code(const uint8_t *event)
{
return event[2];
}
static inline uint8_t hci_event_hsp_meta_get_subevent_code(const uint8_t *event)
{
return event[2];
}
设计要点:
- 事件包布局:
event[0]是事件类型;当事件类型表示“meta 事件”时,event[2]存放子事件码。各 Profile 的 meta getter 都读取event[2],因此应用只需按 Profile 匹配对应的 getter 即可取得该 Profile 的子事件码,再据此进入具体的事件 switch。 - 命名即契约:
hci_event_<profile>_meta_get_subevent_code()的命名模式与上游 BTstack 一致,方便开发者将杰理 SDK 的用法映射回标准 BTstack 文档。 - 覆盖范围:ANCS、AVDTP、A2DP、AVRCP、GOEP、HFP、HSP 均有专属 getter,说明这些 Profile 是 SDK 的“一等公民”;事件头文件同时
#include "le/gatt.h"(在ENABLE_BLE定义下),LE 侧事件走 GATT/ATT 事件通道。
事件分发时序
sequenceDiagram
participant CTRL as 蓝牙控制器(HCI)
participant STACK as btstack.a 协议栈任务
participant EVT as btstack_event.h 解析层
participant APP as 应用回调
CTRL->>STACK: HCI Event Packet
STACK->>STACK: 按 event[0] 识别事件类型
STACK->>EVT: hci_event_packet_get_type(event)
alt A2DP 事件
EVT-->>APP: hci_event_a2dp_meta_get_subevent_code(event)
APP->>APP: 处理 A2DP 子事件 (连接/流状态)
else AVRCP 事件
EVT-->>APP: hci_event_avrcp_meta_get_subevent_code(event)
APP->>APP: 处理 AVRCP 控制事件
else HFP/HSP 事件
EVT-->>APP: hci_event_hfp_meta_get_subevent_code(event)
APP->>APP: 处理通话事件
else LE 事件
EVT-->>APP: GATT/ATT 事件通道
APP->>APP: 处理 LE 连接/服务发现/通知
end
整个机制保证:无论底层是哪个 Profile 产生的事件,应用只面对一个事件回调 + 一组子事件码,避免了多回调注册的复杂度,也使得“协议栈内部异步、应用侧同步处理”的模型易于实现。
配置选项汇总
以下配置符号全部为 const int,由应用工程定义(主要来自 lib_btstack_config.c),在链接期生效。
| 配置符号 | 类型 | 默认/来源 | 说明 |
|---|---|---|---|
CONFIG_BTSTACK_BIG_FLASH_ENABLE | int | 256K Flash 固件=0,否则=1 | 大 Flash 优化路径开关 |
CONFIG_BTSTACK_SUPPORT_AAC | int | 跟随 TCFG_BT_SUPPORT_AAC | A2DP AAC 编解码支持 |
config_btstask_auto_exit_sniff | int | 跟随 SNIFF_MODE_RESET_ANCHOR(定义=0,否则=1) | 协议栈收到命令是否自动退出 sniff |
config_rcsp_stack_enable | int | 跟随 SMART_BOX_EN | RCSP 智能音箱协议栈 |
config_le_hci_connection_num | int | 跟随 TCFG_USER_BLE_ENABLE/CONFIG_BT_GATT_CONNECTION_NUM,非连接应用=0 | LE 同时连接数 |
config_le_sm_support_enable | int | 跟随 CONFIG_BT_SM_SUPPORT_ENABLE,非连接应用=0 | SM 加密配对支持 |
config_le_gatt_server_num | int | 跟随 CONFIG_BT_GATT_SERVER_NUM,非连接应用=0 | GATT Server 角色数 |
config_le_gatt_client_num | int | 跟随 CONFIG_BT_GATT_CLIENT_NUM,非连接应用=0 | GATT Client 角色数 |
config_le_sm_sub_sc_enable | int | 恒 0(CONFIG_BT_SM_SUPPORT_ENABLE & 0) | LE Secure Connections 模式(当前关闭) |
config_le_sm_sub_sc_bridge_edr_enable | int | 0 | SC-over-BREDR 桥接使能 |
config_stack_modules | int | 应用定义 | 模块掩码:BT_BTSTACK_CLASSIC(BIT0) / BT_BTSTACK_LE_ADV(BIT1) / BT_BTSTACK_LE(BIT2) |
调试开关(源码中为注释示例,默认关闭):l2cap_debug_enable、rfcomm_debug_enable、profile_debug_enable、ble_debug_enable,可用于按模块打开协议栈内部日志。
API 参考
int btstack_init()
初始化并启动 btstack 协议栈任务。应用应在系统资源(内存池、HCI 传输)就绪后调用。
- 返回:0 表示成功,非 0 表示初始化失败(如资源不足)。
- 设计意图:一次性拉起协议栈事件循环;实际启用的模块由
config_stack_modules与各config_*配额符号决定,因此不同固件调用同一函数得到不同规模的协议栈实例。
int btstack_exit()
停止并拆除协议栈任务,释放协议栈占用的运行资源。通常用于系统级休眠/关机或固件升级前的协议栈卸载。
void ble_bqb_test_thread_init(void)
初始化 BQB 蓝牙认证测试线程,仅测试固件使用,量产固件不应调用。
uint8_t hci_event_packet_get_type(const uint8_t *event)
返回事件包的事件类型(读取 event[0]),用于识别事件属于哪个大类。
uint8_t hci_event_<profile>_meta_get_subevent_code(const uint8_t *event)
其中 <profile> 可为 ancs、avdtp、a2dp、avrcp、goep、hfp、hsp。返回对应 Profile meta 事件的子事件码(读取 event[2]),应用据此进入该 Profile 的事件分支。
- 参数:
event— 协议栈回调传入的事件包指针。 - 返回:子事件码
uint8_t。 - 注意:调用前应先确认事件类型确为该 Profile 的 meta 事件,否则
event[2]语义不成立。
u16 get_bredr_pool_len() / get_le_pool_len() / get_l2cap_stack_len() / get_profile_pool_len()
返回各协议栈内存池的容量(字节数),供应用核对内存占用或调整池大小。
bool STACK_MODULES_IS_SUPPORT(x)
模块支持判断宏:config_stack_modules & (x) 非零表示该模块被编译进当前固件。应用可在运行时据此决定是否展示相应功能 UI 或注册相关回调。
使用示例
1. 按应用类型裁剪 LE 协议栈(无连接 vs 全功能)
以下代码展示如何根据应用形态(Beacon/24G 无连接、普通 BLE)设置 LE 配额:
#if TCFG_USER_BLE_ENABLE
#if CONFIG_APP_NONCONN_24G || CONFIG_APP_BEACON
//无链接,不需要gatt功能
const int config_le_hci_connection_num = 0;//支持同时连接个数
const int config_le_sm_support_enable = 0; //是否支持加密配对
const int config_le_gatt_server_num = 0; //支持server角色个数
const int config_le_gatt_client_num = 0; //支持client角色个数
#else
const int config_le_hci_connection_num = CONFIG_BT_GATT_CONNECTION_NUM;//支持同时连接个数
const int config_le_sm_support_enable = CONFIG_BT_SM_SUPPORT_ENABLE; //是否支持加密配对
const int config_le_gatt_server_num = CONFIG_BT_GATT_SERVER_NUM; //支持server角色个数
const int config_le_gatt_client_num = CONFIG_BT_GATT_CLIENT_NUM; //支持client角色个数
#endif
#else
const int config_le_hci_connection_num = 0;//支持同时连接个数
const int config_le_sm_support_enable = 0; //是否支持加密配对
const int config_le_gatt_server_num = 0; //支持server角色个数
const int config_le_gatt_client_num = 0; //支持client角色个数
#endif
来源:apps/spp_and_le/config/lib_btstack_config.c
2. 声明模块掩码与内存池
应用侧需提供 config_stack_modules 定义,并声明四个字节池供协议栈使用:
#define BT_BTSTACK_CLASSIC BIT(0)
#define BT_BTSTACK_LE_ADV BIT(1)
#define BT_BTSTACK_LE BIT(2)
extern const int config_stack_modules;
#define STACK_MODULES_IS_SUPPORT(x) (config_stack_modules & (x))
extern u8 app_bredr_pool[];
extern u8 app_le_pool[];
extern u8 app_l2cap_pool[];
extern u8 app_bredr_profile[];
extern u16 get_bredr_pool_len(void);
extern u16 get_le_pool_len(void);
extern u16 get_l2cap_stack_len(void);
extern u16 get_profile_pool_len(void);
来源:include_lib/btstack/bt_profile_config.h
3. 解析 A2DP 子事件
事件回调中按 Profile 解析子事件码的惯用法:
static inline uint8_t hci_event_packet_get_type(const uint8_t *event)
{
return event[0];
}
static inline uint8_t hci_event_a2dp_meta_get_subevent_code(const uint8_t *event)
{
return event[2];
}
来源:include_lib/btstack/btstack_event.h
失败模式、边界情况与并发注意
1. 配置符号缺失或冲突
lib_btstack_config.c 中的 const int 符号是库的正常链接依赖。若应用工程缺少某个符号定义,链接期会报未定义符号错误;若定义类型不一致(如改成了 volatile 或非 const),可能破坏库内的常量折叠优化,导致代码体积与行为偏离预期。边界情况:config_stack_modules 为 0 时,协议栈进入最小化模式(仅任务与 HCI 通道),此时调用 Profile 相关 API 可能无响应——应用应通过 STACK_MODULES_IS_SUPPORT(x) 先行判断。
2. 内存池不足
四个内存池(app_bredr_pool、app_le_pool、app_l2cap_pool、app_bredr_profile)由应用静态分配。若容量估算偏小:
- 经典侧(A2DP/HFP 多连接)会因
app_bredr_pool/app_bredr_profile不足导致建链失败或 Profile 初始化失败; - LE 侧多连接、多 GATT 实例会消耗
app_le_pool与app_l2cap_pool。
由于池是静态数组,运行时无法扩容,只能通过 get_*_pool_len() 返回值核对实际占用并反推余量。修改 CONFIG_BT_GATT_* 等应用宏后必须同步复核池大小。
3. sniff 低功耗与响应延迟
config_btstask_auto_exit_sniff = 0(SNIFF_MODE_RESET_ANCHOR 定义时)意味着协议栈收到命令不自动退出 sniff,命令处理延迟会增加,但保持 sniff 锚点以维持低功耗;反之自动退出则牺牲功耗换响应速度。应用在调试“命令不响应”问题时,应首先确认该开关的取值与预期功耗策略一致。
4. LE SC 模式当前强制关闭
config_le_sm_sub_sc_enable = CONFIG_BT_SM_SUPPORT_ENABLE & 0 在源码层面恒为 0,即 LE Secure Connections 加密模式当前不可通过该符号启用(除非修改源码)。若产品需要 SC 配对(防中间人攻击),需评估此限制;普通 Legacy Pairing 不受影响。
5. 并发与回调上下文
协议栈为独立任务(btstack_init() 启动),事件通过统一事件管道投递到应用回调。应用回调运行在协议栈任务上下文,因此:
- 回调内不应执行长时间阻塞操作(会阻塞后续事件分发);
- 跨任务访问共享数据需加锁或使用消息队列转发到应用任务;
btstack_exit()不应在事件回调中直接调用(存在任务自毁风险),应延后到应用任务执行。
6. 事件包解析的前置条件
hci_event_<profile>_meta_get_subevent_code() 只读取 event[2],不校验事件类型。若事件不是对应 Profile 的 meta 事件,返回的子事件码无意义。应用必须先通过 hci_event_packet_get_type() 确认事件类型,再调用对应 getter。
性能与运维考虑
- 链接期裁剪是最大的性能杠杆:
CONFIG_BTSTACK_BIG_FLASH_ENABLE、AAC、RCSP、LE 配额等开关直接决定 Flash 占用与 RAM 池大小。256K Flash 音箱固件通过CONFIG_SOUNDBOX_FLASH_256K自动降级,印证了该 SDK 对极小 Flash 的适配策略。 - 事件解析为 O(1) 内联函数:
hci_event_*_get_subevent_code()全部是static inline读内存操作,事件分发路径零函数调用开销,适合高频 HFP/A2DP 事件流。 - 多连接能力由配额而非运行时决定:
config_le_hci_connection_num等常量让协议栈在编译期就能确定资源布局,避免动态分配带来的碎片与不确定性,但也意味着固件形态(如 1 连接 vs 2 连接)必须预先规划。 - 产测支持:
ble_bqb_test_thread_init()表明协议栈保留 BQB 认证测试通道,量产与认证固件可共享同一库。
扩展点
- 新增/调整 Profile 编译形态:修改
config_stack_modules掩码即可整体启用/禁用 CLASSIC / LE_ADV / LE 三大域;新增 Profile 实例数通过config_le_gatt_*_num或经典侧 Profile 池配额调节。 - 第三方协议接入:
include_lib/btstack/third_party/(app_protocol_event.h、btstack_3th_protocol_user.h)是杰理私有/第三方协议(如 RCSP)的挂载点,config_rcsp_stack_enable控制其编译。 - 事件扩展:新事件类型沿用统一事件管道,在
btstack_event.h中按既有模式新增hci_event_<profile>_meta_get_subevent_code()即可获得一致的应用侧接口。 - 编解码扩展:
a2dp_media_codec.h与CONFIG_BTSTACK_SUPPORT_AAC表明编解码器以编译开关接入 A2DP 媒体路径,可参照增加其他 codec。
测试
仓库中 apps/*/config/lib_btstack_config.c(spp_and_le、hid、mesh 三份)可视为协议栈的配置矩阵测试用例:
apps/spp_and_le:经典 SPP + LE 双栈典型配置,LE 配额走完整CONFIG_BT_GATT_*映射;apps/hid:HID 应用视角的协议栈配置;apps/mesh:Mesh 应用视角的配置,验证 LE 栈在不同应用层协议下的复用。
三份配置共享同一 btstack.a 的事实,直接验证了“预编译库 + 链接期裁剪”方案的可行性:同一二进制库服务完全不同的应用形态。
相关链接
- 配置源文件:apps/spp_and_le/config/lib_btstack_config.c、apps/hid/config/lib_btstack_config.c、apps/mesh/lib_config/lib_btstack_config.c
- 模块与内存池定义:include_lib/btstack/bt_profile_config.h
- 任务生命周期 API:include_lib/btstack/btstack_task.h
- 事件解析辅助函数:include_lib/btstack/btstack_event.h
- LE 协议族头文件:include_lib/btstack/le/gatt.h、include_lib/btstack/le/sm.h、include_lib/btstack/le/ble_api.h
- 预编译库(按 CPU 选择):cpu/br25/liba/btstack.a、cpu/br23/liba/btstack.a、cpu/bd29/liba/btstack.a
- 关联目录页:蓝牙协议栈总览(5-bluetooth-stack)、各 Profile 应用页(SPP、HID、Mesh 等)