杰理 SDK 文档中心
首页
首页
  • 概述

    • SDK 概览与产品定位
    • 支持芯片平台与蓝牙认证
    • SDK 架构与目录分层
  • 快速开始

    • 环境搭建与编译工具链
    • 编译构建系统
    • 板级工程与配置
    • 烧录与固件升级工具
  • 应用工程

    • 应用选择与工程总览
    • SPP + BLE 数传应用框架
    • 透传与 AT 指令示例
    • BLE 广播/中心与定位示例
    • 2.4G 私有协议与 Dongle 示例
    • 云平台接入示例
    • HID 人机交互应用框架
    • HID 示例工程(键盘/鼠标/遥控器/手柄)
    • Bluetooth Mesh 应用框架
    • Mesh 模型与 Mesh DFU 固件升级
    • Mesh 音频编解码演示
  • 芯片平台与硬件抽象

    • 芯片平台总览与差异
    • 音频编解码与时钟管理
    • 外设驱动接口(ADC/IIC/SPI/PWM/LED/充电)
    • 芯片配置工具与下载支持
  • 蓝牙协议栈

    • 蓝牙控制器层(btctrler)
    • 蓝牙协议栈与 Profile(btstack)
    • 蓝牙模块选择与配置
  • 媒体与音频框架

    • 音频流框架
    • 音频编解码与 A2DP 媒体
    • 音频效果处理(EQ/频谱/变调/环绕/超低音)
    • 本地 TWS 与音频同步
  • 系统服务与运行时

    • 实时操作系统与任务调度
    • 消息事件机制
    • 电源管理与低功耗
    • 存储与配置系统
    • 设备驱动框架(USB/RTC)
  • 应用公共组件

    • 音频应用组件
    • 设备外设抽象(按键/触摸/传感器/存储)
    • 蓝牙公共模块与消息联动
    • 调试与配置组件
    • 杰理关键词唤醒(jl_kws)
  • 第三方协议与云平台接入

    • 杰理 RCSP 私有协议
    • 低功耗蓝牙 Mesh 方案(llsync_mesh)
    • Sig Mesh 方案
    • 涂鸦协议接入
    • 腾讯连连接入
    • 华为 HiLink 接入
  • 固件升级与维护

    • OTA 升级机制
    • 升级补丁与版本维护
    • 升级工具链(BLE OTA / USB Dongle OTA)
  • 文档与开发资源

    • 数据手册与架构文档
    • 协议与云平台开发文档
    • 常见问题与技术支持

HID 示例工程(键盘/鼠标/遥控器/手柄)

本文档介绍 fw-AC63_BT_SDK 中 apps/hid 目录下的 HID(Human Interface Device)示例工程体系,覆盖经典蓝牙(EDR)HID Profile、BLE HOGP(HID over GATT)以及 USB HID 设备三种传输通道上的键盘、鼠标、遥控器、手柄等示例工程的实现方式、公共模块接口与配置方法。

Purpose and Scope

本页聚焦于 apps/hid 应用工程及其示例代码,内容包括:

  • apps/hid/examples/ 下各示例工程(standard_keyboard、keyboard、mouse_single、mouse_dual、voice_remote_control、gamebox、keyfob、keypage、idle)的定位与差异;
  • apps/hid/modules/ 下 HID 公共模块(edr_hid_user.c 经典蓝牙 HID、ble_hogp.c BLE HOGP)的对外接口;
  • apps/hid/board/ 各芯片平台 Makefile 的编译组织方式;
  • USB HID 设备侧的实现(描述符、中断端点、上报回调)。

以下内容属于其他页面,本文仅作交叉指引,不展开:RCSP 私有协议(JL_rcsp/rcsp_hid_inter.c)属于 RCSP 协议主题;音频播放链路属于音频示例主题;apps/spp_and_le 下的 dongle 工程属于 SPP/LE 透传主题。

Overview

HID(Human Interface Device)是蓝牙与 USB 生态中最常用的人机交互设备类别。本 SDK 中一个 HID 设备(例如标准键盘)可以同时支持三种上报通道:

  1. 经典蓝牙 EDR HID:基于 Bluetooth HID Profile,通过 SDP 暴露 HID 服务,使用 L2CAP 控制/中断通道传输 Report;
  2. BLE HOGP:基于 HID over GATT Profile,通过 GATT 的 HID Service(0x1812)与 Report Characteristic 传输 Report;
  3. USB HID:设备枚举为 USB HID 类,通过中断端点(Interrupt IN/OUT)收发 Report,典型场景是 2.4G 接收器(USB Dongle)与 PC 连接。

