杰理 SDK 文档中心
首页
首页
  • 项目概览

    • SDK 简介与核心特性
    • 芯片平台与硬件资料
    • SDK 版本与发布信息
  • 快速开始

    • 环境搭建与工具链
    • 编译工程
    • 烧录与量产工具
  • 工程结构与构建系统

    • 工程目录布局
    • 构建与链接配置
  • 应用层开发

    • mbox_flash 应用框架
    • 板级支持包 (BSP)
    • 公共应用模块
    • UI 显示子系统
  • 蓝牙子系统

    • BLE 控制器、链路层与 HCI 传输
    • GATT 服务框架
    • BLE 应用示例:遥控器 / Dongle / 对讲机
    • 经典蓝牙支持
  • 音频子系统

    • 音频编解码器
    • 音频设备接口 (DAC / ADC / APA)
    • 音效处理与 EQ
    • 播放、录音与 MIO 工作流
  • 设备与文件系统

    • 存储设备驱动 (NorFlash / SDMMC / USB)
    • 文件系统 (FAT / nor_fs / SYDF)
    • 设备管理框架 (dev_mg)
  • 系统服务与电源管理

    • 消息机制 (msg / hot_msg)
    • 配置与参数存储 (app_config / VM)
    • 电源管理 (SOFT OFF / POWER DOWN)
  • 固件升级

    • 升级框架总览 (code_v1 / code_v2)
    • 双 Bank 升级机制
    • 升级通道:UART / 测试盒 / BLE OTA / USB / SD
  • 补丁包与版本维护

    • 版本升级补丁链 (v1.1.0 → v1.4.0)
    • 问题修复补丁
    • 固件裁剪与资源优化
  • 开发工具与支持

    • 辅助工具与脚本
    • 文档、配置说明与常见问题

GATT 服务框架

GATT 服务框架是 AW30N BLE SDK 中基于杰理 gatt_common 模块构建的通用 BLE GATT 服务/客户端子系统,它为上层应用屏蔽了 btstack 协议栈细节,以事件回调 + 配置结构体的方式统一管理广播、扫描、连接、服务搜索、ATT 读写与安全配对。

Purpose and Scope

本文档介绍 AW30N BLE SDK 中 GATT 服务框架的整体设计与实现,覆盖:

  • gatt_common 公共层(le_gatt_common.c/.h):连接句柄管理、角色管理、ATT 数据发送、公共初始化入口 ble_comm_init;
  • GATT Server 子系统(le_gatt_server.c):广播配置、profile(ATT 表)注册、读写回调、连接参数更新、OTA 钩子;
  • GATT Client 子系统(le_gatt_client.c):扫描/连接配置、UUID 搜索、CCC 自动使能、数据上报;
  • 协议栈暴露的 GATT 客户端底层 API(sdk/apps/include_lib/bt_include/le/gatt.h);
  • 安全配对(SM)配置(sm_cfg_t)与事件模型(gatt_comm_event_e)。

以下内容属于兄弟页面范畴,本文不展开:BLE 广播/扫描底层驱动、SM 配对协议细节、具体业务 Profile(如 HID、SPPLE、OTA/RCSP)的属性表定义。如需了解具体业务 Profile 的属性表,请参阅对应 Profile 文档页。

Overview

在 BLE 开发中,GATT(Generic Attribute Profile)负责在已建立的 LE 链路上组织属性(Attribute)与特征(Characteristic)的数据交互。AW30N SDK 将 GATT 的能力封装为三个子模块:

子模块源文件职责
公共层 gatt_commonle_gatt_common.c初始化入口、连接/角色/句柄表、ATT 发送与流控、模块使能
GATT Serverle_gatt_server.c从机角色:广播、profile 注册、ATT 读写回调分发、连接参数更新、OTA
GATT Clientle_gatt_client.c主机角色:扫描匹配、发起连接、UUID 搜索、CCC 使能、数据接收

三个模块共享同一套配置与控制模型:上层通过 gatt_ctrl_t 控制块把 server/client/SM 配置一次性交给 ble_comm_init,之后所有协议栈事件都通过各角色配置中的 event_packet_handler 回调以 gatt_comm_event_e 事件枚举形式上报给应用。这样的设计意图是:把"链路生命周期"与"业务数据"解耦——应用只需要关心连接建立/断开/加密完成等事件以及 ATT 读写内容,而无需理解 btstack 的 packet handler 机制与 HCI 命令细节。

框架由编译宏控制裁剪:

#if (TRANS_DATA_HID_EN || TRANS_DATA_SPPLE_EN)
#if TCFG_USER_BLE_ENABLE && CONFIG_BT_GATT_COMMON_ENABLE

Source: le_gatt_common.h

即:只有使能了 HID 透传或 SPPLE 透传(TRANS_DATA_HID_EN / TRANS_DATA_SPPLE_EN),且打开 TCFG_USER_BLE_ENABLE 与 CONFIG_BT_GATT_COMMON_ENABLE 时,框架代码才会被编译,符合 SDK 面向裁剪(256KB 补丁包等)的定位。

Architecture

