教程:实现WinUSB通信系统的最简单的方式

2019-01-11 09:34:28 来源:ofweek
标签:

 

前言

USB接口作为PC上最流行和通用的接口,具备可连接多种类型的设备,连接简单,即插即用,支持热插拨,多数应用场景下不需要提供独立的电源,高传输速率,高可靠性等特点,被越来越多的产品作为首选接口作为接入PC的连接方式。为了简化USB设备的开发和接入到PC系统,微软开发了WinUSB,可以将Winusb.sys作为设备功能驱动程序安装,并提供WinUSB API供应用程序访问设备。一直以来,除了USB HID设备,其他类型的设备在WINDOWS环境下需要安装驱动程序才能工作。要实现USB设备免驱,就只能使用HID设备。而HID设备传输速度慢,在有些场合必须使用Bulk类型进行批量传输时,就必须使用第三方驱动或者自己开发一个驱动,使得项目开发非常麻烦。现在好了,自从微软推出了WinUSB,在微软的最新操作系统上实现简单的Bulk类型批量传输也变得非常的方便快捷,在研发过程当中或者一些对于差异化要求不高的场合,是非常适用且容易实现的。本文致力于实现一个最简单的WinUSB通信系统,以满足此类需求。

 

如何让嵌入式设备枚举成WinUSB设备

系统通过USB描述符来确定以何种USB Class类型来工作。如果希望WINDOWS能够将嵌入式设备识别为WinUSB设备,则其描述符至少应当包含以下字段:

 

1、支持 OS 字符串描述符:

为了让 USB 驱动程序堆栈了解设备支持扩展的特征描述符,设备必须定义存储在字符串索引 0xEE 处的 OS 字符串描述符。在枚举过程中,驱动程序堆栈查询字符串描述符。如果存在描述符,驱动程序堆栈会假定设备包含一个或多个 OS 特征描述符和检索这些特征描述符所需要的数据。检索的字符串描述符具有 bMS_VendorCode 字段值。该值为1表示USB驱动程序堆栈必须用来检索扩展特征描述符的供应商代码。

 

#define bMS_VendorCode              ( 0x01 )

// "MSFT100" : index : 0xEE : langId : 0x0000

const U8 OS_StringDescritpor[ ] =

{ 0x12,  0x03,  'M',  0,  'S',  0,  'F',  0,  'T',  0,  '1',  0,  '0',  0,  '0',  0,  bMS_VendorCode,  0 };

 

2、设置兼容ID特征描述符:

const U8 WINUSB_ExtendedCompatId_Descritpor[ ] =

0x28, 0x00, 0x00, 0x00,           // dwLength

0x00, 0x01,                             // bcdVersion

0x04, 0x00,                             // wIndex

0x01,                                       // bCount

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,       // Reserved[7]

0x00,                   // bFirstInterfaceNumber

0x01,                  // RESERVED ( 0x01 )

'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,       // compactiableID[8]

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subCompactiableID[8]

0x00, 0x00, 0x00, 0x00, 0x00, 0x00   // Reserved[6]

};

注:WinUSB还支持复合设备,对于单一传输类型最简系统,我们忽略复合设备的要求即可。compatibleID字段必须指定 "WINUSB" 作为字段值。其他可以根据需求更改。

 

3、注册设备接口 GUID描述符:

该描述符用于区分不同的WinUSB设备。

 

const U8 WINUSB_ExtendedProperty_InterfaceGUID_Descritpor[ ] =

0x8E, 0x00, 0x00, 0x00,  // dwTotalSize = Header + All sections

0x00, 0x01,                 // bcdVersion

0x05, 0x00,                 // wIndex

0x01, 0x00,                 // wCount

0x84, 0x00, 0x00, 0x00,     // dwSize -- this section

0x01, 0x00, 0x00, 0x00,     // dwPropertyDataType

0x28, 0x00,               // wPropertyNameLength  'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,'I',0,'n',0x00,'t',0,'e',0,'r',0,'f',0,'a',0,'c',0,'e',0, 'G',0,'U',0,'I',0,'D',0,0,0,

