ESP32 实现配置自定义HID通信设备

目录

开发准备

第一步

        确保硬件支持TinyUSB库并已开启配置

第二步

        初始化USB设备

        设备描述符tusb_desc_device_t

        配置描述符config_descriptor数组

        配置报告描述符desc_hid_report(重点!!!)

        配置字符串描述符string_descriptor

第三步

        发送报告数据

        接收报告数据

总结


开发准备

        硬件型号:ESP32S3

        开发环境:ESP-IDF v4.4

        开发平台:VS Code

        如果在使用官方TinyUSB库时出现问题,可能是作者有使用个人TinyUSB库的原因,资源见文章底部


第一步

        确保硬件支持TinyUSB库并已开启配置

        

 

         如上图所示,开启TinyUSB、HID功能

        如果未找到HID相关开启选项,可自行检查硬件是否支持,或者配置文件是否配对


第二步

        初始化USB设备

    ESP_LOGI(TAG, "USB initialization");
    const tinyusb_config_t tusb_cfg = {};

    ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
    ESP_LOGI(TAG, "USB initialization DONE");

        调用上述代码,初始化ESP32为USB设备,其中tinyusb_config_t 结构体可以为空,tinyusb_driver_install将使用默认参数进行USB配置,如需手动配置,见以下步骤,否则跳过即可

        tinyusb_config_t结构体如下所示:

typedef struct {
    tusb_desc_device_t *descriptor;    //    设备描述符结构体
    const char **string_descriptor;    //    字符串描述符
    const uint8_t *config_descriptor;  //    配置描述符数组
    bool external_phy;                 //    外部PHY,一般为false
} tinyusb_config_t;

        设备描述符tusb_desc_device_t

typedef struct TU_ATTR_PACKED
{
  uint8_t  bLength            ;    //    设备描述符的字节数大小
  uint8_t  bDescriptorType    ;    //    描述符类型,设备描述符为0x01
  uint16_t bcdUSB             ;    //    USB版本号

  uint8_t  bDeviceClass       ;    //    USB分配的设备类代码,0x01~0xfe为标准设备类,0xff为厂商自定义类型
  uint8_t  bDeviceSubClass    ;    //    USB分配的子类代码
  uint8_t  bDeviceProtocol    ;    //    USB分配的设备协议代码
  uint8_t  bMaxPacketSize0    ;    //    端点0的最大信息包大小

  uint16_t idVendor           ;    //    制造商ID
  uint16_t idProduct          ;    //    产品ID

  uint16_t bcdDevice          ;    //    设备出厂编号

  uint8_t  iManufacturer      ;    //    制造商的字符串描述符索引
  uint8_t  iProduct           ;    //    产品的字符串描述符索引
  uint8_t  iSerialNumber      ;    //    设备序列号的字符串描述符索引

  uint8_t  bNumConfigurations ;    //    可能的配置数量
} tusb_desc_device_t;

        TinyUSB官方默认配置如下所示:

tusb_desc_device_t descriptor_kconfig = {
    .bLength = sizeof(descriptor_kconfig),
    .bDescriptorType = TUSB_DESC_DEVICE,        //    0x01
    .bcdUSB = 0x0200,    //    USB2.0                            

    .bDeviceClass = 0x00,
    .bDeviceSubClass = 0x00,
    .bDeviceProtocol = 0x00,

    .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,    //    64

    .idVendor = 0xCafe,
    .idProduct = USB_TUSB_PID,    //    0x4010
    .bcdDevice = 0x0100,

    .iManufacturer = 0x01,
    .iProduct = 0x02,
    .iSerialNumber = 0x03,

    .bNumConfigurations = 0x01
};

        配置描述符config_descriptor数组

        默认配置如下所示:

