Rust USB HID设备开发宏库usbd-hid-macros的使用,简化人机接口设备报告描述符生成
Rust USB HID设备开发宏库usbd-hid-macros的使用,简化人机接口设备报告描述符生成
安装
在项目目录中运行以下Cargo命令:
cargo add usbd-hid-macros
或者在Cargo.toml中添加以下行:
usbd-hid-macros = "0.8.2"
使用示例
以下是一个完整的使用usbd-hid-macros库创建HID设备的示例:
use usbd_hid_macros::gen_hid_descriptor;
use usb_device::class_prelude::*;
// 定义HID报告描述符
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = KEYBOARD) = {
(usage_page = KEYBOARD, usage_min = 0xE0, usage_max = 0xE7) = {
#[packed_bits 8] #[item_settings data,variable,absolute] modifier=input;
};
(usage_min = 0x00, usage_max = 0xFF) = {
#[item_settings constant,variable,absolute] reserved=input;
};
(usage_page = LEDS, usage_min = 0x01, usage_max = 0x05) = {
#[packed_bits 5] #[item_settings data,variable,absolute] leds=output;
};
(usage_page = KEYBOARD, usage_min = 0x00, usage_max = 0x65) = {
#[item_settings data,array,absolute] keycodes=input;
};
}
)]
#[allow(dead_code)]
pub struct KeyboardReport {
pub modifier: u8,
pub reserved: u8,
pub leds: u8,
pub keycodes: [u8; 6],
}
fn main() {
// 初始化USB设备
let usb_bus = UsbBusAllocator::new(/* USB总线实现 */);
// 创建HID设备
let hid = HidClass::new(
&usb_bus,
KeyboardReport::desc(),
60, // 报告间隔(ms)
);
// 创建USB设备
let usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("My Company")
.product("Keyboard")
.serial_number("12345")
.device_class(0)
.build();
// 设备使用循环
loop {
// 处理USB事件
usb_dev.poll(&mut [&mut hid]);
// 生成键盘报告
let report = KeyboardReport {
modifier: 0,
reserved: 0,
leds: 0,
keycodes: [0; 6],
};
// 发送报告
hid.push_input(&report).ok();
}
}
完整示例代码
以下是一个更完整的USB HID鼠标设备示例:
use usbd_hid_macros::gen_hid_descriptor;
use usb_device::class_prelude::*;
use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
use usbd_hid::descriptor::generator_prelude::*;
// 定义鼠标报告描述符
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = MOUSE) = {
(usage_page = BUTTON, usage_min = 0x01, usage_max = 0x08) = {
#[packed_bits 8] #[item_settings data,variable,absolute] buttons=input;
};
(usage_page = GENERIC_DESKTOP,) = {
(usage = X,) = {
#[item_settings data,variable,relative] x=input;
};
(usage = Y,) = {
#[item_settings data,variable,relative] y=input;
};
(usage = WHEEL,) = {
#[item_settings data,variable,relative] wheel=input;
};
};
}
)]
#[allow(dead_code)]
pub struct MouseReport {
pub buttons: u8,
pub x: i8,
pub y: i8,
pub wheel: i8,
}
fn main() {
// 初始化USB总线
let usb_bus = UsbBusAllocator::new(/* 实际的USB总线实现 */);
// 创建HID设备
let hid = HidClass::new(
&usb_bus,
MouseReport::desc(),
10, // 报告间隔(ms)
);
// 创建USB设备
let usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("Mouse Company")
.product("USB Mouse")
.serial_number("M123")
.device_class(0)
.build();
// 设备主循环
loop {
// 处理USB事件
usb_dev.poll(&mut [&mut hid]);
// 创建鼠标移动报告
let report = MouseReport {
buttons: 0, // 无按钮按下
x: 10, // X轴移动10个单位
y: 5, // Y轴移动5个单位
wheel: 0 // 滚轮不移动
};
// 发送报告
if hid.push_input(&report).is_err() {
// 处理发送失败情况
}
// 简单延时
delay_ms(10);
}
}
// 简单的延时函数
fn delay_ms(ms: u32) {
// 实际实现取决于目标平台
}
宏说明
gen_hid_descriptor
宏允许你通过Rust结构体定义HID报告描述符,它会自动生成符合HID规范的二进制描述符。主要特点:
- 使用类似HID描述符的语法定义报告格式
- 支持各种HID用途页(usage page)和用途(usage)
- 自动处理位打包和报告大小
- 生成的结构体可以直接用于USB HID通信
报告描述符结构
在宏中,你可以定义:
- 集合(collection)类型和应用场景
- 输入(input)和输出(output)项
- 用途页(usage page)和具体用途(usage)
- 数据格式(变量、数组、绝对值、相对值等)
- 位打包字段
注意事项
- 需要配合
usb-device
和usbd-hid
库使用 - 适用于嵌入式开发和no_std环境
- 生成的报告描述符需要符合HID规范
- 对于复杂设备,可能需要分多个报告描述符
这个库大大简化了HID设备开发中最复杂的报告描述符生成工作,让开发者可以专注于设备功能的实现。
1 回复
Rust USB HID设备开发宏库usbd-hid-macros使用指南
usbd-hid-macros
是一个简化Rust中USB HID(Human Interface Device)设备开发的宏库,特别专注于简化报告描述符(Report Descriptor)的生成过程。
主要功能
- 提供声明式宏来定义HID报告描述符
- 自动生成符合USB HID规范的描述符结构
- 简化常见HID设备类型的实现
- 与
usbd-hid
库无缝集成
使用方法
基本安装
首先在Cargo.toml中添加依赖:
[dependencies]
usbd-hid-macros = "0.3"
usbd-hid = "0.6"
定义HID设备
使用gen_hid_descriptor!
宏来定义你的HID设备描述符:
use usbd_hid_macros::gen_hid_descriptor;
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = KEYBOARD) = {
(usage_page = KEYBOARD, usage_min = 0xE0, usage_max = 0xE7) = {
#[packed_bits 8] #[item_settings data,variable,absolute] modifier=input;
};
(usage_min = 0x00, usage_max = 0x65) = {
#[item_settings data,array,absolute] keycodes=input;
};
}
)]
#[allow(dead_code)]
pub struct KeyboardReport {
pub modifier: u8, // 修饰键状态(如Ctrl, Alt等)
pub _reserved: u8, // 保留字段
pub keycodes: [u8; 6], // 当前按下的普通键
}
鼠标设备示例
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = MOUSE) = {
(usage_page = GENERIC_DESKTOP, usage = POINTER) = {
(collection = PHYSICAL, usage = POINTER) = {
(usage_page = BUTTON, usage_min = 0x01, usage_max = 0x03) = {
#[packed_bits 3] #[item_settings data,variable,absolute] buttons=input;
#[padding 5] padding=input;
};
(usage_page = GENERIC_DESKTOP,) = {
(usage = X,) = {
#[item_settings data,variable,relative] x=input;
};
(usage = Y,) = {
#[item_settings data,variable,relative] y=input;
};
};
};
};
}
)]
#[allow(dead_code)]
pub struct MouseReport {
pub buttons: u8, // 鼠标按钮状态(左键、右键等)
pub x: i8, // X轴相对移动量
pub y: i8, // Y轴相对移动量
}
游戏手柄示例
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = JOYSTICK) = {
(usage_page = GENERIC_DESKTOP,) = {
(usage = X,) = {
#[item_settings data,variable,absolute] x=input;
};
(usage = Y,) = {
#[item_settings data,variable,absolute] y=input;
};
(usage = Z,) = {
#[item_settings data,variable,absolute] z=input;
};
(usage = RX,) = {
#[item_settings data,variable,absolute] rx=input;
};
(usage = RY,) = {
#[item_settings data,variable,absolute] ry=input;
};
(usage = RZ,) = {
#[item_settings data,variable,absolute] rz=input;
};
};
(usage_page = BUTTON, usage_min = 0x01, usage_max = 0x10) = {
#[packed_bits 16] #[item_settings data,variable,absolute] buttons=input;
};
}
)]
#[allow(dead_code)]
pub struct JoystickReport {
pub x: u8, // X轴位置
pub y: u8, // Y轴位置
pub z: u8, // Z轴位置
pub rx: u8, // X轴旋转
pub ry: u8, // Y轴旋转
pub rz: u8, // Z轴旋转
pub buttons: u16, // 16个按钮状态
}
使用生成的描述符
定义好报告结构后,可以这样使用:
use usbd_hid::descriptor::SerializedDescriptor;
use usbd_hid_macros::gen_hid_descriptor;
// 定义你的报告描述符...
let report_desc = KeyboardReport::desc();
// 然后可以将report_desc传递给usbd-hid的配置
高级用法
组合设备
你可以定义包含多个顶级集合的组合设备:
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = KEYBOARD) = {
// 键盘部分...
};
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = MOUSE) = {
// 鼠标部分...
}
)]
pub struct KeyboardMouseReport {
// 字段...
}
自定义用法页
你可以定义自己的用法页(Usage Page):
const MY_CUSTOM_USAGE_PAGE: u16 = 0xFF00;
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = MY_CUSTOM_USAGE_PAGE, usage = 0x01) = {
// 自定义设备...
}
)]
pub struct CustomDeviceReport {
// 字段...
}
完整示例
下面是一个完整的键盘HID设备实现示例:
use usb_device::prelude::*;
use usbd_hid::hid_class::HIDClass;
use usbd_hid_macros::gen_hid_descriptor;
// 定义键盘报告描述符
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = KEYBOARD) = {
(usage_page = KEYBOARD, usage_min = 0xE0, usage_max = 0xE7) = {
#[packed_bits 8] #[item_settings data,variable,absolute] modifier=input;
};
(usage_min = 0x00, usage_max = 0x65) = {
#[item_settings data,array,absolute] keycodes=input;
};
}
)]
#[allow(dead_code)]
pub struct KeyboardReport {
pub modifier: u8,
pub _reserved: u8,
pub keycodes: [u8; 6],
}
fn main() {
// 初始化USB设备
let usb_bus = UsbBus::new(peripherals.USB);
// 创建HID类实例
let hid = HIDClass::new(&usb_bus, KeyboardReport::desc(), 60);
// 创建USB设备
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("My Company")
.product("HID Keyboard")
.serial_number("123456")
.device_class(0)
.build();
// 主循环
loop {
// 处理USB事件
usb_dev.poll(&mut [&mut hid]);
// 创建键盘报告
let report = KeyboardReport {
modifier: 0, // 无修饰键
_reserved: 0, // 保留字段
keycodes: [0; 6], // 无按键按下
};
// 发送报告
hid.push_input(&report).ok();
}
}
注意事项
- 报告描述符必须符合USB HID规范
- 结构体字段必须与描述符定义匹配
- 使用
#[allow(dead_code)]
可以避免编译器警告 - 确保你的设备实现了正确的报告ID(如果需要)
usbd-hid-macros
大大简化了HID设备开发中最复杂的部分——报告描述符的生成,让开发者可以更专注于设备功能的实现。