0x4E, 0x00, 0x00, 0x00,     // dwPropertyDataLength : 78 Bytes = 0x0000004E

'{',0,'1',0,'2',0,'3',0,'4',0, '5',0,'6',0,'7',0,'8',0,'-',0,'1',0,'2',0,'3',0,'4',0,'-',0,'1',0,'3',0,'4',0,'4',0,'-',0,'1',0,'2',0,'3',0,'4',0,'-',0,'1',0,'2',0,'3',0,'4',0,'5',0,'6',0,'7',0,'8',0,'9',0,'A',0,'B',0,'C',0,'}',0,0,0

};// bPropertyData : WCHAR : L"{12345678-1234-1234-1234-123456789ABC}"

 

4、端点描述符:

按实际的需求的配置端点数量和类型,即可完成嵌入式设备的描述符配置了。

 

一般固件程序可以通过MCU厂家提供的范例程序进行修改,这里省略USB固件功能的说明。只要包含以上三个描述符中的必须的字段,就可以成功枚举成USB Device。枚举成功后在设备WINDOWS设备管理器中可看到类似设备,如下图1所示

 

图1 成功枚举为USB Device

 

如何编写PC应用程序与嵌入式设备进行USB通信

PC机软件相对来说比较简单,并且微软官方也给出了示例代码。唯一需要注意的是,对应的软件程序获取WinUSB设备句柄的GUID参数,需要与嵌入式设备的描述符中的GUID保持一致。GUID是WinUSB用以区分设备的唯一标志。GUID,是Globally Unique Identifier的简称,翻译为全局唯一标识符,是一种由算法生成的二进制数据,长度为128位的数字标识符。

 

具体实现步骤如下:

1、创建设备的文件句柄:

调用SetupDiGetClassDevs 获取设备信息集的句柄;

调用 SetupDiEnumDeviceInterfaces 枚举设备信息集中的设备接口并获取有关设备接口的信息;

调用 SetupDiGetDeviceInterfaceDetail 获取设备接口的详细信息,所获取的信息通过SP_DEVICE_INTERFACE_DETAIL_DATA结构返回。由于该结构大小无法提前获取,故需连续两次调用该函数,第二次调用时接口详细信息将填充到根据第一次调用返回值所确定大小的该缓冲区,通过缓冲内该结构的DevicePath成员中可获得“设备路径”。

 

2、获取设备的 WinUSB 接口句柄:

调用 WinUsb_Initialize通过传递在创建设备的文件句柄中创建的文件句柄。

 

3、查询设备以获取 USB 描述符:

接下来,查询设备以获取特定于 USB 的信息,如设备速度、接口描述符、相关端点及其管道。调用 WinUsb_QueryDeviceInformation 从设备的设备描述符请求信息。调用 WinUsb_QueryInterfaceSettings 并传递设备的接口句柄,以获得对应的接口描述符。调用 WinUsb_QueryPipe 获取有关每个接口每个终结点的信息。此步骤不是必须的,因为端点方向及传输特性由嵌入式设备描述符决定,是已知的。

 

4、向默认端点发送控制传输:

此步骤也不是必须的。一般都不通过默认端点发送有效载荷。

 

5、发送 I/O 请求:

将数据发送到设备的批量输入和批量输出端点,这些端点点可分别用于读取请求和写入请求。调用 WinUsb_ReadPipe 从设备的批量输入端点读取数据。调用 WinUsb_WritePipe 通过批量输出端点将数据写入设备。在嵌入式设备的输出端点内写入数据之后,就可以在PC端读出数据。反之,如果在PC端对嵌入式设备的输入端点写入数据,则嵌入式设备会产生一个USB端点写入事件,具体如何捕捉该事件,则由MCU厂家的产品硬件决定,产生相应的中断信息,供中断服务程序来判断。一般而言,芯片厂家会提供MCU的USB通信基础范例程序,在其基础上做简单的修改和适配即可。

 