uint8_t const desc_configuration[] = {
    
    //    配置描述符
    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, 0, 100),
    
    //    接口描述符、HID描述符、端点描述符
    TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, STRID_HID_INTERFACE, HID_PROTOCOL_NONE, sizeof(desc_hid_report), 0x01, 0x81, 64, 10)
};

        TUD_CONFIG_DESCRIPTOR为宏定义,跳转之后见以下解析: 

        其中配置描述符解析说明如下:

固定长度9   --  配置描述符

配置描述符 -- TUSB_DESC_CONFIGURATION -- 0x02

配置描述符信息总的大小,包括接口描述符、端点描述符等等 -- U16_TO_U8S_LE(TUSB_DESC_TOTAL_LEN) --> 9 + 9 + 9 + 7 + 7(双向通信)

接口数量 -- ITF_NUM_TOTAL --> 1

Set_Configuration命令需要的参数值 -- 1

配置字符串索引 -- 0

bit7=1 bit6:1--自供电 0--总线供电 bit5:1--远程唤起 0--不支持 bit[4:0]=0 -- TU_BIT(7) | 0

供电 -- (100)/2 == 50*2 = 100mA

-------------------------------------------------------------------------------------------------------------------------

9, TUSB_DESC_CONFIGURATION, U16_TO_U8S_LE(TUSB_DESC_TOTAL_LEN), ITF_NUM_TOTAL, 1, 0, TU_BIT(7) | 0, (100)/2,

         如果是双向通信则按上述设置,如果仅是单向输入或输出的话,配置描述符信息总的大小需要变成9 + 9 + 9 + 7

        TUD_HID_INOUT_DESCRIPTOR为宏定义,跳转之后见以下解析: 

        其中接口描述符解析说明如下:

固定长度9   --  接口描述符

接口描述符 -- TUSB_DESC_INTERFACE --> 0x04

接口0  (接口从0开始) -- 0

接口索引值 -- 0

端点个数(端点0不可用)-- 2

3 = HID --  TUSB_CLASS_HID --> 0x03

接口子类型:01为Boot Device,键鼠在BIOS下就启动  --  0

接口协议:00--None  01--Keyboard  02--Mouse -- 0

描述该接口的字符串索引  --  STRID_HID_INTERFACE

-------------------------------------------------------------------------------------------------------------------------

9, TUSB_DESC_INTERFACE, 0, 0, 2, TUSB_CLASS_HID, (uint8_t)((0) ? HID_SUBCLASS_BOOT : 0), 0, STRID_HID_INTERFACE,

        其中HID描述符解析说明如下:

固定长度9   --  HID描述符

HID描述符   --  HID_DESC_TYPE_HID --> 0x21

HID专属版本号 -- 0x0111

国家代码    --  0

附属类描述字的数目1个 -- 1

描述字类型:报告    --  HID_DESC_TYPE_REPORT --> 0x22

HID报告描述字总字节数   --  sizeof(desc_hid_report)

-------------------------------------------------------------------------------------------------------------------------

9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0110), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(sizeof(desc_hid_report)),

         其中端点描述符解析说明如下:

固定长度7   --  端点描述符 -- 输入端点

端点描述符  --  TUSB_DESC_ENDPOINT

bit[7]:1--IN--设备到主机  0--OUT--主机到设备  --  0x81

传输类型    --  TUSB_XFER_INTERRUPT(中断)

端点最大信息包尺寸 --  最大64 -- 0x40

轮询间隔    --  10

-------------------------------------------------------------------------------------------------------------------------

7, TUSB_DESC_ENDPOINT, 0x81, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(0x40), 10,

        如果需要将HID设备设置为双向通信,则需要加上输出端点,如下所示:

固定长度7   --  端点描述符 -- 输出端点

端点描述符  --  TUSB_DESC_ENDPOINT

bit[7]:1--IN--设备到主机  0--OUT--主机到设备  --  0x01

传输类型    --  TUSB_XFER_INTERRUPT(中断)

端点最大信息包尺寸 --  最大64 -- 0x40

