
Flipper Zero 固件中的 BLE LLD基于 BLE 射频硬件的专有协议无线电抽象层解析【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware本指南以 lib/stm32wb_copro/wpan/ble_lld/README.md 为骨架结合lib/stm32wb_copro/wpan/ble_lld/目录下的头文件与实现源码讲解 BLE LLD 的架构设计、Action Packet 链接机制、双核appli core / radio core协作方式以及 HAL/LLD 两套 API 的配置与通信流程。读完本文你将理解如何利用 BLE 射频硬件实现自定义专有无线协议并掌握从初始化、事件处理到发射/接收动作链配置的完整调用链。什么是 BLE LLDBLE LLDBLE Low Level Driver是 STM32WB 平台上的一层专有射频通信抽象层它依赖 BLE 射频硬件但本身并不是一个 BLE 协议栈。正如 README 开篇所述它的定位是light and simple layer to develop proprietary protocols and applications——即为开发者提供一套轻量、简单的接口用来在 2.4GHz BLE 射频硬件之上构建自定义的专有协议而不是实现 BLE 标准通信。BLE LLD 对外提供两层接口LLD全功能层暴露射频核心支持的全部特性API 较为复杂支持自定义动作包Action Packet链式调度HAL简单 API 层建立在 LLD 之上把常见通信场景封装成简单函数适合不需要自定义动作链的场合。对应源码分布在 lib/stm32wb_copro/wpan/ble_lld/ 下其中lld/ble_lld.h、lld/ble_lld.c是 LLD 实现hal/ble_hal.h、hal/ble_hal.c是 HAL 封装lld/ipBLE_lld_public.h定义了动作包、事件枚举等公共数据结构lld/ble_lld_transport.h则定义了两核之间传输层使用的命令码与参数结构。双核架构与 IPCC 通信appli core 与 radio core 的分工BLE LLD 专为双核硬件设计见 README Dual core 一节Application core应用核运行用户代码Radio core射频核运行专用于射频管理的私有代码。两核之间的软件通信层称为IPCCInter-Processor Communication Controller。README 特别强调IPCC 这一传输层与 BLE LLD 本身解耦也就是说 BLE LLD 不关心底层消息如何送达射频核具体如何接线由应用层负责。在仓库中与传输层相关的基础设施位于 lib/stm32wb_copro/wpan/interface/patterns/如tl/目录下的 hci_tl、shci_tl 等模块。README 中的架构示意图清晰地展示了两个核上的软件栈appli core: HAL - LLD - LLD proxy radio core: LLD proxy - LLD - BLE radio 中间以 IPCC 分隔appli core上Application 是使用自定义射频协议的用户程序HAL 是基于 LLD 的简单通信封装LLD 是全功能通信层LLD proxy 负责把数据和命令打包/解包与射频核往来。radio core上LLD proxy 与 appli core 侧对应打包/解包LLD 提供射频抽象BLE radio 是 RF 硬件。双核架构带来的约束这一架构带来两个重要约束射频核上不运行任何应用代码射频事件发生后应用代码的响应耗时较长因为要跨 IPCC 往返。为了在约束下依然能实现快速的射频操作序列射频核支持将action packets动作包链接chain起来执行而链接方式由应用侧配置。这两个约束会直接影响协议设计以及 LLD/HAL 的选择——如果需要极低延迟的连续收发就需要用 LLD 精心编排动作链如果时序要求不苛刻用 HAL 即可。Action Packet射频核执行 Tx/Rx 的最小单位Action packet 是射频核用来控制无线电数据包**发射Tx与接收Rx**的结构README Action Packet 一节。在源码中它的定义见 lib/stm32wb_copro/wpan/ble_lld/lld/ble_lld.h 的ActionPacket结构体typedef struct ActPac_s { uint8_t StateMachineNo; /* 状态机编号 (0 - 7) */ uint8_t ActionTag; /* 动作包配置位域: PLL_TRIG, TXRX, TIMER_WAKEUP, TIMESTAMP_POSITION 等 */ uint32_t WakeupTime; /* 执行动作包前的唤醒时间 (us)仅 TIMER_WAKEUP 置位时有效 */ uint32_t ReceiveWindowLength; /* Rx 窗口大小 (us)仅 Rx 有效 */ void *data; /* 待发送的载荷仅 Tx 有效 */ uint8_t dataSize; /* 载荷大小仅 Tx 有效 */ uint32_t status; /* 来自硬件的中断状态寄存器 */ int32_t rssi; /* 收到数据包的 RSSI仅 Rx 有效 */ uint8_t nextTrue; /* 操作成功时执行的下一个动作包 */ uint8_t nextFalse; /* 操作失败时执行的下一个动作包 */ uint8_t actionPacketNb; /* 动作包编号 (0 - 7) */ void (*callback)(radioEventType, struct ActPac_s *, void *, uint8_t); /* 动作包结束时运行可为 NULL */ } ActionPacket;动作链成功/失败双分支动作包可以链式执行以完成复杂射频序列。链接通过两个字段配置见 READMEnextTrue本次操作Tx 或 Rx成功时接下来执行的动作包nextFalse操作失败时接下来执行的动作包。射频核会根据动作包配置与操作结果自动沿分支跳转。仓库中ipBLE_lld_public.h还定义了每个动作包编号的取值范围APACKET_0~APACKET_7共 8 个ACTION_PACKET_NB 8以及用于终止链的APACKET_STOP0xFFto be placed at the end放在链末尾用于停止。同时每个动作包还挂在一个状态机上StateMachine_t枚举定义了STATE_MACHINE_0~STATE_MACHINE_7共 8 个状态机LLD 的BLE_LLD_SetChannel()、BLE_LLD_SetTxAttributes()等配置函数都以StateMachineNo为参数——不同状态机可以持有各自独立的信道、网络 ID、PHY 等配置。Back-to-back 与 wake-up 两种模式动作包之间或第一个动作包之前的延迟可用两种模式配置README Back-to-back vs wake-up 一节Back-to-back 模式射频全程保持供电动作包间延迟最低。该时间是全局参数通过BLE_LLD_SetBackToBackTime()配置不能为单个动作包单独设置。源码注释补充了更精确的信息该模式对应TIMER_WAKEUP位为 0 时使用的 back-to-back 定时器是全局定时器、所有动作包共用同一个值且can be used for low values (down to ~150us)即最低可到约 150 微秒。Wake-up 模式射频在等待期间进入睡眠因此动作包间延迟不可能像 back-to-back 那样短。唤醒时间是每个动作包单独配置的对应ActionPacket.WakeupTime字段。源码注释同样给出了量级参考wakeup 是本地定时器、每个动作包可有不同值但cannot be used for low values (minimum is ~700us)即最小约 700 微秒。序列中的第一个动作包必须使用 wake-up 模式README 明确说明因为此时射频处于空闲未运行状态需要先被唤醒。ActionTag位域在 lib/stm32wb_copro/wpan/ble_lld/lld/ipBLE_lld_public.h 中给出了完整的位掩码定义位掩码宏名含义0x01PLL_TRIG使能射频 PLL 校准0 关闭 / 1 开启0x02TXRX动作类型1 Tx0 Rx0x04TIMER_WAKEUP定时器选择0 back-to-back 全局定时器1 wake-up 本地定时器0x08NS_EN自动 NSNetwork Select使能0x10INC_CHAN自动信道递增0 不递增 / 1 自动递增0x80TIMESTAMP_POSITION时间戳采样位置仅 Rx0 包尾1 包头无线数据包的细节README Radio packet details 一节给出了数据包的三个关键属性地址匹配每个数据包包含一个地址接收时必须与接收方配置的地址匹配才会被接受最大载荷 255 字节ipBLE_lld_public.h中的IPBLE_LLDANT_MAX_PAYLOAD_SIZE 255与之对应且less if using encryption——启用加密后载荷上限会减小CRC 校验数据包包含 CRC接收时会被检查错误。从源码的ipBLE_lld_txrxdata_Type结构见ipBLE_lld_public.h可以看到实际的数据包内存布局typedef struct { uint8_t header; // 被硬件按 BLE flags 解释LLD 中保留默认安全值 LLD_HEADER 0x55 uint8_t length; // 实际载荷大小 uint8_t payload[IPBLE_LLDANT_MAX_PAYLOAD_SIZE]; // 用户数据 } ipBLE_lld_txrxdata_Type;注意两点实现细节header 字段不能随意改动。ble_lld.c中定义了#define LLD_HEADER 0x55并注明Header field is interpreted by hardware and has an impact on some flags, so user should not change it因此封装数据包时应使用默认安全值。加密时尾部预留 MIC。启用加密后payload[length]起始的 4 字节MIC_SIZE 4会保留给 MIC消息完整性校验码。BLE_LLD_packetPrepareCopy()在encrypt为真时会把actual_size size MIC_SIZE写入length。加密相关参数AES_KEY_SIZE 16、AES_IV_SIZE 8、AES_BLOCK_SIZE 16同样定义在该头文件中。数据包的准备与解析有四个配套函数ble_lld.hBLE_LLD_packetPrepareCopy()/BLE_LLD_packetPrepareInPlace()将用户数据写入 Tx 缓冲Copy 版带拷贝InPlace 版就地在共享内存缓冲中构建BLE_LLD_packetExtractCopy()/BLE_LLD_packetExtractInPlace()从 Rx 缓冲提取载荷BLE_LLD_packetGetSize()查询包大小encrypt 参数决定是否包含 MIC。使用指南从初始化到事件处理阻塞式 API 语义README Blocking functions 一节指出所有 API 函数都是阻塞的——它们会等待射频核完成处理后才返回但不等候实际的数据包发射/接收完成。换句话说调用返回只代表命令已送达射频核并被处理真正的空中收发结果要通过事件回调获知。Radio proxy 配置无论 HAL/LLD 都必须先做由于双核架构用户无法直接访问射频核必须通过proxy控制射频。而 BLE LLD 与两核间的通信层解耦因此需要应用侧把 proxy 接上线——这些接线函数统一以BLE_LLD_PRX_为前缀README Radio proxy configuration 一节BLE_LLD_PRX_Init()必须第一个调用用于配置射频核 proxy。函数签名见ble_lld.h为void BLE_LLD_PRX_Init(param_BLE_LLD_t *parameters, ipBLE_lld_txrxdata_Type *transmitBuffer, ipBLE_lld_txrxdata_Type *receiveBuffer, uint8_t (*callbackSend)(BLE_LLD_Code_t bleCmd));它需要传入命令参数联合体param_BLE_LLD_t、Tx/Rx 共享内存缓冲以及一个发送回调callbackSend——这个回调把BLE_LLD_Code_t命令码实际投递到两核通信层正是BLE LLD 与传输层解耦、由应用接线的体现。BLE_LLD_PRX_EventProcessInter()在射频事件中断内部调用负责记录事件数据见下文事件机制BLE_LLD_PRX_EventProcessTask()在中断之后的某个任务上下文中调用负责执行用户回调。README 特别提醒无论使用 LLD 还是 HAL APIproxy 配置都是必需的。射频事件与回调机制用户在启动/配置一次射频操作时可以注册回调函数README Radio events 一节。当射频核发生事件如发送成功、接收失败等BLE LLD proxy 被通知进而运行针对该事件注册的回调从而让用户应用对射频事件做出反应。事件类型在ipBLE_lld_public.h中完整定义共 12 种命名规律为[TX|RX]_[OK|FAIL|TIMEOUT|CRC_KO]_[BUSY|READY]TX_OK_BUSY / TX_OK_READY /* 发送成功radio busy / ready */ TX_FAIL_BUSY / TX_FAIL_READY /* 发送失败radio busy / ready */ RX_OK_BUSY / RX_OK_READY /* 接收成功radio busy / ready */ RX_TIMEOUT_BUSY / RX_TIMEOUT_READY /* 接收超时radio busy / ready */ RX_CRC_KO_BUSY / RX_CRC_KO_READY /* 接收 CRC 错误radio busy / ready */ RX_FAIL_BUSY / RX_FAIL_READY /* 接收失败其他原因radio busy / ready */radioEventType枚举从 1 开始0 被保留且最多 32 个事件与中断过滤实现相关。ble_lld.c实现了两阶段事件处理中断阶段BLE_LLD_PRX_EventProcessInter()根据params-reply.actionPacketNb找到对应的ActionPacket保存事件与状态并在RADIO_IS_RX_OKRX_OK_BUSY或RX_OK_READY时记录 RSSI任务阶段BLE_LLD_PRX_EventProcessTask()若该动作包注册了回调则在RADIO_IS_RX_OK时先从 Rx 缓冲就地提取数据然后调用radioEventAp-callback(radioEvent, radioEventAp, data, size)。调试时可使用eventToString()ble_lld.h/ble_lld.c将事件枚举转为可读字符串。HAL 接口简单通信的正确打开方式HAL 是 LLD 之上的一层封装目标是让简单通信更省事。README 明确说明HAL 提供特性受限的简单 API适用于不需要自定义动作包链接的场景底层它通过调用 LLD 来配置若干动作包。配置流程README Configuration先用HAL_BLE_LLD_Init()初始化再用HAL_BLE_LLD_Configure()配置。完整函数签名见 lib/stm32wb_copro/wpan/ble_lld/hal/ble_hal.huint8_t HAL_BLE_LLD_Init(uint16_t hsStartupTime, bool lsOscInternal); uint8_t HAL_BLE_LLD_Configure(txPower_t txPower, uint8_t channel, bool phy2mbps, uint32_t b2bTimeUs, uint32_t networkId);通信接口分为两组README Communication无 ACKHAL_BLE_LLD_SendPacket()/HAL_BLE_LLD_ReceivePacket()——射频只发射/接收一个数据包带 ACKHAL_BLE_LLD_SendPacketWithAck()/HAL_BLE_LLD_ReceivePacketWithAck()——射频发射/接收一个数据包后另一个数据包向相反方向发送。带 ACK的函数可用于检测丢包因此可以在此基础上实现带重传的可靠通信信道README 原话。ACK 方向的具体实现由 README 中ReceivePacketWithAck 动作链图给出见下文 LLD 部分。LLD 接口全功能与自定义动作链LLD 暴露射频核心支持的全部特性API 更复杂用于实现自定义动作包链接README LLD interface 一节。配置流程先用BLE_LLD_Init()初始化再用下列函数配置BLE_LLD_SetChannel(StateMachineNo, channel)—— 设置状态机对应的射频信道BLE_LLD_SetTxAttributes(StateMachineNo, NetworkID)—— 设置网络 ID即包地址匹配依据对应 README 中address must match configured address of the recipientBLE_LLD_SetTxPower(powerLevel)—— 设置发射功率txPower_t枚举覆盖TX_POW_MIN_40_DB0到TX_POW_PLUS_6_DB31共 32 档例如TX_POW_MIN_20_85_DB表示约 -20.85 dBBLE_LLD_SetTx_Rx_Phy(StateMachineNo, txPhy, rxPhy)—— 设置收发 PHY宏定义RX_PHY_1MBPS 0x00、RX_PHY_2MBPS 0x10、TX_PHY_1MBPS 0x00、TX_PHY_2MBPS 0x01。BLE_LLD_Init()的签名是void BLE_LLD_Init(uint16_t hsStartupTime, uint8_t lowSpeedOsc, FunctionalState whitening);它对应传输层命令BLE_LLD_INIT_CMDCODE的参数param_BLE_LLD_init_tstartupTime、lowSpeedOsc、whiteningwhitening 即数据白化开关。通信流程使用 LLD API 时每个动作包都要由用户负责配置README 原话为期望的动作设置ActionPacket的全部必需字段部分字段仅 Tx 有效部分仅 Rx 有效调用BLE_LLD_SetReservedArea()把动作包下发到射频核对链首动作包调用BLE_LLD_MakeActionPacketPending()启动执行——射频核将依据各动作包的配置与操作结果自动链接后续动作包每个动作包结束时若注册了回调会向应用发送事件。中断动作链BLE_LLD_StopActivity()可随时终止动作包链接。README 强调该调用会杀掉射频之后任何其他操作前都必须重新初始化。源码注释同样说明BLE_LLD_StopActivity()对应BLE_LLD_STOPACTIVITY_CMDCODE。状态码ipBLE_lld_public.h定义了一组命令返回码SUCCESS_0、INVALID_PARAMETER_C0无效参数、WAKEUP_NOTSET_C2唤醒未设置、RADIO_BUSY_C4射频忙、COMMAND_DISALLOWED命令不允许可作为MakeActionPacketPending等函数的返回判断依据。一个完整的动作链示例带 ACK 的接收README 用图示展示了HAL_BLE_LLD_ReceivePacketWithAck()底层的动作包编排三个动作包组成的链START → Action Packet 2 (reception, wake-up, timeout) ├─ SUCCESS → Action Packet 3 (transmission, back-to-back) │ ├─ SUCCESS → STOP │ └─ FAILURE → STOP └─ FAILURE → STOP执行逻辑README 原话转述Action Packet 2 首先执行配置数据包的接收若数据正确收到CRC OK则执行Action Packet 3配置 ACK 包的发射之后射频停止若任一动作包失败射频停止。可以看到nextTrue/nextFalse的双分支字段正是这个链的控制骨架动作包 2 的失败分支直接指向停止成功分支指向动作包 3动作包 3 的成功/失败分支都指向停止。这正是自定义协议可靠通信如自动应答的最小范式。附加工具Tone 生成测试用为了测试目的BLE LLD 提供单音发射功能README Tone generation 一节BLE_LLD_StartTone(rfChannel, powerLevel)开始发射单音参数为射频信道0-39与输出功率等级0-31对应传输层param_BLE_LLD_toneStart_tBLE_LLD_StopTone()停止单音。源码注释补充了两个要点这两个函数专用于测试且会销毁上下文与多状态配置destroys context and multistate因此单音发射后、进行任何其他操作之前射频必须重新初始化与StopActivity后的要求一致。源码结构速览若要在 Flipper Zero 固件仓库中进一步深入 BLE LLD可沿以下路径阅读关注点文件官方 README本文骨架lib/stm32wb_copro/wpan/ble_lld/README.mdLLD 头文件ActionPacket、全部 API 声明lib/stm32wb_copro/wpan/ble_lld/lld/ble_lld.hLLD 实现proxy 事件处理、packet 准备/提取lib/stm32wb_copro/wpan/ble_lld/lld/ble_lld.c公共定义ActionTag 位域、事件枚举、功率/PHY 枚举lib/stm32wb_copro/wpan/ble_lld/lld/ipBLE_lld_public.h传输层命令码与参数结构lib/stm32wb_copro/wpan/ble_lld/lld/ble_lld_transport.hHAL 封装头文件lib/stm32wb_copro/wpan/ble_lld/hal/ble_hal.h两核通信传输层参考实现lib/stm32wb_copro/wpan/interface/patterns/ble_thread/tl/小结BLE LLD 的价值在于把 STM32WB 的 BLE 射频硬件抽象成可编程的、支持动作包链接的专有协议引擎。理解ActionPacket的nextTrue/nextFalse分支与TIMER_WAKEUP定时器模式是设计低延迟自定义射频协议的关键而BLE_LLD_PRX_系列接线函数则体现了它与 IPCC 传输层解耦的架构取舍——接入不同通信层时只需替换BLE_LLD_PRX_Init()传入的callbackSend实现。【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考