基于WinUSB与SHT75的温湿度数据采集与控制系统设计仿真

2013-10-17 13:28
制造业自动化 2013年15期
关键词:描述符固件驱动程序

彭 伟

PENG Wei

(武汉城市职业学院 电子信息工程学院,武汉 430064)

0 引言

每年有超过20亿的新安装器件,可连接多种类型的设备,能够自动进行配置,连接简单且支持热插拨,多数时候不需要提供独立的电源,具有高传输速率,高可靠性等,上述诸多优点使USB成为了最成功的PC机接口。微软为大量USB类设备提供类驱动程序,且一直在改进和增加类驱动程序的数量,对于USB设备独立硬件供应商(IHV),Windows驱动程序基础(Windows Driver Foundation,WDF)已成为USB驱动程序开发的首选模型,它提供3个选项来访问USB设备:1)使用UMDF实现用户模式驱动程序;2)使用KMDF实现内核模式驱动程序;3)将Winusb.sys作为设备功能驱动程序安装,并提供WinUSB API供应用程序访问设备。本文将基于WinUSB通用驱动程序及Microchip公司USB接口系统固件框架,以PIC微控制器与SHT75传感器为核心,上位机选用C#开发平台,开发实现基于WinUSB的温湿度数据采集与控制系统并进行仿真。

1 WinUSB通用驱动程序框架

Windows驱动程序包括总线驱动程序(bus driver)、功能驱动程序(function driver)、过滤驱动程序(filter driver),图1给出了Windows USB驱动程序堆栈体系结构框图,图中出现了微软推出的USB设备通用驱动程序WinUSB,它基于Windows Driver Frameworks (WDF)开发,由内核模式驱动程序(Winusb.sys)与用户模式DLL(winusb.dll)共两个基本组件构成。WinUSB(Winusb.sys)可作为单一USB设备或复合设备功能驱动程序安装在设备内核模式堆栈中。用户程序通过调用WinUSB用户模式动态链接库winusb.dll公开的函数与USB设备通信。图1展示了包含3个Winusb.sys实例的USB驱动程序堆栈,实例1注册设备接口A,支持用户模式驱动程序(Usboem.dll),实例2注册设备接口B,支持通过系统服务(SVCHOST)与winusb.dll进行通信的扫描仪用户模式程序(Usbscan.exe),实例3注册设备接口C,支持固件升级程序(Usbfw.exe)。实例2、3基于USB通用父驱动程序usbccgp.sys,为复合设备提供服务。所有用户程序均通过加载winusb.dll,调用DLL所提供的函数与USB驱动程序栈通讯。

通过WinUSB驱动程序与设备通讯,用户程序不需要构造设备I/O控制请求来执行标准的USB操作(如配置设备、发送控制请求以及与设备互传数据)等,用户程序调用winusb.dll提供的函数时,winusb.dll将使用应用程序提供的数据构建适当的IOCTL调用DeviceIoControl,将请求发送至Winusb.sys处理,请求完成时WinUSB将Winusb.sys返回的信息回传到调用进程,调用失败或挂起时将返回零值,调用GetLastError可获取详细的错误消息。

图1 Windows USB驱动程序堆栈体系结构及含多个Winusb.sys实例的USB驱动程序堆栈

Winusb.sys还是UMDF功能驱动程序与关联的设备之间链接的关键部分,Winusb.sys作为上层过滤驱动程序安装在设备的内核模式堆栈中,应用程序通过与设备的UMDF用户模式驱动程序通信来发送读、写或设备I/O控制请求,驱动程序则与框架交互,后者将请求传递到Winusb.sys,Winusb.sys处理该请求并将其传递到协议驱动程序,最后传递到设备,任何响应均通过反向路径返回。

2 WinUSB驱动包构成及其INF文件解析