应用层通过统一的"Report 上报"抽象(report_id + data + len)向三条通道发送数据,由 edr_hid_user.c、ble_hogp.c 与 USB 设备栈各自完成协议封装。这种"应用层统一、传输层分离"的设计使同一个键盘矩阵、同一个按键事件处理逻辑可以无差别地运行在蓝牙或 USB 模式下。

apps/hid 目录下的示例工程覆盖了市面上主流 HID 产品形态:

示例工程产品形态主要传输通道
standard_keyboard标准键盘(全键矩阵 + FN 组合键)EDR HID / BLE HOGP / USB HID
keyboard基础键盘EDR HID / BLE HOGP
mouse_single单模鼠标BLE HOGP
mouse_dual双模(2.4G/BLE)鼠标BLE HOGP / USB
voice_remote_control语音遥控器(含音频编解码)EDR HID
gamebox手柄/游戏盒EDR HID
keyfob遥控钥匙扣EDR HID
keypage翻页笔/演示器BLE HOGP
idle空工程模板—

Architecture

下图展示 apps/hid 示例工程的整体架构:应用示例位于顶层,通过 apps/hid/modules 中的 EDR/BLE HID 公共模块接入协议栈,USB 通道则由公共组件 apps/common/device/usb 提供,HID Report 描述符与键值定义统一来自 apps/common/include/standard_hid.h 与 apps/common/device/usb/host/usb_hid_keys.h。

flowchart TD
    subgraph sg_Examples["示例应用 apps/hid/examples"]
        SK["standard_keyboard"]
        KB["keyboard"]
        MS["mouse_single / mouse_dual"]
        RC["voice_remote_control"]
        GB["gamebox"]
        KF["keyfob / keypage"]
    end

    subgraph sg_Modules["HID 公共模块 apps/hid/modules"]
        EDR["edr_hid_user.c(EDR HID Profile)"]
        HOGP["ble_hogp.c(BLE HOGP)"]
    end

    subgraph sg_Common["公共组件 apps/common"]
        USBDEV["usb/device/hid.c(USB 设备类)"]
        STD["include/standard_hid.h(Report 定义)"]
        KEYS["usb/host/usb_hid_keys.h(键值定义)"]
        RCSP["JL_rcsp/bt_trans_data/rcsp_hid_inter.c"]
    end

    subgraph sg_Board["板级编译 apps/hid/board"]
        MK["bd19 / bd29 / br23 / br25 / br30 / br34 Makefile"]
    end

    SK --> EDR
    SK --> HOGP
    SK --> USBDEV
    KB --> EDR
    KB --> HOGP
    MS --> HOGP
    RC --> EDR
    GB --> EDR
    KF --> EDR
    KF --> HOGP
    EDR --> STD
    HOGP --> STD
    USBDEV --> STD
    EDR --> RCSP
    MK --> SK
    MK --> RC

各层职责与连接关系:

  • 应用示例层:负责按键扫描/事件处理、设备状态机(配对、回连、关机)、以及把按键事件封装成 HID Report 后调用 hid_report_send 分发到各通道;
  • HID 公共模块层:edr_hid_user.c 封装经典蓝牙 HID Profile 的初始化、Report Map 下发、中断通道发送;ble_hogp.c 封装 BLE 端 HID Service 与上报;两者对外接口风格一致(*_hid_data_send(report_id, data, len)),便于应用层切换;
  • 公共组件层:standard_hid.h 定义标准 HID Report 描述符与报告 ID,usb_hid_keys.h 定义键盘键值宏(_KEY_Q、_KEY_MOD_LCTRL 等),USB 设备栈提供中断端点收发与枚举描述符;
  • 板级编译层:每个芯片平台(bd19=AC632N、bd29=AC631N、br23=AC635N、br25=AC636N、br30=AC637N、br34=AC638N)的 Makefile 以宏开关选择示例工程并链接对应源文件。

示例工程与编译组织

apps/hid/board 下每个平台目录包含独立 Makefile,通过 CONFIG_APP_XXX 宏选择编译哪个示例,并显式列出参与编译的源文件。以 bd19/Makefile 为例,它同时链接了标准键盘示例与语音遥控器示例的源文件,并包含 RCSP 协议交互模块:

../../../../apps/hid/examples/standard_keyboard/app_standard_keyboard.c \
../../../../apps/hid/examples/standard_keyboard/usb_hid_devices.c \
../../../../apps/hid/examples/voice_remote_control/app_remote_control.c \
../../../../apps/hid/modules/bt/edr_hid_user.c \
../../../../apps/hid/modules/bt/ble_hogp.c \
../../../../apps/hid/modules/misc.c \

Source: apps/hid/board/bd19/Makefile

Makefile 中的编译宏(如 CONFIG_HID_CASE_ENABLE、CONFIG_TWS_ENABLE、CONFIG_TRANSFER_ENABLE)决定设备特性开关;CONFIG_HID_CASE_ENABLE 使能 HID 充电仓(case)相关逻辑,CONFIG_TRANSFER_ENABLE 使能数据传输。换平台时只需替换 board 目录,示例代码本身跨平台复用。

EDR HID 用户模块

经典蓝牙侧 HID 的对外接口统一在 apps/hid/include/edr_hid_user.h 中声明,是示例工程与 EDR HID Profile 之间的唯一契约。接口设计上刻意保持与 BLE 侧 ble_hid_data_send 相似,让应用层可以用同一套 report_id + data + len 逻辑同时驱动两条蓝牙通道:

enum {
    HID_USER_ERR_NONE = 0x0,
    HID_USER_ERR_DONE,
    HID_USER_ERR_SEND_FAIL,
};

void user_hid_init(void (*user_hid_output_handler)(u8 *packet, u16 size, u16 channel));
void user_hid_exit(void);
void user_hid_enable(u8 en);
int  user_hid_send_data(u8 *buf, u32 len);
void user_hid_disconnect(void);
void user_hid_set_icon(u32 class_type);
void user_hid_set_ReportMap(u8 *map, u16 size);
int edr_hid_data_send(u8 report_id, u8 *data, u16 len);
int edr_hid_data_send_ext(u8 report_type, u8 report_id, u8 *data, u16 len);
void edr_hid_key_deal_test(u16 key_msg);
int edr_hid_is_connected(void);
int edr_hid_tx_buff_is_ok(void);

Source: apps/hid/include/edr_hid_user.h

设计要点:

  • user_hid_init 注册 Output 回调:HID 对端(主机)可能通过控制通道下发 Output Report(例如键盘 LED 状态、鼠标特性配置),该回调把数据交回应用层处理;
  • user_hid_set_ReportMap 下发 Report 描述符:必须先于连接建立前调用,主机枚举 HID 服务时读取该描述符;
  • edr_hid_data_send 是发送主入口:report_id 对应 Report Map 中定义的报告 ID(如键盘 0x01、多媒体 0x02),返回值为 HID_USER_ERR_* 枚举,调用方可据 HID_USER_ERR_SEND_FAIL 做重试或提示;
  • edr_hid_is_connected / edr_hid_tx_buff_is_ok 用于发送前的链路状态与发送缓冲检查,避免在未连接时把数据丢弃到空中。

API 参考

函数参数返回值说明
user_hid_inituser_hid_output_handler:Output Report 回调void初始化 EDR HID,注册主机下发数据回调
user_hid_exit无void退出/反初始化 HID 用户模块
user_hid_enableen:1 使能 / 0 禁止void动态开关 HID 功能(多连接场景切换)
user_hid_send_databuf 数据指针,len 长度int通用数据发送(非 Report 封装)
user_hid_disconnect无void主动断开当前 HID 连接
user_hid_set_iconclass_type:HID 设备类别图标void设置主机端显示的设备图标(Class of Device)
user_hid_set_ReportMapmap 描述符指针,size 长度void下发 HID Report Map
edr_hid_data_sendreport_id,data,lenint按 Report ID 发送中断通道数据
edr_hid_data_send_extreport_type,report_id,data,lenint扩展版发送,可指定 Report 类型(Input/Output/Feature)
edr_hid_is_connected无int链路是否已连接(非 0 表示已连接)
edr_hid_tx_buff_is_ok无int发送缓冲是否可写(避免阻塞/丢包)

BLE HOGP 侧对应接口

