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规范的二进制描述符。主要特点:

  1. 使用类似HID描述符的语法定义报告格式
  2. 支持各种HID用途页(usage page)和用途(usage)
  3. 自动处理位打包和报告大小
  4. 生成的结构体可以直接用于USB HID通信

报告描述符结构

在宏中,你可以定义:

  • 集合(collection)类型和应用场景
  • 输入(input)和输出(output)项
  • 用途页(usage page)和具体用途(usage)
  • 数据格式(变量、数组、绝对值、相对值等)
  • 位打包字段

注意事项

  1. 需要配合usb-deviceusbd-hid库使用
  2. 适用于嵌入式开发和no_std环境
  3. 生成的报告描述符需要符合HID规范
  4. 对于复杂设备,可能需要分多个报告描述符

这个库大大简化了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();
    }
}

注意事项

  1. 报告描述符必须符合USB HID规范
  2. 结构体字段必须与描述符定义匹配
  3. 使用#[allow(dead_code)]可以避免编译器警告
  4. 确保你的设备实现了正确的报告ID(如果需要)

usbd-hid-macros大大简化了HID设备开发中最复杂的部分——报告描述符的生成,让开发者可以更专注于设备功能的实现。

回到顶部