蓝牙入门理解

其实蓝牙和其他各种通讯协议是一样的,都是通讯的方式和过程不相同。就像是一个人要到一个目的地,方式有很多种,例如汽车,轮船,飞机。其承载的内容都是一致的(同一个数据帧--同一个人),只不过方式不同。

一、蓝牙协议结构

其实在硬件上,蓝牙协议就分为两个,一个是host-->mcu(微控制器),另一个是controller-->蓝牙射频芯片。两者的链接亦有多种方式,或集成一个芯片或使用spi等。这里的链接方式就对应了HCI的方式。所以HCI就是用来表达host和controller的一个链接中间层。而最后,蓝牙协议则是主要集中在host中,定义了host对数据接收和处理方式,而controller更多的是硬件上的操作,将射频信号转化为电信号,拼接成常见的数据帧。而对数据帧的处理,就是这个蓝牙协议栈结合host(mcu)的功能所在。

1. MCU(微控制器)

**作用:**MCU是蓝牙设备的核心,负责运行蓝牙协议栈、处理数据、控制外设以及执行应用程序逻辑。

功能: 运行蓝牙协议栈(如BLE协议栈)。 管理射频硬件(通过SPI、UART等接口)。 处理用户应用程序(如传感器数据采集、逻辑控制等)。

2. 射频硬件(RF Transceiver)

**作用:**射频硬件负责发送和接收蓝牙信号(2.4GHz频段)。

功能: 调制和解调蓝牙信号。 实现蓝牙物理层(PHY)和链路层(Link Layer)的功能。

常见射频硬件: Nordic nRF52系列(集成射频和MCU)。 TI CC2640系列。 ESP32(集成Wi-Fi和蓝牙)。

3. 蓝牙协议栈

**作用:**蓝牙协议栈是MCU上运行的软件,负责实现蓝牙的通信协议。

分层结构:分以下三层

物理层(PHY):处理射频信号。

链路层(Link Layer):管理连接、广播和数据包传输。
主机控制接口(HCI):MCU与射频硬件之间的通信接口。
逻辑链路控制与适配协议(L2CAP):数据包的分段和重组。

属性协议(ATT):定义数据的读写操作。

通用属性配置文件(GATT):定义服务和特征值。

通用访问配置文件(GAP):管理设备发现和连接。

**小结:**硬件上分为mcu与射频硬件,mcu通过蓝牙协议去驱动射频硬件,即完成了蓝牙功能。

二、蓝牙协议的合作

蓝牙的就像包裹,层层封装。

  • 数据本身:要发的那几个字节(比如心率 = 70)
  • 蓝牙协议:一层一层往上包,保证能安全发到对方

就像:物品 → 小袋子 → 盒子 → 快递单 → 运输

蓝牙也是:应用数据 → GATT → L2CAP → Link Layer → PHY → 无线电发出去

ble controller是员工,接受mcu的指令。

角色 对应硬件 / 层级 具体工作
MCU(老板) 应用层 / GATT/ATT/L2CAP/GAP(Host 层) 1. 给员工定规则(设置广播名、连接参数、GATT 服务);2. 不参与具体执行,只等员工汇报结果;3. 处理最终数据(比如把心率值显示在屏幕)。
BLE Controller(员工) LL 层 / PHY 层 1. 严格执行老板的指令(按参数广播、扫描、建立连接);2. 自主完成 LL 层核心工作(跳频、时序、CRC 校验、加密);3. 只把结果汇报给老板(比如 "已连接手机""收到读心率指令")。

三、蓝牙协议栈

我们从Host层开始,层层向上解析。

蓝牙协议栈主要分为两个部分:

配对链接-->GAP和SM :在两个设备互相发现,链接的配置和操作就由这两个层面完成;

数据传输-->GATT和ATT :在链接之后,两个设备互相传输数据,则是通过这两个层进行操作;

而最后一个 L2CAP则是一个分岔口,使得传输的数据通过L2CAP时能够被分拣到对应的 配对链接块 或 数据传输块。

  1. GAP

GAP(Generic Access Profile)通用访问协议:GAP 是所有的蓝牙设备均需实现的Profile,主要用于描述device discovery(设备发现)、connection(连接)、security requirement(安全要求)和authentication(认证) 的行为和方法。

GAP可以简单理解为"对LL层的配置"。但是不是直接 "配置 LL 层",而是「定义设备的对外行为规则」,并通过协议栈向下传递这些规则,最终由 LL 层(Controller)执行这些规则。

简单说:

  • GAP 管「规则 / 策略」:比如 "我要可被发现""我要广播 100ms 一次""我是 Peripheral 角色";
  • LL 层管「执行 / 落地」:严格按照 GAP 定义的规则,硬件级执行广播、扫描、连接等操作。

