《电子技术应用》

基于K64的USB驱动构件化设计

2017年电子技术应用第7期 作者:胡唯唯1,王宜怀1,张 永2
2017/8/3 10:12:00

胡唯唯1,王宜怀1,张  永2

(1.苏州大学 计算机科学与技术学院,江苏 苏州215006;2.苏州华祥信息科技有限公司,江苏 苏州215006)


    摘  要: USB由于其支持热插拔、接口简单、扩展方便以及数据传输率高等特点,已经成为当前主流的数据通信方式。USB协议比较复杂,驱动程序的开发具有一定难度,同时由于硬件平台的多样性,使得开发过程中有很多重复性的工作。鉴于此,根据USB协议栈架构和驱动构件化思想设计了USB驱动构件;并使用恩智浦半导体公司的K64微控制器,在KDS 3.0环境下对该构件进行测试,作为一个HID(人机接口)设备与PC的上位机软件之间通信,为USB驱动程序的开发提供了基本规范和参考。

    关键词: USB;驱动构件;K64;枚举

    中图分类号: TP311

    文献标识码: A

    DOI:10.16157/j.issn.0258-7998.2017.07.014


    中文引用格式: 胡唯唯,王宜怀,张永. 基于K64的USB驱动构件化设计[J].电子技术应用,2017,43(7):55-58.

    英文引用格式: Hu Weiwei,Wang Yihuai,Zhang Yong. The development of USB driver component based on K64[J].Application of Electronic Technique,2017,43(7):55-58.

0 引言

    USB现已成为嵌入式设备的一种主要通信接口,但是由于USB协议的复杂性和硬件平台的多样性,使得USB驱动程序开发存在难度大、成本高、可移植差、难以维护等缺点。为了解决这些问题,本文在深入分析USB协议的基础上,对USB设备的功能进行抽象,并采用驱动构件化的设计思想开发USB驱动构件[1]。同时,在Kinetis Design Studio 3.0集成开发环境下,使用恩智浦半导体公司的K64微控制器对该构件进行测试,将其作为一个HID设备与上位机程序进行通信。上位机软件在VS 2010环境下使用C#语言开发,可以动态找到目标设备,实现快速连接和通信。另外,将该通信系统用于3D打印中,通过对打印产品的纹理进行分析,验证本文所开发的USB构件的稳定性。

1 USB协议分析

    USB驱动程序主要完成USB设备的初始化、枚举和数据传输。USB主机与USB设备之间有4种数据传输类型,分别是批量传输、中断传输、同步传输和控制传输,每种传输方式执行一次需要多个事务处理[2]。对于控制传输,其只能用于USB设备枚举,包括3个阶段,分别为设置阶段、数据阶段和状态阶段。设置阶段是一次SETUP事务处理,数据阶段是多个IN或者OUT事务处理,状态阶段是一次无数据传输的IN或者OUT事务处理。USB设备枚举完成后,将使用其他传输方式用于实际数据收发,具体使用哪种方式则取决于设备的类型。USB设备连接到PC后,PC便开始对其进行枚举[3]。不同操作系统下枚举过程可能会有所不同,Windows操作系统下的USB设备枚举过程为:

    (1)用户将USB设备插入到PC(USB主机)的USB端口上,USB主机给USB设备上电,USB设备获取100 mA的电流,并处于上电状态;

    (2)USB主机检测USB设备是全速还是低速设备,如果D+数据线上有高电平则是全速设备,如果D-数据线上有高电平则是低速设备;

    (3)USB主机复位USB设备,复位时间至少10 ms。复位结束后USB设备处于默认状态,并使用默认的地址0和端点0与USB主机进行通信;

    (4)USB主机获取USB设备的18 B的设备描述符,完成一次控制传输。这是USB主机第一次得到设备描述符,主机并不会分析各个字段的含义,只会得到设备描述符中端点0所支持的最大数据包长度(设备描述符的第8字节);

    (5)USB主机再一次复位USB设备,这一步在USB 2.0协议中并不要求。另外,对于高速设备,Windows 8及以上版本会跳过这一步;

    (6)USB主机给USB设备分配一个唯一的地址,USB设备进入地址状态,之后USB设备将使用这个新的地址与USB主机进行通信。只要USB设备不被移除、复位或者重新启动,那么这个地址将一直存在;

    (7)USB主机获取配置描述符,及其从属的接口和端点描述符。如果还有字符串描述符,则继续获取。对于HID类设备,USB主机还会获取报告描述符; 

    (8)获取以上USB设备的相关信息后,USB主机会为USB设备分配并加载一个合适的设备驱动程序;

    (9)USB主机对USB设备进行配置,USB设备进入配置状态。对于HID类设备,则初始化一个上行传输IN端点和一个下行传输OUT端点,上行传输是USB设备向PC上传数据,下行传输是PC向USB设备发送数据[4]

    枚举完成后,USB设备可以和USB主机进行数据传输。枚举和枚举完成后的数据传输过程中执行一次事务处理就会产生一次中断。USB中断包括复位中断、令牌中断、STALL中断和SOF中断等,其中令牌中断包括SETUP令牌中断、IN令牌中断和OUT令牌中断。一次事务处理由令牌包、数据包和握手包组成,令牌包表明此次事务处理的目的,数据包中包含了要传输的数据,握手包表明此次事务处理的完成状态[5]。图1是USB设备枚举和数据收发的执行流程图。