WinUSB作为设备功能驱动程序安装时,其驱动程序包共有4项内容:1)WinUSB辅助安装程序(Co-installer)Winusbcoinstaller.dll;2)KMDF辅助安装程序WdfcoinstallerXXX.dll;3)将Winusb.sys安装为设备功能驱动程序的INF文件;4)签名的数据包目录文件(Signed Catalog File)。KMDF和WinUSB辅助安装程序须从同一版本的WDK获取,WDF辅助安装程序文件须从最新版本的WDK安装目录获取,以便该驱动程序支持所有最新的Windows版本。

WinUSB驱动包中的INF文件将Winusb.sys安装为设备的功能驱动程序,为操作系统匹配驱动程序与设备提供重要信息。INF文件中与上、下位机USB接口软件开发关系最为密切的信息包括:1)USB固件内设备描述符定义的VID/PID。例如本文INF文件中[Manufacturer]区有:%USBMyDevice.DeviceDesc% = USB_Install,USBVID_00AA&PID_00BB,它将设备VID/PID设为0x00AA/00BB。2)设备接口类(Device Interface Class)GUID。上位机用户程序使用该GUID枚举WinUSB设备接口,新项目中的GUID可由guidgen.exe生成,INF文件[USB_Install.HW]区通过AddReg指示符在注册表中的DeviceInterfaceGUIDs键下存储一个或多个设备接口类GUID,例如本文所编写的INF文件中有:HKR,,DeviceInterfaceGUIDs,0x10000,"{B6B014A8-8CDE-4112-9474-F15486E401BE}"。Winusb.sys每次加载时都会根据注册表中的DeviceInterfaceGUIDs项指定的设备接口类注册一个设备接口实例。INF文件[Version]区还有一个ClassGUID条目,例如本文中该条目定义为:ClassGUID={36FC9E60-C465-11CF-8056-444553540000},它是设备安装类(Device Setup Class)GUID,INF文件的ClassInstall32指示符将在注册表中创建:…CurrentControlSetControlClass{36FC9E60-C465-11CF-8056-444553540000},并导入INF文件内的相关子键值,用于标识该设备类安装程序。

Winusb.sys安装为内核功能驱动程序时使用[DDInstall.Services]的AddService=WinUSB,…实现,ServiceType设为SERVICE_KERNEL_DRIVER,ServiceBinary设为Winusb.sys。安装为设备上层过滤驱动程时,需通过[DDInstall.HW]的AddReg指示符在注册表中创建上层过滤驱动条目“UpperFilters”。

Windows 8之前的操作系统将Winusb.sys作为功能驱动程序加载时需提供定制的INF文件,该文件指定了设备特定的硬件ID,同时包括内置Winusb.inf的部分,这些部分是实例化服务、复制内置二进制文件以及注册设备接口GUID所必需的。

3 基于C#与WinUSB的上位机接口程序设计

为探测下位机USB设备是否接入主机,可调用RegisterDeviceNotification注册设备通知消息,在设备移除或接入时通知窗体WndProc进程,该进程检测到有设备接入主机时进一步获取“设备路径”。

Winusb.sys被加载时,它根据注册表中DeviceInterfaceGUIDs键下该设备接口类注册一个设备接口。用户模式程序调用SetupDiGetClassDevs可枚举该设备接口类设备并返回该类的“设备信息集”句柄,循环调用SetupDiEnumDeviceInterfaces可逐一枚举设备信息集中的设备接口,提取索引指定的设备接口详细信息时可调用SetupDiGetDeviceInterfaceDetail,所获取的信息通过SP_DEVICE_INTERFACE_DETAIL_DATA结构返回。由于该结构大小无法提前获取,故需连续两次调用该函数,第二次调用时接口详细信息将填充到根据第一次调用返回值所确定大小的该缓冲区,通过缓冲内该结构的DevicePath成员中可获得“设备路径”。本文有:DevicePath="\\?\usb#vid_00aa&pid_0 0bb#2&2260b228&0&e1#{b6b014a8-8cde-4112-9474-f15486e401be}”。当检测到设备路径中含有“Vid_00AA&Pid_00BB”时即表示搜索成功,否则继续枚举下一接口。

