2. 蓝牙协议栈架构:Controller 与 Host 的分层,HCI 接口在休眠唤醒中的作用

聊 BLE 休眠唤醒,绕不开协议栈的分层结构。很多刚入行的朋友觉得这玩意儿太抽象,说白了,它就是一套分工明确的组织架构。我习惯把蓝牙协议栈比作一个公司:Controller 是干活的一线员工,Host 是制定策略的管理层,而 HCI 就是他们之间的传话员。

这一章,我们就来拆开看看这三者到底怎么配合,尤其是在休眠唤醒这个场景下,它们各自扮演什么角色。

2.1 为什么非要分层?

你想想看,如果所有功能都揉在一起,芯片厂商换一个硬件,整个协议栈都得重写。这谁受得了?

分层的好处很明显:

  • 硬件无关性:Host 层不用管你是 Nordic 还是 TI 的芯片,它只认标准化的接口。
  • 职责清晰:Controller 管射频、管时序、管物理层的收发;Host 管逻辑链路、管属性协议、管应用数据。
  • 低功耗设计的基础:休眠唤醒时,Controller 可以独立运行,Host 可以睡大觉。这正是 BLE 省电的精髓。

我在项目中遇到过一种情况:有人把 Host 和 Controller 跑在同一颗 MCU 上,结果休眠时为了等 Host 处理完一个事件,Controller 迟迟不能进入睡眠,功耗直接翻倍。这就是没理解分层带来的代价。

2.2 Controller:一线员工的活

Controller 是离硬件最近的一层。它直接操作射频、定时器、中断。它的核心任务就三个:

  • 发送和接收数据包:调制解调、跳频、CRC 校验,这些脏活累活都是它干。
  • 管理连接事件:每个连接间隔里,什么时候醒来、什么时候发数据、什么时候听数据,Controller 说了算。
  • 执行休眠唤醒:这是重点。Controller 内部有一个精确的定时器,它知道下一个连接事件什么时候到。在空闲时,它会自动关闭射频、关闭大部分时钟,只留一个低速的睡眠时钟跑着。

关键点:Controller 的休眠是“硬件级”的。它不需要 Host 的参与,自己就能决定什么时候睡、什么时候醒。这也是 BLE 能做到微安级待机电流的根本原因。

2.3 Host:管理层的职责

Host 层跑在更高级的处理器上,通常是 MCU 的主核。它负责:

  • 逻辑链路控制:L2CAP 层负责数据的分段重组。
  • 安全管理:配对、绑定、加密。
  • 属性协议:GATT 层,应用数据就是通过它来读写的。
  • 通用访问规范:GAP 层,负责广播、扫描、连接建立。

Host 层本身不直接参与休眠唤醒的时序控制。但它有一个重要的任务:决定什么时候可以睡,以及什么时候必须醒。

举个例子:你的应用层发了一个通知,Host 层把它封装成 ATT 包,然后通过 HCI 丢给 Controller。Controller 会在下一个连接事件里发出去。发完之后,Host 就可以告诉系统:“我没事了,可以睡了。” 但 Controller 可能还在跑着定时器,等着下一个连接事件。

我的经验:我曾经调试过一个项目,Host 层因为一个定时任务每 10 毫秒醒来一次,导致 Controller 虽然能睡,但 Host 频繁唤醒,整体功耗居高不下。后来我把那个定时任务挪到了 Controller 的调度里,用硬件定时器触发,问题才解决。记住:Host 的唤醒频率,直接决定了系统的平均功耗。

2.4 HCI:传话员的艺术

HCI(Host Controller Interface)是连接 Host 和 Controller 的桥梁。它定义了标准的命令、事件和数据格式。常见的物理层实现有:UART、SPI、USB。

在休眠唤醒中,HCI 的作用非常微妙:

  • 命令通道:Host 通过 HCI 命令告诉 Controller 进入某个模式,比如“进入深度睡眠”或“设置唤醒条件”。
  • 事件通道:Controller 通过 HCI 事件通知 Host 发生了什么事,比如“连接断开”、“数据到达”、“唤醒成功”。
  • 数据通道:应用数据通过 HCI 在 Host 和 Controller 之间传递。

