1 硬件设计
USB数据采集设备的硬件构成见图1.从图1可知,32路模拟输入信号由多路模拟开关控制将其中的某一路信号接入串行A/D(选用B-B公司的ADS7809),A/D转换的结构经光电隔离后串行输出到移位寄存器,移位寄存器将此结果转为并行数据并写入FIFO存储器,80C52系统将数据从FIFO存储器中读出并通过USB控制器送到主机。
当前可供选择的USB控制器很多,如朗讯公司的USS820、国家半导体公司的USBN9602.另外,还有将微控制器和USB控制器集成在一起的芯片,如Intel公司的8x930Hx和8x930Ax,其中8x930Hx支持USB集线器功能。在实际开发中选择了朗讯公司的USS820,其主要特点是完全遵循USB协议1.1,支持12Mbps的全速传输,支持四种传输方式,提供8个端点(Endpoint),且每个端点的传输类型、传输方向均可自由配置。另外,它还为每个端点提供两套FIFO数据缓冲区,总的数据缓冲区大小可达2240字节。
2 firmware设计
此外firmware(固件)是指被固化到89C52E2PROM中的程序。Firmware主要完成两个方面的工作:控制A/D的采样和通过USB控制器与主机通信。由于89C52系统控制A/D采样的工作非常简单,此处不做介绍。详细介绍89C52系统通过USB控制器与主机通信的工作。
因为89C52系统对USB控制器的操作是严格按照USB协议1.1进行的,因此先对USB协议1.1做一简单的介绍。按照USB协议1.1的规定,USB的传输方式分为4种:控制传输(Control transfer),块传输(Bulk transactions),中断传输(Interrupt transactions)和等时传输(Isochronous transactions)。控制传输主要用来完成主机对设备各种控制操作(如获取设备的设备描述符,设置设备的USB地址等,可以通过控制传输来定义自己对设备的控制操作,职设置采样参数、开始或停止采样等);块传输主要用业完成主机和设备之间的大指数据传输,由于块传输在硬件级上对传输数据进行错误检测(若发生错误,它支持“重传”功能),因此它能保证数据传输的可靠性,块传输支持两个方面的数据传输(即主机到设备和设备到主机);中断传输用来完成设备到主机的少量数据传输,它只支持设备到主机方向数据传输(中断传输跟实现的“中断”概念没有任何联系);等时传输主要用来完成主机和设备之间连接、实时数据的传输(如语音信号),同步传输不对传输数据进行错误检测,它主要保证数据的连续传输。另外,需要注意的是所有的USB传输事务均由主机启动,即USB总线采取主从式结构(master to slave)。
在实际开发中使用了两种传输方式:控制传输和块传输。控制传输用来实现位于主机上的USB总线驱动程序(USBD.SYS)以及编写的功能驱动程序对设备的各种控制操作,而块传输用来完成将采集数据从设备传送到主机。
USB控制器的工作原理可以简单地描述为:当USB控制从USB总线检测到主机启动的某一传输请求时,USB控制器通过中断方式将此请求通知89C52系统。89C52系统通过访问USB控制器的状态寄存器和数据寄存器获得与此次传输有关的各种参数,并根据具体传输参数,对USB控制器的控制寄存器和数据寄存器进行相应的操作,以完成主机的传输请求。
3 设备驱动程序设计
USB设备驱动程序的设计是基于WDM(Windows Driver Model,Windows驱动程序模型)的[4]。WDM采用分层驱动程序模型,对于USB设备来说,可分为USB总线驱动程序和USB功能驱动程序(见图2)。USB总线驱动程序由操作系统提供,它位于USB功能驱动程序的下面,负责与实际的硬件打交道,实现烦琐的低层通信。USB功能驱动程序由设备开发者编写,位于USB总线驱动程序的上面,不与实际的硬件打交道,而是通过向USB总线驱动程序发送包含URB(USB Request Block,USB请求块)的IRP(I/O Request Packet,I/O请求包),来实现对USB设备信息的发送或接收。采用这种分层驱动程序的设备方法有两个优点:(1)多个USB设备可以通过USB总线驱动程序来协调它们的工作;(2)编写分层驱动程序较之编写单一驱动程序相对简单,且可以节省内存和资源,不易出错。USB设备驱动程序的工作原理可以通过图2简单描述。
若应用程序想对设备进行I/O操作,它便使用Windows API函数(如DeviceIoControl ( )函数)对WIN32子系统进行WIN32调用。此调用由I/O系统服务接收并通知I/O管理器,I/O管理器将此请求构造成一个合适的I/O请求包(I/O Request Packet,IRP)并把它传送递给USB功能驱动程序。USB功能驱动程序接收到这个IRP以后,根据IRP中包含的具体操作代码,构造相应的USB请求块并把引URB放到一个新的IRP中,然后把此IRP传递到USZB总线驱动程序,USB总线驱动程序根据IRP中所含的URB执行相应的操作(如从USB设备读取数据),并把操作结果通过IRP返还给USB功能驱动程序。USB功能驱动程序接收到此IRP后,将操作结果通过IRP返还给I/O管理器,最后I/O管理器将此IRP中操作结果返还给应用程序,至此应用程序对USB设备的一次I/O操作完成。
USB功能驱动程序除负责处理应用程序的I/O请求外,还要处理PnP管理器发送给它的PnP请求(如设备启动请求IRP_MN_START_DEVICE,设备删除请求IRP_MN_REMOVE_DEVICE等)。通过对这些PnP请求的处理,USB功能驱动程序可支持设备的热插拔和即插即用功能。
驱动程序的入口函数是DriverEntry( ),所有对各种IRP的处理例程都在此入口函数中做出定义。
开发USB设备驱动程序的工具有Microsoft公司的Win98DDK,Compuware公司的Numega DriverStudio等。笔者在实际发中使用了Win98DDK。
4 应用软件的设计
用户态的软件设计由两个部分组成:动态链接库和应用唷。动态连接库负责与内核态的USB功能驱动程序通信并接受应用程序的各种操作请求,而应用程序负责对所采集的数据进行实时显示、分析和存盘。
动态链接库的工作原理如下:当它收到应用程序开始采样的请求后,便创建两个线程:采样线程和显示存盘线路。采样线程负责将采集数据写到应用程序提交的内存;而显示存盘线程由多媒体定时器控制(每隔一段时间多媒体定时器就调用一次此线路),此线程负责给应用程序发送显示和存盘消息。当应用程序接收到此消息后,便从它提交内存中读取数据并显示和存盘。此处需要注意的是采样线程和显示存盘线程在读写应用程序提交的内存时要保持同步(如当采样线程正在向内存进行操作时显示存盘线程就不能对此段内存进行读操作,否则就可能导致读写错误)。保持线程同步的方法很多,如互斥量(Mutex)、信号量(Semaphore)和事件(Event)。此处使用了互斥量。