flowchart TD
    subgraph sg_App["应用层 (App Profile)"]
        AppCB["event_packet_handler<br/>GATT 事件回调"]
        AttCB["att_read_cb / att_write_cb<br/>ATT 读写回调"]
    end

    subgraph sg_Common["GATT 公共层 gatt_common"]
        Ctrl["gatt_ctrl_t 控制块"]
        Comm["le_gatt_common.c<br/>句柄/角色/状态管理"]
        Server["le_gatt_server.c<br/>server_ctl_t"]
        Client["le_gatt_client.c<br/>client 控制块"]
    end

    subgraph sg_Stack["BT 协议栈 (btstack)"]
        GattH["gatt.h<br/>gatt_client_* 系列 API"]
        HCI["HCI / Link Layer<br/>链路与广播"]
    end

    AppCB -->|"注册回调"| Ctrl
    AttCB -->|"注册回调"| Server
    Ctrl -->|"ble_comm_init"| Comm
    Ctrl --> Server
    Ctrl --> Client
    Comm -->|"连接/状态管理"| Server
    Comm -->|"连接/状态管理"| Client
    Server -->|"ATT 收发"| GattH
    Client -->|"ATT 收发/搜索"| GattH
    GattH --> HCI
    Server -->|"事件上报"| AppCB
    Client -->|"事件上报"| AppCB
    Server -->|"读写请求分发"| AttCB

架构说明:

  • 应用层通过 gatt_ctrl_t 把三个配置结构体(gatt_server_cfg_t、gatt_client_cfg_t、sm_cfg_t)注入公共层,并在结构体中提供回调函数指针。这是典型的"配置注入 + 回调"模式:框架不感知具体业务,业务通过回调感知链路事件与数据。
  • 公共层持有 gatt_server_conn_handle[] / gatt_client_conn_handle[] 等静态句柄表(见下文"连接与角色管理"),为 Server/Client 提供统一的连接状态跟踪与 index 分配。
  • Server/Client 子模块分别处理从机侧(广播、被连接、ATT 读写)与主机侧(扫描、连接、搜索、通知)的完整生命周期,最终都通过 btstack 的 gatt_client_* API 与 HCI 交互。
  • 事件上报统一走 gatt_comm_event_e 枚举(见"事件模型"),使上层代码只需一个 switch 分支即可处理所有链路事件。

关键设计决策

  1. 单一控制块 gatt_ctrl_t:把所有配置(MTU、缓存、server/client/sm 配置、HCI 回调)聚合在一个结构体中,ble_comm_init 一次注册,避免了多个分散的初始化接口,也便于多机(multi_dev_flag)场景扩展。
  2. 回调优先于轮询:GATT_COMM_EVENT_CAN_SEND_NOW 等事件让应用在协议栈发送完成后才填充数据,实现"填满即发、发完再填"的流控,避免数据堆积。
  3. 可裁剪编译:整个框架以 TRANS_DATA_HID_EN || TRANS_DATA_SPPLE_EN 和 TCFG_USER_BLE_ENABLE && CONFIG_BT_GATT_COMMON_ENABLE 双重宏包裹,未使能时零代码体积。

实现剖析

1. 公共层:初始化与控制块

公共层的核心状态与入口集中在 le_gatt_common.c:

static u32 *gatt_ram_buffer;
static u8 cbk_event_type;
static gatt_ctrl_t *gatt_control_block;

#define CLR_HANDLER_ROLE()      cbk_event_type  = 0
#define SET_HANDLER_ROLE(a)     cbk_event_type  = (1<<a)
#define ADD_HANDLER_ROLE(a)     cbk_event_type |= (1<<a)
#define CHECK_HANDLER_ROLE(a)   (cbk_event_type & (1<<a))

static u16 gatt_server_conn_handle[SUPPORT_MAX_GATT_SERVER];
static u16 gatt_client_conn_handle[SUPPORT_MAX_GATT_CLIENT];
static u8 gatt_server_conn_handle_state[SUPPORT_MAX_GATT_SERVER];//BLE_ST_CONNECT,BLE_ST_SEND_DISCONN,BLE_ST_NOTIFY_IDICATE
static u8 gatt_client_conn_handle_state[SUPPORT_MAX_GATT_CLIENT];//BLE_ST_CONNECT,BLE_ST_SEND_DISCONN,BLE_ST_SEARCH_COMPLETE

Source: le_gatt_common.c

要点:

  • gatt_control_block 保存 ble_comm_init 传入的 gatt_ctrl_t 指针,是全局唯一的配置持有者;
  • cbk_event_type 是一个位掩码,用 SET/ADD/CHECK_HANDLER_ROLE 宏标记当前事件由哪个角色(server/client)处理,避免两个角色争抢同一协议栈事件;
  • server 与 client 各自维护独立的连接句柄表与状态表,容量由 SUPPORT_MAX_GATT_SERVER / SUPPORT_MAX_GATT_CLIENT 决定,这两个宏来自配置 CONFIG_BT_GATT_SERVER_NUM / CONFIG_BT_GATT_CLIENT_NUM:
#define SUPPORT_MAX_GATT_SERVER       CONFIG_BT_GATT_SERVER_NUM
#define SUPPORT_MAX_GATT_CLIENT       CONFIG_BT_GATT_CLIENT_NUM

#define GATT_ROLE_CLIENT         1
#define GATT_ROLE_SERVER         0

#define INVAIL_INDEX            ((s8)-1)
#define INVAIL_CONN_HANDLE      (0)

Source: le_gatt_common.h

2. 连接与角色管理

公共层以"句柄 → index"的线性映射管理所有连接。ble_comm_dev_get_index 根据角色在对应的句柄表中顺序查找:

s8 ble_comm_dev_get_index(u16 handle, u8 role)
{
    s8 i;
    u16 *group_handle;
    u8 count;

    if (GATT_ROLE_SERVER == role) {
        group_handle = gatt_server_conn_handle;
        count = SUPPORT_MAX_GATT_SERVER;
    } else {
        group_handle = gatt_client_conn_handle;
        count = SUPPORT_MAX_GATT_CLIENT;
    }

    for (i = 0; i < count; i++) {
        if (handle == group_handle[i]) {
            return i;
        }
    }
    return INVAIL_INDEX;
}

Source: le_gatt_common.c

