教程:实现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 )
限量版产业观察、行业动态、技术大餐每日推荐
享受快时代的精品慢阅读
 

 

继续阅读
各大公司纷纷回头,华为 MateBook 重上微软商店
各大公司纷纷回头,华为 MateBook 重上微软商店

6月18日消息 5月22日,所有的华为MateBook笔记本在微软美国商店中删除,现在包括华为MateBook X Pro在内的多款笔记本已经重新上架微软在线商店。

微软、谷歌相继发布云游戏服务,究竟谁能抢占先机?

短短数月,云游戏卷土重来。不论是电视机前头晕眼花还紧抓手柄不放的硬核主机玩家,还是直播间里两眼放光不断重复敲击「666 - 回车」的快乐云玩家,人人都看到了一个崭新的未来。

微软新专利曝光:触控手写笔问世,可实现功能“无限”
微软新专利曝光:触控手写笔问世,可实现功能“无限”

在触控化的新时代,很多传统的产品都开始陆续加入触控屏幕,从而变成一个新的产品形态。最近有专利图显示,微软提交了一份带有触控显示面板的手写笔专利。

一文看懂 PS5 与 Xbox Scarlett 参数对比
一文看懂 PS5 与 Xbox Scarlett 参数对比

 IGN整理的这份PS5 vs。 Xbox Scarlett对照表将直观地显示这两款设备的硬件、用户界面、媒体兼容性以及其他功能。表格中有许多“待定”项,这也是我们接下来需要关注的重点信息。

E3 难掩无趣真相,背后谷歌为何强刷存在感?
E3 难掩无趣真相,背后谷歌为何强刷存在感?

几天下来,玩家们或许为《赛博朋克2077》带给他们的“今年是2076年”的幻觉而欢呼;或许为乔治·马丁与宫崎英高的奇妙组合感到惊叹;也可能为《塞尔达传说“荒野之息2》的公布大呼过瘾。但这无法掩盖厂商们再次集体画大饼、炒冷饭的行为。也无法掩盖E3越来越无趣的真相。

更多资讯
工程师故事 | 李工的“博弈”小妙招,让我拿到了全额项目奖金

一大清早李工突然和我聊起《楚汉争霸》,李工说得对,要是项羽不死,韩信还有用兵之处。项羽兵败乌江,韩信又不会搞经济建设,自然也就没有了存在的价值。

联芸科技亮相全国双创周 创新成果助力双创升级发展

6月13日下午,以“科技引领,双创升级”为主题的2019科技创新创业高峰论坛在双创周主会场浙江省杭州市梦想小镇国际会议中心举办。该论坛在科技部的指导和支持下,由科技部火炬中心、浙江省科技厅、杭州市政府联合主办,杭州市科技局、创头条、首都科技发展战略研究院等单位承办,科技部、国务院发展研究中心、浙江省、杭州市等政府领导以及相关学者专家将出席

MicroPython做嵌入式开发的优缺点
MicroPython做嵌入式开发的优缺点

C / C ++编程语言长期以来一直主导着嵌入式系统行业,很少有其他语言可以找到立足点。虽然Java和Ada等语言已经尝试过,但Python的流行程度不仅在不断提高,而且已经在基于Linux的应用程序中找到了很长一段时间。

一文了解Python中的数字类型
一文了解Python中的数字类型

数字类型是不可变类型。所谓的不可变类型,指的是类型的值一旦有不同了,那么它就是一个全新的对象。数字1和2分别代表两个不同的对象,对变量重新赋值一个数字类型,会新建一个数字对象。

谷歌抛弃 32 位版本安卓,明年底将结束支持
谷歌抛弃 32 位版本安卓,明年底将结束支持

谷歌宣布将淘汰32位版Android Studio与Android模拟器