BLE 通道由 apps/hid/modules/bt/ble_hogp.c 提供,标准键盘示例通过 extern 声明使用其接口:ble_hid_data_send(report_id, data, len) 发送、ble_hid_is_connected() 查询连接状态、le_hogp_set_output_callback() 注册 Output 回调(例如接收主机下发的 CapsLock LED 状态)。应用层在 hid_report_send 中先判断当前处于哪条链路,再选择调用 edr_hid_data_send 或 ble_hid_data_send。

标准键盘示例(standard_keyboard)

app_standard_keyboard.c 是功能最完整的键盘示例,同时支持 EDR HID、BLE HOGP 与 USB 三条通道,是理解整个 HID 体系的入口。文件顶部以 #if(CONFIG_APP_STANDARD_KEYBOARD) 作为编译开关,并定义了一系列特性宏:

#define SUPPORT_RECONN_ADV_OR_DIRECT        0    //0:ADV回连接 1:DIRECT回连接
#define SUPPORT_KEYBOARD_NO_CONFLICT        0    //无冲按键支持
#define SUPPORT_USER_PASSKEY                0
#define CAP_LED_ON_VALUE                    1

//2.4G模式: 0---ble ,非0 2.4G配对码
#define CFG_RF_24G_CODE_ID  (0)//32bits
#define CFG_RF_24G_CODE_CHANNEL 0x4//2.4g对应的通道

Source: apps/hid/examples/standard_keyboard/app_standard_keyboard.c

这些宏直接决定产品行为:SUPPORT_RECONN_ADV_OR_DIRECT 选择 BLE 回连方式(广播回连或直连回连);CFG_RF_24G_CODE_ID 为 0 时走 BLE,非 0 时启用 2.4G 配对码(配对码为 32bit);CFG_RF_24G_CODE_CHANNEL 指定 2.4G 射频通道。

连接状态机

示例内部维护了几个核心状态量,用于协调 EDR/BLE/USB 三条链路与配对流程:

typedef enum {
    EDR_OPERATION_NULL = 0,
    EDR_OPERATION_RECONN,
    EDR_OPERATION_PAGESCAN_IRQUIRY_SCAN,
} edr_operation_t;

typedef enum {
    SYSTEM_IOS,
    SYSTEM_WIN,
    SYSTEM_ARD,
};

Source: apps/hid/examples/standard_keyboard/app_standard_keyboard.c

  • edr_operation_t 描述 EDR 侧当前操作:空闲 / 回连 / 同时进行寻呼扫描与查询扫描,用于在键盘开机的不同阶段切换扫描策略;
  • SYSTEM_IOS/WIN/ARD 表示当前对端主机系统类型,键盘据此切换键值映射(例如 Command 键与 Ctrl 键的差异);
  • 其他静态变量如 g_auto_shutdown_timer(自动关机计时)、cur_bt_idx(当前蓝牙索引)、paired_flag(是否已配对)、keyboard_system(当前系统)共同驱动整机行为。

键盘矩阵与 FN 键映射

键盘按键布局由二维矩阵表 matrix_key_table[ROW_MAX][COL_MAX] 定义,表项使用 usb_hid_keys.h 中的标准键值宏(_KEY_Q、_KEY_A…),S_KEY() 包裹的项表示组合修饰键(如 S_KEY(_KEY_MOD_LSHIFT))。矩阵表上方有 FN_ROW/FN_COL 指定 FN 键位置:

#define FN_ROW                   (6)
#define FN_COL                   (0)

const u16 matrix_key_table[ROW_MAX][COL_MAX] = {                //高八位用来标识是否特殊键
    /**/{0,                     _KEY_Q,   _KEY_W,         _KEY_E,  _KEY_R,  _KEY_U,  _KEY_I,          _KEY_O,    _KEY_P,             0,                     0,             0,         0,       0,    0,      0},
    /**/{0,                     _KEY_TAB, _KEY_CAPSLOCK,  _KEY_F3, _KEY_T,  _KEY_Y,  _KEY_RIGHTBRACE, _KEY_F7,   _KEY_LEFTBRACE,     0,                  _KEY_BACKSPACE,   0,         0,       0,    0,  S_KEY(_KEY_MOD_LSHIFT), S_KEY(_KEY_MOD_LALT)},
    /**/{0,                     _KEY_A,   _KEY_S,         _KEY_D,  _KEY_F,  _KEY_J,  _KEY_K,          _KEY_L,    _KEY_SEMICOLON,  S_KEY(_KEY_MOD_LCTRL), _KEY_BACKSLASH,   0,         0,       0,    0,  S_KEY(_KEY_MOD_RSHIFT)},
    ...
};

