Zephyr 提供了完整的 USB HID 设备支持,包括 HID 类驱动和 API,方便开发键盘、鼠标等 HID 设备。
当前 Zephyr 样例中,samples/subsys/usb/hid-keyboard 和 hid-mouse 默认走新的 USB device stack(CONFIG_USB_DEVICE_STACK_NEXT=y,运行时调用 usbd_enable())。本文中的 usb_hid_register_device()、hid_int_ep_write()、usb_enable() 仍然可以用来理解 legacy HID API,但它们已经属于兼容层/旧栈接口。
# 新 USB 设备栈(当前样例主线)
CONFIG_USB_DEVICE_STACK_NEXT=y
# legacy 栈 HID 示例才会使用以下配置
# CONFIG_USB_DEVICE_STACK=y
# CONFIG_USB_DEVICE_HID=y
# HID 报告轮询间隔(ms)
CONFIG_USB_HID_POLL_INTERVAL_MS=10
# USB 设备厂商 ID 和产品 ID
CONFIG_USB_DEVICE_VID=0x2FE3
CONFIG_USB_DEVICE_PID=0x0001
# 字符串描述符
CONFIG_USB_DEVICE_MANUFACTURER="MyCompany"
CONFIG_USB_DEVICE_PRODUCT="MyHIDDevice"
#include <zephyr/usb/class/usb_hid.h> // legacy HID API
#include <zephyr/usb/usb_device.h> // legacy usb_enable()
#include <zephyr/usb/usbd.h> // 新 USB device stack
usb_hid_register_device() 和 hid_int_ep_write() 对应 legacy HID API;如果直接参考当前样例,请优先看 zephyr/samples/subsys/usb/hid-keyboard 与 hid-mouse。
/* 键盘报告描述符 */
static const uint8_t hid_keyboard_report_desc[] = {
HID_GI_USAGE_PAGE, HID_USAGE_GEN_DESKTOP,
HID_LI_USAGE, HID_USAGE_GEN_DESKTOP_KEYBOARD,
HID_MI_COLLECTION, HID_COLLECTION_APPLICATION,
/* 修饰键(Ctrl, Shift, Alt, GUI) */
HID_GI_USAGE_PAGE, HID_USAGE_GEN_KEYBOARD,
HID_LI_USAGE_MIN(1), 0xE0,
HID_LI_USAGE_MAX(1), 0xE7,
HID_GI_LOGICAL_MIN(1), 0x00,
HID_GI_LOGICAL_MAX(1), 0x01,
HID_GI_REPORT_SIZE, 0x01,
HID_GI_REPORT_COUNT, 0x08,
HID_MI_INPUT, 0x02,
/* 填充字节 */
HID_GI_REPORT_SIZE, 0x08,
HID_GI_REPORT_COUNT, 0x01,
HID_MI_INPUT, 0x03,
/* 按键码(最多6个) */
HID_GI_LOGICAL_MAX(1), 0x65,
HID_LI_USAGE_MAX(1), 0x65,
HID_GI_REPORT_SIZE, 0x08,
HID_GI_REPORT_COUNT, 0x06,
HID_MI_INPUT, 0x00,
HID_MI_COLLECTION_END,
};
/* 键盘报告结构 */
struct hid_keyboard_report {
uint8_t modifiers;
uint8_t reserved;
uint8_t keycodes[6];
} __packed;
/* 修饰键定义 */
enum hid_keyboard_modifiers {
HID_MOD_LCTRL = BIT(0),
HID_MOD_LSHIFT = BIT(1),
HID_MOD_LALT = BIT(2),
HID_MOD_LGUI = BIT(3),
HID_MOD_RCTRL = BIT(4),
HID_MOD_RSHIFT = BIT(5),
HID_MOD_RALT = BIT(6),
HID_MOD_RGUI = BIT(7),
};
/* 常用按键码 */
enum hid_keycodes {
HID_KEY_A = 0x04,
HID_KEY_B = 0x05,
HID_KEY_1 = 0x1E,
HID_KEY_2 = 0x1F,
HID_KEY_ENTER = 0x28,
HID_KEY_SPACE = 0x2C,
HID_KEY_UP = 0x52,
HID_KEY_DOWN = 0x51,
HID_KEY_LEFT = 0x50,
HID_KEY_RIGHT = 0x4F,
};
static struct hid_keyboard_report report;
static const struct device *hid_dev;
/* HID 回调函数 */
static void hid_int_in_ready_cb(const struct device *dev)
{
/* IN 端点准备好,可以发送下一个报告 */
ARG_UNUSED(dev);
}
/* 接收主机数据回调 */
static void hid_out_ready_cb(const struct device *dev)
{
uint8_t buf[64];
int len = hid_report_len(dev);
int ret = hid_int_ep_read(dev, buf, len, NULL);
if (ret > 0) {
/* 处理主机发送的数据(如 LED 状态) */
printk("Received %d bytes from host\n", ret);
}
}
/* HID 操作回调 */
static const struct hid_ops hid_ops = {
.int_in_ready = hid_int_in_ready_cb,
.out_ready = hid_out_ready_cb,
};
/* 初始化 */
int init_hid(void)
{
hid_dev = device_get_binding("HID_0");
if (!hid_dev) {
printk("HID device not found\n");
return -ENODEV;
}
/* 注册回调 */
usb_hid_register_device(hid_dev, hid_keyboard_report_desc,
sizeof(hid_keyboard_report_desc), &hid_ops);
/* 初始化 HID 设备 */
usb_hid_init(hid_dev);
return 0;
}
int send_key_press(uint8_t keycode, uint8_t modifiers)
{
/* 清空报告 */
memset(&report, 0, sizeof(report));
/* 设置修饰键 */
report.modifiers = modifiers;
/* 设置按键码 */
report.keycodes[0] = keycode;
/* 发送报告 */
return hid_int_ep_write(hid_dev, (uint8_t *)&report, sizeof(report), NULL);
}
int send_key_release(void)
{
/* 发送空报告(释放按键) */
memset(&report, 0, sizeof(report));
return hid_int_ep_write(hid_dev, (uint8_t *)&report, sizeof(report), NULL);
}
/* 发送按键事件 */
int send_key_event(uint8_t keycode, uint8_t modifiers, bool pressed)
{
if (pressed) {
return send_key_press(keycode, modifiers);
} else {
return send_key_release();
}
}
int send_hotkey(uint8_t keycode, uint8_t modifiers)
{
/* 按下组合键 */
send_key_press(keycode, modifiers);
/* 等待一小段时间 */
k_sleep(K_MSEC(10));
/* 释放按键 */
send_key_release();
return 0;
}
/* 示例:发送 Ctrl+C */
void send_ctrl_c(void)
{
send_hotkey(HID_KEY_C, HID_MOD_LCTRL);
}
static enum usb_dc_status_code usb_status;
static void usb_status_cb(enum usb_dc_status_code status, const uint8_t *param)
{
usb_status = status;
switch (status) {
case USB_DC_CONNECTED:
printk("USB connected\n");
break;
case USB_DC_CONFIGURED:
printk("USB configured\n");
break;
case USB_DC_DISCONNECTED:
printk("USB disconnected\n");
break;
case USB_DC_SUSPEND:
printk("USB suspended\n");
break;
case USB_DC_RESUME:
printk("USB resumed\n");
break;
case USB_DC_ERROR:
printk("USB error\n");
break;
default:
break;
}
}
/* 初始化时注册回调 */
int init_usb(void)
{
/* legacy 栈写法 */
int ret = usb_enable(usb_status_cb);
if (ret != 0) {
printk("USB enable failed: %d\n", ret);
return ret;
}
return 0;
}
/* 带 Report ID 的描述符 */
static const uint8_t multi_report_desc[] = {
HID_GI_REPORT_ID, 0x01, /* Report ID 1 */
/* 键盘报告描述 */
...
HID_GI_REPORT_ID, 0x02, /* Report ID 2 */
/* 鼠标报告描述 */
...
};
/* 发送带 ID 的报告 */
struct keyboard_report_with_id {
uint8_t report_id;
struct hid_keyboard_report report;
} __packed;
int send_keyboard_report(struct hid_keyboard_report *rpt)
{
struct keyboard_report_with_id msg = {
.report_id = 1,
.report = *rpt,
};
return hid_int_ep_write(hid_dev, (uint8_t *)&msg, sizeof(msg), NULL);
}
/* 自定义 HID 报告描述符 */
static const uint8_t custom_hid_report_desc[] = {
HID_GI_USAGE_PAGE, 0xFF, /* Vendor-defined */
HID_LI_USAGE, 0x01,
HID_MI_COLLECTION, HID_COLLECTION_APPLICATION,
HID_GI_LOGICAL_MIN(1), 0x00,
HID_GI_LOGICAL_MAX(2), 0xFF, 0x00, /* 255 */
HID_GI_REPORT_SIZE, 0x08,
HID_GI_REPORT_COUNT, 0x40, /* 64 bytes */
HID_MI_INPUT, 0x02, /* Data, Var, Abs */
HID_GI_REPORT_SIZE, 0x08,
HID_GI_REPORT_COUNT, 0x40,
HID_MI_OUTPUT, 0x02, /* Output report */
HID_MI_COLLECTION_END,
};
下面这个完整示例是 legacy HID 写法,便于理解 API 关系;如果要跟着当前 Zephyr 样例跑,优先参考 samples/subsys/usb/hid-keyboard 的 usbd_enable() 初始化流程。
#include <zephyr/kernel.h>
#include <zephyr/usb/class/usb_hid.h>
#include <zephyr/usb/usb_device.h>
static const struct device *hid_dev;
static struct hid_keyboard_report report;
static void hid_int_in_ready(const struct device *dev)
{
/* 可以发送下一个按键 */
}
static const struct hid_ops hid_ops = {
.int_in_ready = hid_int_in_ready,
};
void main(void)
{
/* 初始化 USB */
usb_enable(NULL);
/* 获取 HID 设备 */
hid_dev = device_get_binding("HID_0");
/* 注册 HID */
usb_hid_register_device(hid_dev, hid_keyboard_report_desc,
sizeof(hid_keyboard_report_desc), &hid_ops);
usb_hid_init(hid_dev);
/* 等待 USB 配置完成 */
while (usb_status != USB_DC_CONFIGURED) {
k_sleep(K_MSEC(100));
}
/* 发送按键 */
while (1) {
send_key_press(HID_KEY_A, 0);
k_sleep(K_MSEC(50));
send_key_release();
k_sleep(K_SECONDS(1));
}
}
# 新栈样例常见配置
CONFIG_USB_DEVICE_STACK_NEXT=y
# legacy HID 示例配置
# CONFIG_USB_DEVICE_STACK=y
# CONFIG_USB_DEVICE_HID=y
# 设备信息
CONFIG_USB_DEVICE_VID=0x2FE3
CONFIG_USB_DEVICE_PID=0x0100
CONFIG_USB_DEVICE_MANUFACTURER="MyCompany"
CONFIG_USB_DEVICE_PRODUCT="HID Keyboard"
# HID 配置
CONFIG_USB_HID_POLL_INTERVAL_MS=10
# 日志
CONFIG_USB_DEVICE_LOG_LEVEL_INF=y
*学习笔记创建时间: 2026-03-19*