6、释放设备句柄

在完成对设备的所有必要的调用之后,释放设备的文件句柄和 WinUSB 接口句柄。CloseHandle 释放由 CreateFile 创建的句柄。

WinUsb_Free 释放由 WinUsb_Initialize 返回的设备的 WinUSB 接口句柄。

至此,已经完成了嵌入式设备端固件的USB代码移植和PC端应用程序的编写,就可以实现USB免驱设备的通信方式了。

 
关注与非网微信 ( ee-focus )
限量版产业观察、行业动态、技术大餐每日推荐
享受快时代的精品慢阅读
 

 

继续阅读
2018年被冰封的AR,2019年也没有机会破冰?

2018年,AR曾被寄予厚望,这一年也曾被称作AR的基础技术发展成熟的一年。事实上,AR行业也确实经历了几次大飞跃,但却未在舆论中占有一席之地,只是淹没在了区块链的浪潮里。

微软秘密开发4K摄像头,集成Windows Hello功能和深度感应功能

有消息显示微软正在开发一款具有Windows Hello功能的4K网络摄像头,特别面向Windows 10 PC和Xbox One设备开发。

谷歌VS微软,谁能拔得人工智能头筹?

微软和谷歌母公司Alphabet均在机器学习这一前沿软件领域扮演重要角色。但谷歌手握着当下*的机器学习框架TensorFlow,让其在与微软的竞争中占据了优势。

腾讯AI实验室负责人张潼离职,张正友接替
腾讯AI实验室负责人张潼离职,张正友接替

这位腾讯AI实验室的创始掌门,2017年3月官宣加盟,2019年伊始离开,任职不到2年,参与了腾讯AI实验室的从0到1。

2018年度十大电子产品,手机不配拥有姓名?
2018年度十大电子产品,手机不配拥有姓名?

美国媒体近日评选出了 2018 年度十大电子产品,然而略微意外的是没有一款手机上榜,来看看都有哪些~

更多资讯
当树莓派遇到FPGA ——拥有立体视觉和LiDAR的“驴车”
当树莓派遇到FPGA ——拥有立体视觉和LiDAR的“驴车”

为大家介绍一款基于树莓派和Xilinx FPGA的拥有立体视觉和LiDAR的 DonkeyCar。

2018年半导体存储领域排行榜,哪些企业排在前列?

16日,由国内存储行业门户网站中国存储网主办的“2018年度中国存储市场影响力排行榜”榜单揭晓,本届评选活动由中国存储网联合ITbrand品牌研究中心,综合网络投票、第三方评测、专家评审意见,最终二十多家存储企业及产品上榜。

将区块链技术应用到智能锁设计中的可行性分析

智能”或许是科技行业中被用得最多的一个词。有些公司认为项目的智能就是在上面安装一个WiFi适配器,然后制作一个应用程序。但智能的真正含义是什么呢?物联网设备意味着什么?我相信,当设备集合在一起,创造出比单个设备的总和大得多的东西时,它们就会变得“智能”。

从编码规范到编码注意事项解析Linux内核编码风格

这是一份简短的,描述linux内核首选编码风格的文档。编码风格是很个人化的东西,而且我也不愿意把我的观点强加给任何人,不过这里所讲述的是我必须要维护的代码所遵守的风格,并且我也希望绝大多数其他代码也能遵守这个风格。所以请至少考虑一下本文所述的观点。

希捷和西部数据领跑下一代磁存储技术,微波和激光谁将胜出
希捷和西部数据领跑下一代磁存储技术,微波和激光谁将胜出

在过去50年的大部分时间里,硬盘的面密度——一个用以衡量工程师能在给定区域内塞进多少位数据的指标——每年平均增长近40%。但最近,这一增长速率已经放缓至10%左右。从事磁存储工作的人都很清楚这个问题,但只是在过去一年左右的时间里,希捷和西部数据这两家领先的硬盘制造商的高管才在如何解决这个问题上公开表露分歧。在2017年10月发布的一系列声

电路方案