Source: apps/hid/examples/standard_keyboard/app_standard_keyboard.c

设计意图:把物理矩阵与逻辑键值解耦。硬件改版(换矩阵走线)时只需改表,业务代码(组合键、配对键、多媒体键处理)完全不动。FN 组合键通过 fn_remap_key 表把物理位置重映射为功能事件(如 _KEY_CUSTOM_CTRL_HOME、_KEY_BRIGHTNESS_INCREASE),并支持自定义事件 KEYBOARD_ENTER_PAIR0~3(一键进入四设备配对)、KEYBOARD_SYSTEM_IOS/WIN/ARD(切换主机系统)。

键盘上报主路径

示例声明了统一的上报入口 hid_report_send(u8 report_id, u8 *data, u16 len),并根据当前链路分发:

  • EDR 已连接 → edr_hid_data_send(report_id, data, len);
  • BLE 已连接 → ble_hid_data_send(report_id, data, len)(外部声明);
  • USB 已连接(PC 模式)→ 走 USB 中断端点发送。

链路状态由 edr_hid_is_connected()、ble_hid_is_connected() 与 usb_hid_register_state_callback 注册的 stdkb_usb_state_callback 维护。此外示例还通过 le_hogp_set_output_callback 接收 BLE 主机的 Output Report(如 LED 状态),实现 CapsLock 指示灯,CAP_LED_ON_VALUE 即定义 LED 点亮电平。

USB HID 设备层(usb_hid_devices.c)

当键盘作为 USB 设备(2.4G 接收器模式或有线模式)连接 PC 时,由 usb_hid_devices.c 提供枚举描述符与端点收发。整个文件由 CONFIG_APP_STANDARD_KEYBOARD && TCFG_PC_ENABLE && TCFG_USB_SLAVE_USER_HID 三个宏同时门控,表明只有标准键盘示例且开启 PC 模式、开启 USB HID 从机时才编译。

描述符定义

static const u8 sHIDDescriptor[] = {
    //InterfaceDescriptor:
    USB_DT_INTERFACE_SIZE,     // Length
    USB_DT_INTERFACE,          // DescriptorType
    0x00,                       // bInterface number
    0x00,                      // AlternateSetting
    0x02,                        // NumEndpoint
    USB_CLASS_HID,             // Class = Human Interface Device
    0x00,                      // Subclass, 0 No subclass
    0x00,                      // Protocol, 0 None
    0x00,                      // Interface Name
    //HIDDescriptor:
    0x09,                      // bLength
    USB_HID_DT_HID,            // bDescriptorType
    0x01, 0x02,                // bcdHID, HID Class Specification 1.02
    0x00,                      // bCountryCode
    0x01,                      // bNumDescriptors
    0x22,                      // bDescriptorType = Report Desc.
    ...
};

Source: apps/hid/examples/standard_keyboard/usb_hid_devices.c

描述符定义了两个中断端点:HID_EP_IN(键盘上报,最大包长 MAXP_SIZE_HIDIN)与 HID_EP_OUT(接收主机 Output Report,最大包长 MAXP_SIZE_HIDOUT),轮询间隔 1ms。接口描述符的 Class 字段为 USB_CLASS_HID。

Report Map 注入与回调注册

设备层暴露了三个供应用层调用的钩子,实现"描述符固定、Report Map 可变":

void usb_hid_set_repport_map(const u8 *map, int size)
{
    hid_report_map_size = size;
    hid_report_map = map;
}

static void (*usb_hid_state_ck)(u8 state) = NULL;
void usb_hid_register_state_callback(void *callback)
{
    usb_hid_state_ck = callback;
}

Source: apps/hid/examples/standard_keyboard/usb_hid_devices.c

  • usb_hid_set_repport_map:把标准键盘的 Report 描述符(来自 standard_hid.h)注入设备层,主机请求 Report Descriptor 时通过 get_hid_report_desc() 返回;若未注入,get_hid_report_desc_len 会触发 ASSERT 报 "report map err";
  • usb_hid_register_state_callback:注册枚举/断开状态回调,应用层据此切换上报通道(stdkb_usb_state_callback)。

端点收发

static u32 hid_tx_data(struct usb_device_t *usb_device, const u8 *buffer, u32 len)
{
    const usb_dev usb_id = usb_device2id(usb_device);
    return usb_g_intr_write(usb_id, HID_EP_IN, buffer, len);
}