但这里有一个坑:HCI 本身是异步的。Host 发一个命令,Controller 不一定马上执行。尤其是在休眠场景下,如果 Host 发了一个“睡眠”命令,Controller 可能还在处理上一个连接事件,需要等一会儿才能进入睡眠。如果 Host 不管三七二十一直接睡了,HCI 的 UART 可能就收不到 Controller 的响应了。

避坑指南:我曾经在一个量产项目中遇到 HCI 死锁。原因是 Host 在发送“进入睡眠”命令后,没有等待 Controller 的“命令完成”事件,就直接关闭了 UART 时钟。结果 Controller 醒来后发的事件,Host 收不到,两边状态不同步,导致连接断开。解决方案很简单:Host 必须等待 Controller 确认睡眠命令执行完毕,才能关闭 HCI 接口的时钟。

2.5 分层架构下的休眠唤醒流程

为了让你更直观地理解,我画了一张流程图。这张图展示了从 Host 决定睡眠,到 Controller 管理唤醒的完整过程。

BLE 分层架构下的休眠唤醒流程 Host 层 应用 / GATT / GAP / L2CAP HCI 接口 命令 / 事件 / 数据 Controller 层 链路层 / 射频 / 定时器 ① 睡眠命令 ② 转发命令 ③ 执行睡眠 ④ 等待连接事件 ⑤ 定时器唤醒 ⑥ 唤醒事件 ⑦ 事件上报 ⑧ Host 恢复运行 说明: • 步骤①-②:Host 通过 HCI 发送睡眠命令,Controller 确认后进入低功耗模式。 • 步骤③-⑤:Controller 独立管理睡眠和唤醒,Host 全程不参与。

这张图里,我最想强调的是 步骤③到⑤。你看,Controller 进入睡眠后,Host 其实已经没事干了。Controller 自己用硬件定时器算好时间,在下一个连接事件到来前自动醒来。整个过程,Host 完全不用管。这就是分层带来的好处——各司其职,互不干扰。

2.6 实际项目中的分层选择

在实际产品中,Host 和 Controller 的物理分布有两种常见方案:

方案 描述 休眠唤醒特点 典型场景
双芯片方案 Host 跑在主 MCU,Controller 跑在蓝牙 SoC 主 MCU 可以深度睡眠,蓝牙 SoC 独立维持连接 智能手表、传感器节点
单芯片方案 Host 和 Controller 跑在同一颗芯片的不同核或线程 休眠时需要协调两个模块的时钟,复杂度高 蓝牙耳机、遥控器

我个人更倾向于双芯片方案,尤其是在低功耗要求严苛的项目里。为什么呢?因为双芯片方案里,主 MCU 可以彻底断电,只靠蓝牙 SoC 维持连接。而单芯片方案里,即使 Controller 睡了,Host 的某些外设可能还在跑,漏电很难控制。

一个小技巧:如果你用双芯片方案,记得把 HCI 的 UART 引脚配置成唤醒源。这样 Controller 在收到数据或连接事件时,可以通过 UART 的 RX 引脚唤醒主 MCU。我习惯在硬件设计时,把 UART 的 RX 直接连到主 MCU 的一个外部中断引脚上,这样唤醒延迟可以做到最低。

2.7 本章小结

Controller 和 Host 的分层,不是设计者闲得没事干搞出来的。它是 BLE 实现低功耗的基石。Controller 管硬件、管时序、管休眠唤醒的细节;Host 管逻辑、管协议、管应用数据。HCI 接口在中间做翻译,确保两边能沟通顺畅。

理解了这个架构,你再看后面的休眠唤醒机制,就会觉得顺理成章。记住一句话:Controller 是执行者,Host 是决策者,HCI 是传话筒。各司其职,才能把功耗做到极致。