qrs2-t1.gif

2 USB驱动构件设计

    驱动构件的设计应满足可复用性、可移植性和可维护性,其中复用性是设计目标,是软件成熟的标志。软件的复用可以降低软件开发的难度、减少重复劳动、降低开发成本、提高开发效率和软件质量、缩短软件开发周期[6]。驱动构件由.c源文件和.h头文件组成。源文件是构件的功能函数实现,函数分为对外接口函数和内部函数,对外接口函数供外部调用,内部函数仅内部调用;头文件是构件的功能描述,其中包含了对外接口函数声明、相关宏定义和类型定义等。在实际使用时,构件应满足两点:无需打开源文件,只要通过读头文件就知道如何使用;在不同芯片上使用时,只需要做少量修改或者无需修改。

    K64要作为USB设备使用,必须初始化USB模块。上电后需要完成设备枚举,设备枚举过程需要做很多处理,但是所有USB设备的枚举过程基本上是一样的,因此设备枚举可以作为一个函数进行集中处理。USB总线是轮询式的,所有的通信都是由USB主机发起的,因此设备不能主动向USB主机发送数据,只能将要发给USB主机的数据准备好等待USB主机来取。为了方便向USB主机发送数据,在构件中封装一个发送数据函数。如果有数据要发送给USB主机,则调用该函数将要发送的数据填入指定的缓冲区内即可,在下一次事务处理中由USB主机取出,使得USB设备可以“主动”发送数据。基于以上分析, USB驱动构件将封装4个函数,分别是初始化函数usb_init、枚举处理函数usb_enumerate、发送数据函数usb_send和接收数据函数usb_recv。

2.1 usb_init函数

    初始化函数usb_init完成对USB模块的初始化,主要包括内存分配、时钟源使能和使能USB中断等。每一个USB设备都有一个序列号,同VID和PID一起作为设备的标识符,当两个VID和PID都一样的USB设备插入到PC时,序列号可以起到进一步区分的作用。序列号实际上是一个字符串描述符,考虑其用于唯一性标识USB设备的作用,将其作为USB设备的名称,并传入初始化函数中,这样开发者就能够方便命名自己的USB设备。

