返回首页

Zephyr USB HID API 学习笔记

概述

Zephyr 提供了完整的 USB HID 设备支持,包括 HID 类驱动和 API,方便开发键盘、鼠标等 HID 设备。

当前 Zephyr 样例中,samples/subsys/usb/hid-keyboardhid-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"

API 头文件

#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

HID 设备初始化

usb_hid_register_device()hid_int_ep_write() 对应 legacy HID API;如果直接参考当前样例,请优先看 zephyr/samples/subsys/usb/hid-keyboardhid-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,
};

初始化 HID 设备

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);
}

USB 设备管理

设备状态回调

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;
}

高级用法

多个 HID 报告(Report ID)

/* 带 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 设备

/* 自定义 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-keyboardusbd_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));
    }
}

常见问题

1. 设备未被识别

2. 数据未发送

3. 主机不响应

配置参考

# 新栈样例常见配置
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*