static void hid_rx_data(struct usb_device_t *usb_device, u32 ep)
{
    u8 data[MAXP_SIZE_HIDOUT];
    ...
    u32 rx_len = usb_g_intr_read(usb_id, ep, data, MAXP_SIZE_HIDOUT, 0);
    if (usb_hid_output_callback) {
        usb_hid_output_callback(data, rx_len);
    }
}

Source: apps/hid/examples/standard_keyboard/usb_hid_devices.c

hid_tx_data 走中断 IN 端点把键盘 Report 发给 PC;hid_rx_data 在中断 OUT 端点收到主机数据后回调 usb_hid_set_output_callback 注册的应用层函数。端点在 hid_endpoint_init 中通过 usb_g_ep_config 配置,DMA 缓冲由 hid_register 调用 usb_alloc_ep_dmabuffer 分配,hid_itf_hander 处理标准请求(USB_REQ_GET_DESCRIPTOR 时返回 HID 描述符与 Report 描述符长度)。

鼠标 / 遥控器 / 手柄示例

鼠标(mouse_single / mouse_dual)

  • mouse_single/app_mouse.c:单模鼠标,主通道为 BLE HOGP,上报鼠标移动(X/Y 位移)与按键(左/中/右)Report;
  • mouse_dual/app_mouse_dual.c:双模鼠标,在 BLE 与 2.4G/USB 之间切换,结构上复用标准键盘示例的"统一 hid_report_send 分发"思路——根据当前链路(BLE 或 USB)选择 ble_hid_data_send 或 USB 中断端点发送。

鼠标类设备的实现差异主要在 Report 描述符:标准鼠标 Report 为 4 字节(Button + X + Y + Wheel),而键盘为 8 字节按键数组,因此两个示例各自注入不同的 Report Map,但传输层代码完全复用。

语音遥控器(voice_remote_control)

app_remote_control.c 实现带语音功能的遥控器,配套 audio_codec_demo.c 完成语音数据的采集/编解码。遥控器按键(音量、上下曲、语音键)通过 EDR HID 上报;语音数据流则在 HID 之外通过 RFCOMM/自定义通道传输(依赖 RCSP 交互模块 rcsp_hid_inter.c 协调 HID 与透传通道)。该示例同时被链接进各平台 Makefile(与标准键盘示例共存),由 CONFIG_APP_REMOTE_CONTROL 类宏区分。

手柄(gamebox)

gamebox/ 目录包含 app_gamebox.c(应用入口)、gamebox.c(手柄逻辑)与 key_mapping.c(按键映射),走 EDR HID 通道。手柄 Report 结构与键盘/鼠标不同:通常包含方向键状态位、12~16 个按键位以及模拟摇杆数据,Report 长度更长。key_mapping.c 将物理按键映射为手柄 HID 键值(如 A/B/X/Y、Start/Select),与标准键盘的 matrix_key_table 职责相同——物理输入与逻辑键值解耦。

Core Flow:按键上报全流程

以下序列图展示标准键盘一次按键从物理触发到主机收到的完整路径,覆盖三条传输通道:

sequenceDiagram
    participant U as User
    participant M as matrix_keyboard(矩阵扫描)
    participant A as app_standard_keyboard(事件处理)
    participant H as hid_report_send(统一分发)
    participant E as edr_hid_user / ble_hogp
    participant UD as usb_hid_devices(中断端点)
    participant P as Host(手机/PC)

    U->>M: 按下按键
    M->>A: 按键事件(键值 + 组合键)
    A->>A: 查 matrix_key_table / fn_remap_key 映射
    A->>H: hid_report_send(report_id, data, len)
    alt EDR 已连接
        H->>E: edr_hid_data_send(report_id, data, len)
        E->>P: L2CAP 中断通道 Report
    else BLE 已连接
        H->>E: ble_hid_data_send(report_id, data, len)
        E->>P: GATT HID Report Characteristic
    else USB 已连接
        H->>UD: hid_tx_data(usb_g_intr_write)
        UD->>P: 中断 IN 端点 Report
    end
    P-->>A: Output Report(LED 等,可选)
    A->>A: stdkb_edr_led_status_callback / USB 输出回调