配套 API(见 le_gatt_common.h 声明)构成完整的连接管理原语集:

函数作用
ble_comm_dev_get_index(handle, role)由连接句柄查角色索引,未找到返回 INVAIL_INDEX (-1)
ble_comm_dev_get_idle_index(role)查找空闲槽位,用于分配新连接
ble_comm_dev_get_handle(index, role)由索引反查连接句柄
ble_comm_dev_get_handle_state(handle, role)查询连接状态(连接中/断开中/可发送等)
ble_comm_dev_set_handle_state(handle, role, state)设置连接状态
ble_comm_dev_get_handle_role(handle)查询句柄属于 server 还是 client
ble_comm_register_state_cbk(cbk)注册连接状态变化回调,供上层(如 UI/状态机)监听
ble_comm_disconnect(conn_handle)主动断开指定连接

设计意图:用 u16 连接句柄作为唯一键,把多连接(多机)场景收敛为一张可线性遍历的表;INVAIL_INDEX = -1 作为哨兵值区分"有效连接"与"未使用槽位"。由于单芯片 BLE 连接数通常为 1~3,线性查找的 O(n) 开销可接受,避免了维护哈希表的复杂度。

3. GATT Server 子系统

le_gatt_server.c 的核心状态机保存在 server_ctl_t 中:

typedef struct {
    gatt_server_cfg_t *server_config; //server 配置
    adv_cfg_t *adv_config;            //adv 配置
    const u8 *profile_data;           //profile
    u16 profile_data_len;             //profile 长度
    u8  server_work_state;            //未链接ble 状态变化
    u8  adv_ctrl_en: 4;               //广播控制
    u8  rcsp_ctrl_en: 4;              //ota 升级控制
    const struct conn_update_param_t *server_connection_param_table; //连接参数更新表
    u8  server_connection_update_index: 4; //连接参数表执行id
    u8  server_connection_update_count: 4; //连接参数表内个数
    u8  server_encrypt_process;            //配对执行流程
    u16 server_operation_handle;           //当前执行更新参数流程操作handle
    u16 update_conn_handle;                //ota 升级连接handle
    u16 update_send_att_handle;            //ota发送att handle
    u8  update_send_att_handle_type;       //ota发送att handle的类型
    void (*update_ble_state_callback)(void *priv, ble_state_e state); //ota 状态回调
    void (*update_recieve_callback)(void *priv, void *buf, u16 len);  //ota 接收数据回调
    void (*update_resume_send_wakeup)(void);                          //ota 发送唤醒
} server_ctl_t;

Source: le_gatt_server.c

Server 子系统职责分解:

  • profile 注册:profile_data + profile_data_len 指向编译期生成的 ATT 属性表(由 ble_gatt_server_profile_init / ble_comm_server_profile_init 注册进协议栈),这是从机暴露给主机的全部服务/特征/描述符;
  • 广播控制:adv_cfg_t 决定广播包内容、周期、类型与通道;adv_ctrl_en 位域控制模块是否自动开关广播(使能/断开后自动恢复广播);adv_auto_do 位域支持"连接断开后自动重新广播";
  • 连接参数更新:server_connection_param_table 是一张"连接参数表"(interval/latency/super timeout),server_connection_update_index/count 记录当前执行到第几组参数,用于连接建立后按序请求更优的链路参数;
  • OTA 钩子:update_* 字段把 OTA(RCSP)升级流程直接嵌入 server 状态机——OTA 需要独占一条连接并持续大块发送数据,因此 server 额外保存 update_conn_handle、update_send_att_handle 及发送唤醒回调;
  • 事件转发:__gatt_server_event_callback_handler 把协议栈事件原样转发给应用注册的 event_packet_handler,未注册时返回 GATT_OP_RET_SUCESS 兜底:
static int __gatt_server_event_callback_handler(int event, u8 *packet, u16 size, u8 *ext_param)
{
    if (__this->server_config->event_packet_handler) {
        return __this->server_config->event_packet_handler(event, packet, size, ext_param);
    }
    return GATT_OP_RET_SUCESS;
}

Source: le_gatt_server.c

Server 对外 API 包括 ble_gatt_server_init、ble_gatt_server_exit、ble_gatt_server_adv_enable、ble_gatt_server_get_work_state、ble_gatt_server_get_connect_state、ble_gatt_server_module_enable、ble_gatt_server_disconnect_all 等(声明于 le_gatt_common.h 第 234 行起)。

4. GATT Client 子系统

Client 子系统(le_gatt_client.c)把主机侧三段流程(扫描 → 连接 → 服务搜索)做成可配置的自动化链路,配置结构体如下:

typedef struct {
    //common
    u8 scan_auto_do: 4;	       /*是否gatt模块自动打开搜索(使能,断开等状态下)*/
    u8 creat_auto_do: 4;       /*是否gatt模块搜索到匹配的设备自动发起连接*/
    u8  set_local_addr_tag;
    u8  local_address_info[7];

    //scan
    u8 scan_type: 4;
    u8 scan_filter: 4;
    u16 scan_interval;
    u16 scan_window;

    //creat
    u16 creat_conn_interval;
    u16 creat_conn_latency;
    u16 creat_conn_super_timeout;

    //control
    u32 creat_state_timeout_ms;	    /*创建连接后,超时未连上会取消连接,重新开搜索*/
    u8  conn_update_accept;
} scan_conn_cfg_t;

typedef struct {
    const client_match_cfg_t  *match_devices;     /*扫描匹配设备表*/
    u16   match_devices_count;
    u8    match_rssi_enable;
    s8    match_rssi_value;

    const target_uuid_t       *search_uuid_group; /*搜索uuid表*/
    u16 search_uuid_count;
    u8  auto_enable_ccc;      /*是否执行使能匹配的 NOTIFY和INDICATE 通知功能*/
} gatt_search_cfg_t;

