前言
文章中的部分概念可參考第9課【USB協(xié)議】USB總線 接口 端點(diǎn) 管道 數(shù)據(jù)包 枚舉 STM32_USB-FS-Device_Lib V4.1.0
USB設(shè)備類別
USB協(xié)議中為了提供對多樣設(shè)備的支持,定義了許多外部設(shè)備子類,常見的包括:
- 人機(jī)交互類設(shè)備HID(Human Interface Device)
- 通信類設(shè)備CDC(Communicate Device Class)
- 大容量存儲設(shè)備MSC(Mass Storage Class)
- 視頻類設(shè)備UVC(USB Video Class)
- 音頻類設(shè)備UAC(USB Audio Class)
USB支持的設(shè)備眾多,實(shí)現(xiàn)方法上會有很多不同點(diǎn)。但從實(shí)現(xiàn)思路上可以重點(diǎn)關(guān)注兩個點(diǎn):
- 設(shè)備描述符實(shí)現(xiàn):配置從機(jī)類型,方便主機(jī)加載從機(jī)對應(yīng)驅(qū)動及對從機(jī)數(shù)據(jù)的識別
- 配置描述符實(shí)現(xiàn):配置設(shè)備專用的描述符,方便啟用特定的設(shè)備操作和特性;配置從機(jī)端點(diǎn)信息,方便主機(jī)從端點(diǎn)讀/寫數(shù)據(jù)
未定義設(shè)備
指僅能讓計(jì)算機(jī)識別出其USB設(shè)備身份,沒有具體指明其功能的USB設(shè)備
識別USB設(shè)備的硬件前提
USB集線器識別USB設(shè)備是否接入依賴的是USB總線上D+/D-的電平變化。在USB集線器上,D+/D-信號線上都會接一個15kΩ的下拉電阻,而對應(yīng)的USB設(shè)備上都會在D+/D-接上一個1.5kΩ上拉電阻(高速全速設(shè)備上拉電阻在D+,低速設(shè)備的上拉電阻在D-),當(dāng)集線器檢測到空閑的USB接口D+/D-信號線的電平突然被拉高時,就知道有USB設(shè)備接入了,進(jìn)而進(jìn)入美劇流程
設(shè)備描述符/配置描述符分析
const uint8_t Undefined_USB_DeviceDescriptor[18] =
{
0x12, // bLength:描述符長度,固定為0x12
0x01, // bDescriptorType:描述符類型,固定為0x01,表示設(shè)備描述符
0x00, 0x02, // bcdUSB:USB協(xié)議版本號,這里是2.0
0x00, // bDeviceClass:設(shè)備類別,0表示未定義
0x00, // bDeviceSubClass:設(shè)備子類別,0表示未定義
0x00, // bDeviceProtocol:設(shè)備協(xié)議,0表示未定義
0x40, // bMaxPacketSize:最大數(shù)據(jù)包大小,這里是64字節(jié)
0x83, 0x04, // idVendor:USB設(shè)備廠商ID,這里是0x0483
0x20, 0x57, // idProduct:USB設(shè)備產(chǎn)品ID,這里是0x5720
0x00, 0x01, // bcdDevice:設(shè)備版本號,這里是1.0
0x01, // iManufacturer:USB設(shè)備制造商字符串描述符的索引值,這里是1
0x02, // iProduct:USB設(shè)備產(chǎn)品字符串描述符的索引值,這里是2
0x03, // iSerialNumber:USB設(shè)備序列號字符串描述符的索引值,這里是3
0x01 // bNumConfigurations:USB設(shè)備支持的配置數(shù)量,這里是1
}
const uint8_t Undefined_USB_ConfigDescriptor[32] =
{
/************** 配置描述符 ****************/
0x09 // bLength: 描述符長度,固定為9
0x02 // bDescriptorType: 描述符類型,表示該描述符的類型為Configuration Descriptor,固定為2
0x20
0x00 // wTotalLength: 該配置描述符及其所包含的所有描述符的總長度,單位為字節(jié)
0x01 // bNumInterfaces: 該配置所包含的接口數(shù)量
0x01 // bConfigurationValue: 該配置的值,用于選擇設(shè)備的配置
0x00 // iConfiguration: 該配置的字符串描述符的索引,如果不存在則為0
0xA0 // bmAttributes: 該配置的屬性,包括供電方式和遠(yuǎn)程喚醒功能等
0x32 // bMaxPower: 該配置所需的最大電流,單位為2mA
/************** 接口描述符 ****************/
0x09 // bLength: 描述符長度,固定為9
0x04 // bDescriptorType: 描述符類型,表示該描述符的類型為Interface Descriptor,固定為4
0x00 // bInterfaceNumber: 該接口的編號
0x00 // bAlternateSetting: 備用設(shè)置的編號,用于支持多種設(shè)置
0x02 // bNumEndpoints: 該接口所包含的端點(diǎn)數(shù)量
0x00 // bInterfaceClass: 該接口的類別,0表示未定義
0x00 // bInterfaceSubClass: 該接口的子類別,0表示未定義
0x00 // bInterfaceProtocol: 該接口的協(xié)議,0表示未定義
0x00 // iInterface: 該接口的字符串描述符的索引,如果不存在則為0
/************** 端點(diǎn)描述符 ****************/
0x07 // bLength: 描述符長度,固定為7
0x05 // bDescriptorType: 描述符類型,表示該描述符的類型為Endpoint Descriptor,固定為5
0x81 // bEndpointAddress: 該端點(diǎn)的地址,包括端點(diǎn)方向和端點(diǎn)編號,表示為IN端點(diǎn)1
0x02 // bmAttributes: 該端點(diǎn)的屬性,包括傳輸類型和數(shù)據(jù)類型等,表示為Bulk傳輸類型
0x40
0x00 // wMaxPacketSize: 該端點(diǎn)所支持的最大數(shù)據(jù)包大小,單位為字節(jié)
0x00 // bInterval: 該端點(diǎn)所需的輪詢間隔,單位為毫秒
/************** 端點(diǎn)描述符 ****************/
0x07 // bLength: 描述符長度,固定為7
0x05 // bDescriptorType: 描述符類型,表示該描述符的類型為Endpoint Descriptor,固定為5
0x02 // bEndpointAddress: 該端點(diǎn)的地址,包括端點(diǎn)方向和端點(diǎn)編號,表示為OUT端點(diǎn)2
0x02 // bmAttributes: 該端點(diǎn)的屬性,包括傳輸類型和數(shù)據(jù)類型等,表示為Bulk傳輸類型
0x40
0x00 // wMaxPacketSize: 該端點(diǎn)所支持的最大數(shù)據(jù)包大小,單位為字節(jié)
0x00 // bInterval: 該端點(diǎn)所需的輪詢間隔,單位為毫秒
};
設(shè)備描述符定義了一個未指定功能的USB設(shè)備,支持的USB協(xié)議為USB2.0,單次傳輸最大數(shù)據(jù)包傳輸大小為64字節(jié),配置描述符描述了未定義USB設(shè)備的接口,接口對應(yīng)功能也是未定義的,所以也不能通過端點(diǎn)傳輸數(shù)據(jù)
配置從機(jī)類型
以上設(shè)備描述符中有三個重要的值,用于指定USB設(shè)備身份:
- bDeviceClass:表明設(shè)備類別
- bDeviceSubClass:表明設(shè)備子類別
- bDeviceProtocol:表明設(shè)備協(xié)議
對于常用的設(shè)備類型,它們有以下組合方式:
設(shè)備類別 | bDeviceClass | bDeviceSubClass(多選一) | bDeviceProtocol(多選一) |
---|---|---|---|
未定義 | 0x00 | 0x00 | 0x00 |
人機(jī)交互類設(shè)備 HID | 0x03 | 未定義:0x00 啟動接口子類:0x01 鼠標(biāo)接口子類:0x02 搖桿接口子類:0x03 游戲手柄接口子類:0x04 鍵盤接口子類:0x05 … |
未定義:0x00 鍵盤協(xié)議:0x01 鼠標(biāo)協(xié)議:0x02 搖桿協(xié)議:0x03 游戲手柄協(xié)議:0x04 … |
通信類設(shè)備 CDC | 0x02 | 未定義:0x00 ACM子類:0x01 ECM子類:0x02 NCM子類:0x03 自定義子類:0xFE |
未定義:0x00 V.25ter協(xié)議:0x01 AT命令協(xié)議:0x02 PPP協(xié)議:0x03 Ethernet Emulation協(xié)議:0x04 ATM Emulation協(xié)議:0x05 供應(yīng)商自定義協(xié)議: 0xFF |
大容量存儲設(shè)備 MSC | 0x08 | 未定義:0x00 公開SCSI命令集子類:0x01 ATAPI命令集子類:0x02 USB命令集子類:0x03 自定義子類:0xFE |
未定義:0x00 USB批量傳輸協(xié)議:0x01 公開SCSI協(xié)議:0x50 UAS傳輸協(xié)議:0x62 自定義協(xié)議:0x80~0xFF |
組合設(shè)備 | 0xEF | 通用子類:0x01 IAD多接口子類:0x02 供應(yīng)商自定義子類:0x0F |
未定義:0x00 IAD多接口協(xié)議:0x01 供應(yīng)商自定義協(xié)議:0xFF |
配置設(shè)備專用的描述符
要配置設(shè)備專用描述符和從機(jī)端點(diǎn),首先要了解配置描述符的組成。常見的配置描述符結(jié)構(gòu)如下:
能看出配置描述符的一些特點(diǎn):
- 上層描述符如果包含下層描述符,那么下層描述符的實(shí)現(xiàn)必須緊挨著上層描述符
- 一個上層描述符如果包含了多個下層描述符,那多個下層描述符的實(shí)現(xiàn)按照順序緊挨著上層描述符
- 一個配置描述符中可以有多個接口描述符
- 一個接口描述符可以包含多個端點(diǎn)描述符
- 多個接口描述符通過接口關(guān)聯(lián)描述符整合
接口關(guān)聯(lián)描述符
Interface Associate Descriptor接口關(guān)聯(lián)描述符。在USB部分設(shè)備中,一般實(shí)現(xiàn)某個設(shè)備功能只會要求使用一個接口,但也可能會要求使用兩個或以上的接口,這時候就需要將使用到的多個接口,通過IAD描述符關(guān)聯(lián)起來,以便于主機(jī)識別這些關(guān)聯(lián)接口之間的關(guān)系。一般來說IAD描述符出現(xiàn)在配置描述符之后,接口描述符之前,IAD描述符之后會緊跟著相關(guān)聯(lián)的接口描述符
根據(jù)上述的特點(diǎn),設(shè)備專用的描述符只要插入了其上層描述符之后就可以了,如果有多個,按照包含順序緊挨著實(shí)現(xiàn)。一般來說因?yàn)榻涌诿枋龇驮O(shè)備的功能有緊密的聯(lián)系,所以設(shè)備專用的描述符位置都和接口描述符并列,或者包含于接口描述符之中
配置從機(jī)端點(diǎn)
端點(diǎn)描述符的配置較為簡單,一般要:
- 確定需要的端點(diǎn)數(shù)量,以及每個端點(diǎn)的數(shù)據(jù)方向:輸入/輸出
- 配置緩沖區(qū)描述表,為緩沖區(qū)描述表頭和端點(diǎn)分配讀寫緩沖區(qū)
緩沖區(qū)及緩沖區(qū)描述表
緩沖區(qū)是在STM32 USB標(biāo)準(zhǔn)庫開發(fā)中的概念。緩沖區(qū)實(shí)際大小為512Bytes,位于STM32的RAM中,地址為0x40006000~0x400063FF(大小為1KByte,由于對于內(nèi)核是通過32位的讀取方式讀取緩沖的,而USB外設(shè)是通過16位的方式讀取緩沖區(qū)。內(nèi)核讀取到的四個字節(jié)只有兩個是有意義的,另一半由于沒有對應(yīng)的實(shí)際存儲器,會讀取不到東西),用于存放緩沖區(qū)描述表,同時還用作端點(diǎn)收發(fā)數(shù)據(jù)Buffer。緩沖區(qū)描述表是緩沖區(qū)中的一個區(qū)域,記錄緩沖區(qū)端點(diǎn)相關(guān)寄存器信息。緩沖區(qū)中除了描述表的以外空間可以分配給端點(diǎn),作為收發(fā)數(shù)據(jù)Buffer,Buffer之間不能重疊,一個Buffer的最大為64Bytes
緩沖區(qū)描述表頭和端點(diǎn)分配
緩沖區(qū)描述表頭應(yīng)該位于緩沖區(qū)的最前面,記錄每個端點(diǎn)相關(guān)的四個寄存器信息,包括緩沖地址寄存器,發(fā)送數(shù)據(jù)字節(jié)寄存器,接收緩沖地址寄存器,接收數(shù)據(jù)字節(jié)寄存器,每個寄存器占用兩個字節(jié),所以每增加一個端點(diǎn)會讓表頭增大八個字節(jié)。一般來說緊挨著表頭是端點(diǎn)0Buffer地址,會定義在距離表頭64Bytes的位置,因?yàn)閱蝹€USB設(shè)備可用端點(diǎn)為8個,所以所有的寄存器信息大小為8*8=64Bytes。之后的表頭地址就根據(jù)各個端點(diǎn)Buffer需求的大小定義即可,可參考以下示例:
// 端點(diǎn)數(shù)量
#define EP_NUM (5)
// 緩沖區(qū)描述表定義
// 緩沖區(qū)描述表頭
#define BTABLE_ADDRESS (0x00)
// 端點(diǎn)0發(fā)送/接收Buffer首地址,大小都為64Bytes
#define ENDP0_RXADDR (0x40)
#define ENDP0_TXADDR (0x80)
// 端點(diǎn)1發(fā)送/接收Buffer首地址,大小都為64Bytes
#define ENDP1_TXADDR (0xC0)
#define ENDP1_RXADDR (0x100)
// 端點(diǎn)2發(fā)送/接收Buffer首地址,大小都為64Bytes
#define ENDP2_TXADDR (0x140)
#define ENDP2_RXADDR (0x180)
// 端點(diǎn)3發(fā)送Buffer首地址,大小都為64Bytes
#define ENDP3_TXADDR (0x1C0)
實(shí)現(xiàn)設(shè)備類特定請求
USB設(shè)備類特定請求屬于USB設(shè)備標(biāo)準(zhǔn)請求的拓展,其數(shù)據(jù)內(nèi)容類似于標(biāo)準(zhǔn)請求,都有請求碼,索引,值和長度。USB設(shè)備標(biāo)準(zhǔn)請求是所有USB設(shè)備都支持的,它可以實(shí)現(xiàn)最基礎(chǔ)的USB設(shè)備配置和控制功能。而USB設(shè)備類特定請求指的是針對特定設(shè)備的請求,例如HID設(shè)備的GET_REPORT/SET_REPORT請求,CDC設(shè)備的GET_LINE_CODING/SET_LINE_COFING請求。具體到每種設(shè)備,其對類特定請求的定義和實(shí)現(xiàn)都不同,這類請求一般由開發(fā)者來實(shí)現(xiàn)
HID設(shè)備
HID設(shè)備是USB協(xié)議中定義的一種廣泛運(yùn)用于計(jì)算機(jī),游戲機(jī),工業(yè)設(shè)備等電子設(shè)備中的輸入設(shè)備子類,包括鼠標(biāo),鍵盤,搖桿,游戲手柄等。符合HID規(guī)范的設(shè)備不僅可以通過USB,還可以通過藍(lán)牙,無線電的方式同主機(jī)相連接,通過特殊的HID報文同主機(jī)進(jìn)行數(shù)據(jù)傳輸
HID設(shè)備有以下優(yōu)點(diǎn):
- 免驅(qū)動:HID設(shè)備的驅(qū)動通常都已經(jīng)內(nèi)置于操作系統(tǒng)之中
- 低功耗:HID設(shè)備通常都通過總線供電或自帶電池供電
- 多樣性:HID設(shè)備種類繁多,基本的輸入設(shè)備都可以作為HID設(shè)備,例如鼠標(biāo),鍵盤,手柄,指點(diǎn)桿等等
特點(diǎn)
USB HID協(xié)議中額外規(guī)定了HID設(shè)備/主機(jī)之間的通信格式和通信協(xié)議。所以相對于標(biāo)準(zhǔn)USB設(shè)備,HID設(shè)備實(shí)現(xiàn)上有以下的特點(diǎn):
- 設(shè)備描述符不同,配置描述符中多出了HID設(shè)備專用的HID主描述符,HID報文描述符,HID實(shí)體描述符(可選)
- 僅需定義三個端點(diǎn):一個端點(diǎn)0用傳輸控制;一個IN端點(diǎn),用于設(shè)備向主機(jī)發(fā)送數(shù)據(jù);一個OUT端點(diǎn),用于主機(jī)向設(shè)備發(fā)送數(shù)據(jù),其中IN,OUT端點(diǎn)都使用中斷傳輸模式
- 通過HID報文描述符定義的報文格式同主機(jī)溝通(報文格式多變,幾乎可以傳輸任何數(shù)據(jù),但其內(nèi)容也幾乎相當(dāng)于一門編程語言)
HID鍵盤描述符實(shí)例
const uint8_t HID_Keyboard_DeviceDescriptor[18] =
{
0x12, // bLength: 描述符長度,固定為0x12
0x01, // bDescriptorType: 描述符類型,固定為0x01,表示設(shè)備描述符
0x00, 0x02, // bcdUSB: USB協(xié)議版本號,這里是2.0
0x03, // bDeviceClass: 設(shè)備類別,3表示HID設(shè)備
0x01, // bDeviceSubClass: 設(shè)備子類別,1表示啟動接口子類
0x01, // bDeviceProtocol: 設(shè)備協(xié)議,1表示鍵盤協(xié)議
0x40, // bMaxPacketSize0: 最大數(shù)據(jù)包大小,這里是64字節(jié)
0x83, 0x04, // idVendor: USB設(shè)備廠商ID,這里是0x0483
0x20, 0x57, // idProduct: USB設(shè)備產(chǎn)品ID,這里是0x5720
0x00, 0x01, // bcdDevice: 設(shè)備版本號,這里是1.0
0x01, // iManufacturer: USB設(shè)備制造商字符串描述符的索引值,這里是1
0x02, // iProduct: USB設(shè)備產(chǎn)品字符串描述符的索引值,這里是2
0x03, // iSerialNumber: USB設(shè)備序列號字符串描述符的索引值,這里是3
0x01 // bNumConfigurations: USB設(shè)備支持的配置數(shù)量,這里是1
}
const uint8_t HID_Keyboard_ConfigDescriptor[41] =
{
/************** 配置描述符 ****************/
0x09 // bLength: 描述符長度,固定為9
0x02 // bDescriptorType: 描述符類型,表示該描述符的類型為Configuration Descriptor,固定為2
0x29
0x00 // wTotalLength: 該配置描述符及其所包含的所有描述符的總長度,單位為字節(jié)
0x01 // bNumInterfaces: 該配置所包含的接口數(shù)量
0x01 // bConfigurationValue: 該配置的值,用于選擇設(shè)備的配置
0x00 // iConfiguration: 該配置的字符串描述符的索引,如果不存在則為0
0xA0 // bmAttributes: 該配置的屬性,包括供電方式和遠(yuǎn)程喚醒功能等
0x32 // bMaxPower: 該配置所需的最大電流,單位為2mA
/************** 接口描述符 ****************/
0x09 // bLength: 描述符長度,固定為9
0x04 // bDescriptorType: 描述符類型,表示該描述符的類型為Interface Descriptor,固定為4
0x00 // bInterfaceNumber: 該接口的編號
0x00 // bAlternateSetting: 備用設(shè)置的編號,用于支持多種設(shè)置
0x02 // bNumEndpoints: 該接口所包含的端點(diǎn)數(shù)量
0x03 // bInterfaceClass: 該接口的類別,表示為Human Interface Device
0x01 // bInterfaceSubClass: 該接口的子類別,表示為啟動接口子類
0x01 // bInterfaceProtocol: 該接口的協(xié)議,表示為鍵盤協(xié)議
0x00 // iInterface: 該接口的字符串描述符的索引,如果不存在則為0
/************** HID主描述符 ****************/
0x09 // bLength: 描述符長度,固定為9
0x21 // bDescriptorType: 描述符類型,表示該描述符的類型為HID Descriptor,固定為33
0x10, 0x01, // bcdHID:HID規(guī)范版本(1.1)
0x00, // bCountryCode:國家代碼(未指定)
0x01, // bNumDescriptors:支持的描述符類型數(shù)
0x22, // bDescriptorType:描述符類型(報告描述符)
0x3F, 0x00, // wDescriptorLength:描述符長度
/************** 端點(diǎn)描述符 ****************/
0x07 // bLength: 描述符長度,固定為7
0x05 // bDescriptorType: 描述符類型,表示該描述符的類型為Endpoint Descriptor,固定為5
0x01 // bEndpointAddress: 該端點(diǎn)的地址,包括端點(diǎn)方向和端點(diǎn)編號,表示為OUT端點(diǎn)1
0x03 // bmAttributes: 該端點(diǎn)的屬性,包括傳輸類型和數(shù)據(jù)類型等,表示為Interrup傳輸類型
0x40
0x00 // wMaxPacketSize: 該端點(diǎn)所支持的最大數(shù)據(jù)包大小64字節(jié)
0x0A // bInterval: 該端點(diǎn)所需的輪詢間隔10ms
/************** 端點(diǎn)描述符 ****************/
0x07 // bLength: 描述符長度,固定為7
0x05 // bDescriptorType: 描述符類型,表示該描述符的類型為Endpoint Descriptor,固定為5
0x81 // bEndpointAddress: 該端點(diǎn)的地址,包括端點(diǎn)方向和端點(diǎn)編號,表示為IN端點(diǎn)1
0x03 // bmAttributes: 該端點(diǎn)的屬性,包括傳輸類型和數(shù)據(jù)類型等,表示為Interrup傳輸類型
0x40
0x00 // wMaxPacketSize: 該端點(diǎn)所支持的最大數(shù)據(jù)包大小64字節(jié)
0x0A // bInterval: 該端點(diǎn)所需的輪詢間隔10ms
};
以上描述符實(shí)現(xiàn)了一個HID鍵盤設(shè)備,其描述符分別指出:
- 設(shè)備描述符:支持USB 1.1協(xié)議,支持啟動接口的HID鍵盤
- 配置描述符:包含一個接口
- 接口描述符:定義了設(shè)備為HID設(shè)備,支持在BIOS使用,支持鍵盤HID協(xié)議
- HID主描述符:HID設(shè)備有兩個端點(diǎn),最大包大小為64字節(jié),最大電流為100mA,支持自供電,不支持遠(yuǎn)程喚醒。同時還指定了HID設(shè)備支持HID報文描述符,報文描述符長度為63字節(jié)
- 備注:HID報文描述符的實(shí)現(xiàn)需要額外定義,不在配置描述符中實(shí)現(xiàn)
HID報文描述符
前面提到HID設(shè)備通過特殊的HID報文同主機(jī)進(jìn)行數(shù)據(jù)傳輸。這里的報文,指的是可變長度,指定格式的特殊數(shù)據(jù),其中:
- 可變長度:HID報文描述符中沒有指定描述符長度,其長度由開發(fā)者定義
- 指定格式:HID報文數(shù)據(jù)需要根據(jù)HID報文描述符中描述的格式進(jìn)行解析,才能變成有效的信息
HID報文描述符中最基本的單元是條目(Item),條目又分為長條目和短條目,長條目不經(jīng)常使用,所以此處僅討論短條目
短條目
短條目由前綴+可選的數(shù)據(jù)組成,其中:
- 前綴:1字節(jié)的數(shù)據(jù),用于描述可選數(shù)據(jù)含義的元信息
短條目的前綴部分大小為一個字節(jié),這一個字節(jié)的八個比特位被分為三部分:
- bSize:D0~D1,占用1位,用于表示該條目的數(shù)據(jù)長度,單位為字節(jié)。0表示0字節(jié),1表示1字節(jié),2表示2字節(jié),3表示4字節(jié)
- bType:D2~D3,占用2位,用于表示該條目的數(shù)據(jù)類型。0表示主條目,1表示全局條目,2表示局部條目
- bTag:D4~D7,占用4位,用于表示該條目的功能。根據(jù)不同的條目類型,同樣的bTag會有不同的含義
![]()
- 可選的數(shù)據(jù):可以是0字節(jié),1字節(jié),2字節(jié)或者4字節(jié)大小的數(shù)據(jù),一般使用1字節(jié)居多
可選數(shù)據(jù)不是獨(dú)立存在的,需要通過條目的前綴去解析可選數(shù)據(jù),到官方提供的HID Usage Table中查詢到對應(yīng)的意義
實(shí)際的代碼中,通常將短條目分為前綴和可選數(shù)據(jù),分別用兩位十六進(jìn)制數(shù)表示,其形式為:
// 前綴 數(shù)據(jù)
0x81, 0x06 // 表示主條目的Input
其形式類似于鍵值對,前綴為鍵,數(shù)據(jù)為值
報文生成
通過將相關(guān)的條目進(jìn)行組合,能得到有層次結(jié)構(gòu)的HID報文描述符。基本的報文描述符結(jié)構(gòu)如下:
可以看出,報文描述符分為兩部分:
- 外層綠色部分:由Usage Page/Usage/Collection/End Collection控制符組成,被稱為集合,它描述了此報文的最基本功能,定義了報文的使用頁面,使用類型和字段組合方式。值得注意的是,最外層并不一定就包含了此報文的所有信息,復(fù)雜的報文,可能會涉及多語言設(shè)計(jì),加密,壓縮等機(jī)制,需要具體分析
- 內(nèi)層橙色/藍(lán)色部分:由Usage Page/Usage Minimum/Usage Maximum/Logical Minimum/Logical Maximum/Input/Output等控制符組成,被稱為字段,它描述了報文數(shù)據(jù)的具體組合方式,各個元素的含義和取值范圍
HID鍵盤報文描述符實(shí)例
通過對一個HID鍵盤的基礎(chǔ)報文描述符的分析,可以很好的對以上所有的HID描述符相關(guān)概念進(jìn)行收尾
const uint8_t CustomHID_ReportDescriptor[63] ={
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xE0, // Usage Minimum (224)
0x29, 0xE7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data, Variable, Absolute) ; Modifier byte
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x81, 0x01, // Input (Constant) ; Reserved byte
0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (1)
0x29, 0x05, // Usage Maximum (5)
0x75, 0x01, // Report Size (1)
0x95, 0x05, // Report Count (5)
0x91, 0x02, // Output (Data, Variable, Absolute) ; LED report
0x75, 0x03, // Report Size (3)
0x95, 0x01, // Report Count (1)
0x91, 0x01, // Output (Constant) ; LED report padding
0x05, 0x07, // Usage Page (Key Codes)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x75, 0x08, // Report Size (1)
0x95, 0x06, // Report Count (48)
0x81, 0x00, // Input (Data, Array) ; Key array (6 keys)
0xC0 // End Collection
};
可以看出HID報文描述符在C語言中,是通過結(jié)構(gòu)體實(shí)現(xiàn)的。其中每一行都是一個短條目,短條目由前綴+1字節(jié)數(shù)據(jù)構(gòu)成(可以參考上面說到的條目表現(xiàn)形式)。最外層的集合告知計(jì)算機(jī),要將此報文作為鍵盤數(shù)據(jù)進(jìn)行解析。最內(nèi)層的字段描述了報文數(shù)據(jù)的結(jié)構(gòu),報文數(shù)據(jù)大小為9個字節(jié),共分為3部分:
閱讀報文描述符的技巧
由于報文由條目組成,結(jié)構(gòu)上有一定的特殊性,按照正常的順序?yàn)g覽可能會比較亂,所以閱讀前先將報文合理分段,每段按照從后往前閱讀,會比較好理解
-
Modifier Keys:結(jié)合Modifier Keys段報文描述符代碼進(jìn)行單獨(dú)解析
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) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 【表示單個按鍵可存在的邏輯狀態(tài),為0釋放/1按下】
0x75, 0x01, // Report Size (1)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 【表示報文大小,單位比特】
0x95, 0x08, // Report Count (8)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 【表示報文數(shù)量】
0x81, 0x02, // Input (Data, Variable, Absolute) ; Modifier byte 【表示該段報文的數(shù)據(jù)方向,數(shù)據(jù)類型】
【分界線】
0x95, 0x01, // Report Count (1)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 【表示報文大小,單位比特】
0x75, 0x08, // Report Size (8)? ? ? ? ? ???? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 【表示報文數(shù)量】
0x81, 0x01, // Input (Constant) ; Reserved byte???????????????????????【表示報文的數(shù)據(jù)方向,數(shù)據(jù)類型】
代碼可以通過【分界線】分為兩部分來看:
前半部分:這部分定義了報文數(shù)據(jù)類型為輸入計(jì)算機(jī)的絕對值變量,用途為鍵碼,報文數(shù)據(jù)數(shù)量為8,單個報文數(shù)據(jù)大小為1bit,所以總的數(shù)據(jù)量為8x1bit=8bits=1Byte。而八個比特位能表示八個按鍵,鍵碼范圍從224~231,對應(yīng)如下圖的八個特殊按鍵(參考官方HID Usage Table)八個比特位分別按照高位對大鍵碼,低位位對小鍵碼的關(guān)系一一對應(yīng),每個比特位的0/1裝袋分別表示按鍵被釋放/按下。例如0b0000 0001對應(yīng)就是LeftControl鍵按下
后半部分:這部分定義了報文數(shù)據(jù)類型為輸入計(jì)算機(jī)的常量,沒有指明實(shí)際用途,主要起到了填充數(shù)據(jù)的作用,保持整體數(shù)據(jù)按照8bits對齊,報文數(shù)據(jù)數(shù)量為8,單個報文數(shù)據(jù)大小為1bit,所以后半部分的數(shù)據(jù)量為8x1bit=8bits=1Byte
整體字段描述的數(shù)據(jù)的大小為2Bytes,描述的數(shù)據(jù)內(nèi)容為Modified Keys -
LED:這部分字段定義了報文數(shù)據(jù)類型為輸出計(jì)算機(jī)的絕對值變量,用途為LED燈,報文數(shù)據(jù)數(shù)量為48,單個報文數(shù)據(jù)大小為1bit,所以總的數(shù)據(jù)量為5x1bit=5bits。五個比特位能表示五個LED燈,燈代表的含義范圍從1~5,對應(yīng)如下圖的五個LED(參考官方HID Usage Table):
LED由計(jì)算機(jī)控制亮滅。這部分字段還額外帶了一個 3bits的填充數(shù)據(jù)。
整體字段描述的數(shù)據(jù)的大小為1Bytes,描述的數(shù)據(jù)內(nèi)容為LED -
Normal Keys:這部分字段定義了報文數(shù)據(jù)類型為輸入計(jì)算機(jī)的絕對值變量,用途為鍵碼,報文數(shù)據(jù)數(shù)量為48,單個報文數(shù)據(jù)大小為1bit,所以總的數(shù)據(jù)量為48x1bit=48bits=6Byte。而六個字節(jié)能表示四十八個按鍵,鍵碼范圍從0~101,對應(yīng)如下圖的102個特殊按鍵(參考官方HID Usage Table)
整體字段描述的數(shù)據(jù)的大小為6Bytes,描述的數(shù)據(jù)內(nèi)容為Normal Keys
報文數(shù)據(jù)大小為2+1+6=9Bytes,報文內(nèi)容如下
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 |
報文數(shù)據(jù)本身由HID鍵盤生成并放置到對應(yīng)IN端點(diǎn),等待計(jì)算機(jī)按照端點(diǎn)描述符中定義的中斷時間間隔內(nèi)來獲取。計(jì)算機(jī)取到報文數(shù)據(jù)后,自帶的HID驅(qū)動會按照HID鍵盤報文描述符解析此報文,得到鍵盤釋放/按下的消息。最終按鍵消息會轉(zhuǎn)發(fā)給操作系統(tǒng)底層,由操作系統(tǒng)執(zhí)行對應(yīng)響應(yīng)操作
總結(jié)
HID報文描述符可能是所有USB描述符中最復(fù)雜的,抽象的概念相當(dāng)多,理解起來也需要一定的時間。但是復(fù)雜的定義帶來的卻是報文易用性和靈活性,它幾乎可以描述任何形式的數(shù)據(jù),而且長度都由開發(fā)者來定義,形式也較為簡潔清晰
CDC設(shè)備
CDC設(shè)備USB協(xié)議中定義的一種通信設(shè)備(通常指電信通信設(shè)備和中速網(wǎng)絡(luò)通信設(shè)備)子類,包括調(diào)制解調(diào)器,網(wǎng)絡(luò)適配器,手機(jī),PDA等。通信設(shè)備領(lǐng)域,使用到很多種無線/有線通信協(xié)議以及各式的數(shù)據(jù)傳輸方式,CDC協(xié)議提供了對這些設(shè)備的協(xié)議和數(shù)據(jù)傳輸支持,例如常見的COM口UART協(xié)議,以太網(wǎng)口IP協(xié)議等。通過CDC協(xié)議可以將USB設(shè)備虛擬成通信設(shè)備,通過指定的傳輸協(xié)議和數(shù)據(jù)傳輸方式同USB設(shè)備進(jìn)行通信
CDC設(shè)備有以下優(yōu)點(diǎn):
- 免驅(qū)動:使用標(biāo)準(zhǔn)的USB接口和通信協(xié)議,部署方便。例如USB虛擬COM口,無需安裝驅(qū)動即可使用
- 支持多種通信協(xié)議:支持很多通信領(lǐng)域的通信協(xié)議,可以滿足不同行業(yè),不同場景的需求
特點(diǎn)
USB CDC協(xié)議中同樣額外規(guī)定了CDC設(shè)備/主機(jī)之間的通信格式和通信協(xié)議,以CDC-ACM設(shè)備為例,相對于標(biāo)準(zhǔn)USB設(shè)備,它實(shí)現(xiàn)上有以下的特點(diǎn):
- 設(shè)備描述符不同,配置描述符中多出了CDC設(shè)備專用的CDC控制接口描述符,頭部功能描述符,呼叫管理描述符,抽象控制模型描述符,聯(lián)合功能描述符,CDC數(shù)據(jù)接口描述符,
CDC設(shè)備的接口組成
不同于常見的單接口設(shè)備,CDC設(shè)備由多個接口類組成,分別是【CDC控制接口類】和【CDC數(shù)據(jù)接口類】(可選)。所以在較新的操作系統(tǒng)中需要用額外的IAD描述符將兩個接口關(guān)聯(lián)起來。其中:
- CDC控制接口類:負(fù)責(zé)完成通信設(shè)備的配置和管理,包含了CDC控制接口,頭部功能描述符,呼叫管理功能描述符,抽象控制模型描述符,聯(lián)合功能描述符。各個描述符的作用分別為:
【CDC控制接口】:用于設(shè)備管理/電話管理(可選),設(shè)備管理一般涉及請求(request)和通知(notification),請求一般由端點(diǎn)0進(jìn)行處理,通知需要額外配置其他端點(diǎn)進(jìn)行處理
【頭部功能描述符】:用于表示CDC功能描述符的開始,緊跟其后的是CDC設(shè)備的其他功能描述符
【呼叫管理功能描述符】:負(fù)責(zé)電話,AT指令相關(guān)的功能
【抽象控制模型描述符】:PSTN(Public Switched Telephone Net)下定義的抽象控制模型。用于描述使用AT指令,或AT.25Ver指令的調(diào)制解調(diào)設(shè)備
【聯(lián)合功能描述符】:用于告知計(jì)算機(jī),將哪幾個接口聯(lián)合起來,用來表示CDC功能。類似于IAD接口關(guān)聯(lián)描述符- CDC數(shù)據(jù)接口類:負(fù)責(zé)進(jìn)行數(shù)據(jù)的傳輸,包含了數(shù)據(jù)接口,數(shù)據(jù)調(diào)制解調(diào)器接口(可選),后者用于模擬調(diào)制解調(diào)設(shè)備的控制信號
接口關(guān)聯(lián)描述符和聯(lián)合功能描述符的關(guān)系
在USB協(xié)議的初期,沒有規(guī)劃好多接口設(shè)備的支持,基本上都是一個設(shè)備只使用一個接口。后來出現(xiàn)了CDC設(shè)備這樣需要使用多接口的設(shè)備,于是便專門出了聯(lián)合功能描述符用于應(yīng)對。等到后期USB協(xié)議逐漸完善,推出了接口關(guān)聯(lián)描述符這樣的用于多個接口關(guān)聯(lián)的描述符。兩者使用起來并不會沖突,在舊版操作系統(tǒng)中,會忽略接口關(guān)聯(lián)描述符,識別聯(lián)合功能描述符;在新版操作系統(tǒng)中會忽略聯(lián)合功能描述符,僅識別接口關(guān)聯(lián)描述符
CDC-ACM虛擬串口描述符實(shí)例
const uint8_t VirtualComPort_DeviceDescriptor[18] =
{
0x12, // bLength: 描述符長度,固定為0x12
0x01, // bDescriptorType: 描述符類型,固定為0x01,表示設(shè)備描述符
0x00, 0x02, // bcdUSB: USB協(xié)議版本號,這里是2.0
0x02, // bDeviceClass: 設(shè)備類別,2表示CDC設(shè)備
0x01, // bDeviceSubClass: 設(shè)備子類別,1表示使用抽象控制模型
0x01, // bDeviceProtocol: 設(shè)備協(xié)議,1表示使用AT指令交互
0x40, // bMaxPacketSize0: 最大數(shù)據(jù)包大小,這里是64字節(jié)
0x83, 0x04, // idVendor: USB設(shè)備廠商ID,這里是0x0483
0x20, 0x57, // idProduct: USB設(shè)備產(chǎn)品ID,這里是0x5720
0x00, 0x01, // bcdDevice: 設(shè)備版本號,這里是1.0
0x01, // iManufacturer: USB設(shè)備制造商字符串描述符的索引值,這里是1
0x02, // iProduct: USB設(shè)備產(chǎn)品字符串描述符的索引值,這里是2
0x03, // iSerialNumber: USB設(shè)備序列號字符串描述符的索引值,這里是3
0x01 // bNumConfigurations: USB設(shè)備支持的配置數(shù)量,這里是1
}
const uint8_t VirtualComPort_ConfigDescriptor[41] =
{
/************** 配置描述符 ****************/
0x09 // bLength: 描述符長度,固定為9
0x02 // bDescriptorType: 描述符類型,表示該描述符的類型為Configuration Descriptor,固定為2
0x29
0x00 // wTotalLength: 該配置描述符及其所包含的所有描述符的總長度,單位為字節(jié)
0x01 // bNumInterfaces: 該配置所包含的接口數(shù)量
0x01 // bConfigurationValue: 該配置的值,用于選擇設(shè)備的配置
0x00 // iConfiguration: 該配置的字符串描述符的索引,如果不存在則為0
0xA0 // bmAttributes: 該配置的屬性,包括供電方式和遠(yuǎn)程喚醒功能等
0x32 // bMaxPower: 該配置所需的最大電流,單位為2mA
/************** 接口關(guān)聯(lián)描述符 ****************/
/* 09 */
0x08, // bLength:描述符長度,固定為8
0x0B, // bDescriptorType:描述符類型,表示該描述符的類型為接口關(guān)聯(lián)描述符,固定為b
0x00, // bFirstInterface:第一個接口描述符編號 0
0x02, // bInterfaceCount:關(guān)聯(lián)接口描述符總數(shù) 2
0x02, // bFunctionClass:功能類 CDC
0x02, // bFunctionSubClass:功能子類
0x01, // bFunctionProtocol:功能協(xié)議
0x00, // iFunction: 字符串描述符編號
/************** 控制接口描述符 ****************/
/* 17 */
0x09, // bLength:描述符長度,固定為9
0x04, // bDescriptorType:描述符類型,表示該描述符的類型為接口描述符,固定為4
0x00, // bInterfaceNumber:接口描述符編號
0x00, // bAlternateSetting:替換設(shè)置
0x01, // bNumEndpoints 包含端點(diǎn)數(shù)
0x02, // bInterfaceClass: 接口類型 CDC
0x02, // bInterfaceSubClass:接口子類 抽象控制模型
0x01, // nInterfaceProtocol:接口協(xié)議 Common AT commands V.250
2, // iInterface: 字符串描述符編號
/************** 頭部功能描述符 ****************/
/* 26 */
0x05, // bLength:描述符長度,固定為5
0x24, // bDescriptorType:描述符類型 CS_INTERFACE
0x00, // bDescriptorSubtype:描述符子類 頭部功能描述符
0x10, // bcdCD:CDC協(xié)議版本1.10
0x01,
/************** 呼叫功能描述符 ****************/
/* 31 */
0x05, // bFunctionLength:描述符長度,固定為5
0x24, // bDescriptorType:描述符類型 CS_INTERFACE
0x01, // bDescriptorSubtype:描述符子類 頭部功能描述符
0x00, // bmCapabilities: D0+D1 */
0x01, // bDataInterface: 數(shù)據(jù)描述符接口號 1 */
/************** 抽象控制模型描述符 ****************/
/* 36 */
0x04, // bFunctionLength:描述符長度,固定為4
0x24, // bDescriptorType:描述符類型 CS_INTERFACE
0x02, // bDescriptorSubtype:描述符子類 頭部功能描述符
0x02, // bmCapabilities */
/************** 聯(lián)合功能描述符 ****************/
/* 40 */
0x05, // bFunctionLength:描述符長度,固定為5
0x24, // bDescriptorType:描述符類型 CS_INTERFACE
0x06, // bDescriptorSubtype:描述符子類 聯(lián)合功能描述符
0x00, // bMasterInterface:主接口描述符描述符
0x01, // bSlaveInterface:附屬接口描述符描述符
/************** 控制端點(diǎn)描述符 ****************/
/* 45 */
0x07, // bLength:描述符長度,固定為7
0x05, // bDescriptorType:描述符類型 端點(diǎn)描述符
0x83, // bEndpointAddress:IN端點(diǎn)3
0x03, // bmAttributes:端點(diǎn)類型 中斷
0x40 // wMaxPacketSize:最大數(shù)據(jù)包容量 64字節(jié)
0x00,
0x02, // bInterval:數(shù)據(jù)獲取間隔 2ms
/************** 數(shù)據(jù)接口描述符 ****************/
/* 52 */
0x09, // bLength:描述符長度,固定為9
0x04, // bDescriptorType:描述符類型,表示該描述符的類型為接口描述符,固定為4
0x02, // bInterfaceNumber:接口描述符編號
0x00, // bAlternateSetting:替換設(shè)置
0x02, // bNumEndpoints 包含端點(diǎn)數(shù)
0x0A, // bInterfaceClass: 接口類型 CDC數(shù)據(jù)接口
0x00, // bInterfaceSubClass:接口子類 未定義
0x00, // nInterfaceProtocol:接口協(xié)議 未定義
0, // iInterface: 字符串描述符編號
/************** 數(shù)據(jù)端點(diǎn)描述符 ****************/
/* 61 */
0x07, // bLength:描述符長度,固定為7
0x05, // bDescriptorType:描述符類型 端點(diǎn)描述符
0x02, // bEndpointAddress:OUT端點(diǎn)2
0x02, // bmAttributes:端點(diǎn)類型 批量
0x40 // wMaxPacketSize:最大數(shù)據(jù)包容量 64字節(jié)
0x00,
0x00, // bInterval:數(shù)據(jù)獲取間隔 忽略
/* 68 */
0x07, // bLength:描述符長度,固定為7
0x05, // bDescriptorType:描述符類型 端點(diǎn)描述符
0x82, // bEndpointAddress:IN端點(diǎn)2
0x02, // bmAttributes:端點(diǎn)類型 批量
0x40 // wMaxPacketSize:最大數(shù)據(jù)包容量 64字節(jié)
0x00,
0x00, // bInterval:數(shù)據(jù)獲取間隔 忽略
/* 75 */
};
以上描述符實(shí)現(xiàn)了一個CDC虛擬串口設(shè)備,其描述符分別指出:
- 設(shè)備描述符:支持USB 1.1協(xié)議,支持抽象控制模型,使用AT指令的CDC設(shè)備
- 配置描述符:包含兩個接口,分別是CDC控制接口,CDC數(shù)據(jù)接口
- 數(shù)據(jù)關(guān)聯(lián)描述符:實(shí)現(xiàn)多個接口對應(yīng)一個功能,例子中是CDC設(shè)備功能
- 控制接口描述符:定義一個其他端點(diǎn),用作異步傳輸
- 頭部功能描述符:CDC設(shè)備支持的協(xié)議版本1.10
- 呼叫功能描述符:只有在電話功能中會使用,虛擬串口設(shè)備中沒有使用
- 抽象控制模型描述符:表明CDC設(shè)備使用抽象控制模型進(jìn)行通信
- 聯(lián)合功能描述符:關(guān)聯(lián)CDC設(shè)備的控制接口和數(shù)據(jù)接口
- 控制端點(diǎn)描述符:實(shí)現(xiàn)設(shè)備控制功能,控制功能可以處理通信設(shè)備需要的請求和通知
- 數(shù)據(jù)接口描述符:定義2個數(shù)據(jù)傳輸所使用的端點(diǎn)
- 數(shù)據(jù)端點(diǎn)描述符:處理數(shù)據(jù)傳輸
類特定請求
相對于HID設(shè)備通過特殊的HID報文進(jìn)行數(shù)據(jù)傳輸,CDC-ACM設(shè)備直接通過端點(diǎn)發(fā)送/接收數(shù)據(jù),無需進(jìn)行其他轉(zhuǎn)換和解析。但是需要在CDC-ACM設(shè)備端實(shí)現(xiàn)CDC-ACM類的設(shè)備類特定請求SetLineCoding和GetLineCoding,這兩個請求主要實(shí)現(xiàn)了設(shè)置和獲取虛擬串口的串行通信參數(shù),包括波特率,數(shù)據(jù)傳輸位數(shù),奇偶校驗(yàn)位,停止位,具體實(shí)現(xiàn)如下:
typedef struct
{
uint32_t bitrate;
uint8_t format;
uint8_t paritytype;
uint8_t datatype;
}LINE_CODING;
LINE_CODING linecoding =
{
115200, // 波特率
0x00, // 停止位 1
0x00, // 奇偶校驗(yàn)位 無
0x08 // 數(shù)據(jù)位數(shù) 8
};
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;
}
// 函數(shù)等待實(shí)現(xiàn)
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組合設(shè)備
在一個USB設(shè)備上實(shí)現(xiàn)多個功能,例如HID鍵盤+CDC虛擬串口。有兩種方法:
- 組合設(shè)備:Composite Device,內(nèi)部通過組合設(shè)備描述符,以及組織多個功能的接口實(shí)現(xiàn)多功能組合。所有功能共用一套PID/VID/DID
- 復(fù)合設(shè)備:Compound Device,內(nèi)部通過USB HUB來實(shí)現(xiàn),將多個單一功能的USB設(shè)備,通過USB HUB連接整合,每個設(shè)備有自己獨(dú)立的PID/VID/DID。這種方法復(fù)雜度相當(dāng)高,具體可以參考USB2.0協(xié)議
實(shí)現(xiàn)思路
通過以上對HID設(shè)備和CDC設(shè)備的解析。通過STM32去實(shí)現(xiàn)USB組合設(shè)備的思路就相當(dāng)清晰了:文章來源:http://www.zghlxwxcb.cn/news/detail-444253.html
- 首先需要將USB的設(shè)備描述符中設(shè)備類型改為Miscellaneous Device,表示組合設(shè)備
- 然后將HID鍵盤的配置描述符和CDC虛擬串口的配置描述符整合,主要注意處理接口關(guān)聯(lián)描述符,接口描述符,CDC控制接口描述符,CDC數(shù)據(jù)接口描述符,HID描述符的次序即可,HID報文描述符可以直接復(fù)用
- 之后實(shí)現(xiàn)HID鍵盤的GET_REPORT/SET_REPORT設(shè)備類特定請求,以及CDC虛擬串口的GET_LINE_CODING/SET_LINE_COFING請求
- 最后按照自己的需求完成端點(diǎn)數(shù)據(jù)的填充即可,USB主機(jī)會按照端點(diǎn)描述符要求進(jìn)行數(shù)據(jù)傳輸
代碼實(shí)例
USB組合設(shè)備代碼已上傳Github:Sinuxtm32文章來源地址http://www.zghlxwxcb.cn/news/detail-444253.html
到了這里,關(guān)于第10課【STM32 USB通訊協(xié)議實(shí)戰(zhàn)】HID鍵盤+CDC虛擬串口組合設(shè)備的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!