但是同时,GAP 不止管 LL 层:GAP 还定义了「设备配对 / 安全」「角色切换」「连接参数协商」等规则,这些规则不仅作用于 LL 层,还会涉及 ATT/GATT 层的安全逻辑;

GAP 还是 "行为规范" 而非 "配置工具":GAP 是蓝牙联盟定义的「通用访问规范」,是一套标准,而不是直接操作 LL 层的函数;LL 层是这套标准的 "硬件执行者"。

  1. GATT

GATT(Generic Attribute profile)通用属性协议:定义了服务的流程、格式及其所包含的特征,包含特征的发现、读取、写入、通知、指示;主要用来规范attribute中的数据内容,并将不同attribute进行分组分类。

从机中可以有多个服务(service),一个服务中可以有多个特征值(Characteristic),每个特征值又有自己的属(property),属性的取值有读、写、通知(Notify)。每个服务和特征值都有唯一的UUID标识(标准UUID为128位,协议栈中一般为16位)。

而我的理解是,GATT就是像给Modbus的寄存器定义数据含义,读写属性。用「Modbus 地址定义」对标「GATT 数据定义」(逐点对齐)

维度 Modbus 地址定义 GATT 数据定义 核心共性
核心目的 规定「哪个地址对应什么数据」,避免混乱 规定「哪个 Handle/UUID 对应什么数据」,避免混乱 给裸数据赋予「业务含义」
数据标识 地址(如 40001 = 温度、40002 = 湿度) Handle(如 0x0012 = 心率)/ UUID(如 0x2A37 = 心率特征值) 用唯一标识绑定「位置 - 数据」
数据属性 读 / 写 / 只读(如 40001 只读、40003 可写) 读 / 写 / Notify/Indicate(如心率特征值 可读 + Notify) 定义数据的操作权限
数据分类 功能码区分(如 03 读保持寄存器、06 写单个寄存器) Service 分类(如 0x180D = 心率服务、0x180F = 电池服务) 按业务逻辑分组数据
使用场景 上位机读 40001 → 知道是温度,按规则解析 手机读 0x2A37 → 知道是心率,按规则解析 双方按约定解析数据,不用传 "数据说明"
  1. ATT

ATT(Attribute Protocol)配置属性协议:用于发现、读取和写入对端设备上的属性的规范;

它分为两个角色:Server和Client,通常从机为服务端,主机为客户端;服务端提供拥有关联值的属性集 ,客户端发现、读、写这些属性,服务端也可以主动通知客户端。

属性类型:用UUID(16bit or 128 bit)的形式来表现;

属性句柄:用于标识一个属性,服务器上的所有属性都会分配一个唯一非零的属性句柄;

属性权限:使用许可、认证许可、授权许可;

属性值 :0-512 byte。

ATT 不是 "定义规则" 的层,而是 "执行规则" 的层 ------ 它是 GATT 数据规则的「搬运执行者」,也是 BLE 数据交互的「底层操作指令集」,类比到 Modbus 里,ATT 就是「实现读 / 写寄存器的底层指令」。

层级 核心角色 Modbus 类比 运行阶段
GAP 连接规则定义者(配置 LL 层行为) 串口通信参数配置(波特率、校验位) 初始化阶段为主
GATT 数据字典定义者(给数据赋含义 / 权限) Modbus 地址表定义(40001 = 温度) 初始化阶段为主
ATT 数据操作执行者(按 GATT 规则搬数据) Modbus 读 / 写指令(03 读寄存器、06 写寄存器) 运行阶段全程参与
  1. L2CAP

L2CAP(Logical Link Control and Adaption Protocol)链路控制和适配协议:对LL进行了一次简单封装,LL只关心传输的数据本身,L2CAP就要区分是加密通道还是普通通道,同时还要对连接间隔进行管理。

作用:把蓝牙的数据分成不同通道; 让 ATT、安全加密、其他数据互不干扰;分包、拼包

对应例子:心率数据走通道 1;配对加密走通道 2;

L2CAP = 交通分流,互不干扰

  1. Link Layer(链路层 LL)

Link layer链路层:链路管理,是整个协议栈的核心,定义了空中接口数据包格式、比特流处理程序(例如错误检查)、状态机以及用于无线通信和链路控制的协议;主要负责信道管理、广播和扫描、创建和保持连接、收发空中包和加密链路。

现实角色:两个人面对面喊话、建立对话

它干的事:

  • 手环广播:"我在这,我叫 XX 手环"
  • 手机扫描:"我听到你了"
  • 双方建立连接
  • 规定什么时候发、什么时候收、跳频、加密
  • 保证包不乱、不丢

