目录
- 前言
- USB设备类别
- 未定义设备
- 设备描述符/配置描述符分析
- 配置从机类型
- 配置设备专用的描述符
- 配置从机端点
- 实现设备类特定请求
- HID设备
-
- CDC设备
-
- USB组合设备
-
前言
文章中的部分概念可参考第9课【USB协议】USB总线 接口 端点 管道 数据包 枚举 STM32_USB-FS-Device_Lib V4.1.0
USB设备类别
USB协议中为了提供对多样设备的支持,定义了许多外部设备子类,常见的包括:
- 人机交互类设备HID(Human Interface Device)
- 通信类设备CDC(Communicate Device Class)
- 大容量存储设备MSC(Mass Storage Class)
- 视频类设备UVC(USB Video Class)
- 音频类设备UAC(USB Audio Class)
USB支持的设备众多,实现方法上会有很多不同点。但从实现思路上可以重点关注两个点:
- 设备描述符实现:配置从机类型,方便主机加载从机对应驱动及对从机数据的识别
- 配置描述符实现:配置设备专用的描述符,方便启用特定的设备操作和特性;配置从机端点信息,方便主机从端点读/写数据
未定义设备
指仅能让计算机识别出其USB设备身份,没有具体指明其功能的USB设备
识别USB设备的硬件前提
USB集线器识别USB设备是否接入依赖的是USB总线上D+/D-的电平变化。在USB集线器上,D+/D-信号线上都会接一个15kΩ的下拉电阻,而对应的USB设备上都会在D+/D-接上一个1.5kΩ上拉电阻(高速全速设备上拉电阻在D+,低速设备的上拉电阻在D-),当集线器检测到空闲的USB接口D+/D-信号线的电平突然被拉高时,就知道有USB设备接入了,进而进入美剧流程
设备描述符/配置描述符分析
const uint8_t Undefined_USB_DeviceDescriptor[18] =
{
0x12,
0x01,
0x00, 0x02,
0x00,
0x00,
0x00,
0x40,
0x83, 0x04,
0x20, 0x57,
0x00, 0x01,
0x01,
0x02,
0x03,
0x01
}
const uint8_t Undefined_USB_ConfigDescriptor[32] =
{
0x09
0x02
0x20
0x00
0x01
0x01
0x00
0xA0
0x32
0x09
0x04
0x00
0x00
0x02
0x00
0x00
0x00
0x00
0x07
0x05
0x81
0x02
0x40
0x00
0x00
0x07
0x05
0x02
0x02
0x40
0x00
0x00
};
设备描述符定义了一个未指定功能的USB设备,支持的USB协议为USB2.0,单次传输最大数据包传输大小为64字节,配置描述符描述了未定义USB设备的接口,接口对应功能也是未定义的,所以也不能通过端点传输数据
配置从机类型
以上设备描述符中有三个重要的值,用于指定USB设备身份:
- bDeviceClass:表明设备类别
- bDeviceSubClass:表明设备子类别
- bDeviceProtocol:表明设备协议
对于常用的设备类型,它们有以下组合方式:
设备类别 | bDeviceClass | bDeviceSubClass(多选一) | bDeviceProtocol(多选一) |
---|
未定义 | 0x00 | 0x00 | 0x00 |
人机交互类设备 HID | 0x03 | 未定义:0x00 启动接口子类:0x01 鼠标接口子类:0x02 摇杆接口子类:0x03 游戏手柄接口子类:0x04 键盘接口子类:0x05 … | 未定义:0x00 键盘协议:0x01 鼠标协议:0x02 摇杆协议:0x03 游戏手柄协议:0x04 … |
通信类设备 CDC | 0x02 | 未定义:0x00 ACM子类:0x01 ECM子类:0x02 NCM子类:0x03 自定义子类:0xFE | 未定义:0x00 V.25ter协议:0x01 AT命令协议:0x02 PPP协议:0x03 Ethernet Emulation协议:0x04 ATM Emulation协议:0x05 供应商自定义协议: 0xFF |
大容量存储设备 MSC | 0x08 | 未定义:0x00 公开SCSI命令集子类:0x01 ATAPI命令集子类:0x02 USB命令集子类:0x03 自定义子类:0xFE | 未定义:0x00 USB批量传输协议:0x01 公开SCSI协议:0x50 UAS传输协议:0x62 自定义协议:0x80~0xFF |
组合设备 | 0xEF | 通用子类:0x01 IAD多接口子类:0x02 供应商自定义子类:0x0F
| 未定义:0x00 IAD多接口协议:0x01 供应商自定义协议:0xFF |
配置设备专用的描述符
要配置设备专用描述符和从机端点,首先要了解配置描述符的组成。常见的配置描述符结构如下:
能看出配置描述符的一些特点:
- 上层描述符如果包含下层描述符,那么下层描述符的实现必须紧挨着上层描述符
- 一个上层描述符如果包含了多个下层描述符,那多个下层描述符的实现按照顺序紧挨着上层描述符
- 一个配置描述符中可以有多个接口描述符
- 一个接口描述符可以包含多个端点描述符
- 多个接口描述符通过接口关联描述符整合
接口关联描述符
Interface Associate Descriptor接口关联描述符。在USB部分设备中,一般实现某个设备功能只会要求使用一个接口,但也可能会要求使用两个或以上的接口,这时候就需要将使用到的多个接口,通过IAD描述符关联起来,以便于主机识别这些关联接口之间的关系。一般来说IAD描述符出现在配置描述符之后,接口描述符之前,IAD描述符之后会紧跟着相关联的接口描述符
根据上述的特点,设备专用的描述符只要插入了其上层描述符之后就可以了,如果有多个,按照包含顺序紧挨着实现。一般来说因为接口描述符和设备的功能有紧密的联系,所以设备专用的描述符位置都和接口描述符并列,或者包含于接口描述符之中
配置从机端点
端点描述符的配置较为简单,一般要:
- 确定需要的端点数量,以及每个端点的数据方向:输入/输出
- 配置缓冲区描述表,为缓冲区描述表头和端点分配读写缓冲区
缓冲区及缓冲区描述表
缓冲区是在STM32 USB标准库开发中的概念。缓冲区实际大小为512Bytes,位于STM32的RAM中,地址为0x40006000~0x400063FF(大小为1KByte,由于对于内核是通过32位的读取方式读取缓冲的,而USB外设是通过16位的方式读取缓冲区。内核读取到的四个字节只有两个是有意义的,另一半由于没有对应的实际存储器,会读取不到东西),用于存放缓冲区描述表,同时还用作端点收发数据Buffer。缓冲区描述表是缓冲区中的一个区域,记录缓冲区端点相关寄存器信息。缓冲区中除了描述表的以外空间可以分配给端点,作为收发数据Buffer,Buffer之间不能重叠,一个Buffer的最大为64Bytes
缓冲区描述表头和端点分配
缓冲区描述表头应该位于缓冲区的最前面,记录每个端点相关的四个寄存器信息,包括缓冲地址寄存器,发送数据字节寄存器,接收缓冲地址寄存器,接收数据字节寄存器,每个寄存器占用两个字节,所以每增加一个端点会让表头增大八个字节。一般来说紧挨着表头是端点0Buffer地址,会定义在距离表头64Bytes的位置,因为单个USB设备可用端点为8个,所以所有的寄存器信息大小为8*8=64Bytes。之后的表头地址就根据各个端点Buffer需求的大小定义即可,可参考以下示例:
#define EP_NUM (5)
#define BTABLE_ADDRESS (0x00)
#define ENDP0_RXADDR (0x40)
#define ENDP0_TXADDR (0x80)
#define ENDP1_TXADDR (0xC0)
#define ENDP1_RXADDR (0x100)
#define ENDP2_TXADDR (0x140)
#define ENDP2_RXADDR (0x180)
#define ENDP3_TXADDR (0x1C0)
实现设备类特定请求
USB设备类特定请求属于USB设备标准请求的拓展,其数据内容类似于标准请求,都有请求码,索引,值和长度。USB设备标准请求是所有USB设备都支持的,它可以实现最基础的USB设备配置和控制功能。而USB设备类特定请求指的是针对特定设备的请求,例如HID设备的GET_REPORT/SET_REPORT请求,CDC设备的GET_LINE_CODING/SET_LINE_COFING请求。具体到每种设备,其对类特定请求的定义和实现都不同,这类请求一般由开发者来实现
HID设备
HID设备是USB协议中定义的一种广泛运用于计算机,游戏机,工业设备等电子设备中的输入设备子类,包括鼠标,键盘,摇杆,游戏手柄等。符合HID规范的设备不仅可以通过USB,还可以通过蓝牙,无线电的方式同主机相连接,通过特殊的HID报文同主机进行数据传输
HID设备有以下优点:
- 免驱动:HID设备的驱动通常都已经内置于操作系统之中
- 低功耗:HID设备通常都通过总线供电或自带电池供电
- 多样性:HID设备种类繁多,基本的输入设备都可以作为HID设备,例如鼠标,键盘,手柄,指点杆等等
特点
USB HID协议中额外规定了HID设备/主机之间的通信格式和通信协议。所以相对于标准USB设备,HID设备实现上有以下的特点:
- 设备描述符不同,配置描述符中多出了HID设备专用的HID主描述符,HID报文描述符,HID实体描述符(可选)
- 仅需定义三个端点:一个端点0用传输控制;一个IN端点,用于设备向主机发送数据;一个OUT端点,用于主机向设备发送数据,其中IN,OUT端点都使用中断传输模式
- 通过HID报文描述符定义的报文格式同主机沟通(报文格式多变,几乎可以传输任何数据,但其内容也几乎相当于一门编程语言)
HID键盘描述符实例
const uint8_t HID_Keyboard_DeviceDescriptor[18] =
{
0x12,
0x01,
0x00, 0x02,
0x03,
0x01,
0x01,
0x40,
0x83, 0x04,
0x20, 0x57,
0x00, 0x01,
0x01,
0x02,
0x03,
0x01
}
const uint8_t HID_Keyboard_ConfigDescriptor[41] =
{
0x09
0x02
0x29
0x00
0x01
0x01
0x00
0xA0
0x32
0x09
0x04
0x00
0x00
0x02
0x03
0x01
0x01
0x00
0x09
0x21
0x10, 0x01,
0x00,
0x01,
0x22,
0x3F, 0x00,
0x07
0x05
0x01
0x03
0x40
0x00
0x0A
0x07
0x05
0x81
0x03
0x40
0x00
0x0A
};
以上描述符实现了一个HID键盘设备,其描述符分别指出:
- 设备描述符:支持USB 1.1协议,支持启动接口的HID键盘
- 配置描述符:包含一个接口
- 接口描述符:定义了设备为HID设备,支持在BIOS使用,支持键盘HID协议
- HID主描述符:HID设备有两个端点,最大包大小为64字节,最大电流为100mA,支持自供电,不支持远程唤醒。同时还指定了HID设备支持HID报文描述符,报文描述符长度为63字节
- 备注:HID报文描述符的实现需要额外定义,不在配置描述符中实现
HID报文描述符
前面提到HID设备通过特殊的HID报文同主机进行数据传输。这里的报文,指的是可变长度,指定格式的特殊数据,其中:
- 可变长度:HID报文描述符中没有指定描述符长度,其长度由开发者定义
- 指定格式:HID报文数据需要根据HID报文描述符中描述的格式进行解析,才能变成有效的信息
HID报文描述符中最基本的单元是条目(Item),条目又分为长条目和短条目,长条目不经常使用,所以此处仅讨论短条目
短条目
短条目由前缀+可选的数据组成,其中:
短条目的前缀部分大小为一个字节,这一个字节的八个比特位被分为三部分:
- bSize:D0~D1,占用1位,用于表示该条目的数据长度,单位为字节。0表示0字节,1表示1字节,2表示2字节,3表示4字节
- bType:D2~D3,占用2位,用于表示该条目的数据类型。0表示主条目,1表示全局条目,2表示局部条目
- bTag:D4~D7,占用4位,用于表示该条目的功能。根据不同的条目类型,同样的bTag会有不同的含义
- 可选的数据:可以是0字节,1字节,2字节或者4字节大小的数据,一般使用1字节居多
可选数据不是独立存在的,需要通过条目的前缀去解析可选数据,到官方提供的HID Usage Table中查询到对应的意义
实际的代码中,通常将短条目分为前缀和可选数据,分别用两位十六进制数表示,其形式为:
0x81, 0x06
其形式类似于键值对,前缀为键,数据为值
报文生成
通过将相关的条目进行组合,能得到有层次结构的HID报文描述符。基本的报文描述符结构如下:
可以看出,报文描述符分为两部分:
- 外层绿色部分:由Usage Page/Usage/Collection/End Collection控制符组成,被称为集合,它描述了此报文的最基本功能,定义了报文的使用页面,使用类型和字段组合方式。值得注意的是,最外层并不一定就包含了此报文的所有信息,复杂的报文,可能会涉及多语言设计,加密,压缩等机制,需要具体分析
- 内层橙色/蓝色部分:由Usage Page/Usage Minimum/Usage Maximum/Logical Minimum/Logical Maximum/Input/Output等控制符组成,被称为字段,它描述了报文数据的具体组合方式,各个元素的含义和取值范围
HID键盘报文描述符实例
通过对一个HID键盘的基础报文描述符的分析,可以很好的对以上所有的HID描述符相关概念进行收尾
const uint8_t CustomHID_ReportDescriptor[63] ={
0x05, 0x01,
0x09, 0x06,
0xA1, 0x01,
0x05, 0x07,
0x19, 0xE0,
0x29, 0xE7,
0x15, 0x00,
0x25, 0x01,
0x75, 0x01,
0x95, 0x08,
0x81, 0x02,
0x75, 0x08,
0x95, 0x01,
0x81, 0x01,
0x05, 0x08,
0x19, 0x01,
0x29, 0x05,
0x75, 0x01,
0x95, 0x05,
0x91, 0x02,
0x75, 0x03,
0x95, 0x01,
0x91, 0x01,
0x05, 0x07,
0x15, 0x00,
0x25, 0x65,
0x19, 0x00,
0x29, 0x65,
0x75, 0x08,
0x95, 0x06,
0x81, 0x00,
0xC0
};
可以看出HID报文描述符在C语言中,是通过结构体实现的。其中每一行都是一个短条目,短条目由前缀+1字节数据构成(可以参考上面说到的条目表现形式)。最外层的集合告知计算机,要将此报文作为键盘数据进行解析。最内层的字段描述了报文数据的结构,报文数据大小为9个字节,共分为3部分:
阅读报文描述符的技巧
由于报文由条目组成,结构上有一定的特殊性,按照正常的顺序浏览可能会比较乱,所以阅读前先将报文合理分段,每段按照从后往前阅读,会比较好理解
-
Modifier Keys:结合Modifier Keys段报文描述符代码进行单独解析
0x05, 0x07, // Usage Page (Key Codes) 【表明字段的用途是表示键码】
0x19, 0xE0, // Usage Minimum (224)
0x29, 0xE7, // Usage Maximum (231) 【表示键码的值范围从224~231】
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1) 【表示单个按键可存在的逻辑状态,为0释放/1按下】
0x75, 0x01, // Report Size (1) 【表示报文大小,单位比特】
0x95, 0x08, // Report Count (8) 【表示报文数量】
0x81, 0x02, // Input (Data, Variable, Absolute) ; Modifier byte 【表示该段报文的数据方向,数据类型】
【分界线】
0x95, 0x01, // Report Count (1) 【表示报文大小,单位比特】
0x75, 0x08, // Report Size (8) 【表示报文数量】
0x81, 0x01, // Input (Constant) ; Reserved byte 【表示报文的数据方向,数据类型】
代码可以通过【分界线】分为两部分来看:
前半部分:这部分定义了报文数据类型为输入计算机的绝对值变量,用途为键码,报文数据数量为8,单个报文数据大小为1bit,所以总的数据量为8x1bit=8bits=1Byte。而八个比特位能表示八个按键,键码范围从224~231,对应如下图的八个特殊按键(参考官方HID Usage Table)
八个比特位分别按照高位对大键码,低位位对小键码的关系一一对应,每个比特位的0/1装袋分别表示按键被释放/按下。例如0b0000 0001对应就是LeftControl键按下
后半部分:这部分定义了报文数据类型为输入计算机的常量,没有指明实际用途,主要起到了填充数据的作用,保持整体数据按照8bits对齐,报文数据数量为8,单个报文数据大小为1bit,所以后半部分的数据量为8x1bit=8bits=1Byte
整体字段描述的数据的大小为2Bytes,描述的数据内容为Modified Keys
-
LED:这部分字段定义了报文数据类型为输出计算机的绝对值变量,用途为LED灯,报文数据数量为48,单个报文数据大小为1bit,所以总的数据量为5x1bit=5bits。五个比特位能表示五个LED灯,灯代表的含义范围从1~5,对应如下图的五个LED(参考官方HID Usage Table):
LED由计算机控制亮灭。这部分字段还额外带了一个 3bits的填充数据。
整体字段描述的数据的大小为1Bytes,描述的数据内容为LED
-
Normal Keys:这部分字段定义了报文数据类型为输入计算机的绝对值变量,用途为键码,报文数据数量为48,单个报文数据大小为1bit,所以总的数据量为48x1bit=48bits=6Byte。而六个字节能表示四十八个按键,键码范围从0~101,对应如下图的102个特殊按键(参考官方HID Usage Table)
整体字段描述的数据的大小为6Bytes,描述的数据内容为Normal Keys
报文数据大小为2+1+6=9Bytes,报文内容如下
Byte 9 | Byte 8 | Byte 7 | Byte 6 | Byte 5 | Byte 4 | Byte 3 | Byte 2 | Byte 1 | Byte 0 |
---|
- | - | - | - | - | - | Normal Keys | LED+Padding | Padding | Modif Keys |
报文数据本身由HID键盘生成并放置到对应IN端点,等待计算机按照端点描述符中定义的中断时间间隔内来获取。计算机取到报文数据后,自带的HID驱动会按照HID键盘报文描述符解析此报文,得到键盘释放/按下的消息。最终按键消息会转发给操作系统底层,由操作系统执行对应响应操作
总结
HID报文描述符可能是所有USB描述符中最复杂的,抽象的概念相当多,理解起来也需要一定的时间。但是复杂的定义带来的却是报文易用性和灵活性,它几乎可以描述任何形式的数据,而且长度都由开发者来定义,形式也较为简洁清晰
CDC设备
CDC设备USB协议中定义的一种通信设备(通常指电信通信设备和中速网络通信设备)子类,包括调制解调器,网络适配器,手机,PDA等。通信设备领域,使用到很多种无线/有线通信协议以及各式的数据传输方式,CDC协议提供了对这些设备的协议和数据传输支持,例如常见的COM口UART协议,以太网口IP协议等。通过CDC协议可以将USB设备虚拟成通信设备,通过指定的传输协议和数据传输方式同USB设备进行通信
CDC设备有以下优点:
- 免驱动:使用标准的USB接口和通信协议,部署方便。例如USB虚拟COM口,无需安装驱动即可使用
- 支持多种通信协议:支持很多通信领域的通信协议,可以满足不同行业,不同场景的需求
特点
USB CDC协议中同样额外规定了CDC设备/主机之间的通信格式和通信协议,以CDC-ACM设备为例,相对于标准USB设备,它实现上有以下的特点:
- 设备描述符不同,配置描述符中多出了CDC设备专用的CDC控制接口描述符,头部功能描述符,呼叫管理描述符,抽象控制模型描述符,联合功能描述符,CDC数据接口描述符,
CDC设备的接口组成
不同于常见的单接口设备,CDC设备由多个接口类组成,分别是【CDC控制接口类】和【CDC数据接口类】(可选)。所以在较新的操作系统中需要用额外的IAD描述符将两个接口关联起来。其中:
- CDC控制接口类:负责完成通信设备的配置和管理,包含了CDC控制接口,头部功能描述符,呼叫管理功能描述符,抽象控制模型描述符,联合功能描述符。各个描述符的作用分别为:
【CDC控制接口】:用于设备管理/电话管理(可选),设备管理一般涉及请求(request)和通知(notification),请求一般由端点0进行处理,通知需要额外配置其他端点进行处理
【头部功能描述符】:用于表示CDC功能描述符的开始,紧跟其后的是CDC设备的其他功能描述符
【呼叫管理功能描述符】:负责电话,AT指令相关的功能
【抽象控制模型描述符】:PSTN(Public Switched Telephone Net)下定义的抽象控制模型。用于描述使用AT指令,或AT.25Ver指令的调制解调设备
【联合功能描述符】:用于告知计算机,将哪几个接口联合起来,用来表示CDC功能。类似于IAD接口关联描述符 - CDC数据接口类:负责进行数据的传输,包含了数据接口,数据调制解调器接口(可选),后者用于模拟调制解调设备的控制信号
接口关联描述符和联合功能描述符的关系
在USB协议的初期,没有规划好多接口设备的支持,基本上都是一个设备只使用一个接口。后来出现了CDC设备这样需要使用多接口的设备,于是便专门出了联合功能描述符用于应对。等到后期USB协议逐渐完善,推出了接口关联描述符这样的用于多个接口关联的描述符。两者使用起来并不会冲突,在旧版操作系统中,会忽略接口关联描述符,识别联合功能描述符;在新版操作系统中会忽略联合功能描述符,仅识别接口关联描述符
CDC-ACM虚拟串口描述符实例
const uint8_t VirtualComPort_DeviceDescriptor[18] =
{
0x12,
0x01,
0x00, 0x02,
0x02,
0x01,
0x01,
0x40,
0x83, 0x04,
0x20, 0x57,
0x00, 0x01,
0x01,
0x02,
0x03,
0x01
}
const uint8_t VirtualComPort_ConfigDescriptor[41] =
{
0x09
0x02
0x29
0x00
0x01
0x01
0x00
0xA0
0x32
0x08,
0x0B,
0x00,
0x02,
0x02,
0x02,
0x01,
0x00,
0x09,
0x04,
0x00,
0x00,
0x01,
0x02,
0x02,
0x01,
2,
0x05,
0x24,
0x00,
0x10,
0x01,
0x05,
0x24,
0x01,
0x00,
0x01,
0x04,
0x24,
0x02,
0x02,
0x05,
0x24,
0x06,
0x00,
0x01,
0x07,
0x05,
0x83,
0x03,
0x40
0x00,
0x02,
0x09,
0x04,
0x02,
0x00,
0x02,
0x0A,
0x00,
0x00,
0,
0x07,
0x05,
0x02,
0x02,
0x40
0x00,
0x00,
0x07,
0x05,
0x82,
0x02,
0x40
0x00,
0x00,
};
以上描述符实现了一个CDC虚拟串口设备,其描述符分别指出:
- 设备描述符:支持USB 1.1协议,支持抽象控制模型,使用AT指令的CDC设备
- 配置描述符:包含两个接口,分别是CDC控制接口,CDC数据接口
- 数据关联描述符:实现多个接口对应一个功能,例子中是CDC设备功能
- 控制接口描述符:定义一个其他端点,用作异步传输
- 头部功能描述符:CDC设备支持的协议版本1.10
- 呼叫功能描述符:只有在电话功能中会使用,虚拟串口设备中没有使用
- 抽象控制模型描述符:表明CDC设备使用抽象控制模型进行通信
- 联合功能描述符:关联CDC设备的控制接口和数据接口
- 控制端点描述符:实现设备控制功能,控制功能可以处理通信设备需要的请求和通知
- 数据接口描述符:定义2个数据传输所使用的端点
- 数据端点描述符:处理数据传输
类特定请求
相对于HID设备通过特殊的HID报文进行数据传输,CDC-ACM设备直接通过端点发送/接收数据,无需进行其他转换和解析。但是需要在CDC-ACM设备端实现CDC-ACM类的设备类特定请求SetLineCoding和GetLineCoding,这两个请求主要实现了设置和获取虚拟串口的串行通信参数,包括波特率,数据传输位数,奇偶校验位,停止位,具体实现如下:
typedef struct
{
uint32_t bitrate;
uint8_t format;
uint8_t paritytype;
uint8_t datatype;
}LINE_CODING;
LINE_CODING linecoding =
{
115200,
0x00,
0x00,
0x08
};
uint8_t *Virtual_Com_Port_GetLineCoding(uint16_t Length)
{
if (Length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = sizeof(linecoding);
return NULL;
}
return(uint8_t *)&linecoding;
}
uint8_t *Virtual_Com_Port_SetLineCoding(uint16_t Length)
{
if (Length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = sizeof(linecoding);
return NULL;
}
return(uint8_t *)&linecoding;
}
USB组合设备
在一个USB设备上实现多个功能,例如HID键盘+CDC虚拟串口。有两种方法:
- 组合设备:Composite Device,内部通过组合设备描述符,以及组织多个功能的接口实现多功能组合。所有功能共用一套PID/VID/DID
- 复合设备:Compound Device,内部通过USB HUB来实现,将多个单一功能的USB设备,通过USB HUB连接整合,每个设备有自己独立的PID/VID/DID。这种方法复杂度相当高,具体可以参考USB2.0协议
实现思路
通过以上对HID设备和CDC设备的解析。通过STM32去实现USB组合设备的思路就相当清晰了:
- 首先需要将USB的设备描述符中设备类型改为Miscellaneous Device,表示组合设备
- 然后将HID键盘的配置描述符和CDC虚拟串口的配置描述符整合,主要注意处理接口关联描述符,接口描述符,CDC控制接口描述符,CDC数据接口描述符,HID描述符的次序即可,HID报文描述符可以直接复用
- 之后实现HID键盘的GET_REPORT/SET_REPORT设备类特定请求,以及CDC虚拟串口的GET_LINE_CODING/SET_LINE_COFING请求
- 最后按照自己的需求完成端点数据的填充即可,USB主机会按照端点描述符要求进行数据传输
代码实例
USB组合设备代码已上传Github:Sinuxtm32
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)