Source: le_gatt_common.h

设计意图:

  • 扫描即配置:scan_interval/scan_window 以 0.625ms 为单位,creat_conn_interval 以 1.25ms 为单位,全部对齐 BLE 规范的时间单位,避免换算错误;
  • 自动连接链:scan_auto_do 打开扫描 → match_devices 表匹配(可选 RSSI 门限)→ creat_auto_do 自动发起连接 → 连接后按 search_uuid_group 搜索 → auto_enable_ccc 自动使能 NOTIFY/INDICATE。整条链路只需配置位域即可全自动执行,无需业务代码介入;
  • 失败自愈:creat_state_timeout_ms 在连接超时后自动取消连接并重新开搜索,形成"扫描-连接-失败-重扫"的闭环。

5. 安全配对(SM)配置

sm_cfg_t 集中了链路加密与配对策略,按主机/从机拆分自动请求与等待行为:

typedef struct {
    u8 master_security_auto_req: 1; /*主机主动发起加密*/
    u8 master_set_wait_security: 1; /*主机等待加密完成再执行profile搜索*/
    u8 slave_security_auto_req: 1;  /*从机发起加密请求命令*/
    u8 slave_set_wait_security: 1;  /*从机等待加密处理*/
    u8 io_capabilities: 4;          /*加密io能力配置*/
    u8 authentication_req_flags;    /*加密认证配置*/
    u8 min_key_size;                /*加密key支持的最小长度,range:7~16*/
    u8 max_key_size;
    int (*sm_cb_packet_handler)(...);
} sm_cfg_t;

Source: le_gatt_common.h

框架还定义了链路加密等级枚举 LINK_ENCRYPTION_NULL(无加密)、LINK_ENCRYPTION_PAIR_JUST_WORKS(Just Works 配对)、LINK_ENCRYPTION_PAIR_SC(安全连接 SC)以及 LINK_ENCRYPTION_RECONNECT = 0xf(重连场景)。GATT_COMM_EVENT_ENCRYPTION_REQUEST / GATT_COMM_EVENT_ENCRYPTION_CHANGE 事件把配对过程异步通知上层,GATT_COMM_EVENT_SM_PASSKEY_INPUT(0x90)用于需要输入 PIN 的场景(如键盘类设备)。

6. 事件模型 gatt_comm_event_e

框架把 btstack 原始事件归一化为统一枚举,按角色分组(见 le_gatt_common.h 第 49~92 行):

分组事件含义
公共(0x01~0x15)GATT_COMM_EVENT_CONNECTION_COMPLETELE 链路连接完成
GATT_COMM_EVENT_DISCONNECT_COMPLETE断开完成
GATT_COMM_EVENT_CONNECTION_COMPLETE_FAIL连接建立失败
GATT_COMM_EVENT_ENCRYPTION_REQUEST/CHANGE加密请求 / 加密完成
GATT_COMM_EVENT_CAN_SEND_NOW协议栈可发送新数据(流控信号)
GATT_COMM_EVENT_CONNECTION_UPDATE_COMPLETE连接参数更新完成
GATT_COMM_EVENT_CONNECTION_PHY_UPDATE_COMPLETEPHY 速率更新
GATT_COMM_EVENT_CONNECTION_DATA_LENGTH_CHANGEDLE 数据长度更新
ATT(0x20)GATT_COMM_EVENT_MTU_EXCHANGE_COMPLETEMTU 交换完成
从机(0x30~0x40)GATT_COMM_EVENT_CONNECTION_UPDATE_REQUEST_RESULT请求连接参数更新的反馈
GATT_COMM_EVENT_DIRECT_ADV_TIMEOUT定向广播超时未被连接
GATT_COMM_EVENT_SERVER_STATEServer 状态变化
GATT_COMM_EVENT_SERVER_INDICATION_COMPLETEINDICATE 应答结束
主机(0x50~0x60)GATT_COMM_EVENT_SCAN_DEV_MATCH / SCAN_ADV_REPORT扫描到匹配设备 / 原始广播报告
GATT_COMM_EVENT_CLIENT_STATEClient 状态变化
GATT_COMM_EVENT_CREAT_CONN_TIMEOUT建立连接超时
GATT_COMM_EVENT_GATT_SEARCH_PROFILE_START/MATCH_UUID/PROFILE_COMPLETE服务搜索生命周期
GATT_COMM_EVENT_GATT_DATA_REPORT收到对端 NOTIFY/INDICATE 数据
GATT_COMM_EVENT_GATT_SEARCH_DESCRIPTOR_RESULT搜索到描述符内容
SM(0x90)GATT_COMM_EVENT_SM_PASSKEY_INPUT需要输入配对密钥

事件编号按段分配(0x01、0x20、0x30、0x40、0x50、0x60、0x90),既保留了扩展空间,也让应用层可以按数值区间快速判断事件来源角色。配套的错误码枚举 gatt_op_ret_e(GATT_OP_RET_SUCESS=0,GATT_CMD_RET_BUSY=-100 段,GATT_OP_ROLE_ERR=-200)统一了所有 API 的返回值语义,见 le_gatt_common.h 第 94~109 行。

Core Flow

从机(Server)侧完整流程

