键盘与按键设备示例
本文档介绍 fw-AW33N_BLE_SDK 中「键盘与按键设备示例」(apps/demo/hid)的完整实现:从应用入口的分支选择、USB HID 键盘设备描述符与报表描述符,到 BLE HID 服务的标准 UUID 定义,以及配套的板级配置。它面向需要基于 AW33N 平台开发 BLE/USB 键盘、按键遥控器、Keyfob 等 HID 设备的工程师,是一份源码级参考。
Purpose and Scope
本页面覆盖与 HID 键盘/按键类设备 相关的能力:
apps/demo/hid示例工程的入口与启动流程(app_main.c)- USB HID 键盘设备实现(
hid_keyboard.c)中的设备/接口/端点描述符与 HID Report Descriptor - BLE HID 服务(HID over GATT)的标准 UUID 定义(
standard_hid.h) - 示例工程板级目录(
apps/demo/hid/board/bd57)中键盘、鼠标、遥控器多个变体配置
以下内容属于兄弟页面,不在本页展开:鼠标类设备的完整实现(hid_mouse.c 与 board_aw33n_mouse*.c 的详细行为)、USB 音频/媒体类设备(hid_media.c)、RCSP 遥控器协议(rcsp_hid)、BLE 协议栈底层与 GATT 服务注册机制。如需了解 BLE 协议栈接入,请参见协议栈相关页面。
Overview
在 AW33N 平台上,HID 设备有两条输出路径:
- USB HID:设备通过 USB 枚举为标准 HID 键盘,Host(PC/手机)通过中断端点轮询读取按键报表。
- BLE HID(HID over GATT):设备作为 BLE Peripheral 广播 HID Service(UUID
0x1812),Host 通过 GATT 的 Report Characteristic 收发按键数据。
示例工程 apps/demo/hid 把这两条路径所需的描述符、报表定义与启动逻辑集中在一起,并通过编译期宏(CONFIG_APP_*)在同一个 SDK 中切换出键盘(hid_key)、Keyfob、按键翻页器(keypage)、遥控器(hid_rc)、鼠标(mouse_dual / mouse_low_latency)等不同产品形态。这样设计的好处是:应用框架、电源管理、消息分发、USB 枚举等基础设施完全复用,只有"应用分支"与"板级配置"不同,厂商只需修改板级头文件即可快速产出新产品。
核心设计要点:
- 描述符驱动一切:USB HID 的键盘功能完全由
sHIDDescriptor(设备描述)与sHIDReportDesc(报表描述)决定,代码本身不参与"哪些键被按下"的语义判断,只负责搬运报表字节。 - 报表 ID 区分通道:键盘报表使用 Report ID
0x3,Consumer Control(多媒体键)报表使用 Report ID0x05,同一根中断端点上用 Report ID 区分两种输入。 - BLE 与 USB 共享同一套 HID 语义:BLE 侧只定义 GATT 特征 UUID(Report、Report Map、Protocol Mode 等),报表内容与 USB 侧一致,因此同一套按键扫描代码可以同时服务两种传输。
- 中断轮询节奏:USB 端点配置为 Interrupt 传输、
bInterval = 0x01(约 10ms 轮询一次),兼顾响应速度与总线占用。
Architecture
下图展示了键盘示例从应用入口到 USB/BLE 两条 HID 输出路径的整体架构,节点名称均取自仓库中的实际文件/模块:
flowchart TD
subgraph sg_App["应用层 apps/demo/hid"]
AppMain["app_main.c<br/>app_main() / main_app_get_name()"]
Board["board/bd57<br/>board_aw33n_demo.c + board_config.h"]
end
subgraph sg_Usb["USB 设备层"]
HidKeyboard["usb/device/hid_keyboard.c<br/>sHIDDescriptor + sHIDReportDesc"]
UsbStack["usb_stack.h / usb_config.h"]
end
subgraph sg_Ble["BLE HID 层"]
StdHid["include/standard_hid.h<br/>HID Service 0x1812"]
BleStack["BLE GATT Stack"]
end
subgraph sg_Host["Host 端"]
PcHost["PC / 手机 Host"]
end
AppMain -->|"CONFIG_APP_KEYBOARD -> hid_key"| Board
Board -->|"USB_DEVICE_CLASS_CONFIG & HID_CLASS"| HidKeyboard
HidKeyboard --> UsbStack
UsbStack -->|"Interrupt EP IN/OUT<br/>10ms poll"| PcHost
Board -->|"HID over GATT"| StdHid
StdHid --> BleStack
BleStack -->|"Report Characteristic"| PcHost
架构解读:
app_main.c是唯一入口:app_main()完成内存段、中断优先级、开机按键、低压检测等平台初始化后,调用main_application_operation_state(NULL, APP_STA_START)进入应用状态机;随后main_app_get_name()依据CONFIG_APP_*宏选出实际应用分支(键盘 →hid_key/ACTION_HID_MAIN)。- 板级目录 决定"这个产品长什么样":
board_aw33n_demo.c是键盘/按键演示板,board_aw33n_mouse*.c是鼠标变体,board_aw33n_rc.c是遥控器变体,它们通过Makefile/.cbp工程文件单独编译。 - USB 路径:
hid_keyboard.c在USB_DEVICE_CLASS_CONFIG & HID_CLASS条件编译下提供完整描述符,#pragma段指令把 HID 相关数据放入.usb_slave.hid.*专用段,便于链接器布局与低功耗管理。 - BLE 路径:
standard_hid.h只做一件事——定义 HID over GATT 的标准 16-bit UUID 常量,避免魔法数散落各处;真正的服务注册与报表收发由 BLE 协议栈(本页不展开)完成。
应用分支与启动流程
入口初始化:app_main()
app_main() 是整个示例的起点,位于 apps/demo/hid/app_main.c:
void app_main()
{
#if UPDATE_V2_EN
/* 初始化检查升级状态, 测试盒升级后要关机, APP-OTA升级后要开机*/
update_success_boot_check();
#endif
log_info(">>>>>>>>>>>>>>>>>>app_main...\n");
log_info("nk_malloc: %08x,%04x, nv_malloc: %08x,%04x", NK_RAM_MALLOC_START_ADDR, NK_RAM_MALLOC_SIZE,
NV_RAM_MALLOC_START_ADDR, NV_RAM_MALLOC_SIZE);
#if TCFG_POWER_ON_NEED_KEY
check_power_on_key();
#endif
#if TCFG_SYS_LVD_EN
app_power_vbat_check();
#endif
main_application_operation_state(NULL, APP_STA_START);
}
Source: app_main.c
设计意图:
- 先检查升级状态(
UPDATE_V2_EN),保证 OTA 后能正确决定"开机还是关机"; - 打印内存布局(NK/NV RAM malloc 地址与大小),这是调试蓝牙内存池的常用手段;
TCFG_POWER_ON_NEED_KEY开启时,check_power_on_key()会循环等待开机键:长按超过 70 个 10ms 周期才真正开机,否则进入软关机(power_set_soft_poweroff()),防止误触开机;TCFG_SYS_LVD_EN开启时做低压检测,电量不足时阻止启动;- 最后把控制权交给
main_application_operation_state(),进入应用状态机。
分支选择:main_app_get_name()
键盘/按键设备的"形态"由编译宏决定,app_main.c 第 166-200 行:
static void main_app_get_name(struct intent *it)
{
init_intent(it);
// 选择应用分支
#if(CONFIG_APP_KEYBOARD)
it->name = "hid_key";
it->action = ACTION_HID_MAIN;
#elif(CONFIG_APP_KEYFOB)
it->name = "keyfob";
it->action = ACTION_KEYFOB;
#elif(CONFIG_APP_KEYPAGE)
it->name = "keypage";
it->action = ACTION_KEYPAGE;
#elif(CONFIG_APP_REMOTE_CONTROL)
it->name = "hid_rc";
it->action = ACTION_REMOTE_CONTROL;
#elif(CONFIG_APP_MOUSE_DUAL)
it->name = "mouse_dual";
it->action = ACTION_MOUSE_MAIN;
#elif(CONFIG_APP_MOUSE_LOW_LATENCY)
it->name = "mouse_low_latency";
it->action = ACTION_MOUSE_MAIN;
#elif(CONFIG_APP_IDLE)
it->name = "idle";
it->action = ACTION_IDLE_MAIN;
#else
ASSERT(0, "no app!!!");
#endif
}
Source: app_main.c
设计意图: 这是典型的"配置驱动的应用选择器"。struct intent 是 SDK 的启动意图(intent)机制:name 决定加载哪个应用模块,action 决定初始动作。键盘示例对应的 CONFIG_APP_KEYBOARD 会启动名为 hid_key 的应用并执行 ACTION_HID_MAIN。若所有宏都未定义,ASSERT(0, "no app!!!") 会直接触发断言,避免在未配置形态时静默运行——这是嵌入式 SDK 常见的"fail fast"策略。
USB HID 键盘设备实现
段布局与条件编译
hid_keyboard.c 位于 apps/app/bsp/common/usb/device/hid_keyboard.c,文件开头的 #pragma 把 HID 相关的 BSS、数据、常量与代码分别放入 .usb_slave.hid.* 段,日志 Tag 为 [HID_KEYBOARD],并整体被 #if USB_DEVICE_CLASS_CONFIG & HID_CLASS 条件编译包裹:
#pragma bss_seg(".usb_slave.hid.data.bss")
#pragma data_seg(".usb_slave.hid.data")
#pragma const_seg(".usb_slave.hid.text.const")
#pragma code_seg(".usb_slave.hid.text")
#pragma str_literal_override(".usb_slave.hid.text.const")
#include "usb/device/usb_stack.h"
#include "usb/device/hid.h"
#include "usb_config.h"
#include "usb.h"
#include "app_config.h"
#define LOG_TAG_CONST USB
#define LOG_TAG "[HID_KEYBOARD]"
...
#if USB_DEVICE_CLASS_CONFIG & HID_CLASS
Source: hid_keyboard.c
设计意图: 段重定向(#pragma bss_seg/data_seg/const_seg/code_seg)是杰理 SDK 管理 USB RAM 的惯用手段——把 HID 设备代码放进专用段后,链接脚本可以在低功耗(soft power off)时整体回收或保持这些段,从而精确控制功耗与内存占用。条件编译 HID_CLASS 保证未启用 HID 类的工程不会引入任何键盘代码,实现"配置即裁剪"。
设备描述符:sHIDDescriptor
设备描述符(hid_keyboard.c 第 25-70 行)定义了一个标准 HID 键盘接口:
- 接口描述符:
bInterfaceClass = USB_CLASS_HID,bInterfaceSubClass = 0x01(Boot Interface),bInterfaceProtocol = 0x01(Keyboard)。Boot 子类意味着 Host 在进入 OS 前(BIOS/引导阶段)也能识别该键盘,这是键盘类设备的标配。 - HID 描述符:
bcdHID = 0x0110(HID 1.10 规范),后跟一个 Report Descriptor(类型0x22),长度占位符由驱动在枚举时回填。 - 中断 IN 端点:
USB_DIR_IN | HID_KEYBOARD_EP_IN,USB_ENDPOINT_XFER_INT,bInterval = 0x01(10ms 轮询)。 - 可选中断 OUT 端点:当
HID_KEYBOARD_EP_OUT为真时增加第二个端点,用于接收 Host 下发的输出报表(如键盘 LED 状态:Num Lock / Caps Lock)。
HID Report Descriptor:sHIDReportDesc
报表描述符(hid_keyboard.c 第 72-119 行)定义了两个 Application Collection:
- 标准键盘报表(Report ID
0x3),8 字节按键报文:1 字节修饰键(Ctrl/Shift/Alt/GUI,对应 Usage 0xE0~0xE7)+ 1 字节保留位 + 6 字节普通按键数组(Usage 0x00~0xFF 的 Keypad 页面)。这是 USB HID Boot Keyboard 的标准 8 字节格式,Host 端无需额外驱动即可解析。 - Consumer Control 报表(Report ID
0x05):16 位 Consumer 页面 Usage(0x00~0x28C),用于多媒体按键(音量、播放/暂停等),与键盘报表共用同一中断端点,用 Report ID 区分。
#define HID_KEYBOARD_REPORT_ID 0x3
static const u8 sHIDReportDesc[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, HID_KEYBOARD_REPORT_ID,// Report ID (1)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,...)
...
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
...
0x81, 0x00, // Input (Data,Array,Abs,...)
0xC0, // End Collection
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x05, // Report ID (3)
0x75, 0x10, // Report Size (16)
...
0x81, 0x00, // Input (Data,Array,Abs,...)
0xC0, // End Collection
};
Source: hid_keyboard.c
设计意图: 描述符以字节数组手写而非结构体,是 USB 描述符的常见做法——描述符在编译期完全确定、零运行时开销,且能精确控制每个字节。Modifier 键用 8 个 1-bit 字段(Var)而普通键用 6 字节数组(Array),遵循 HID 规范对键盘的特殊约定:修饰键可多键同时按住(位掩码),普通键最多同时按下 6 个(溢出丢弃)。
BLE HID 服务定义
BLE 侧的键盘通过 HID over GATT(标准 HID Service 0x1812)工作。standard_hid.h(apps/demo/hid/include/standard_hid.h)集中定义了该服务涉及的全部标准 16-bit UUID:
#define HID_UUID_16 0x1812
#define HID_INFORMATION_UUID_16 0x2A4A
#define HID_REPORT_MAP_UUID_16 0x2A4B
#define HID_CONTROL_POINT_UUID_16 0x2A4C
#define HID_REPORT_UUID_16 0x2A4D
#define PROTOCOL_MODE_UUID_16 0x2A4E
#define HID_REPORT_REFERENCE_UUID_16 0x2908
Source: standard_hid.h
各 UUID 的角色:
| UUID | 名称 | 用途 |
|---|---|---|
0x1812 | HID Service | HID over GATT 服务声明 |
0x2A4A | HID Information | 设备 HID 版本、国家码、标志位 |
0x2A4B | Report Map | 与 USB 侧 sHIDReportDesc 内容一致的报表描述符,Host 据此解析按键数据 |
0x2A4C | Control Point | 可选;用于 Suspend/Exit Suspend 控制 |
0x2A4D | Report | 实际按键数据特征(可带 Report Reference 描述符 0x2908 指明 Report ID 与类型) |
0x2A4E | Protocol Mode | Boot/Report 两种协议模式切换 |
0x2908 | Report Reference | 描述符,把 Report 特征绑定到具体 Report ID 与输入/输出/特征类型 |
设计意图: BLE HID 之所以复用 USB 的报表概念,是因为 Host(Windows/macOS/Android/iOS)对 HID over GATT 的解析流程与 USB HID 几乎一致:先读 Report Map 知道报文布局,再订阅 Report 特征收按键。因此 SDK 在 standard_hid.h 中只定义 UUID,实际报表内容仍引用 USB 侧的同一套描述符语义,同一份按键扫描/编码逻辑可同时驱动 USB 与 BLE 两条链路。
Core Flow:从按键到 Host 的完整链路
下图展示一次"按键按下 → Host 收到键值"的完整数据流,覆盖 BLE HID 与 USB HID 两条路径:
sequenceDiagram
participant User as 用户
participant Key as 按键扫描 (板级 board_aw33n_demo.c)
participant App as hid_key 应用 (ACTION_HID_MAIN)
participant BLE as BLE GATT (HID Service 0x1812)
participant USB as USB HID 设备 (hid_keyboard.c)
participant Host as Host (PC/手机)
User->>Key: 按下按键
Key->>App: 按键消息 (msg 队列)
App->>App: 映射为 HID 键码 (Usage ID)
par BLE 路径
App->>BLE: 写入 Report 特征 (0x2A4D)
BLE->>Host: GATT Notification/Indication
and USB 路径
App->>USB: 组 8 字节按键报表 (Report ID 0x3)
USB->>Host: Interrupt IN 端点 (10ms 轮询)
end
Host->>Host: 解析报表, 产生键盘事件
流程说明:
- 采集:板级按键扫描(
board_aw33n_demo.c中的按键矩阵/ADC 扫描)检测到按下事件,通过 SDK 消息队列投递给应用。 - 语义转换:
hid_key应用把"物理按键"映射为 HID Usage ID(例如A键 →0x04),并维护修饰键状态(Shift/Ctrl 等)。 - 并行分发:应用按需向两条路径提交:
- BLE 路径写 Report 特征(
0x2A4D),Host 通过 Notification 实时收到; - USB 路径把键码填入 8 字节报表(Report ID
0x3,修饰键 1 字节 + 保留 1 字节 + 键码数组 6 字节),等待 Host 在 10ms 中断轮询时取走。
- BLE 路径写 Report 特征(
- Host 解析:Host 依据 Report Map 把字节还原为键值并产生输入事件。松键时应用必须发送"全零报表"(空键码),否则按键会被视为持续按住——这是 HID 键盘实现最常见的坑。
配置选项
键盘/按键示例的行为完全由编译期宏控制,分布在板级头文件与 app_config.h 中。以下是源码中出现的核心配置:
| 配置宏 | 类型 | 默认/取值 | 说明 |
|---|---|---|---|
CONFIG_APP_KEYBOARD | 编译宏 | 0/1 | 选择键盘应用分支(hid_key + ACTION_HID_MAIN) |
CONFIG_APP_KEYFOB | 编译宏 | 0/1 | 选择 Keyfob(遥控钥匙)应用分支 |
CONFIG_APP_KEYPAGE | 编译宏 | 0/1 | 选择按键翻页器应用分支 |
CONFIG_APP_REMOTE_CONTROL | 编译宏 | 0/1 | 选择遥控器应用分支(hid_rc) |
CONFIG_APP_MOUSE_DUAL / CONFIG_APP_MOUSE_LOW_LATENCY | 编译宏 | 0/1 | 鼠标双模/低延迟变体(兄弟页面) |
CONFIG_APP_IDLE | 编译宏 | 0/1 | 空闲应用(调试用) |
USB_DEVICE_CLASS_CONFIG | 位掩码 | 含 HID_CLASS | 启用 USB HID 类设备 |
HID_KEYBOARD_EP_IN | 宏 | 端点号 | 键盘中断 IN 端点地址 |
HID_KEYBOARD_EP_IN_MAX_SIZE | 宏 | 报文大小 | IN 端点最大包长(如 8/16 字节) |
HID_KEYBOARD_EP_OUT | 宏 | 0/1 | 是否启用中断 OUT 端点(接收 LED 等输出报表) |
HID_KEYBOARD_EP_OUT_MAX_SIZE | 宏 | 报文大小 | OUT 端点最大包长 |
TCFG_POWER_ON_NEED_KEY | 宏 | 0/1 | 开机需按键长按确认,防误触(约 700ms) |
TCFG_SYS_LVD_EN | 宏 | 0/1 | 启动前低压检测 |
TCFG_AUTO_SHUT_DOWN_TIME | 宏 | 秒 | 自动关机时间(示例中 app_var.auto_off_time 默认置 0 关闭) |
上表中的端点宏(
HID_KEYBOARD_EP_*)在 hid_keyboard.c 中被引用;应用分支宏在 app_main.c 中被引用;电源相关宏在 app_main.c 中被引用。
API Reference(关键入口)
void app_main(void)
示例工程入口。完成升级状态检查、内存布局打印、开机键确认、低压检测后,调用 main_application_operation_state(NULL, APP_STA_START) 启动应用状态机。
- 参数:无
- 返回:无
- 说明:
APP_STA_START是状态机首态;实际加载哪个应用由main_app_get_name()决定。
static void main_app_get_name(struct intent *it)
根据 CONFIG_APP_* 编译宏选择应用分支,填充启动意图。
- 参数:
it(struct intent *)——启动意图,name字段为应用模块名,action字段为初始动作。 - 返回:无
- 注意:所有分支宏均未定义时执行
ASSERT(0, "no app!!!"),属于 fail-fast 保护。
void check_power_on_key(void)
开机键确认逻辑(TCFG_POWER_ON_NEED_KEY 开启时生效)。循环检测电源状态:长按超过 70 个 10ms 周期(约 700ms)视为有效开机;松开则进入软关机 power_set_soft_poweroff()。
- 参数:无
- 返回:无
- 依赖:
get_power_on_status()、set_key_poweron_flag()、power_set_soft_poweroff()等电源管理接口。
void app_var_init(void)
初始化全局应用变量 app_var:关闭自动关机(auto_off_time = 0)、设置提示音频率(warning_tone_v / poweroff_tone_v)。
描述符数据(编译期常量)
sHIDDescriptor[]:USB 键盘接口/端点描述符,见 hid_keyboard.c 第 25-70 行。sHIDReportDesc[]:键盘 + Consumer Control 报表描述符,见 hid_keyboard.c 第 72-119 行。
失败模式与边界情况
以下行为均能从源码结构推导并应在移植时重点验证:
- 按键粘滞(Stuck Key):HID 键盘语义要求松键时发送空键码报表。若应用在
ACTION_HID_MAIN循环中漏发"全零报表",Host 会认为按键持续按住。实现时必须在按键释放事件里清零 8 字节报表(含修饰键位)。 - 6 键无冲突(6KRO)限制:报表中普通键数组只有 6 个槽位,第 7 个键同时按下会被 HID 规范定义为"溢出(Rollover)"并被 Host 忽略。需要更多同时按键时,必须修改
sHIDReportDesc的Report Count并同步放大HID_KEYBOARD_EP_IN_MAX_SIZE。 - 描述符长度占位符:
sHIDDescriptor中 Report 描述符长度字段为 0(第 51-52 行),依赖 USB 驱动在枚举阶段回填。若驱动未实现回填,Host 会读到 0 长度的 Report Map 导致设备无法识别——移植新平台时务必确认该回填逻辑。 - 未配置应用分支:
main_app_get_name()在所有CONFIG_APP_*宏都为 0 时直接ASSERT(0, "no app!!!")。新增产品形态时,必须在板级配置中恰好定义一个分支宏。 - 开机误触:
TCFG_POWER_ON_NEED_KEY未开启时,上电即进系统,可能因运输/手持误触导致设备异常开机耗电;量产产品建议开启并按需调整 70 个 10ms 周期的判定时长。 - USB 枚举失败:
#pragma段指令把 HID 代码放入.usb_slave.hid.*段,若链接脚本未包含这些段(或USB_DEVICE_CLASS_CONFIG未含HID_CLASS),USB 侧将静默无 HID 功能,且无编译期报错——排查"插上没反应"时应先检查这两个前提。
并发与功耗考虑
- 中断轮询 vs 通知:USB 路径是 Host 主动轮询(10ms),BLE 路径是设备主动 Notification,两条路径完全独立,不存在共享缓冲区竞争的前提是应用层为每条链路维护独立报表缓冲区;否则多任务同时写同一报表数组会产生数据撕裂。
- 低功耗段管理:HID 代码/数据被显式放入
.usb_slave.hid.*段,配合 SDK 的电源管理可以在无 USB 会话时整体断电。eSystemConfirmStopStatus()返回 1 表示系统进入"无定时唤醒的深度睡眠"(Endless Sleep),键盘示例默认启用,意味着空闲时应无周期性唤醒,此时蓝牙保持连接的功耗完全取决于协议栈。 - 启动路径中的忙等:
check_power_on_key()在开机确认期间os_time_dly(1)循环并喂狗(clr_wdt()),是刻意为之——既保证按键判定实时性,又避免看门狗复位。修改判定时长时需同步确认喂狗节奏。
扩展点
- 新增产品形态:在
main_app_get_name()的#if/#elif链中增加一个CONFIG_APP_XXX分支,并在板级目录新增board_aw33n_xxx.c与配套*_cfg.h/*_global_build_cfg.h,即可复用全部基础设施。仓库中的鼠标(board_aw33n_mouse*.c)与遥控器(board_aw33n_rc.c)变体就是现成范例。 - 自定义报表:修改
sHIDReportDesc的 Collection/Usage/Report ID 即可扩展功能(如加入系统控制页、媒体页的更多按键)。注意:Report ID 是 BLE 与 USB 两侧共享的协议契约,standard_hid.h中HID_REPORT_REFERENCE_UUID_16 (0x2908)描述符必须与之一致,否则 Host 解析 BLE 报表会错位。 - 输出报表(LED):开启
HID_KEYBOARD_EP_OUT后可接收 Host 下发的 LED 状态(Num Lock/Caps Lock/Scroll Lock,见sHIDReportDesc中 Usage Page LEDs 的 Output 字段),可用于驱动键盘指示灯。 - 协议模式:通过
PROTOCOL_MODE_UUID_16 (0x2A4E)可在 Boot 协议与 Report 协议间切换,适配不同的 Host 引导环境。
相关链接
- apps/demo/hid/app_main.c — 示例入口与分支选择
- apps/demo/hid/include/standard_hid.h — BLE HID 服务 UUID 定义
- apps/app/bsp/common/usb/device/hid_keyboard.c — USB HID 键盘描述符与报表描述符
- apps/demo/hid/board/bd57/board_config.h — 板级配置汇总
- apps/demo/hid/board/bd57/AW33N_hid.cbp — Code::Blocks 工程文件(编译入口)
- apps/demo/hid/board/bd57/board_aw33n_demo.c — 键盘演示板初始化
- apps/demo/hid/board/bd57/Makefile — 命令行构建脚本
- 兄弟页面:鼠标设备示例(
hid_mouse.c、board_aw33n_mouse*.c)、遥控器示例(board_aw33n_rc.c、rcsp_hid)、USB 媒体设备(hid_media.c)