6 GAP - 扫描
6.1 概览
接收广播的过程称为扫描。
6.1.1 间隔与窗口
接收机实际进行扫描工作的时机受扫描间隔和窗口两个参数控制,其含义如图 6.1 所示。
注意:Controller 在执行扫描任务时,遵循最大努力原则,实际真正用于扫描的时机可能并不与两个参数所指定的完全一致。
6.1.2 过滤策略
与广播的“过滤策略”类似,扫描也有几种过滤策略:
typedef enum scan_filter_policy
{
// 接收所有广播(定向到其它设备的除外)
,
SCAN_ACCEPT_ALL_EXCEPT_NOT_DIRECTED// 只接收来自白名单内的设备的广播(定向到其它设备的除外)
,
SCAN_ACCEPT_WLIST_EXCEPT_NOT_DIRECTED// 接收所有广播(所定向的设备与本设备身份地址不同的除外)
// SCAN_ACCEPT_ALL_EXCEPT_IDENTITY_NOT_MATCH,
// 只接收来自白名单内的设备的广播(所定向的设备与本设备身份地址不同的除外)
// SCAN_ACCEPT_WLIST_EXCEPT_IDENTITY_NOT_MATCH
} scan_filter_policy_t;
后两种策略23与前两种策略的区别在于判断是否接受定向广播的方法不同。前两种策略只接受满足以下两者之一的定向广播:
- TargetA 与本设备的地址一致,
- TargetA 是可解析地址,且可由本设备的 IRK 解析。
而后两种策略接受满足以下两者之一的定向广播:
- TargetA 与本设备的地址一致,
- TargetA 是可解析地址。
可见后两种策略对 TargetA 的要求略微宽松。
6.2 使用说明
6.2.1 配置参数
在开始扫描之前,需要先通过 gap_set_random_device_address
为设备配置地址,这个地址用于扫描、发起连接等场景。
使用 gap_set_ext_scan_para
配置扫描参数:
uint8_t gap_set_ext_scan_para(
// 本设备地址类型
const bd_addr_type_t own_addr_type,
// 过滤策略
const scan_filter_policy_t filter,
// PHY 配置个数
const uint8_t config_num,
// 关于每种 PHY 的参数配置
const scan_phy_config_t *configs);
这里的 PHY 指的是主广播信道所使用的 PHY,只能为 1M 或 Coded。辅广播信道上所有的 PHY 类型全部支持,不需要配置。 每种 PHY 的参数配置如下:
typedef struct scan_phy_config
{
// PHY
;
phy_type_t phy// 扫描方式:主动或被动
;
scan_type_t type// 扫描间隔,单位是 625us
uint16_t interval;
// 扫描窗口,单位是 625us
uint16_t window;
} scan_phy_config_t;
6.2.2 起停扫描
使用 gap_set_ext_scan_enable
起停扫描:
uint8_t gap_set_ext_scan_enable(
// 开始或者停止扫描
const uint8_t enable,
// 是否对数据做去重处理:
// 0 - 不去重;
// 1 - 去重;
// 2 - 去重,但是每个周期复位过滤器
const uint8_t filter,
// 持续时间,单位为 10ms
const uint16_t duration,
// 周期,单位为 1.28s
const uint16_t period);
duration
和 period
两个参数的目的是实现每个 period
里扫描 duration
长的时间。另有两种特殊情况,从开发者的角度总结如下:
- 持续、一直地扫描:
duration
和period
两个参数皆置为 \(0\) - 只扫描一段时间:
duration
置为扫描时长,period
置为 \(0\)
Controller 在做数据去重时遵循最大努力原则,受限于存储空间、处理能力,去重可能失效。
6.2.3 处理数据
收到广播包后,HCI 回调函数将收到 HCI_SUBEVENT_LE_EXTENDED_ADVERTISING_REPORT
事件。利用 decode_hci_le_meta_event
宏可将事件转换为 le_ext_adv_report_t
结构体指针:
const le_ext_adv_report_t *report =
(packet, le_meta_event_ext_adv_report_t)->reports; decode_hci_le_meta_event
这个结构体的定义如下:
typedef struct le_ext_adv_report
{
// 事件类型比特位组合
uint16_t evt_type;
// 广播者地址类型
;
bd_addr_type_t addr_type// 广播者地址
;
bd_addr_t address// 主信道上用的 PHY
uint8_t p_phy;
// 辅信道上用的 PHY
uint8_t s_phy;
// SID
uint8_t sid;
// 发射功率(单位 dBm)
int8_t tx_power;
// RSSI (单位 dBm)
int8_t rssi;
// 周期广播的间隔(仅对周期广播有效)
uint16_t prd_adv_interval;
// 定向广播的目的地址类型
;
bd_addr_type_t direct_addr_type// 定向广播的目的地址
;
bd_addr_t direct_addr// 广播数据长度
uint8_t data_len;
// 广播数据
uint8_t data[0];
} le_ext_adv_report_t;
6.2.4 与周期广播同步
发现周期广播后,可以通过 gap_periodic_adv_create_sync
与周期广播同步24:
uint8_t gap_periodic_adv_create_sync(
// 过滤策略:目标地址来自白名单还是参数
const periodic_adv_filter_policy_t filter_policy,
// SID
const uint8_t adv_sid,
// 目标地址类型
const bd_addr_type_t addr_type,
// 目标地址
const uint8_t *addr,
// 成功接收一次之后,可跳过的数目
const uint16_t skip,
// 同步超时(单位 100ms)
const uint16_t sync_timeout,
// 周期广播里的 CTE 类型(如果存在)
const uint8_t sync_cte_type
);
成功建立同步后,HCI 回调会收到 HCI_SUBEVENT_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED
事件。接收到的周期广播通过
HCI_SUBEVENT_LE_PERIODIC_ADVERTISING_REPORT
事件上报,其内容与 le_ext_adv_report_t
类似。
对于 5.4,HCI 回调会收到 HCI_SUBEVENT_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_V2
事件。
与 HCI_SUBEVENT_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED
相比,事件内容增加了子事件的相关信息。
周期广播的接收可参考 SDK Periodic Scanner。
6.2.5 与 PAwR 同步
当 HCI_SUBEVENT_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_V2
携带的子事件信息显示存在存在子事件时,可通过
gap_set_periodic_sync_subevent
与指定的子事件同步:
uint8_t gap_set_periodic_sync_subevent(
// 同步
uint16_t sync_handle,
// 发送响应时的属性
// 只能为 0 或者 PERIODIC_ADV_BIT_INC_TX
uint16_t periodic_adv_properties,
// 要同步的子事件数
uint8_t num_subevents,
// 要同步的每个子事件的序号
const uint8_t *subevents)
与子事件同步后,接收到的每个子事件的数据将通过 HCI_SUBEVENT_LE_PERIODIC_ADVERTISING_REPORT_V2
事件上报,
其内容与 HCI_SUBEVENT_LE_PERIODIC_ADVERTISING_REPORT
类似,只是增加了关于子事件的信息。
6.2.5.1 设置、发送响应
通过 gap_set_periodic_adv_rsp_data
在指定的子事件、响应时隙发送响应数据:
uint8_t gap_set_periodic_adv_rsp_data(
// 同步句柄
uint16_t sync_handle,
// 所要响应的 PAwR 所在的周期广播事件序号
uint16_t request_event,
// 所要响应的 PAwR 所在的子事件序号
uint8_t request_subevent,
// 发送响应的子事件序号
uint8_t rsp_subevent,
// 发送响应的时隙序号
uint8_t rsp_slot,
// 响应的长度
uint8_t rsp_data_len,
// 响应数据
const uint8_t *rsp_data);