广告

基于WinUSB实现的嵌入式USB免驱设备通信方式

时间:2018-11-27 阅读:
为了简化USB设备的开发和接入到PC系统,微软开发了WinUSB,可以将Winusb.sys作为设备功能驱动程序安装,并提供WinUSB API供应用程序访问设备。一直以来,除了USB HID设备,其他类型的设备在WINDOWS环境下需要安装驱动程序才能工作……
广告

为了简化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所示。
20181127-windows.gif
图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免驱设备的通信方式了。

本文为EET电子工程专辑 原创文章,禁止转载。请尊重知识产权,违者本司保留追究责任的权利。
  • 中国工程师最喜欢的10大电源管理芯片和功率器件 除了国际巨头如TI、ADI、英飞凌、安森美、ST等,中国电源管理和功率器件市场上还有很多技术和产品都颇具实力的国产厂商,比如圣邦微、芯朋微、士兰微和华润微等。那么,中国的电源设计工程师最喜欢哪些电源管理芯片和功率器件呢?《电子工程专辑》编辑团队从36家国内外芯片厂商中挑选了各自有代表性的一个产品,分别归入10个类别……
  • iFixit拆解小米11:拆卸方便,为减小厚度内部集成度相当高 iFixit 日前拆解了最新的小米11 手机。虽然外媒对小米11 的报导是“小米11 延续了小米高性价比的旗舰智能机市场战略”,而iFixit 的细致拆解分析表明,为了实现高性价比,小米11 确实在多个方面做了取舍,且大部分与手机的是使用寿命和耐用性有关。
  • 一颗芯片差点让上万美元电路板报废,我是这样Debug的 这些”黑盒子”的电路板价格大约1万美元,老板不希望它们就此报废,于是我们的任务就是查出故障根源。经过一番查找后发现,原来是多路复用器芯片54ALS15出了问题,当开关打开或者关闭时,它不应该产生毛刺(Glitch)......
  • 11代酷睿桌面处理器体验:打造一台平价PC,需要注意些什么 我们拿到了最新的Intel酷睿i9-11900K和酷睿i5-11600K。其中i5-11600K目前在京东的报价是2349元人民币,不带Xe核显版的11600KF则为1699元人民币——算是近代酷睿中端定位产品中相当有性价比的一款了,虽然也和AMD太过强势有关。借此机会,我们组装了一台基于酷睿i5-11600K的平价PC,聊聊现如今的平价PC在性能上可发挥到什么程度——比如能玩哪些游戏;顺带谈一谈酷睿i9-11900K这款年度旗舰PC处理器……
  • 国产GPU风生水起,英伟达和AMD感受到威胁了吗? 近段时间,多家新创国产GPU企业完成了高额融资,这些由来自英伟达或AMD等国际巨头的资深华人专家创办的国产GPU新贵们大都只有雄心壮志和发展宏图,还没有具体的产品和应用方案。在短时间内拿到这么大金额的VC投资,这是不是又一轮国产芯片的“泡沫”?要准确回答和预测这一轮国产GPU融资和创业的前景,还要先从GPU的发展历程、全球和中国市场现状,以及未来应用发展潜力来看……
  • 谈SoC设计的两大主题:芯片安全与芯片设计上云 今年Aspencore主办的SoC设计论坛主题为“协作与赋能”。“赋能”一词体现的应该是主体靠上游的解决方案,会上至少有2家企业是为IC设计企业提供芯片安全相关解决方案的;而“协作”则更多意指,同一件事情需要融合不同的力量才能做好,会上至少有3家企业与EDA相关,并且都提到了将以点为单位的国产EDA工具,通过不同的聚合方式,构建相对完整的解决方案。
  • 千亿级别的暴涨与暴跌的比特币为啥 比特币暴跌20%或有400亿美金资金爆仓,显卡行业也深受波及,而这背后一方面是政府的禁令频发,另一方面金融犯罪的案例被推到了台面上,再加持众多资本狂割韭菜使得整个市场乌烟瘴气。而纵观发展历史,速度快的CPU为何没有用武之地呢,本文来带你一探究竟。
  • EE快讯——华为2021第18届全球分析 2021年4月12日,华为在深圳举办了第18届华为全球分析师大会,会上华为徐文伟提出了未来10年九大技术挑战与研究方向;轮值董事长徐直军表示:6G比5G快50倍,将在2030年左右推出,同时表示要提升软件能力减少芯片依赖,对海思的态度是养得起,也会一直养着,对于业界一直猜测的麒麟9000芯片库存还有多少,徐直军表示:华为的库存可以支撑公司活得更长一点。另一方面,欧菲光的前途终于有了结果...
  • 紫光展锐:大破大立,做数字世界的生态 4月20日,以“构go”为主题的2021紫光展锐创见未来大会在线上举行。会上,展锐重磅发布了5G业务新品牌——唐古拉系列,推出了Cat.1bis新技术特性,并分享了创新业务AR领域的最新商用进展。此外,展锐重申了公司产业定位——数字世界的生态承载者,并公布了对消费电子业务和工业物联网领域的前瞻预见和战略规划。
  • 安谋中国发布全新“山海”S12解决 安谋中国今天发布面向 AIoT 系统的全栈安全解决方案“山海”S12,包含硬件加解密引擎、安全软件和安全服务三大部分,从芯片的安全 IP 层到云端安全应用和安全管理提供全链路的安全保护。“山海”S12可以广泛应用于智能手机、平板、智能电视及安防等行业,为多种安全解决方案如数字版权保护、AI 安全、身份认证等提供基础安全能力。
广告
热门推荐
广告
广告
广告
EE直播间
在线研讨会
广告
广告
广告
向右滑动:上一篇 向左滑动:下一篇 我知道了