sequenceDiagram
    participant App as 应用(Profile)
    participant Srv as le_gatt_server
    participant Comm as le_gatt_common
    participant Stack as 协议栈(btstack)
    participant Phone as 手机 Master

    Phone->>Stack: 扫描到广播并发起连接
    Stack->>Srv: 连接建立
    Srv->>App: GATT_COMM_EVENT_CONNECTION_COMPLETE
    Phone->>Stack: ATT MTU 交换
    Stack->>App: GATT_COMM_EVENT_MTU_EXCHANGE_COMPLETE
    Srv->>Stack: 按连接参数表请求更新参数
    Stack-->>Srv: GATT_COMM_EVENT_CONNECTION_UPDATE_COMPLETE
    Phone->>Stack: ATT Read Request
    Stack->>Srv: 分发到 att_read_cb(conn_handle, att_handle, offset, buf, size)
    Srv->>App: 应用填充属性值
    Phone->>Stack: ATT Write Request
    Stack->>Srv: 分发到 att_write_cb(...)
    Phone->>Stack: 写 CCCD 使能通知
    Stack->>App: 使能 NOTIFY/INDICATE
    App->>Comm: ble_comm_att_send_data(conn_handle, att_handle, data, len, op_type)
    Comm->>Stack: ATT Handle Value Notification
    Stack->>App: GATT_COMM_EVENT_CAN_SEND_NOW(可继续发送)
    Phone->>Stack: 断开链路
    Stack->>App: GATT_COMM_EVENT_DISCONNECT_COMPLETE
    Srv->>Stack: adv_auto_do 自动恢复广播

流程要点:从机数据通路是"主机读写 → att_read_cb/att_write_cb 同步回调 → 应用应答";上行通知通路则是"应用主动调 ble_comm_att_send_data → 协议栈发送 → CAN_SEND_NOW 节流"。两条通路分离,写操作天然受 ATT 流控约束,而通知通路依赖 ble_comm_att_check_send 与 cbuffer 做缓冲管理。

主机(Client)侧完整流程

sequenceDiagram
    participant App as 应用(Client)
    participant Cli as le_gatt_client
    participant Stack as 协议栈(btstack)
    participant Dev as 远端 Server 设备

    App->>Cli: 模块使能(scan_auto_do)
    Cli->>Stack: 开启扫描(scan_interval/scan_window)
    Stack->>Cli: GATT_COMM_EVENT_SCAN_DEV_MATCH
    Cli->>App: 上报匹配设备
    Cli->>Stack: creat_auto_do 发起连接(creat_conn_interval/latency/super_timeout)
    Stack->>Cli: GATT_COMM_EVENT_CONNECTION_COMPLETE
    Cli->>App: 连接完成通知
    Cli->>Stack: 搜索 search_uuid_group
    Stack->>Cli: GATT_SEARCH_MATCH_UUID / PROFILE_COMPLETE
    Cli->>Stack: auto_enable_ccc 写 CCCD
    Dev-->>Stack: NOTIFY 数据
    Stack->>Cli: GATT_COMM_EVENT_GATT_DATA_REPORT
    Cli->>App: 数据上报
    App->>Cli: 写特征值请求
    Cli->>Stack: gatt_client_write_value_of_characteristic(...)
    Stack->>Dev: ATT Write Request

流程要点:Client 侧链路完全由配置位域驱动(scan_auto_do → creat_auto_do → auto_enable_ccc),应用只需在 GATT_COMM_EVENT_GATT_DATA_REPORT 中消费数据、在 GATT_COMM_EVENT_GATT_SEARCH_PROFILE_COMPLETE 后获取服务句柄。底层搜索使用 gatt.h 提供的 gatt_client_deserialize_service / gatt_client_deserialize_characteristic 解析 ATT 报文。

Usage Examples

示例一:Server 配置结构与回调注册(框架定义)

以下代码展示了应用如何组装 server 配置——注册读写回调与事件回调(字段定义见 le_gatt_common.h):

typedef struct {
    /*server端被主机操作 读写操作回调*/
    u16(*att_read_cb)(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t *buffer, uint16_t buffer_size);
    int (*att_write_cb)(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size);
    /*协议栈事件回调处理*/
    int (*event_packet_handler)(int event, u8 *packet, u16 size, u8 *ext_param);
} gatt_server_cfg_t;

Source: le_gatt_common.h

  • att_read_cb 返回填充到 buffer 的字节数,offset 参数支持长特性值的分段读取(Read Blob);
  • att_write_cb 的 transaction_mode 区分 Write Request / Write Command / Write Long 等事务模式;
  • event_packet_handler 接收上述 gatt_comm_event_e 事件,是应用感知链路生命周期的唯一入口。

示例二:公共控制块聚合注册(框架定义)

typedef struct {
    //connect
    u16 mtu_size;         /*mtu配置大小, range:23 ~517*/
    u16 cbuffer_size;     /*缓存buffer大小,>= mtu_size*/
    u8  multi_dev_flag;   /*多机使用标识*/

    //config
    gatt_server_cfg_t *server_config; /*gatt server 配置*/
    gatt_client_cfg_t *client_config; /*gatt client 配置*/
    sm_cfg_t *sm_config;

    /*hci 回调,保留未用*/
    int (*hci_cb_packet_handler)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
} gatt_ctrl_t;

Source: le_gatt_common.h

应用侧典型初始化序列(伪代码,依据头文件声明):构造 gatt_ctrl_t → 填充 server_config/client_config/sm_config → 调用 ble_comm_init(&ctrl) → 按需调用 ble_comm_module_enable(1) / ble_gatt_server_adv_enable(1) 使能广播。multi_dev_flag 标记多机场景,cbuffer_size >= mtu_size 是必须满足的约束(ATT 发送缓存不能小于 MTU)。

示例三:协议栈 GATT 客户端 API(gatt.h)

btstack 层暴露给 Client 子系统的底层原语,供特征值读写与流控使用:

uint8_t gatt_client_read_value_of_characteristic_using_value_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle);

