蓝牙 LE PAST 技术详解:让第三台设备免扫描直接接收广播
PAST (Periodic Advertising Sync Transfer) 是蓝牙 5.0 引入的核心特性之一,也是 LE Audio 广播音频的关键使能技术。本文从协议原理、HCI 命令、LL 层 PDU 到代码路径,完整解析 PAST 如何让第三台设备跳过扫描直接接收广播数据。
------------------------------------------------------------------------------------------------------------------------------------------
视频链接:https://item.taobao.com/item.htm?id=1001969040805&mi_id=000032T4qZX9WZoRwX6YbxlNUaZOfOI6XoxDx0jxsfnwlEc&spm=a21xtw.29178619.0.0
Le Audio文章目录:
还在为蓝牙BLE Audio的学习苦恼吗?安排下,让你一文彻底了解Le Audio蓝牙低功耗音频的技术-CSDN博客
---------------------------------------------------------------------------------------------------------------------------------
一、什么是 PAST
PAST 全称Periodic Advertising Sync Transfer(周期广播同步转移),定义在 Bluetooth Core Spec Vol 6, Part B, Section 4.4.5。
一句话概括:设备 B 把自己已经建立的 Periodic Advertising 同步信息,通过 ACL 连接转移给设备 C,使 C 无需扫描就能直接接收设备 A 的周期广播。
PAST 解决的核心矛盾是:
问题 | 传统方式 | PAST 方式 |
发现周期广播 | 必须扫描 ADV_EXT_IND → 跟随 AuxPtr → 接收 AUX_SYNC_IND | 由已同步设备直接转移 SyncInfo |
功耗 | 扫描占空比高,耳机类设备无法承受 | 零扫描,Controller 直接在目标时刻唤醒 |
发现时间 | 需要数个广播周期 | 即时(一次 ACL PDU 传输即完成) |
可靠性 | 扫描可能错过 AUX_SYNC_IND | 通过 ACL 可靠传输 |
二、背景:Periodic Advertising 的工作方式
理解 PAST 之前,必须先理解 Periodic Advertising(周期广播)的常规同步过程。
2.1 周期广播的三层结构
Device A (Advertiser) │ ├─ Primary ADV: ADV_EXT_IND (PDU on ch 37/38/39) │ └─ AuxPtr: 指向 Secondary 信道 │ ├─ Secondary ADV: AUX_SYNC_IND (PDU on secondary ch) │ └─ SyncInfo: 周期广播的同步参数 │ └─ TXPower, AdvDataInfo 等 │ └─ Periodic ADV: ADV_PERIODIC (在 SyncInfo 指定的信道/时间发送) └─ 携带 BIGInfo (如果存在 BIG) └─ 携带周期性广播数据2.2 常规同步流程(无 PAST)
- Scanner 在主信道(37/38/39)扫描,收到ADV_EXT_IND
- ADV_EXT_IND 中的AuxPtr字段指向 Secondary 信道
- Scanner 跳转到 Secondary 信道,接收AUX_SYNC_IND
- AUX_SYNC_IND 中的SyncInfo包含周期广播的完整调度信息
- Scanner 用 SyncInfo 计算下一次 Periodic ADV 的时间和信道
- Scanner 在正确时间唤醒,接收ADV_PERIODICPDU
问题:步骤 1-4 需要持续扫描,功耗极高。对于 TWS 耳机这种电池容量极小的设备,几乎不可行。
三、三设备广播接收原理(核心)
这是本文的核心问题:怎样让第三台设备(C)收到广播者(A)的广播数据?
3.1 三种设备角色
角色 | 设备 | 说明 |
Broadcaster | Device A | 发送 Periodic Advertising 的广播者(如 TV、手机) |
Scanner / PAST Sender | Device B | 已扫描并同步 A 的周期广播,负责转移同步信息(如手机) |
PAST Receiver | Device C | 接收 B 转移的 SyncInfo,免扫描直接接收 A 的广播(如耳机) |
3.2 完整流程(7 步)
阶段一:B 同步 A(步骤 ①②)
- ① B 扫描发现 A 的 ADV_EXT_IND → AUX_SYNC_IND,从中提取SyncInfo
- ② B 的 Controller 建立同步,生成
HCI_LE_Periodic Advertising Sync Established事件,Host 获得sync_handle
阶段二:B-C 建立 ACL 连接(步骤 ③)
- ③ B 与 C 之间必须先有 BLE ACL 连接。PAST 转移的 LL_PERIODIC_SYNC_IND PDU 就走在这个连接上
阶段三:PAST 转移(步骤 ④⑤)
- ④ B 的 Host 发送
HCI_LE_Periodic_Advertising_Sync_Transfer命令 → B 的 Controller 在 ACL 连接上发送LL_PERIODIC_SYNC_INDPDU → C 的 Controller 接收 - ⑤ C 的 Controller 自动解析 PDU 中的 SyncInfo,建立与 A 的周期广播同步,生成
Sync Established事件上报 Host
阶段四:C 直接接收 A 的广播(步骤 ⑥⑦)
- ⑥ C 的 Controller 根据 SyncInfo,在正确的时间、正确的信道唤醒,直接接收 A 的ADV_PERIODICPDU——完全无需扫描
- ⑦ C 从 Periodic Advertising 数据中读取BIGInfo,同步 BIG(Broadcast Isochronous Group),接收BIS(Broadcast Isochronous Stream)音频/数据流
3.3 为什么 C 能直接接收?——原理拆解
这是理解 PAST 的关键。Periodic Advertising 本质上是一个确定性调度系统:
SyncInfo 提供的信息: ├── Offset → "多久之后开始下一次广播" → 解决 WHEN ├── Interval → "每隔多久广播一次" → 解决 PERIODICITY ├── Channel Map→ "用哪些 RF 信道" → 解决 WHERE ├── Access Addr→ "广播的接入地址" → 解决 FILTER ├── CRC Init → "CRC 初始值" → 解决 DECODE └── Event Counter → "当前事件序号" → 解决 CHANNEL HOPPING有了这 6 个参数,C 的 Controller 能:
- 计算下一次广播的绝对时间:参考 ACL 连接的锚点 + Offset
- 计算跳频序列:用 Event Counter 和 Channel Map 推导下一次使用哪个信道
- 在目标时刻唤醒:Radio 直接调谐到目标信道
- 过滤和解码:用 Access Address 过滤,用 CRC Init 校验
类比:传统扫描像「不知道电视节目表,逐个频道翻找」;PAST 像「朋友直接把节目表和频道号给你,你到点打开就行」。
3.4 关键细节:PAST 只转移 SyncInfo,不转移 BIGInfo
一个常见误区:PAST 转移的是 Periodic Advertising 的同步信息,不是BIGInfo。
- PAST 转移后,C 可以直接接收 Periodic Advertising
- 但 BIGInfo 在 Periodic Advertising 的数据载荷中
- C 仍需接收至少一个 Periodic ADV PDU 才能获取 BIGInfo
- 获取 BIGInfo 后,C 才能同步 BIG 并接收 BIS 数据
四、SyncInfo 结构详解
SyncInfo 是 PAST 的核心载荷,定义在 Core Spec Vol 6, Part B, Section 2.3.4.3:
字段 | 大小 | 说明 |
Sync Packet Offset | 13 bits | 距参考点的时间偏移(30us 或 300us 单位) |
Offset Units | 1 bit | 0 = 30us,1 = 300us |
Offset Adjust | 1 bit | 若为 1,偏移量额外增加 2^13 × 30us |
Interval | 12 bits | 广播间隔,单位 1.25ms(范围 7.5ms ~ 81.91875s) |
Channel Map | 37 bits | bit n = 1 表示信道 n 被使用 |
SCA | 3 bits | Sleep Clock Accuracy(发送方时钟精度等级) |
Access Address | 32 bits | Periodic Advertising train 的接入地址 |
CRC Init | 24 bits | CRC 初始值 |
Event Counter | 16 bits | 当前周期广播事件计数器 |
Channel Hopping 计算:
C 的 Controller 收到 SyncInfo 后,用以下公式计算下一个事件的信道:
channelIndex = (EventCounter + 1) mod 37 // 如果 channelIndex 在 Channel Map 中被使用 → 使用该信道 // 否则 → 使用 Channel Map 中 >= channelIndex 的下一个可用信道每次收到一个 ADV_PERIODIC,Event Counter 递增,C 据此计算下一次的信道。
五、PAST 协议详解
5.1 涉及的 HCI 命令
发送方(Device B):
命令 | 作用 |
| 配置 PAST 参数:skip(允许跳过的事件数)、sync_timeout(同步超时)、CTE 类型 |
| 执行转移:指定 conn_handle(到 C 的 ACL)和 sync_handle(要转移的同步) |
接收方(Device C):
命令 | 作用 |
| 使能 PAST 接收:mode=1 使能,指定 conn_handle(到 B 的 ACL)、skip、sync_timeout |
生成的 HCI 事件:
事件 | 触发条件 |
| C 的 Controller 收到 LL_PERIODIC_SYNC_IND 后上报 Host |
| C 成功建立同步后上报,附带新的 sync_handle |
5.2 LL_PERIODIC_SYNC_IND PDU
这是 PAST 在 LL 层的实际传输载体,定义在 Core Spec Vol 6, Part B, Section 2.4.2.12。
LL_PERIODIC_SYNC_IND PDU 结构: ┌──────────────────────┬──────────┬────────────────┬──────────────┬─────┬──────────────────┬─────────────┬───────────────┐ │ ID (1 byte) │ Reserved │ SyncInfo │ Channel Map │ PHY │ Access Address │ CRC Init │ Event Counter │ │ (0x00 = Sync Transfer)│ │ (17 bytes) │ (5 bytes) │(1B) │ (4 bytes) │ (3 bytes) │ (2 bytes) │ └──────────────────────┴──────────┴────────────────┴──────────────┴─────┴──────────────────┴─────────────┴───────────────┘关键点:
- 这个 PDU 在 B-C 的 ACL 连接上以LL Control PDU形式发送
- B 的 Controller 负责 PDU 构造和发送,Host 不需要参与 LL 层细节
- C 的 Controller 负责解析 PDU 并自动建立同步
5.3 PAST 两种模式
模式 | 说明 | 使用场景 |
Sync Transfer | B 转移自己已有的 sync_handle | B 是扫描者,帮助 C 同步到 A |
Set Info Transfer | B 转移自己的广播 Set 信息 | B 本身是广播者,直接告诉 C 自己的周期广播参数 |
Set Info Transfer 用HCI_LE_Periodic_Advertising_Set_Info_Transfer命令,适用于「手机既是广播者又想把广播转给耳机」的场景。
六、代码路径
6.1 BlueZ (Linux)
内核态: net/bluetooth/hci_core.c → HCI 命令发送框架 net/bluetooth/hci_event.c → LE Periodic Advertising Sync Established 事件处理 LE Periodic Advertising Sync Transfer Received 事件处理 net/bluetooth/hci_sync.c → HCI 命令同步执行框架 用户态: src/shared/ad.c → 广播数据解析 src/shared/hci-cmd.c → HCI 命令封装 src/android/hal-hci.c → HAL 层接口 monitor/btmon.c → HCI 日志监控工具 mgmt 接口: MGMT_OP_ADD_EXT_ADV_PARAMS → 配置扩展广播 // PAST 相关通过 mgmt 命令触发关键调试:用btmon抓 HCI log,搜索LE Periodic Advertising Sync Transfer和LL_PERIODIC_SYNC_IND。
6.2 Bluedroid (Android)
system/bt/stack/hcic/ hciblecmds.c → HCI_LE_Set_Periodic_Advertising_Sync_Transfer_Parameters HCI_LE_Periodic_Advertising_Sync_Transfer HCI_LE_Set_Periodic_Advertising_Sync_Transfer_Receive_Enable hcicmd.c → HCI 命令发送 system/bt/stack/btm/ btm_ble_5_gap.c → BLE 5.x GAP 接口 btm_ble_api.c → BTM_BlePeriodicAdvSyncTransfer 等应用层 API system/bt/stack/btu/ hcitask.c → HCI 任务调度,事件分发 system/bt/main/ stack_config.c → 协议栈配置 应用层: frameworks/base/core/java/android/bluetooth/ BluetoothAdapter.java → 公共 API 入口 BluetoothLeBroadcastAssistant.java → LE Audio Broadcast Assistant(PAST 调用方)七、实际应用场景
7.1 LE Audio 广播音频(最核心场景)
这是 PAST 存在的根本原因。
场景:机场候机厅广播音频 Device A = 机场广播发射器(发送 Periodic ADV + BIG + BIS 音频流) Device B = 乘客手机(Broadcast Assistant,扫描发现 A) Device C = 乘客 TWS 耳机(Broadcast Sink,接收音频) 流程: 1. 手机扫描发现机场广播的 Periodic ADV 2. 手机同步 Periodic ADV,读取 BIGInfo 3. 手机通过 BASS(Broadcast Audio Scan Service)写入耳机的 BASS Characteristic 4. 手机通过 PAST 将 SyncInfo 转移给左耳 5. 手机通过 PAST 将 SyncInfo 转移给右耳 6. 左右耳直接接收 A 的 BIS 音频流,无需扫描为什么耳机不能自己扫描?
- TWS 耳机电池通常 40-60mAh,持续扫描会耗尽电量
- 扫描占空比可能需要 10-30%,PAST 方式仅需在 Periodic ADV 时刻唤醒(<0.1%)
- 耳机的天线和 Radio 性能通常弱于手机
7.2 电子货架标签(ESL)
Device A = ESL 网关(广播价格更新数据) Device B = 手机 App(管理标签,扫描发现 A) Device C = 电子货架标签(通过 PAST 接收更新数据) 优势:标签平时深度睡眠,仅在 PAST 转移后唤醒接收,电池寿命可达数年7.3 传感器数据广播
Device A = 环境传感器(周期广播温湿度数据) Device B = 网关(已同步 A) Device C = 新加入的显示设备(通过 PAST 快速同步) 优势:新设备无需扫描即可加入数据接收,部署灵活八、调试技巧
8.1 用 btmon 抓 PAST 流程
# 抓取 HCI 日志 sudo btmon -w past_debug.log # 过滤 PAST 相关命令和事件 btmon -r past_debug.log | grep -E "Periodic|Sync Transfer|PERIODIC_SYNC"关键日志标识:
> HCI_LE_Periodic_Advertising_Sync_Transfer→ B 发起转移< HCI_LE_Periodic Advertising Sync Established→ C 成功同步LL_PERIODIC_SYNC_IND→ LL 层 PDU 传输
8.2 常见问题排查
问题 | 可能原因 | 排查方法 |
C 收不到 PAST | B-C 之间没有 ACL 连接 | 检查连接状态, 确认 |
C 同步建立后立即丢失 | sync_timeout 太短 | 检查 的 timeout 参数 |
C 同步成功但收不到 ADV_PERIODIC | A 已停止广播 | 确认 A 仍在发送 Periodic ADV |
SyncInfo Offset 计算错误 | ACL 锚点参考不正确 | 检查 LL_PERIODIC_SYNC_IND 的发送时间点 |
C 的 Event Counter 与 A 不同步 | PAST 转移时延迟过大 | 检查 ACL 连接的 latency 和 timeout |
8.3 PTS 测试
PAST 相关的 PTS 测试用例:
PAST/SR/BI-01-C:Sync Transfer 基本流程PAST/SR/BI-02-C:Set Info Transfer 流程PAST/SR/BI-03-C:PAST 参数配置PAST/CG/BI-01-C:Controller 在 ACL 上发送 LL_PERIODIC_SYNC_IND
九、自研协议栈实现要点
对于你团队自研蓝牙协议栈 Host,PAST 实现需要关注:
9.1 Host 层职责
Host 需要实现的: ├── HCI 命令封装和发送 │ ├── LE_Set_Periodic_ADV_Sync_Transfer_Parameters │ ├── LE_Periodic_ADV_Sync_Transfer │ ├── LE_Set_Periodic_ADV_Sync_Transfer_Receive_Enable │ └── LE_Periodic_ADV_Set_Info_Transfer ├── HCI 事件处理 │ ├── Periodic_Advertising_Sync_Transfer_Received │ └── Periodic_Advertising_Sync_Established ├── sync_handle 管理 │ ├── 维护 sync_handle → 广播源映射表 │ └── PAST 转移后 C 侧生成新 sync_handle └── 上层接口(GATT BASS / 应用层 API) Host 不需要实现的 (Controller 负责): ├── LL_PERIODIC_SYNC_IND PDU 构造和解析 ├── SyncInfo 中 Offset 的精确时间计算 ├── Channel Hopping 算法 └── Radio 调度(何时唤醒、调谐到哪个信道)9.2 关键实现细节
- sync_handle 生命周期管理:PAST 转移后,B 侧的 sync_handle 仍然有效(B 继续同步),C 侧生成一个新的 sync_handle。两者独立,互不影响。
- skip 和 timeout 参数:
skip:允许 C 连续错过多少个 Periodic ADV 事件而不丢失同步sync_timeout:超过此时间未收到任何 ADV_PERIODIC 则同步丢失(范围 10ms~163.84s)- 建议初始值:skip = 0,sync_timeout = 1000ms(根据实际广播间隔调整)
- Service Data 字段:
HCI_LE_Periodic_Advertising_Sync_Transfer中的 16-bit Service Data 由 B 的 Host 设置,C 的 Host 通过Sync_Transfer_Received事件接收。可携带应用层元数据(如广播源 ID)。 - 与 BASS 的联动:在 LE Audio 场景中,PAST 通常由 BASS(Broadcast Audio Scan Service)触发。B 的 Host 写入 C 的 BASS Add Source Characteristic 后,自动触发 PAST 流程。
十、总结
维度 | 传统扫描同步 | PAST 转移同步 |
功耗 | 高(持续扫描) | 极低(仅目标时刻唤醒) |
发现时间 | 数百ms~数秒 | 即时(一次 ACL PDU) |
可靠性 | 依赖无线扫描 | ACL 可靠传输 |
适用设备 | 手机、网关 | 耳机、标签、传感器 |
蓝牙版本 | 5.0+ | 5.0+ |
依赖条件 | 无 | B-C 间有 ACL 连接 |
PAST 的本质是:把「发现」和「接收」解耦。B 负责「发现」(扫描),C 负责「接收」(直接听)。中间通过 ACL 连接把同步信息可靠地传递过去。
对于你团队的三个工作板块:
- 产测陪测:PAST 测试用例是产线必测项(LE Audio 模组)
- Bringup:BlueZ/Bluedroid 的 PAST 流程调试是客户常见问题
- 自研协议栈:PAST 的 Host 层实现相对简单(HCI 命令+事件管理),但 Controller 侧的 SyncInfo 时间计算是难点
参考文档:Bluetooth Core Specification v5.4, Vol 6, Part B, Section 4.4.5 (PAST), Section 2.4.2.12 (LL_PERIODIC_SYNC_IND)