Windows API提供的CreateFile函数可创建、打开文件、目录、物理盘、卷、COM端口、设备(包括USB设备)、服务、通讯资源、命名管道或控制台等,返回用于访问相应对象的句柄。CreateFile的第一个参数lpFileName是将要创建或打开的对象名称,Unicode允许该参数最多为32000个字符,该类型长字符串所使用的前缀标识为“\?”,C语言表示的DevicePath中长串标识符“\\?\”后面分别是USB设备实例“USBVid_00AA&Pid_00BB2&2260b228&0&E1”和设备接口GUID。将设备路径DevicePath赋给CreateFile函数的lpFileName参数即可获取设备句柄,所获取的设备句柄传给WinUSB函数WinUsb_Initialize,可获取WinUSB句柄。有了WinUSB句柄,即可进一步调用Winusb.dll提供的其他大量WinUSB API实现对USB设备的访问。

C#中调用WinUSB函数,需要通过DllImport引入DLL文件并声明函数,例如:

[DllImport("winusb.dll",SetLastError = true)]

internal static extern Boolean WinUsb_Initialize(…);

所设计的系统中,读传感器温湿度数据及控制继电器开关的自定义命令字节分别为0x81、0x82,向USB设备发送命令字节的函数为WinUsb_WritePipe(写管道),该函数前两个参数分别为WinUSB接口句柄InterfaceHandle和管道号PipeID,其中PipeID由1个方向位+7个地址位构成,对应于下位机USB固件中端点描述符内的bEndpointAddress字段,写管道函数的其他参数分别为:数据缓冲区指针、缓冲区大小、实际传输字节数及可选的Overlapped参数。本文将Overlapped设为IntPtr.Zero,使写管道函数向PipeID(0x01)管道完成一字节控制命令输出并返回后,才开始读取USB设备返回的传感器数据,调用WinUSB读管道数据函数WinUsb_ReadPipe“同步读取”USB设备返回的温湿度数据时,上位机用户程序可能因USB接口断开、返回数据量大等原因导致程序被阻塞。为避免这一问题,可使用Delegate代理实现对USB设备所返回数据的“异步读取”,定义的代理对应于读管道函数WinUsb_ReadPipe,调用BeginInvoke方法时除了给出读管道函数所需要必须的参数以外,另附加的两个参数为:1)new AsyncCallback(GetBulkDat);2)ReadDelegate。前者指明代理调用WinUsb_ReadPipe返回时的异步回调函数为GetBulkDat,后者为当前代理对象实例,当GetBulkDat被回调时,ReadDelegate作为IAsyncResult类型的参数传给该回调函数,AsyncState属性获得代理对象,并执行EndInvoke等待BeginInvoke调用的方法结束。如果IsCompleted属性值为真则表示从USB设备通过批量(Bulk)传输读取的数据已装填到指定缓冲。

瑞士SENSIRION传感器公司推出的数字式温湿度传感器SHT75实现了数字式输出、免调试、免标定、免外围电路及全互换功能。为方便用户开发应用温湿度传感器SHT75,SENSIRION提供了SHT75的8051 C程序范例,可以非常方便的移植为PIC微控制器C程序,PIC从传感器读取4字节数据后,根据函数:Calc_STH75与Calc_Dew_point可分别计算温湿度及露点值。由于这两个函数内浮点计算量大,本文将它们移至上位机C#程序中,PIC微控制器则仅负责从传感器读取四字节温湿度原始数据,通过USB接口直接传输给上位机,上位机C#程序再通过这四字节原始数据调用上述两个函数计算出相应结果,这样处理可提高传输速度并减轻下位机微控制器负荷。C#函数GetBulkDat通过缓冲中的四字节数据完成浮点计算后,仍然需要通过代理在主窗体中刷新显示,因为该函数与主窗体不在同一线程运行。