2.2 usb_enumerate函数

    枚举处理函数usb_enumerate完成枚举过程中的控制传输,该函数一般情况下无需改动。本驱动构件是针对HID设备的,如果要开发为其他类型的设备,在修改描述符后,只需要对该函数做少量修改即可。以MSD设备为例,将HID设备的描述符文件usb_hid_device_descriptor.c替换为MSD设备的描述符文件usb_msd_device_descriptor.c。比较这两个描述符文件,发现前者比后者多一个报告描述符,同时字符串描述符也不同。为了尽量减少对usb_enumerate函数代码的修改,可以将MSD设备的设备描述符、配置描述符及其从属的接口和端点描述符的名称根据HID设备作对应修改。这样,只需要删除usb_enumerate函数中对报告描述符的处理即可。

2.3 usb_send和usb_recv函数

    发送数据函数usb_send和接收数据函数usb_recv用于数据的收发。为了方便数据的收发,这两个函数都有两个参数。usb_send函数的两个参数为SendBuff和DataLenght,usb_recv函数的两个参数为RecvBuff和DataLength。进行数据发送时,只要将待发送数据的缓冲区地址和发送的数据长度传入发送数据函数usb_send的SendBuff和DataLength即可,下一次事务处理结束后,相应缓冲区中的数据就会被发送出去。接收数据和发送数据的执行是一样的,只是接收的数据已经在本次事务处理中,只要使用usb_recv函数从相应端点的BD中取出即可,RecvBuff用于存放取出的数据,DataLength是取出的数据长度。

3 上位机软件设计

    上位机软件使用Windows提供的API函数对HID设备进行访问,这些API函数包含在hid.dll、setupapi.dll、kernel32.dll文件中,分别起到与HID设备通信、寻找与识别设备、交换数据的作用[7],关于相关API函数的介绍可以查看MSDN。

    PC与USB设备建立连接的第一步就是找到该设备,上位机软件必须时刻能够检测到USB设备的插入和移除事件,因此必须在相关窗体句柄创建时将这些事件通过RegisterDeviceNotification函数进行注册,该函数位于user32.dll文件中。

    当检测到一个新的USB设备插入或者被移除时,Windows将向应用程序发送一个WM_DEVICECHANGE消息,该消息宏定义为0x0219。然后由默认的消息处理函数WndProc进行处理,程序中对WndProc进行了重写,以满足寻找目标设备的要求[8]。WndProc函数首先获取USB总线上指定类型的设备列表,通过调用setupapi.dll文件中函数SetupDiGetClassDevs实现。SetupDiGetClassDevs函数的第一个参数是HID GUID,GUID是设备类型的唯一标识符,HID类设备的GUID可以通过hid.dll文件中的HidD_GetHidGuid函数获取,为固定值4d1e55b2-f16f-11cf-88cb-001111000030。该函数的返回值是当前USB总线上所有HID类设备的信息,并保存于InfoSet队列中。为了从InfoSet队列中找到目标设备,需要调用setupapi.dll 文件中的SetupDiEnumDeviceInterfaces函数遍历InfoSet队列,并将每个设备的信息保存于DeviceInterfaceData 结构体变量oInterface中。接着,从oInterface中获取该设备的VID和PID,然后和目标设备的VID和PID进行匹配检查,以确定该设备是否为目标设备[9]。如果匹配则找到了目标设备,否则继续调用SetupDiEnumDeviceInterfaces函数,获取下一个设备的信息并继续进行匹配检查,直到找到目标设备或者InfoSet队列中的设备都查找完毕为止。

    如果没有找到目标设备,则回收InfoSet占用的内存。如果目标设备找到,则使用该设备路径作为参数并调用CreateFile函数打开该设备,之后就可以像读写文件一样操作该设备。图2是上位机软件寻找USB设备和执行数据传输的流程图。

qrs2-t2.gif