对应例子:

  • 手环广播 → LL 层在工作
  • 手机点连接 → LL 层握手
  • 连接成功后,LL 层维持对话节奏
  1. Physical Layer(物理层 PHY)

现实角色:无线电波 / 空气

它只干一件事:把 0 和 1 变成电磁波发出去,再把电磁波变回 0 和 1。

对应例子:

  • 手环把数据变成 2.4GHz 无线电
  • 手机天线接收这个信号
  • 不关心数据是什么,只负责收发比特

你可以理解:PHY = 路 + 交通工具

四、蓝牙开发理解

  1. 初始化 BLE 控制器(Controller 层)

作用:初始化 BLE 硬件核心(LL/PHY 层),给后续所有操作打基础(相当于给 "快递运输车" 通电、检查车况)。

核心操作:

使能 BLE 控制器时钟 / 电源;

  • 配置控制器模式(仅 BLE / 双模,ESP32 需 menuconfig 选);
  • 初始化控制器与 MCU 的通信接口(如 HCI)。

编码重点:

复制代码
// 伪代码(ESP-IDF NimBLE 示例)
nimble_port_init(); // 初始化 NimBLE 端口(含 Controller)
nimble_port_freertos_init(ble_host_task); // 启动 BLE 任务
  • 这一步是 "一次性配置",协议栈封装好,你只需调用初始化 API。
  1. 配置 GAP 层(连接规则)

作用:定义设备的「对外连接规则」,告诉 Controller 怎么广播、怎么被发现、是什么角色(相当于定 "快递揽收规则")。

核心操作:

设定角色:Peripheral(被连接,如传感器)/ Central(主动连接,如网关);

  • 配置广播参数:广播间隔、是否可连接、设备名称;
  • 配置连接参数:连接超时、从机延迟、心跳间隔。

编码重点:

复制代码
// 伪代码:配置 Peripheral 广播
struct ble_gap_adv_params adv_params = {
    .conn_mode = BLE_GAP_CONN_MODE_UNDIR, // 可连接
    .disc_mode = BLE_GAP_DISC_MODE_GEN,   // 可被发现
    .interval_min = 100,                  // 广播间隔100ms
    .interval_max = 100,
};
ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER,
                  &adv_params, gap_event_cb, NULL); // 启动广播
  • 编码量 < 5%,一次性配置,核心是填对广播 / 连接参数;
  • 只需处理 GAP 事件回调(如连接成功 / 断开)。
  1. 定义 GATT 层(数据字典)

作用:定义「数据的业务规则」,相当于给 BLE 数据建 "Modbus 地址表"(告诉协议栈 "哪个 Handle 对应什么数据、有什么权限")。

核心操作

  • 创建 Service(业务分类,如心率服务、电池服务);
  • 在 Service 下创建 Characteristic(具体数据项,如心率值、电池电量);
  • 给 Characteristic 配置:UUID、操作权限(读 / 写 / Notify)、Handle 地址;
  • 绑定读 / 写回调函数(核心:告诉协议栈 "读这个数据时该执行什么逻辑")。

编码重点:

复制代码
// 伪代码:定义心率服务+特征值
// 1. 定义服务 UUID(0x180D 是蓝牙标准心率服务)
static const struct ble_gatt_svc_def gatt_svcs[] = {
    {
        .type = BLE_GATT_SVC_TYPE_PRIMARY,
        .uuid = BLE_UUID16_DECLARE(0x180D), // 心率服务UUID
        .characteristics = (struct ble_gatt_chr_def[]) {
            {
                .uuid = BLE_UUID16_DECLARE(0x2A37), // 心率特征值UUID
                .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, // 权限
                .access_cb = heart_rate_read_cb, // 绑定读回调
            },
            { 0 }, // 结束标记
        },
    },
    { 0 },
};
// 2. 注册 GATT 服务
ble_gatts_count_cfg(gatt_svcs);
ble_gatts_add_svcs(gatt_svcs);
  • 编码量~10%,一次性配置,核心是 "UUID + 权限 + 回调绑定";
  • 这一步是 "数据规则定义",后续所有数据交互都基于此。
  1. 编写 GATT 回调函数(业务逻辑核心)

作用:实现「数据的实际处理逻辑」(协议栈触发回调时,执行 "读传感器、解析写入数据、计算数值" 等核心业务)。

核心操作

  • 读回调:实时获取 / 计算数据(如读心率传感器、读电池电压);
  • 写回调:解析上位机写入的数据,执行硬件操作(如设置闹钟、控制 LED);
  • Notify 逻辑:主动推送数据(如心率变化时,调用 API 推送给上位机)。

编码重点:

复制代码
// 1. 读心率回调(你写的核心业务逻辑)
static int heart_rate_read_cb(uint16_t conn_handle, uint16_t attr_handle,
                              struct ble_gatt_access_ctxt *ctxt, void *arg) {
    // 步骤1:读硬件传感器(你的业务逻辑)
    uint8_t heart_rate = read_hr_sensor(); // 你自己实现的传感器读取函数
    // 步骤2:把数据写入协议栈缓冲区(给 ATT 层自动发送)
    os_mbuf_append(ctxt->om, &heart_rate, 1);
    return 0;
}

// 2. 主动推送心率(Notify)
void send_heart_rate_notify(uint8_t heart_rate) {
    struct ble_gatt_chr *chr = ble_gatt_chr_find_uuid(BLE_UUID16_DECLARE(0x2A37), gatt_svcs);
    ble_gattc_notify(0, chr->value_handle, &heart_rate, 1); // 主动推送
}
  • 编码量~85%,是你开发的核心工作;
  • 所有和 "硬件交互、数据计算、业务规则" 相关的代码都在这里。
  1. 启动广播 / 扫描(GAP 层执行)

作用:让 Controller 按 GAP 配置的规则,开始广播(Peripheral)或扫描(Central)(相当于 "快递运输车按规则出发揽收")。

核心操作

  • Peripheral:调用 ble_gap_adv_start() 启动广播;
  • Central:调用 ble_gap_disc_start() 启动扫描,发现设备后发起连接。

编码重点:

  • 只需调用协议栈 API,无需复杂逻辑;
  • 关注连接事件回调(如 gap_event_cb 中处理连接成功)。
  1. 运行时数据交互(自动执行)

作用:协议栈自动处理底层数据传输,你只需响应回调(相当于 "快递分拣、运输全自动化,你只需要处理包裹内容")。

核心流程(全自动,无需编码)

  1. 上位机发读 / 写指令 → LL 层拆包 → L2CAP 分通道 → ATT 层校验权限;
  2. ATT 层触发你写的 GATT 回调 → 你返回数据 / 处理写入;
  3. ATT 层打包数据 → L2CAP 分通道 → LL 层封装无线包 → 发回上位机。

编码重点:

  • 无需写任何底层代码,只需维护业务数据(如定时更新传感器值)。
  1. 异常处理(可选进阶)

作用:保障稳定性,处理连接断开、超时、权限错误等场景。

核心操作

  • 在 GAP 事件回调中处理 "连接断开",重新启动广播;
  • 在 GATT 回调中校验数据合法性(如写入的闹钟时间不能超 23 点);
  • 处理 ATT 层权限错误(如拒绝非法写入)。

五、初始化用例

gap的配置分三个方面:

层级 核心内容 对应代码 / 函数 你的理解匹配度
1. 静态基础配置 初始化 GAP 标准服务、设置不依赖协议栈运行的静态属性(设备名、GAP 服务本身) gap_init()ble_svc_gap_init() + ble_svc_gap_device_name_set() ✅ 完全匹配(静态数据初始化)
2. 动态广播配置 初始化依赖协议栈同步的动态参数(设备地址、广播数据 / 扫描响应、广播间隔),并启动广播 adv_init() + start_advertising()(地址获取 + 广播参数配置 + ble_gap_adv_start() ✅ 核心匹配(动态数据 + 开启广播)
3. 事件驱动处理 处理广播 / 连接全生命周期事件(连接成功 / 断开、广播完成、订阅 / MTU 更新等),保证设备行为符合预期 gap_event_handler(由 ble_gap_adv_start() 注册,协议栈自动触发) ✅ 完全匹配(链接相关事件处理)

gatt的配置则是

/*

* GATT server initialization

* 1. Initialize GATT service

* 2. Update NimBLE host GATT services counter

* 3. Add GATT services to server

*/

相关推荐
csg11072 小时前
PIC单片机高阶实战(三):PIC32MX电平变化中断输入
单片机·嵌入式硬件·物联网
梁山1号2 小时前
【LCD屏幕相关】针对中景园
stm32·单片机·物联网
银月光科技3 小时前
红外LED加热应用的市场格局与增长潜力
单片机·嵌入式硬件
项目題供诗3 小时前
51单片机入门-蜂鸣器(十一)
单片机·嵌入式硬件·51单片机
行稳方能走远3 小时前
结构体传参,到底该传值还是传指针?
c++·单片机
济6173 小时前
STM32F103 时钟系统从原理到实战:8MHz 到 72MHz 配置与 LED 闪烁实验---STM32 HAL库专栏
stm32·嵌入式·stm32hal库编程
xuxie994 小时前
N2 中断
单片机·嵌入式硬件
Godspeed Zhao4 小时前
现代智能汽车系统——MCULess2
单片机·嵌入式硬件·汽车
子繁~~4 小时前
STM32开发文档:
stm32·单片机·嵌入式硬件