回调函数读取数据管道结束并通过显示代理完成窗体信息刷新显示后,接着再次通过写管道函数WinUsb_WritePipe发出读取SHT75传感器数据的命令字节0x81,实现对温湿度数据的实时连续采集。继电器控制与之类似,唯一差别是刷新显示下位机返回的继电器状态后并不继续调用WinUsb_WritePipe发出控制继电器的命令字节0x82,而是等待上位机用户的下一个操作。

4 下位机Microchip USB协议栈及用户I/O设计

为便于工程技术人员开发应用PIC USB微控制器,Microchip公司提供了PIC微控制器USB协议栈固件框架,可处理大量USB通讯任务,所提供的面向不同客户群的演示项目均采用协作式多任务环境(禁止出现阻塞),内置USB模块的PIC18F4550微控制器具有独立的中断逻辑结构,协议栈框架核心文件usb_device.c提供主状态机函数USBDeviceTasks,对USB中断进行检测并进入相应的处理程序。

固件框架同时提供了处理用户I/O操作的演示函数ProcessIO,由固件程序内的主函数调用。本文程序设计的ProcessIO函数负责读取上位机命令,根据所接收到的上位机命令字节0x81与0x82分别实时回传SHT75传感器温湿度数据(4字节)及控制继电器开关并同时返回继电器状态。

固件框架中的描述符文件usb_descriptors.c给出了VID/PID定义,描述符中设置设备类编码为0x0000(bDeviceClass/bDeviceSubClass),表示一个配置内的每个接口均独立指定了其自身的类信息及各种接口操作,接口类编码0xFFFF(bInterfaceClass/bInterfaceSubClass)则表示接口类由厂商指定。端点描述符中的端点属性(EndpointAttributes)全部设为_BULK(批量传输端点)。PIC固件程序分别定义的数据收/发缓冲为INPacket与OutPacket,与上位机C#程序定义的缓冲OUTBuffer与INBuffer对应。在指定端点非忙时,固件可通过USBGenWrite、USBGenRead分别发送与接收数据包,二者最终均调用USBTransfer OnePacket(ep,dir,data,len),该函数为协议栈提供的通用单包传输函数。

5 温湿度数据采集与控制系统仿真运行测试

Labcenter公司的实物电路仿真软件Proteus具有对微控制器及其外围电路组成的综合应用系统的交互仿真功能,仿真USB接口需要安装虚拟USB驱动程序(Virtual USB Drivers)并添加USBCONN组件。图2给出了以PIC18F4550微控制器及温湿度传感器SHT75为核心的USB接口应用系统仿真电路。

图2 USB接口温湿度检测与控制系统仿真测试电路

PROTEUS提供的USB虚拟分析器可跟踪设备与上位机用户软件及驱动程序的所有交互信息,包括IRP请求(包括IOCTL与MJ_PNP)、USB事务(包括SETUP、IN、OUT)、USB寄存器(包括UCON、UIR、USTAT、BDnSTAT、BDnCNT、BDnADRL/H等)。测试运行USB接口应用系统时,借助USB虚拟分析器可观察到主机给USB设备上电、主机复位设备(RESET)、USB总线驱动程序通过URB(USB请求包)请求USB设备描述符前8字节、主机再次复位设备(RESET)、USB总线驱动程序通过URB执行控制传输,将USB默认地址0x00重设为操作系统分配的地址,及USB总线驱动程序通过URB连续发送提取描述符请求,依次取得完整的设备描述符、配置描述符(包括接口描述符、端点描述符)、字符串描述符。PnP事件触发即插即用管理器(PnP Manager)向WDM驱动程序发送主功能(Major Function)码IRP_MJ_PNP,进入相应的派遣函数,查询设备ID、执行设备启动等。

