我介绍一种直接通过Windows库函数,实现对USB端口的访问,这是一种更底层的操作,当然用起来也就更加灵活,上周刚刚测试成功,也让我对USB有了更深刻的理解。

USB驱动
在嵌入式系统里,USB相对于其它如串口,SPI等外设,要复杂的多,不过随着USB的应用越来越广,学会使用USB是非常重要的。现在USB设备都是即插即用的(Plug and Play, PnP)设备。那么PC机是怎么知道插入的设备是什么,又如何实现与USB设备的读写的呢?
为了保证内容的连惯性。先简单介绍一下USB描述符。简单的说,描述符就是一些连接存储的数据结构,通过第一个结构,可以解析出第二个结构的信息,通过第二个结构,可以解析出第三个结构的信息,如此下去。由此可见,USB描述符是逐个解析的过程。每个描述符的第一个字节是说明了本结构的字节数,第二个字节说明了本结构的类型,从第三字节开始是具体数据。这些描述符定义了USB的所有信息,包含厂家及主机如何与之通信。常用的几种描述符如下:

描述符
1.USB设备描述符
每个USB设备都定义了一个自己的设备描述符,它是第一个描述符。我们在电脑设备管理器里看到的信息,都在这个结构里定义。下面是一个PIC单片机的设备描述符结构。
typedef struct _USB_DEVICE_DESCRIPTOR {UCHAR bLength;//本结构字节数 UCHAR bDescriptorType;//本结构类型 USHORT bcdUSB;//版本2.0,3.0? UCHAR bDeviceClass;//设备类型 UCHAR bDeviceSubClass;//子类型 UCHAR bDeviceProtocol;//通信协议 UCHAR bMaxPacketSize0; USHORT idVendor;//厂家代码 USHORT idProduct;//产品代码 USHORT bcdDevice;//版本 UCHAR iManufacturer; UCHAR iProduct; UCHAR iSerialNumber; UCHAR bNumConfigurations;//配置描述符数量 } USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
复制代码设备插入时,PC机会自动读取这个结构值,通过解析这个结构,PC会知道设备的类型(块设备、通信设备等)、VID(厂家代码)、PID(产品代码)及配置描述符的数量。

设备描述符
2.配置描述符
每个设备都至少有一个配置描述符。主要功有二个,一是指明本配置有几个接口(即虚拟设备数量,如一个USB可以多个串口,但是同一时候只能打开其中一个),很多时候只有一个接口。二是说明本设备在些配置下的功耗。
typedef struct _USB_CONFIGURATION_DESCRIPTOR {UCHAR bLength; UCHAR bDescriptorType;//类型 UORT wTotalLength;//配置总字节数 UCHAR bNumInterfaces;//接口数量 UCHAR bConfigurationValue;//本配置索引 UCHAR iConfiguration;//字索引数量 UCHAR bmAttributes;//位信息 UCHAR MaxPower;//功耗 } USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR;
复制代码当有多个配置时,主机通过bConfigurationValue可以选择启用哪一个配置,进一步可以选哪一个接口。至少有一个接口。
3.接口描述符
紧随上面的结构,是接口描述符。
typedef struct _USB_INTERFACE_DESCRIPTOR {UCHAR bLength; UCHAR bDescriptorType; //本接口的索引 UCHAR bInterfaceNumber; //本接口设置索引 UCHAR bAlternateSetting; //端点数量 UCHAR bNumEndpoints; //类型 UCHAR bInterfaceClass; //子类型 UCHAR bInterfaceSubClass; //通信协议 UCHAR bInterfaceProtocol量 UCHAR iInterface; } USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;
复制代码这个结构指明具体功能。包括端点的数量 ,功能类型(有些在设备描述符中定义,但是这里的信息更加具体)。
4.端点描述符
每个端点都有一个这个结构。指明端点的地址(即端点号),缓冲区大小,传输方式。
typedef struct _USB_ENDPOINT_DESCRIPTOR {UCHAR bLength; UCHAR bDescriptorType; UCHAR bEndpointAddress; UCHAR bmAttributes; //缓冲大小 USHORT wMaxPacketSize; //主机查询间隔 UCHAR bInterval; } USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;
复制代码注意,USB传输是主从模式,都是由主机发起的。传输方向也是相对主机而言的。这些描述符是层次递进的关系。上一层结构除了说明自身的信息外,还指明了下一个结构是什么。
本文是为C#调用Window库函数作一个技术准备,因为后面的函数里要用到这些概念。