4 构件测试与分析

    图3为数据和局部波形图,图3(a)是获取设备描述符时使用TravelBus协议分析仪采集的数据,图3(b)是对应的局部波形。枚举过程中USB主机首先获取USB设备的设备描述符。USB主机向USB设备发送一个SETUP令牌包,然后是DATA0数据包,该数据包中包含了8个字节的十六进制数据80 06 00 01 00 00 40 00,该8个字节的数据是获取设备描述符的标准设备请求。USB设备接收到该请求后开始处理并向USB主机返回一个ACK握手包,表明此次SETUP事务处理是成功的,从而完成控制传输的设置阶段。随后USB主机发送IN令牌包开始取设备描述符,但是USB设备此时并没有将设备描述符准备好,因此USB设备直接向USB主机发送一个NACK不确认包。

qrs2-t3.gif

    USB主机继续发送获取设备描述符的IN令牌包,USB设备返回准备好的18个字节的设备描述符12 01 00 02 00 00 00 40 A2 15 7F 00 01 01 01 02 00 01,其中PID是0x15A2(第9和第10字节),VID是0x007F(第11和第12字节)。之后,USB主机向USB设备发送一个ACK确认包,完成控制传输的数据阶段。最后,USB主机发送一个OUT令牌包,再发送一个无数据的DATA1数据包,USB设备接收到之后返回一个ACK握手包,从而完成此次控制传输的状态阶段[10]。设备枚举成功后,PC将USB设备挂载到设备列表中。

5 应用

    目前使用DLP技术的3D打印机需要与PC进行通信,通信的实时性和稳定性至关重要。将该USB通信系统应用于3D打印中,可以提高打印的稳定性和实时性。打印过程中数据丢包率低、实时性高,使得所打印的产品纹理的连续性高(无断层)、效果逼真。打印成品与局部纹理放大20 000倍效果如图4所示。

qrs2-t4.gif

6 结论

    本文在深入分析USB协议的基础上,按照构件化设计思想编写USB驱动构件,同时以恩智浦半导体公司的K64作为测试对象,并编写上位机软件,实现与PC之间的USB通信。另外,将该USB通信系统用于实际项目3D打印中,打印的产品满足要求。本文所设计的USB驱动构件封装简单合理、设备枚举清晰、代码移植性高、通信稳定高效,可以作为驱动程序的开发模板,同时对USB驱动程序开发的规范性和可移植性具有很高的参考意义。

参考文献

[1] 龙飞,何钦铭.构件化开发方法在J2EE 项目中的应用[J].计算机工程与设计,2007,28(3):591-594.

[2] 黄樱,刘君,刘卉,等.基于ARM的嵌入式USB主机系统设计[J].微计算机信息(嵌入式与SOC),2007,22(2):156-157.

[3] ZHU J,WANG S,ZHANG S Y,et al.Embedded diver system for USB mouse[C].International Conference on Electrical & Control Engineering,2011:180-183.

[4] 侯代文,孙涛,邓磊明.TMS320VC33与主机通信的USB接口设计[J].电子设计工程,2015,23(7):166-170.

[5] 王宜怀,吴璟,蒋银珍.嵌入式系统原理与实践—ARM Cortex-M4 Kinetis微控制器[M].北京:电子工业出版社,2012.

[6] 吕明琪,薛锦云,胡启敏.基于软件体系结构的可复用构件模型[J].计算机应用研究,2008,25(1):120-122.

[7] 杨晶晶,江春华.USB HID设备驱动程序设计[J].微计算机信息(嵌入式与SOC),2006,22(6):140-142.

[8] 郭夏夏.动平衡测试系统的关键技术研究[D].上海:上海交通大学,2014.

[9] WATANABE H,MASAOKA H,OHIGASHI T,et al.Supporting USB devices for the global migration[J].IPSJ International Symposium on Applications & the Internet,2010:153-156.

[10] DONG Z Y,ZHAO H.Data transfer principles and implementation in USB microwave power sensor[C].Seventh International Symposium on Computational Intelligence & Design,2014:76-79.

继续阅读>>