打开上位机C#软件时将自动搜索WinUSB设备,成功搜索到USB设备后,每当C#调用WinUSB的读、写管道函数与USB设备交换数据时,将分别访问对应的端点(EP1_IN或EP1_OUT)缓冲,运行过程中均可通过USB虚拟分析器观察到:IOCTL: BULK_OR_INTERRUPT_TRANSFER,展开该层时可观察到相应的IN事务或OUT事务。

图3 温湿度数据采集与控制系统上位机C#程序

6 结束语

由于WinUSB的通用性与易用性,大量厂商开始基于WinUSB开发USB接口程序,尽管当前版本的WinUSB驱动尚存在若干限制,例如一次仅允许一个应用程序与设备通信且不支持在同步端点之间传输流数据。本文通过研究微软WinUSB通用驱动程序框架及WinUSB驱动包构成及其INF文件关键条目、Microchip USB接口系统固件框架,基于C#软件开发平台设计了以PIC18F455微控制器及SHT75传感器为核心的温湿度数据采集与控制系统,通过了仿真运行及实物测试,为基于WinUSB的接口系统开发提供了重要参考。为更快速的开发WinUSB的上位机用户程序,基于微软VS2012+WDK8.0的开发环境除了提供USB KMDF与UMDF驱动程序开发模板以外,还提供了基于WinUSB的应用程序开发模板,通过该模板能够自动生成通用代码框架,包括自动生成INF文件。另外,微软的Windows 8已经不再要求为WinUSB提供定制的INF文件,这无疑将使得基于WinUSB的系统开发变得更加便捷高效。

[1] 曾希强.Linux下基于libusb的USB设备驱动程序设计与实现[J].信息与电脑,2009(7):101-103.

[2] 彭定军,陈安,高健.嵌入式Linux下基于libusb的USB驱动开发[J].技术与市场,2008(11):4-5.

[3] 安爱美,徐建建.基于WDF的USB驱动程序设计[J].自动化与仪表,2012(1):57-60.

[4] 钱宇红. USB 数据传输卡WDF 驱动程序开发[J].计算机应用与软件,2012(6):225-227.

[5] 邹敬轩,蔡皖东.基于WDF过滤驱动的USB存储设备监控系统[J].计算机工程与科学,2010(3):42-44,71.

[6] 朱诚,左辉.利用KMDF 驱动程序实现USB 设备的功耗控制[J].计算机应用与软件,2012(12):252-254,321.

[7] 牛继来,王海霞.基于WDM 的USB驱动程序研究与设计[J].计算机与数字工程,2007(12):127-129,139.

[8] 张智邦,鲍苏苏,金敏.基于WDM 模型USB 驱动程序的设计与研究[J].计算机系统应用,2011(11):185-188.

[9] 谢晶石.PIC单片机USB接口应用设计[J].数字技术与应用,2011(8):146-148.

[10] 覃冬华.基于Visual C# 的USB 接口通信程序设[J].数字技术与应用,2010(8):90-91.

[11] 胡键勋.基于Visual C++编写USB接口温度采集设计[J].现代计算机,2010(4):148-150.

[12] 李伟,张东亮,杨丽丽.利用Visual C++实现USB设备与PC机通信[J].仪器仪表用户,2007(6):87-89.

[13] Microchip Libraries for Applications [OL].[201211]:http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2680&dDocName=en547784.

[14] USB Driver Stack Architecture[OL].[201303]:http://msdn.microsoft.com/zh-cn/library/windows/hardware/hh406256#usb_2.0_driver_stack.

猜你喜欢
描述符固件驱动程序
基于AKAZE的BOLD掩码描述符的匹配算法的研究
欧洲共同语言参考标准在中国高校学术英语写作教学适用性的研究:可理解性,可行性和有用性
阻止Windows Update更新驱动程序
基于深度学习的局部描述符
计算机硬件设备驱动程序分析
一种基于PCIE总线的改进分散集聚DMA的设计
基于SHA1的SCADA系统PLC固件完整性验证方法
基于固件的远程身份认证
英特尔发布免费固件引擎
提取ROM固件中的APP