从零实现BLE协议栈(4-4)实验:04_phy_connection

4-4 动手实验:编译运行 04_phy_connection

本篇是第 4 章的配套实验,将 4-1 ~ 4-4 中讲解的首锚点计算、锚点递推与 CSA#1 跳频完整跑通。


一、概述

04_phy_connection 在收到 CONNECT_IND 后:

  1. 计算首锚点 A0:6 步流程将 T0 + winOffset 转换为精确的 RTC tick + 皮秒余量
  2. 进入连接事件循环:每 connInterval 递推锚点、CSA#1 跳频、SN/NESN 流控,直到 supervision timeout

本 Demo 没有 Window Widening 补偿,因此连接只能维持约 3 秒(~16 个事件后锚点漂移超出接收窗口)。第 5 章的 Demo 05 将引入 WW 解决此问题。


二、编译与烧录

west build -b nrf52dk/nrf52832 .\write-BLE-stack-from-scratch\04_phy_connection\ -p
west flash

编译产物:

Memory region         Used Size  Region Size  %age Used
           FLASH:      100172 B       512 KB     19.11%
             RAM:       18944 B        64 KB     28.91%

三、运行测试

.\write-BLE-stack-from-scratch\tools\venv\Scripts\python.exe `
  .\write-BLE-stack-from-scratch\tools\bumble_connect.py `
  --transport usb:2FE3:000B `
  --target 66:55:44:33:22:11 `
  --duration 20

Bumble 侧:连接建立后约 2.75 秒超时断开(无 WW 补偿,锚点漂移导致 RX 丢包无法恢复)。


四、串口日志

连接事件循环

[CONN] #1 ch=11 OK | rtc=456053 | rx=1/1 miss=0
[CONN] #2 ch=22 OK | rtc=457692 | rx=2/2 miss=0
[CONN] #3 ch=33 OK | rtc=459330 | rx=3/3 miss=0
[CONN] #4 ch=7 OK  | rtc=460969 | rx=4/4 miss=0
[CONN] #5 ch=18 OK | rtc=462607 | rx=5/5 miss=0
[CONN] #6 ch=29 OK | rtc=464245 | rx=6/6 miss=0
[CONN] #7 ch=3 OK  | rtc=465884 | rx=7/7 miss=0
[CONN] #8 ch=14 OK | rtc=467522 | rx=8/8 miss=0
[CONN] #9 ch=25 OK | rtc=469161 | rx=9/9 miss=0
[CONN] #10 ch=36 OK | rtc=470799 | rx=10/10 miss=0
...
[CONN] #20 ch=35 MISS | rtc=487183 | rx=16/20 miss=4
[CONN] #21 ch=9 MISS  | rtc=488821 | rx=16/21 miss=5
...
[CONN] Supervision timeout! no RX for 2000000 us

连接总结

========== Connection Summary ==========
  Total events:   56
  RX OK:          16
  RX timeout:     40
  RX CRC error:   0
  Anchor updates: 16

锚点计算详情(连接结束后打印)

===== Anchor Point Calculation =====

[Step 1] T0: CONNECT_IND end time
  connect_end_rtc = 456016 (RTC0 ticks, 32768 Hz)
  T0 = 456016 x 30.52us = ~13916503 us

[Step 2] Calculate first anchor offset
  BLE Spec formula:
    A0 = T0 + winOffset*1.25ms + transmitWindowDelay
  Implementation adds margins:
    conn_offset_us = winOffset*1250 + WIN_DELAY(1250)
                   - TICKER_RES_MARGIN(31)
                   - EVENT_JITTER(16)
                   - rx_ready_delay(41)
                   - ww_periodic(5)
    = 0*1250 + 1250 - 31 - 16 - 41 - 5
    = 1157 us

[Step 3] Convert to RTC ticks + remainder
  RTC runs at 32768 Hz, 1 tick = 30.517578125 us
  offset_ticks     = 37
  offset_remainder = 27849609 ps (sub-tick precision)

[Step 4] First anchor point (absolute RTC tick)
  initial_anchor = connect_end_rtc + offset_ticks
                 = 456016 + 37 = 456053

[Step 5] connInterval in RTC ticks
  interval = 50000 us
  interval_ticks     = 1638
  interval_remainder = 12207031 ps
  (remainder accumulates each event, carries +1 tick
   when >= 1 tick period, preventing drift)

[Step 6] Window Widening parameters
  Master SCA: 50 ppm
  Local SCA:  50 ppm
  Combined:   100 ppm
  WW periodic = ceil(100 * 50000 / 1000000) = 5 us/event

===== Anchor Sequence Preview =====
  A0 (first)  : RTC tick 456053
  A1 (A0+intv) : RTC tick 457691 (delta=1638 ticks)
  A2 (A1+intv) : RTC tick 459329 (delta=1638 ticks)
  (remainder carry may add +1 tick per event)
=====================================

五、日志解读

跳频序列

ch=11, 22, 33, 7, 18, 29, 3, 14, 25, 36... 是 CSA#1 跳频序列(hop increment=11):

Event#  unmappedChannel  mappedChannel
  0           11              11
  1           22              22
  2           33              33
  3           44 mod 37 = 7    7
  4           18              18
  5           29              29
  6           40 mod 37 = 3    3
  7           14              14
  ...

unmappedChannel = (lastUnmapped + hopIncrement) mod 37

如果 unmappedChannel 落在 Channel Map 的禁用位上,则 remap 到已启用通道列表。

RX 成功率衰减(无 WW 时)

前 10~16 个事件 RX 成功,之后因为没有 Window Widening 补偿,锚点漂移超出接收窗口导致持续 MISS:

  RX OK ─────────┐
                  │  ← 锚点漂移累积超过接收窗口
  RX MISS ────────┼──────────────────────────►
                  │
           ~Event #16                    Event #56
                                    (Supervision Timeout)

56 个事件中只有 16 个收到包(28.6%)——这恰好说明了为什么 Demo 05 需要引入 WW。

锚点计算公式分解

A0 = T0 + winOffset × 1.25ms + transmitWindowDelay - margins

其中 margins = TICKER_RES_MARGIN (31µs)    ← RTC 分辨率补偿
             + EVENT_JITTER     (16µs)    ← ISR 延迟裕量
             + rx_ready_delay   (41µs)    ← RADIO RXRU 预热
             + ww_periodic       (5µs)    ← 首事件的最小 WW

RTC tick 与 remainder 协作

connInterval = 50000 µs

50000 ÷ 30.517578125 = 1638.4 ticks

  → interval_ticks     = 1638
  → interval_remainder = 0.4 × 30517578.125 ps ≈ 12207031 ps

每个事件累加 remainder:
  Event 0: remainder =  12207031 ps
  Event 1: remainder =  24414062 ps
  Event 2: remainder =  36621093 ps  > 30517578 ps!
           → carry +1 tick, remainder -= 30517578
           → 实际 interval = 1639 ticks(补偿 0.4 的累积)


本系列教程同款硬件:👇
芯片: nRF 52832 开发板
工具: nRF 52840 BLE Dongle 蓝牙嗅探器
工具: 逻辑分析仪
工具: BPA low energy 蓝牙分析仪

posted @ 2026-04-02 09:30  ixbwer  阅读(23)  评论(0)    收藏  举报