轮询间隔    --  10

-------------------------------------------------------------------------------------------------------------------------

7, TUSB_DESC_ENDPOINT, 0x01, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(0x40), 10

        到这里,描述符config_descriptor数组基本配置完成了!

        配置报告描述符desc_hid_report(重点!!!)

        这里作者使用的是TinyUSB官方的HID通用输入输出设备模板,如需自定义报告描述符,可适用于HID Descriptor Tool官方生成工具,资源在文章底部

uint8_t const desc_hid_report[] = {

    TUD_HID_REPORT_DESC_GENERIC_INOUT(63, HID_REPORT_ID(3))

};

        上述代码中,63表示一次最大发送报告数量,HID_REPORT_ID(3)表示HID通用设备的报告ID为3。

        每个HID设备只能有一个设备描述符,但可以有多个报告描述符,每个描述符表示一种类型,比如说我可以同时在desc_hid_report数组中添加上键盘、鼠标的报告描述符,这样我在插上电脑后,电脑会设备出三种不同类型的设备,需要注意的是,不同类型的设备报告ID必要不同!,因为在发送和接收报告数据时,需要通过报告ID来辨别是哪个类型的设备,如下所示:

uint8_t const desc_hid_report[] = {
    TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)),    //    1
    TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_MOUSE)),    //    2
    TUD_HID_REPORT_DESC_GENERIC_INOUT(63, HID_REPORT_ID(3))
};

        关于一次最大发送报告数量的设置,必要比端点描述符中定义的最大信息包尺寸小一个字节,这是因为默认第0字节会存放报告ID,占位一个字节,例如:端点最大信息包尺寸为64,那么一次最大发送报告数量应该为63;端点最大信息包尺寸为32,那么一次最大发送报告数量应该为31。

        配置字符串描述符string_descriptor

tusb_desc_strarray_device_t string_descriptor= {
    // array of pointer to string descriptors
    (char[]){0x09, 0x04}, // 0: 支持语言:英语 (0x0409)
    "TinyUSB",            // 1: 制造商
    "TinyUSB Device",     // 2: 产品
    "123456",             // 3: 串行、芯片ID
    "TinyUSB HID"         // 4: HID
};

        这些都可以自定义,不一定要按照上面的配置

        关于语言的配置,一般都用英语,如需配置其他语言,可下载文档:Universal Serial Bus (USB),自行参阅

        到这里,USB HID设备基本配置初始完成!


第三步

        发送报告数据

        调用TinyUSB库中tud_hid_report函数发送报告

//    检查接口是否可用
if (tud_hid_ready())
{    
    uint8_t report_data[63] = {1, 2, 3, 4};
    
    //    3 -- 报告ID
    //    发送的报告数组
    //    发送的长度
    tud_hid_report(3, report_data, 63);
}

        其中发送的长度为报告描述符中一次最大发送报告数量,即63,必须设置正确,否则使用调试工具无法在HID设备上抓取报告数据,只能在USB总线上抓取!!

        下图为在HID设备上抓取报告数据(注意在抓取数据时,必须先打开HID设备(例如PortHelper),才能抓取到数据):

         上图中,03表示报告ID,后面数据为用户发送报告数据,调试工具为Bus Hound,资源见文章底部

        接收报告数据

        需要重定向接收报告回调函数,在HID接收到报告数据时,则触发该回调函数

void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
{
    /*
    (void) itf;              //    一般都为0
    (void) report_id;        //    报告ID
    (void) report_type;      //    报告类型
    (void) buffer;           //    接收报告数组
    (void) bufsize;          //    接收报告长度
    */

    //    处理代码
}

总结

        以上为作者自己在开发过程中一些见解,如有错误还请各位大佬多多指点,我是陈师傅,我们下章再见!

资源下载https://pan.baidu.com/s/1x2iweOhID6AAAPnns8qLzA?pwd=2b39

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值