关键设计决策与原因:

  1. 统一分发入口:应用层不关心底层是 EDR、BLE 还是 USB,只需调用 hid_report_send;链路切换(如 BLE 断开自动切 USB)对业务透明;
  2. 发送前查连接状态:edr_hid_is_connected / ble_hid_is_connected / USB 状态回调确保数据只发往已建立的链路,避免无连接时无效发包导致的功耗浪费;
  3. 事件驱动而非轮询:矩阵扫描产生按键事件后才组包上报,空闲时键盘可进入 sniff 模式(见"性能与运维"),这是 HID 设备低功耗的关键。

使用示例

注册 USB HID 状态回调与 Report Map

标准键盘示例初始化时把 USB 设备层与应用层对接起来(USB 通道):

void usb_hid_register_state_callback(void *callback);
static void stdkb_usb_state_callback(u8 state);
extern void le_hogp_set_output_callback(void *cb);
extern int ble_hid_data_send(u8 report_id, u8 *data, u16 len);
extern int ble_hid_is_connected(void);

Source: apps/hid/examples/standard_keyboard/app_standard_keyboard.c

应用层通过 usb_hid_register_state_callback(stdkb_usb_state_callback) 感知 USB 枚举/断开,切换上报通道;le_hogp_set_output_callback 接收 BLE 主机下发的 LED 状态。这种"回调注册 + 统一发送"模式是新增自定义 HID 设备(如新键位的手柄)时最直接的参考模板。

组合键(修饰键)定义

usb_hid_keys.h 提供标准键值宏,S_KEY() 表示修饰键,键盘矩阵中可直接组合:

{0, _KEY_TAB, _KEY_CAPSLOCK, _KEY_F3, _KEY_T, _KEY_Y, ..., S_KEY(_KEY_MOD_LSHIFT), S_KEY(_KEY_MOD_LALT)},
{0, _KEY_A,   _KEY_S,        _KEY_D,  _KEY_F, _KEY_J, ..., S_KEY(_KEY_MOD_LCTRL), _KEY_BACKSLASH, 0, 0, 0, 0, S_KEY(_KEY_MOD_RSHIFT)},

Source: apps/hid/examples/standard_keyboard/app_standard_keyboard.c

S_KEY(_KEY_MOD_LSHIFT) 表示该物理键按下时,Report 中需同时置位 Shift 修饰键位,实现大写字母或符号输入——这是 HID 键盘协议的标准做法:按键数组只放普通键,修饰键单独编码在 Report 第 0~1 字节。

USB HID 描述符注入

设备层以静态描述符 + 动态 Report Map 组合完成 USB 枚举:

void usb_hid_set_repport_map(const u8 *map, int size)
{
    hid_report_map_size = size;
    hid_report_map = map;
}

Source: apps/hid/examples/standard_keyboard/usb_hid_devices.c

应用层在 USB 初始化时把来自 standard_hid.h 的 Report 描述符(键盘/多媒体等)注入设备层;主机 GET_DESCRIPTOR 请求 Report Descriptor 时,get_hid_report_desc() 直接返回该指针。更换产品(如改造成鼠标)时,只需换注入的 Report Map,无需改动枚举逻辑。

配置选项

下表汇总 HID 示例工程中直接可见的配置项(编译宏与运行时开关):

配置项类型默认值说明
CONFIG_APP_STANDARD_KEYBOARD编译宏由 Makefile 决定使能标准键盘示例主体代码
TCFG_PC_ENABLE编译宏0/1使能 PC(2.4G/有线)模式
TCFG_USB_SLAVE_USER_HID编译宏0/1使能 USB HID 从机设备类
CONFIG_HID_CASE_ENABLE编译宏板级决定使能 HID 充电仓(case)功能
CONFIG_TRANSFER_ENABLE编译宏板级决定使能数据传输功能
SUPPORT_RECONN_ADV_OR_DIRECT宏00=ADV 广播回连;1=DIRECT 直连回连
SUPPORT_KEYBOARD_NO_CONFLICT宏0无冲按键支持开关
SUPPORT_USER_PASSKEY宏0用户自定义配对 Passkey
CAP_LED_ON_VALUE宏1CapsLock LED 点亮电平
CFG_RF_24G_CODE_ID宏00 走 BLE;非 0 为 2.4G 配对码(32bit)
CFG_RF_24G_CODE_CHANNEL宏0x42.4G 模式使用的射频通道
FN_ROW / FN_COL宏6 / 0FN 组合键在矩阵中的位置

来源:apps/hid/examples/standard_keyboard/app_standard_keyboard.c、apps/hid/examples/standard_keyboard/app_standard_keyboard.c、apps/hid/board/bd19/Makefile