uint8_t gatt_client_write_value_of_characteristic_without_response(hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t *value);

uint8_t gatt_client_write_value_of_characteristic(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle, uint16_t value_length, uint8_t *data);

void gatt_client_request_can_send_now_event(hci_con_handle_t con_handle);

Source: gatt.h

配套的数据结构 le_service_t(服务起止句柄 + UUID)、le_characteristic_t(start_handle/value_handle/end_handle/properties + UUID)与 gatt_client_characteristic_descriptor_t 由 gatt_client_deserialize_* 系列函数从 ATT 报文反序列化得到,是 Client 搜索 Profile 时句柄/UUID 信息的来源。

示例四:公共数据发送与流控

框架对上层提供统一的 ATT 发送与缓存查询接口(声明于 le_gatt_common.h):

u32  ble_comm_cbuffer_vaild_len(u16 conn_handle);          /*查询发送缓存可用长度*/
int ble_comm_att_send_data(u16 conn_handle, u16 att_handle, u8 *data, u16 len, att_op_type_e op_type); /*发送通知/指示数据*/
bool ble_comm_att_check_send(u16 conn_handle, u16 pre_send_len); /*检查能否发送 pre_send_len 字节*/

Source: le_gatt_common.h

典型的发送模式:先 ble_comm_att_check_send 判断缓存余量,再 ble_comm_att_send_data 发送,收到 GATT_COMM_EVENT_CAN_SEND_NOW 后继续填下一包,从而在不丢包的前提下最大化吞吐。

Configuration Options

公共控制块 gatt_ctrl_t

字段类型默认/范围说明
mtu_sizeu1623 ~ 517ATT MTU 大小
cbuffer_sizeu16≥ mtu_sizeATT 发送缓存大小
multi_dev_flagu80多机(多连接)使用标识
server_configgatt_server_cfg_t*NULLServer 配置(含读写/事件回调)
client_configgatt_client_cfg_t*NULLClient 配置(含事件回调)
sm_configsm_cfg_t*NULL安全配对配置
hci_cb_packet_handler函数指针NULLHCI 回调(保留未用)

广播配置 adv_cfg_t

字段类型说明
adv_data / adv_data_lenconst u8* / u8无定向广播包数据及长度
rsp_data / rsp_data_lenconst u8* / u8扫描响应包数据及长度
adv_intervalu16广播周期,单位 0.625ms,范围 0x0020~0x4000
adv_auto_do位域:4模块是否自动打开广播(使能/断开等状态)
adv_type位域:4广播类型(无定向可连接/不可连接/定向等)
adv_channelu8广播通道,bit0~2 对应 channel 37~39
direct_address_info[7]u8[7]定向广播对端地址(addr_type + address)
set_local_addr_tagu8= USE_SET_LOCAL_ADDRESS_TAG(0x5a) 时使用 local_address_info,用于多机指定设备地址
local_address_info[7]u8[7]指定设备地址(addr_type + address)

扫描/连接配置 scan_conn_cfg_t

字段类型说明
scan_auto_do位域:4模块自动打开搜索(使能/断开等状态)
creat_auto_do位域:4搜索到匹配设备后自动发起连接
set_local_addr_tagu8同 adv_cfg_t,用于指定本机地址
local_address_info[7]u8[7]指定本机地址
scan_type位域:4扫描类型
scan_filter位域:4扫描重复过滤开关
scan_intervalu16扫描周期,单位 0.625ms,≥ scan_window,范围 0x0004~0x4000
scan_windowu16扫描窗口,单位 0.625ms,≤ scan_interval
creat_conn_intervalu16连接周期,单位 1.25ms,范围 0x0006~0x0c08
creat_conn_latencyu16从机忽略连接事件个数(建议 interval×latency ≤ 2s)
creat_conn_super_timeoutu16连接超时,单位 10ms,范围 0x000a~0x0c08,建议 600
creat_state_timeout_msu32建连超时后自动取消并重扫;=0 时只能手动取消
conn_update_acceptu8连接过程中是否接受从机的连接参数更新请求

搜索配置 gatt_search_cfg_t

字段类型说明
match_devices / match_devices_countconst client_match_cfg_t* / u16扫描匹配设备表及数量
match_rssi_enable / match_rssi_valueu8 / s8自动建连时是否检测 RSSI 及最低门限
search_uuid_group / search_uuid_countconst target_uuid_t* / u16连接后搜索的 UUID 表及数量
auto_enable_cccu8是否自动使能匹配的 NOTIFY/INDICATE

SM 配置 sm_cfg_t

字段类型说明
master_security_auto_req位域:1主机主动发起加密
master_set_wait_security位域:1主机等待加密完成再执行 profile 搜索
slave_security_auto_req位域:1从机发起加密请求命令
slave_set_wait_security位域:1从机等待加密处理
io_capabilities位域:4加密 IO 能力配置
authentication_req_flagsu8加密认证配置
min_key_size / max_key_sizeu8加密密钥长度范围,7~16
sm_cb_packet_handler函数指针SM 回调(保留未用)

API Reference

