多连接与无连接传输
本文档介绍 AW31N BLE SDK 中「多连接(Multi-Connection)」与「无连接传输(Non-Conn / Connectionless)」两种数据传输模式的实现,涵盖应用入口、角色模块划分、低功耗联动与启动流程,帮助开发者理解并基于这两个示例搭建自己的 BLE 传输应用。
Purpose and Scope
本页面覆盖 SDK 中 apps/demo/transfer/examples/multi_conn 与 apps/demo/transfer/examples/nonconn_trans 两个示例工程:
- 多连接(multi_conn):以
CONFIG_APP_MULTI编译开关驱动的多角色 BLE 应用,同时支持 Central(主机)与 Peripheral(从机)角色,包含扫描、广播、GATT 客户端/服务器能力,以及与之配套的低功耗(软关机)处理。 - 无连接传输(nonconn_trans):以
CONFIG_APP_NONCONN_24G编译开关驱动的 24G 无连接(广播)数据传输应用,不建立 GATT 连接即完成数据收发,同样包含低功耗联动处理。
以下主题由其它目录页面覆盖,不在本页展开:单连接透传数据(见 trans_data 示例)、AT 指令透传(见 at_char_com 示例)、USB Dongle 应用(见 dongle 示例)。本页聚焦这两个传输模式自身的工作机制。
Overview
BLE 数据传输通常依赖 GATT 连接,但存在两类常见需求:
多连接:一个设备同时承担多种角色,例如耳机既要作为 Peripheral 被手机连接,又要作为 Central 连接手表/手环;或一个中心设备同时维护多条 GATT 连接。SDK 通过
multi_conn示例展示多角色共存:ble_multi_adv(广播)、ble_multi_scan(扫描)、ble_multi_central(主机)、ble_multi_peripheral(从机)四个模块由ble_multi.c统一调度,ble_multi_profile.h定义 GATT 服务。无连接传输:数据无需建立连接即可通过广播信道下发(类似私有 24G 广播协议)。
nonconn_trans示例即此类"即发即走"的低开销传输,适合遥控器、键鼠等一对多、低时延、无需应答的场景。
两个示例均与低功耗(软关机)机制深度耦合:multi_state_idle_set_active() / noconn_state_idle_set_active() 标记活动状态,软关机时先断开链路再进入低功耗,避免链路超时阻塞关机流程。
Architecture
下图展示两个传输模式的应用层模块划分及其与 BLE 协议栈、低功耗框架的关系:
flowchart TD
subgraph sg_App["应用层 (apps/demo/transfer/examples)"]
subgraph sg_Multi["多连接 multi_conn"]
MultiApp["app_multi_conn.c<br/>(CONFIG_APP_MULTI)"]
MultiCore["ble_multi.c"]
Adv["ble_multi_adv.c<br/>广播"]
Scan["ble_multi_scan.c<br/>扫描"]
Central["ble_multi_central.c<br/>Central 角色"]
Peripheral["ble_multi_peripheral.c<br/>Peripheral 角色"]
MultiProfile["ble_multi_profile.h<br/>GATT Profile"]
MultiApp --> MultiCore
MultiCore --> Adv
MultiCore --> Scan
MultiCore --> Central
MultiCore --> Peripheral
MultiCore --> MultiProfile
end
subgraph sg_NonConn["无连接传输 nonconn_trans"]
NonConnApp["app_nonconn_trans.c<br/>(CONFIG_APP_NONCONN_24G)"]
NonConnDeal["ble_noconn_deal.c<br/>无连接数据收发"]
NonConnApp --> NonConnDeal
end
end
subgraph sg_Stack["BLE 协议栈"]
Btstack["btstack_task / btstack_ble_*"]
Gatt["GATT 层<br/>GATT_ROLE_SERVER / GATT_ROLE_CLIENT"]
end
subgraph sg_Power["低功耗框架"]
PowerMg["app_power_mg.c"]
BtExit["btstack_ble_exit()"]
end
MultiCore --> Btstack
NonConnDeal --> Btstack
MultiApp -->|"ble_comm_dev_is_connected()"| Gatt
MultiApp --> PowerMg
NonConnApp --> PowerMg
PowerMg --> BtExit
模块职责说明:
app_multi_conn.c/app_nonconn_trans.c:应用入口,分别由CONFIG_APP_MULTI、CONFIG_APP_NONCONN_24G编译开关控制;负责启动协议栈、处理消息、联动低功耗。ble_multi.c:多连接应用的核心调度,初始化各角色模块并分发事件。ble_multi_adv.c/ble_multi_scan.c:广播与扫描,是多角色共存时建立/被建立连接的前提。ble_multi_central.c/ble_multi_peripheral.c:Central(主动发起连接)与 Peripheral(被动接受连接)的 GATT 交互逻辑。ble_noconn_deal.c:无连接模式下的数据打包与收发处理,直接使用广播信道而不经过 GATT 连接。- 底层统一走
btstack(杰理 BLE 协议栈),软关机流程统一由app_power_mg驱动。
设计意图:将角色拆分为独立模块(adv/scan/central/peripheral),使多连接能力可按需裁剪组合;无连接传输与应用解耦为
ble_noconn_deal.c,便于在其它工程复用。
多连接示例实现分析(multi_conn)
编译开关与入口
整个多连接示例由 #if CONFIG_APP_MULTI 包裹,只有使能该宏时相关代码才会被编译进固件:
#if CONFIG_APP_MULTI
static volatile uint8_t multi_is_active = 0;
Source: app_multi_conn.c
multi_is_active 是一个 volatile 标志,用于标记多连接应用当前是否处于活动状态,供低功耗框架查询。这是典型的"应用活动状态 ↔ 低功耗准入"协作模式:只要应用还有广播/扫描/连接活动,系统就不允许进入深度睡眠。
低功耗状态标记
void multi_state_idle_set_active(uint8_t active)
{
multi_is_active = active;
}
Source: app_multi_conn.c
该函数被其它模块调用,用于告知低功耗框架当前多连接任务是否仍处于活动状态。例如当 GATT 客户端(Central)开启扫描时不应进入低功耗,相关代码注释说明了这一点:
void app_to_recover(void)
{
#if CONFIG_BT_GATT_CLIENT_NUM
// 默认scan不开低功耗
/* multi_state_idle_set_active(1); */
#endif
}
Source: app_multi_conn.c
app_to_recover() 是软关机恢复回调:从关机状态恢复时执行。CONFIG_BT_GATT_CLIENT_NUM 决定是否启用 GATT 客户端(Central)能力;若启用,扫描期间默认不进入低功耗(注释掉的 multi_state_idle_set_active(1) 表明该策略默认关闭,开发者可按需打开)。
软关机流程:先断链,再休眠
多连接场景下软关机是最关键的边界条件——如果设备仍保持着 GATT 连接就进入低功耗,链路会因无响应而超时断开,造成体验问题。因此实现上遵循"先主动断开链路,再进入低功耗"的顺序:
static void multi_set_soft_poweroff(void)
{
log_info("set_soft_poweroff\n");
#if (TCFG_LOWPOWER_PATTERN == SOFT_MODE)
multi_is_active = 1;
#elif (TCFG_LOWPOWER_PATTERN == SOFT_BY_POWER_MODE)
multi_is_active = 0;//allow enter lowpower
#endif
//必须先主动断开蓝牙链路,否则要等链路超时断开
btstack_ble_exit(0);
//延时,确保BT退出链路断开
if (ble_comm_dev_is_connected(GATT_ROLE_SERVER) || ble_comm_dev_is_connected(GATT_ROLE_CLIENT)) {
#if (TCFG_LOWPOWER_PATTERN == SOFT_MODE)
//soft 方式非必须等链路断开
sys_timeout_add(NULL, app_power_set_soft_poweroff, WAIT_DISCONN_TIME_MS);
#elif (TCFG_LOWPOWER_PATTERN == SOFT_BY_POWER_MODE)
//must wait disconn
app_power_soft.wait_disconn = 1;
#endif
} else {
app_power_set_soft_poweroff(NULL);
}
}
Source: app_multi_conn.c
流程要点与设计意图:
- 调用
btstack_ble_exit(0)主动退出 BLE 协议栈,从源头切断所有链路。 - 通过
ble_comm_dev_is_connected(GATT_ROLE_SERVER)与ble_comm_dev_is_connected(GATT_ROLE_CLIENT)分别查询从机与主机两条链路的连接状态——这正是多连接场景特有的判断:任何一条链路存在都要等待断开。 - 按
TCFG_LOWPOWER_PATTERN区分两种低功耗策略:SOFT_MODE:软关机无需等链路真正断开,使用sys_timeout_add()定时WAIT_DISCONN_TIME_MS后回调app_power_set_soft_poweroff();SOFT_BY_POWER_MODE:必须等链路断开,置位app_power_soft.wait_disconn = 1,由低功耗框架在收到断链事件后再继续关机。
- 若无任何连接,直接调用
app_power_set_soft_poweroff(NULL)立即关机。
角色模块划分
多连接的能力被拆分为四个可独立维护的模块(由目录文件清单确认):
| 模块文件 | 职责 |
|---|---|
ble_multi_adv.c | 多角色广播:配置广播参数、广播数据,供 Peripheral 被发现 |
ble_multi_scan.c | 扫描:发现周边设备,供 Central 发起连接 |
ble_multi_central.c | Central 角色:主动建立 GATT 连接、执行客户端读写 |
ble_multi_peripheral.c | Peripheral 角色:接受连接、提供 GATT 服务端 |
ble_multi.c / ble_multi.h | 统一入口与公共接口,初始化上述模块 |
ble_multi_profile.h | 自定义 GATT Profile 定义(服务/特征 UUID、回调) |
设计意图:把角色职责隔离到独立文件,使得"只做 Central"或"只做 Peripheral"的裁剪只需在
ble_multi.c中注释对应模块的初始化调用,而不必改动协议逻辑。
无连接传输示例实现分析(nonconn_trans)
编译开关与入口
无连接传输示例由 #if CONFIG_APP_NONCONN_24G 包裹,日志标签为 NCON_24G,其文件名注释为 app_nonconn_24g.c,说明该模式沿用了杰理私有 24G 无连接协议的数据通路——即通过广播信道传输数据、不建立 GATT 连接:
#if CONFIG_APP_NONCONN_24G
static volatile uint8_t noconn_is_active = 0;
Source: app_nonconn_trans.c
与多连接示例对称,noconn_is_active 同样用于标记应用活动状态以配合低功耗准入。
协议栈启动流程
无连接模式的数据通路不依赖 GATT,因此启动流程更精简:配置 PLL 时钟后直接初始化 BLE 协议栈:
static void noconn_bt_start()
{
uint32_t sys_clk = clk_get("sys");
bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);
btstack_ble_start_before_init(NULL, 0);
btstack_init();
}
Source: app_nonconn_trans.c
逐步说明:
clk_get("sys")获取当前系统时钟频率;bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0)依据外部晶振频率与系统时钟配置 BLE 所需 PLL 参数——射频时序精度依赖该配置,是 BLE 正常收发的前提;btstack_ble_start_before_init(NULL, 0)完成协议栈启动前的准备工作;btstack_init()正式初始化协议栈任务。
应用启动与软关机
static void noconn_app_start()
{
log_info("=======================================");
log_info("-----------nonconn_24g demo------------");
log_info("=======================================");
log_info("app_file: %s", __FILE__);
...
}
Source: app_nonconn_trans.c
noconn_app_start() 在系统启动后被调用,打印 demo 标识后继续完成无连接数据的初始化与周期广播。软关机实现与多连接示例同构,但无需查询 GATT 连接状态——无连接模式本就没有链路,因此直接退出协议栈并关机:
static void noconn_set_soft_poweroff(void)
{
log_info("set_soft_poweroff\n");
noconn_is_active = 1;
btstack_ble_exit(0);
app_power_set_soft_poweroff(NULL);
}
Source: app_nonconn_trans.c
数据收发模块
无连接数据的具体打包/解析与周期发送由 ble_noconn_deal.c(头文件 ble_noconn_deal.h)实现,app_nonconn_trans.c 仅通过该模块的接口驱动数据流程。该模块是"无连接传输"能力的内核,将待发送数据按 24G 广播帧格式组织,并通过协议栈广播接口发出;接收侧则解析广播包数据域完成还原。
核心流程
软关机流程(多连接)
sequenceDiagram
participant App as app_multi_conn.c
participant Bt as btstack (协议栈)
participant Gatt as GATT 层
participant Power as app_power_mg
App->>App: multi_set_soft_poweroff()
App->>Bt: btstack_ble_exit(0)
Bt-->>App: 退出协议栈
App->>Gatt: ble_comm_dev_is_connected(SERVER/CLIENT)
alt 仍有连接 (SOFT_BY_POWER_MODE)
App->>Power: app_power_soft.wait_disconn = 1
Gatt-->>App: 断链事件
App->>Power: app_power_set_soft_poweroff()
else 仍有连接 (SOFT_MODE)
App->>App: sys_timeout_add(WAIT_DISCONN_TIME_MS)
App->>Power: app_power_set_soft_poweroff() (定时回调)
else 无连接
App->>Power: app_power_set_soft_poweroff() (立即)
end
Power-->>App: 进入软关机
设计意图:先
btstack_ble_exit主动断链、再根据低功耗模式决定"等链"或"定时关",是平衡关机时延与链路稳定性的关键取舍——SOFT_BY_POWER_MODE牺牲时延换取绝对安全,SOFT_MODE以定时兜底换取快速关机。
无连接传输启动流程
sequenceDiagram
participant App as app_nonconn_trans.c
participant Clk as 时钟管理
participant Bt as btstack (协议栈)
participant Nc as ble_noconn_deal.c
App->>Clk: clk_get("sys")
App->>Bt: bt_pll_para(OSC_HZ, sys_clk)
App->>Bt: btstack_ble_start_before_init(NULL, 0)
App->>Bt: btstack_init()
App->>Nc: 初始化无连接数据模块
loop 周期发送
Nc->>Bt: 广播信道发送数据帧
end
Note over App,Bt: 全程不建立 GATT 连接
使用示例
示例 1:多连接模式下标记应用活动状态(配合低功耗)
在扫描或广播期间调用 multi_state_idle_set_active() 控制低功耗准入;恢复函数 app_to_recover() 在软关机恢复时被框架回调:
void multi_state_idle_set_active(uint8_t active)
{
multi_is_active = active;
}
void app_to_recover(void)
{
#if CONFIG_BT_GATT_CLIENT_NUM
// 默认scan不开低功耗
/* multi_state_idle_set_active(1); */
#endif
}
Source: app_multi_conn.c
用法说明:应用层在开始扫描/广播前调用 multi_state_idle_set_active(1) 阻止休眠,结束活动后调用 multi_state_idle_set_active(0) 恢复休眠准入;若启用 GATT 客户端(CONFIG_BT_GATT_CLIENT_NUM),可取消注释让恢复后扫描期间默认不进入低功耗。
示例 2:多连接软关机前主动断链
无论当前是否存在 GATT 链路,软关机都先调用 btstack_ble_exit(0) 主动断开,再按低功耗模式决定等待策略:
//必须先主动断开蓝牙链路,否则要等链路超时断开
btstack_ble_exit(0);
if (ble_comm_dev_is_connected(GATT_ROLE_SERVER) || ble_comm_dev_is_connected(GATT_ROLE_CLIENT)) {
app_power_soft.wait_disconn = 1; // SOFT_BY_POWER_MODE:等待断链事件
} else {
app_power_set_soft_poweroff(NULL); // 无连接:立即关机
}
Source: app_multi_conn.c
示例 3:无连接模式协议栈启动
不依赖 GATT 的 24G 无连接传输,启动时只需配置 PLL 并初始化协议栈:
static void noconn_bt_start()
{
uint32_t sys_clk = clk_get("sys");
bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);
btstack_ble_start_before_init(NULL, 0);
btstack_init();
}
Source: app_nonconn_trans.c
用法说明:若移植到其它工程,需确保 TCFG_CLOCK_OSC_HZ(外部晶振频率)与实际硬件一致,否则 bt_pll_para() 计算出的射频时钟偏差会导致广播无法被接收。
配置选项
| 配置项 | 类型 | 默认/取值 | 说明 |
|---|---|---|---|
CONFIG_APP_MULTI | 宏 | 0/1 | 使能多连接示例工程;为 0 时 app_multi_conn.c 全部逻辑不参与编译 |
CONFIG_APP_NONCONN_24G | 宏 | 0/1 | 使能无连接传输示例工程;为 0 时 app_nonconn_trans.c 全部逻辑不参与编译 |
CONFIG_BT_GATT_CLIENT_NUM | 宏 | ≥0 | GATT 客户端(Central)数量;为 0 时无主机能力,影响扫描低功耗策略 |
TCFG_LOWPOWER_PATTERN | 枚举 | SOFT_MODE / SOFT_BY_POWER_MODE | 低功耗模式:SOFT_MODE 软关机无需等链路断开(定时兜底);SOFT_BY_POWER_MODE 必须等断链事件后关机 |
WAIT_DISCONN_TIME_MS | 宏 | 毫秒值 | SOFT_MODE 下软关机的等待断链超时,经 sys_timeout_add 定时触发 |
TCFG_CLOCK_OSC_HZ | 宏 | 晶振频率 | 外部晶振频率,作为 bt_pll_para() 计算 PLL 的输入 |
RCSP_BTMATE_EN | 宏 | 0/1 | 是否启用杰理 RCSP 伴侣调试协议(两示例入口均预留了该宏的包含分支) |
说明:以上开关定义于
app_config.h/btcontroller_config.h等工程配置文件,示例源码通过#if条件编译消费它们。
API 参考
多连接示例(app_multi_conn.c)
void multi_state_idle_set_active(uint8_t active)
设置多连接应用的活动状态,供低功耗框架查询是否允许休眠。
参数:
active(uint8_t):1 表示应用处于活动状态(禁止休眠),0 表示空闲(允许休眠)。
说明: 内部写入 volatile 标志 multi_is_active;广播/扫描/连接进行中应置 1,结束后置 0。
void app_to_recover(void)
软关机恢复回调,由低功耗框架在设备从软关机恢复时调用。
说明: 默认仅预留 CONFIG_BT_GATT_CLIENT_NUM 分支;若需扫描期间禁止低功耗,可在此调用 multi_state_idle_set_active(1)。
static void multi_set_soft_poweroff(void)
多连接场景的软关机流程入口(模块内部静态函数)。先 btstack_ble_exit(0) 主动断链,再依据 TCFG_LOWPOWER_PATTERN 选择等待断链(SOFT_BY_POWER_MODE → app_power_soft.wait_disconn = 1)、定时关机(SOFT_MODE → sys_timeout_add(..., WAIT_DISCONN_TIME_MS))或立即关机(无连接时 → app_power_set_soft_poweroff(NULL))。
无连接传输示例(app_nonconn_trans.c)
void noconn_state_idle_set_active(uint8_t active)
设置无连接应用的活动状态标志 noconn_is_active,语义同多连接版本。
参数:
active(uint8_t):1 活动(禁止休眠),0 空闲(允许休眠)。
static void noconn_bt_start(void)
无连接模式协议栈启动:clk_get("sys") → bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0) → btstack_ble_start_before_init(NULL, 0) → btstack_init()。
static void noconn_app_start(void)
应用启动入口:打印 nonconn_24g demo 标识后初始化无连接数据模块(ble_noconn_deal)并启动周期广播。
static void noconn_set_soft_poweroff(void)
无连接模式软关机:置 noconn_is_active = 1、btstack_ble_exit(0) 退出协议栈,随即 app_power_set_soft_poweroff(NULL) 关机(无 GATT 链路需要等待,因此比多连接版本更简洁)。
失败模式、边界情况与并发
软关机时仍有 GATT 连接
多连接场景最典型的边界情况。若不做处理直接休眠,链路将等待超时断开,导致关机流程卡顿或体验异常。源码的解法是先 btstack_ble_exit(0) 主动断链,再按低功耗模式分流(见 app_multi_conn.c L85-L109):
SOFT_BY_POWER_MODE下若断链事件迟迟不来(对端不响应),会一直等待——这是该模式的有意取舍(安全优先)。SOFT_MODE用WAIT_DISCONN_TIME_MS定时兜底,避免无限等待,但可能在链路尚未真正断开时进入休眠。
Central + Peripheral 双角色同断
multi_set_soft_poweroff() 同时检查 GATT_ROLE_SERVER 与 GATT_ROLE_CLIENT 两条链路(|| 逻辑),只要任一条存在就进入等待分支——这是多连接特有的并发关注点:两条链路的断开事件可能先后到达,低功耗框架需在最后一条断开后才继续关机。
活动状态标志的并发一致性
multi_is_active / noconn_is_active 均声明为 volatile uint8_t,可被中断/任务上下文读写。该标志只用于低功耗准入判断,不承载数据一致性要求,因此 volatile 即可满足需求,无需加锁。
无连接模式无链路超时问题
由于不建立 GATT 连接,无连接传输天然规避了"链路未断开导致无法休眠"的问题,软关机可以立即执行(noconn_set_soft_poweroff() 中无任何等待分支)。
时钟配置错误导致射频不可用
bt_pll_para() 的输入 TCFG_CLOCK_OSC_HZ 若与实际晶振不符,PLL 计算偏差会使广播/接收完全失效,且无连接模式下无链路层错误提示,排障难度更高。开发时应优先核对晶振配置。
性能与运维注意事项
- 低功耗切换点:广播/扫描期间调用
multi_state_idle_set_active(1)会阻止休眠,影响续航;建议仅在真正需要收发时开启活动标志,空闲即复位。 - 关机时延:
SOFT_MODE的关机时延受WAIT_DISCONN_TIME_MS约束,调小可加快关机但增加风险,需按实际链路表现权衡。 - 双角色资源占用:Central 与 Peripheral 同时运行时,
CONFIG_BT_GATT_CLIENT_NUM决定客户端实例数,直接影响内存与连接表占用;无连接传输不占连接表,适合资源受限场景。 - 日志定位:两示例分别使用
LOG_TAG "[MULTI_CONN]"与"[NCON_24G]",开启LOG_INFO_ENABLE后可从串口日志快速区分两个传输模式的活动。
扩展点
- 新增角色模块:多连接结构按 adv/scan/central/peripheral 拆分,新增角色(如 Observer、Broadcaster 纯广播模式)可仿照
ble_multi_scan.c新建模块并在ble_multi.c挂载。 - 自定义 GATT Profile:修改
ble_multi_profile.h中的服务/特征定义即可扩展多连接业务数据通道。 - 无连接协议复用:
ble_noconn_deal.c与应用入口解耦,可整体移植到其它工程,仅需替换应用层的周期发送调度。 - RCSP 调试:两示例均预留
#if RCSP_BTMATE_EN分支,使能后可接入杰理 RCSP 调试工具。 - 低功耗策略切换:通过切换
TCFG_LOWPOWER_PATTERN即可在"快速关机"与"安全断链"两种策略间切换,无需改动业务代码。
Related Links
- trans_data 示例(单连接透传) —— 基于 GATT 的单连接数据透传实现
- at_char_com 示例(AT 指令透传) —— 通过 AT 指令控制 BLE 透传
- dongle 示例(USB Dongle) —— 基于 USB 的 BLE 桥接应用
- 源码目录:apps/demo/transfer/examples/multi_conn(含
ble_multi_adv.c、ble_multi_scan.c、ble_multi_central.c、ble_multi_peripheral.c、ble_multi.c、ble_multi_profile.h) - 源码目录:apps/demo/transfer/examples/nonconn_trans(含
app_nonconn_trans.c、ble_noconn_deal.c)