失败模式、边界情况与并发

基于源码可直接验证的失败处理与边界逻辑:

  • Report Map 未注入:get_hid_report_desc_len 中 ASSERT(hid_report_map_size != NULL, "report map err") —— 若应用层忘记调用 usb_hid_set_repport_map 就枚举 USB,会直接断言失败,提示开发者配置缺失(见 usb_hid_devices.c);
  • 发送失败:edr_hid_data_send 返回 HID_USER_ERR_SEND_FAIL,调用方需检查链路状态(edr_hid_is_connected)与发送缓冲(edr_hid_tx_buff_is_ok)后再重发,避免无效发包与缓冲溢出;
  • 未连接时发包:三通道统一在发送前查询连接状态,键盘开机未配对时不构造 Report,配合 edr_operation_t 状态机进入寻呼扫描/回连流程;
  • 并发/多链路:键盘可能同时维护 EDR 与 BLE 两条链路(多设备配对 cur_bt_idx),volatile u8 cur_bt_idx、volatile u8 paired_flag 的 volatile 声明表明这些状态量会被中断上下文或协议栈任务异步修改,应用层读取时需注意时序;
  • 自动关机边界:g_auto_shutdown_timer 实现无操作自动关机,与 stdkb_set_soft_poweroff 配合,防止 HID 设备长期空转耗电。

性能与运维考虑

  • 低功耗核心手段——sniff:HID 链路建立后进入 sniff 模式,示例声明了 lmp_sniff_t_slot_attemp_reset(slot, attemp) 与 sniff_support_reset_anchor_point(sniff 状态下是否支持 reset 到最近一次通信点)。该能力对 HID 尤其重要:键盘长时间无按键时深度休眠,按键瞬间需能快速唤醒并维持低延迟上报;
  • USB 中断端点轮询:端点描述符 bInterval = 0x01(1ms 轮询),MAXP_SIZE_HIDIN/HIDOUT 决定单包最大字节数,键盘 8 字节、鼠标 4 字节,均远小于全速 64 字节上限,保证低延迟;
  • DMA 缓冲:hid_register 通过 usb_alloc_ep_dmabuffer 为 IN/OUT 端点分配 DMA 缓冲,收发不占 CPU 拷贝路径,适合持续滚动的鼠标位移数据;
  • 多平台复用:同一份示例代码由 bd19/bd29/br23/br25/br30/br34 六套 Makefile 编译,换芯片只换 board 目录,降低维护成本。

扩展点

在源码中可识别的扩展/定制入口:

  1. 新增 Report Map:通过 user_hid_set_ReportMap(EDR)与 usb_hid_set_repport_map(USB)注入自定义 Report 描述符,即可把同一传输框架复用到任意 HID 设备类型;
  2. Output Report 回调:user_hid_init 的 user_hid_output_handler、usb_hid_set_output_callback、le_hogp_set_output_callback 三条回调分别接收 EDR/USB/BLE 主机下发的数据(如 LED、特性配置),是双向交互的扩展点;
  3. 键值映射表:修改 matrix_key_table 与 fn_remap_key/fn_remap_event 即可定制键盘布局与 FN 功能,无需改动协议代码;手柄示例的 key_mapping.c 是同一思路的独立实现;
  4. 系统键值切换:KEYBOARD_SYSTEM_IOS/WIN/ARD 自定义事件允许用户在 iOS/Windows/Android 键位间热切换,新系统适配只需在事件处理中追加映射分支;
  5. 2.4G 模式:CFG_RF_24G_CODE_ID 非 0 时启用 2.4G 私有射频通道(配对码 32bit),为双模(BLE+2.4G)产品提供扩展路径。

相关链接

  • edr_hid_user.h(EDR HID 用户接口)
  • edr_hid_user.c(EDR HID 用户模块实现)
  • app_standard_keyboard.c(标准键盘示例)
  • usb_hid_devices.c(USB HID 设备层)
  • bd19/Makefile(示例编译组织)
  • standard_hid.h(标准 HID Report 定义)
  • usb_hid_keys.h(USB HID 键值宏)
  • 相关页面:BLE 协议栈 / HOGP 主题页、RCSP 协议页、USB 设备栈页、音频示例页(语音遥控器的音频编解码链路)。
Prev
HID 人机交互应用框架
Next
Bluetooth Mesh 应用框架