公共层(le_gatt_common.h 第 213~231 行)

  • void ble_comm_init(const gatt_ctrl_t *control_blk)
    • 框架初始化入口,注册全部配置。应在 BLE 功能使能前调用。
  • void ble_comm_exit(void) / void ble_comm_module_enable(u8 en)
    • 退出 / 使能或关闭整个 GATT 公共模块。
  • u32 ble_comm_cbuffer_vaild_len(u16 conn_handle)
    • 返回指定连接的发送缓存可用字节数。
  • int ble_comm_att_send_data(u16 conn_handle, u16 att_handle, u8 *data, u16 len, att_op_type_e op_type)
    • 发送 NOTIFY/INDICATE 数据;返回 gatt_op_ret_e 错误码。
  • bool ble_comm_att_check_send(u16 conn_handle, u16 pre_send_len)
    • 预检查能否发送 pre_send_len 字节,用于大包拆分前的流控。
  • const char *ble_comm_get_gap_name(void) / void ble_comm_set_config_name(const char *name_p, u8 add_ext_name)
    • 读取/设置广播中的 GAP 设备名。
  • int ble_comm_disconnect(u16 conn_handle) — 主动断开连接。
  • u8 ble_comm_dev_get_handle_state(u16 handle, u8 role) / void ble_comm_dev_set_handle_state(u16 handle, u8 role, u8 state) — 查询/设置连接状态(BLE_ST_CONNECT、BLE_ST_SEND_DISCONN、BLE_ST_NOTIFY_IDICATE、BLE_ST_SEARCH_COMPLETE 等)。
  • void ble_comm_register_state_cbk(void (*cbk)(u16 handle, u8 state)) — 注册连接状态变化回调。
  • s8 ble_comm_dev_get_index(u16 handle, u8 role) / s8 ble_comm_dev_get_idle_index(u8 role) — 句柄→索引 / 查找空闲索引。
  • u8 ble_comm_dev_get_handle_role(u16 handle) / u16 ble_comm_dev_get_handle(u8 index, u8 role) — 句柄↔角色/索引互查。
  • int ble_comm_set_connection_data_length(u16 conn_handle, u16 tx_octets, u16 tx_time) — 请求 DLE 数据长度更新。
  • int ble_comm_set_connection_data_phy(u16 conn_handle, u8 tx_phy, u8 rx_phy, u16 phy_options) — 请求 PHY 速率更新。

Server 层(le_gatt_common.h 第 234 行起)

  • void ble_gatt_server_init(gatt_server_cfg_t *server_cfg) / void ble_gatt_server_exit(void) — 初始化/退出 Server。
  • ble_state_e ble_gatt_server_get_work_state(void) — 获取 Server 整体工作状态(广播中/已连接等)。
  • ble_state_e ble_gatt_server_get_connect_state(u16 conn_handle) — 获取指定连接状态。
  • int ble_gatt_server_adv_enable(u32 en) — 打开/关闭广播。
  • void ble_gatt_server_module_enable(u8 en) — 使能/关闭 Server 模块(关闭时停止广播与连接)。
  • void ble_gatt_server_disconnect_all(void) — 断开所有 Server 侧连接(如关机流程)。

协议栈 GATT 客户端 API(gatt.h)

  • uint8_t gatt_client_read_value_of_characteristic_using_value_handle(btstack_packet_handler_t callback, hci_con_handle_t con_handle, uint16_t value_handle) — 读特征值。
  • uint8_t gatt_client_read_long_value_of_characteristic_using_value_handle_with_offset(...) — 带偏移读长特征值(Read Blob)。
  • uint8_t gatt_client_write_value_of_characteristic(callback, con_handle, value_handle, value_length, uint8_t *data) — 写特征值(带应答)。
  • uint8_t gatt_client_write_value_of_characteristic_without_response(con_handle, value_handle, value_length, uint8_t *value) — 无应答写(Write Command)。
  • void gatt_client_request_can_send_now_event(hci_con_handle_t con_handle) — 请求 CAN_SEND_NOW 事件,用于无应答写流控。
  • void gatt_client_deserialize_service/characteristic/characteristic_descriptor(const uint8_t *packet, int offset, ...) — 从 ATT 报文反序列化服务/特征/描述符结构。

Failure Modes、边界与并发

错误码语义

框架统一使用 gatt_op_ret_e 作为所有 API 的返回类型(见 le_gatt_common.h 第 94~109 行):

错误码值触发场景
GATT_OP_RET_SUCESS0执行成功
GATT_CMD_RET_BUSY-100命令处理忙(协议栈正忙)
GATT_CMD_PARAM_OVERFLOW-101传参数溢出(超长数据)
GATT_CMD_OPT_FAIL-102操作失败
GATT_BUFFER_FULL-103发送缓存已满
GATT_BUFFER_ERROR-104缓存出错
GATT_CMD_PARAM_ERROR-105传参出错
GATT_CMD_STACK_NOT_RUN-106协议栈未运行
GATT_CMD_USE_CCC_FAIL-107未使能通知,导致 NOTIFY/INDICATE 发送失败
GATT_OP_ROLE_ERR-200角色错误(在错误的角色上下文中调用)

典型边界与失败场景

  • 未使能 CCC 就发送通知:GATT_CMD_USE_CCC_FAIL 是上层最常见的错误。主机必须先写 CCCD 使能 NOTIFY/INDICATE(Client 侧由 auto_enable_ccc 自动完成,Server 侧需在收到使能后标记状态),否则发送被拒。
  • 缓存满与背压:GATT_BUFFER_FULL 表示 cbuffer_size 耗尽。正确做法是收到 GATT_COMM_EVENT_CAN_SEND_NOW 再继续发送,而非轮询重试;ble_comm_att_check_send 可在发送前预判。
  • 句柄/索引越界:ble_comm_dev_get_index 未命中返回 INVAIL_INDEX((s8)-1);连接句柄 0 被定义为 INVAIL_CONN_HANDLE。上层用返回索引访问数组前必须先判 >= 0。
  • MTU 与缓存约束:cbuffer_size 必须 ≥ mtu_size,否则单包 ATT 数据无法放入缓存。MTU 交换完成后,单次通知最大长度受 MTU-3 限制,大 payload 需应用层分包。
  • 建连超时:GATT_COMM_EVENT_CREAT_CONN_TIMEOUT 与 GATT_COMM_EVENT_CONNECTION_COMPLETE_FAIL 分别覆盖"发起连接超时"与"连接建立失败"。creat_state_timeout_ms=0 时框架不自动重扫,需业务手动处理,避免无限重连。
  • 定向广播超时:GATT_COMM_EVENT_DIRECT_ADV_TIMEOUT 表明目标设备未在定向广播窗口内连接,Server 应切换为无定向广播或降频重试。
  • 并发/重入:协议栈事件回调(event_packet_handler、att_read_cb、att_write_cb)运行在蓝牙任务上下文中,上层回调内不应执行阻塞操作(如长时间循环、等待信号量),否则会阻塞整条链路事件处理。状态位域(adv_ctrl_en、server_connection_update_index 等)由蓝牙任务独占读写,未加锁——这既是性能优化,也意味着跨任务访问这些字段是不安全的。
  • 多连接(多机):multi_dev_flag 与 set_local_addr_tag=USE_SET_LOCAL_ADDRESS_TAG 配合,允许每台设备指定自己的广播/扫描地址;连接句柄表按角色分槽,任一槽位独立管理状态,互不阻塞。

Performance 与运维注意事项

  • 流控模型:上行数据采用"检查-发送-等待 CAN_SEND_NOW"的推拉结合模型,避免无应答写导致的协议栈缓冲溢出;ble_comm_cbuffer_vaild_len 可实时查询余量。
  • 链路参数优化:Server 通过 server_connection_param_table 在连接建立后按序请求更优的连接参数(更小 interval、合理 latency),从而降低延迟与功耗;Client 可用 conn_update_accept 接受对端请求。GATT_COMM_EVENT_CONNECTION_PHY_UPDATE_COMPLETE 与 GATT_COMM_EVENT_CONNECTION_DATA_LENGTH_CHANGE 事件可确认 2M PHY/DLE 生效,是吞吐优化的依据。
  • 内存分段:框架代码在 SUPPORT_MS_EXTENSIONS 下被放置到专用段(.ble_app_bss / .ble_app_data / .ble_app_text 等),便于链接脚本统一规划 RAM/Flash 布局,做 256KB 级裁剪时不会污染主程序段。
  • 运行时能力探测:config_le_hci_connection_num、config_le_sm_support_enable、config_le_gatt_server_num、config_le_gatt_client_num 是协议栈导出的全局配置常量,配合 STACK_IS_SUPPORT_GATT_SERVER() 等宏(le_gatt_common.h 第 44~47 行)可在运行时判断当前固件的 GATT 能力,防止调用未编译的功能。
  • 看门狗:le_gatt_server.c 中引用了 clr_wdt(),在长流程(如 OTA 大块发送)中需要喂狗,上层回调若耗时过长必须自行确保看门狗不被饿死。

Extension Points

  1. 自定义 Profile(ATT 表):通过 ble_gatt_server_profile_init / ble_comm_server_profile_init(le_gatt_common.c 前向声明)注册自定义 profile_data,即可扩展任意服务/特征;属性值的读写逻辑在 att_read_cb/att_write_cb 中实现。这是 HID、SPPLE、OTA/RCSP 等业务 Profile 的公共挂载点。
  2. 事件回调:gatt_server_cfg_t::event_packet_handler 与 gatt_client_cfg_t::event_packet_handler 是业务接收所有链路事件的钩子;ble_comm_register_state_cbk 提供更细粒度的连接状态通知,适合驱动 UI 状态机。
  3. 配对行为:通过 sm_cfg_t 的 4 个安全位域 + io_capabilities + authentication_req_flags 组合即可定制 Just Works / SC / 需要 PIN 的配对流程,无需改动框架代码;GATT_COMM_EVENT_SM_PASSKEY_INPUT 提供密钥输入入口(对应 ble_gatt_server_passkey_input / ble_gatt_client_passkey_input)。
  4. OTA 集成:server_ctl_t 内置 update_* 钩子(状态回调、接收回调、发送唤醒),RCSP/OTA 模块可直接挂接,无需另起连接管理逻辑。
  5. 底层能力:gatt.h 的 gatt_client_* 原语是 Client 侧定制数据通路的最后一道接口,如需绕过框架的自动搜索逻辑可在此层直接调用。

Tests

在已阅读的源码范围内未发现针对 gatt_common 框架的独立单元测试或自动化测试用例(SDK 为嵌入式固件工程,测试以整机联调与协议分析仪抓包为主)。框架的验证主要依赖:LOG_TAG "[GATT_SERVER]" / "[GATT_COMM]" 日志系统(LOG_ERROR_ENABLE/LOG_DEBUG_ENABLE/LOG_INFO_ENABLE,le_gatt_server.c 第 44~50 行)在真机上观察事件时序,以及补丁包目录(补丁包/)中随版本发布的增量代码作为回归参考。建议新增 Profile 时,先以 GATT_COMM_EVENT_* 事件日志核对连接、MTU、搜索、CCC 使能的完整时序,再验证数据收发。

Related Links

  • le_gatt_common.h(框架全部配置结构与 API 声明)
  • le_gatt_common.c(公共层:句柄/角色/状态管理)
  • le_gatt_server.c(GATT Server 子系统)
  • le_gatt_client.c(GATT Client 子系统)
  • gatt.h(btstack GATT 客户端底层 API)

相关能力请参见:BLE 协议栈与广播/扫描机制、SM 安全配对、HID/SPPLE 业务 Profile、OTA/RCSP 升级流程等兄弟页面。

Prev
BLE 控制器、链路层与 HCI 传输
Next
BLE 应用示例:遥控器 / Dongle / 对讲机