摘要: 本文介绍一种基于DSP平台,以USB接口方式实现用于继电器检测与保护装置的通信方式的实现。它采用Philips公司的ISP1581_BD接口芯片,实现DSP数据采集与PC机的高速传输。并介绍了在设计开发USB外设时主机应用程序及设备驱动程序的方法及代码。
关键词:USB设备驱动程序 ISP1581_BD
1 引言
ISP1581 是一种价格低、功能强的通用串行总线(USB)接口器件,它完全符合USB 2.0 规范,并为基于微控制器或微处理器的系统提供了高速USB 通信能力。ISP1581与系统的微控制器/微处理器的通信是通过一个高速的通用并行接口来实现的。它符合现有的大多数器件的分类规格,比如:成像类、海量存储器件、通信器件、打印设备以及人机接口设备。
内部通用DMA 模块使得数据流很方便的集成。另外,多种结构的DMA模块实现了海量存储的应用。这种实现USB 接口的标准组件使得使用者可以在各种不同类型的微控制器中选择出一种最合适的微控制器。通过使用已有的结构和减少固件上的投资缩短了开发时间、减少了开发风险和费用。从而用最快捷的方法实现了最经济的USB外设的解决方案。ISP1581可理想地用于许多外设,例如:打印机、扫描仪、MO、CD、DVD 和Zip/Jaz 驱动器、数码相机、USB和以太网的链接、电缆和DSL调制解调器等等。另外,ISP1581 所具有的低挂起功耗还可以满足ACPITM,OnNOWTM和USB电源管理的要求。此外,ISP1581内部还集成了许多特性,包括SoftConnectTM、低频晶体振荡器和集成的终止寄存器。所有这些特性都为系统大大节约了成本,同时使强大的USB功能很容易地用于PC机外设。
2 系统设计
系统设计包括硬件方案设计与USB设备的软件设计。而USB设备的软件设计主要包括两部分:一是USB设备端的软件,主要完成USB协议处理与数据交换(多数情况下是一个中断子程序)以及其它应用功能程序;二是PC端的程序,由USB通信程序和用户服务程序两部分组成,用户服务程序通过USB通信程序与系统USBDI(USB设备接口)通信,USB通信程序开发难度比较大,可用DDK或DriverWork等开发工具开发。
2.1 USB芯片与DSP的硬件连接
本方案以TI公司的TMS320C5XX作为微控制器,采用外部供电方式,连接电路如图1所示。
ISP1581的16根数据线直接与DSP的数据线相连,存储管理单元(MMU)和集成RAM作为USB的缓冲区,允许微控制器以自己的速率对USB信息包进行读写。D+,D-信号线上串接18Ω电阻。XTAL1接12MHz晶振。
2.2 固件程序设计
由于采用的是不带微控制器内核的USB接口芯片,所以关于USB2.0协议规范的实现都必须靠DSP控制ISP1581芯片来完成。固件的主要设计任务是:在DSP的平台上编写程序,以完成USB2.0规范所要求的标准请求及用户根据产品需要自己定义的请求。为了不影响程序的执行效率,本方案采用中断方式完成固件的编写,同时,为了保证程序的模块化及良好的可移植性,在设计中采用分层结构进行固件的编写。详细方法可参见文献 。
在本方案中,采用端点2以批量方式来与上位机进行通信,采用端点0来进行除设备枚举之外的设备命令控制字的处理,这在下面的设备驱动程序中将会谈到。因为对于继电器来说,有很多标志位需要传送;且因USB是单向控制的,故在每次读写数据时均需首先发送一命令控制字,然后再通过API读写函数进行双方通信。所以我在此设一个IO请求结构:struct IOREQ{unsigned int wValue;unsigned int wIndex;},wValue代表16位标志位,wIndex代表需传送的字节数,这个结构命令传送的驱动需要自己在设备驱动程序中实现(通过设备类请求的方式,见2.3述),且在上层应用程序中通过DeviceIoControl(…)函数,通过驱动的类请求传给USB设备端点0,USB设备则以类设备请求方式处理这个设备控制命令。
本驱动中采用0x00作为USB设备写命令的请求类型号,0x01作为USB设备读命令的请求类型号,设备固件中处理IO控制请求的代码如下:
unsigned char type, req;
type = ControlData.DeviceRequest.bmRequestType & 0x60;//请求类型
req = ControlData.DeviceRequest.bRequest & 0x0F;//请求号
if (type == 0x00)
(*StandardDeviceRequest[req])();//调用标准请求
else if (type ==0x40)//类请求
{if (req==0x00) Handle_Write_DeviceIo();//处理写控制命令
if(req==0x01) Handle_Read_DeviceIo();//处理读控制命令
}
2.3 PC端的USB驱动程序设计
DriverStudio 是一套用来简化微软Windows 平台下设备驱动程序的开发、调试和测试的工具包。DriverStudio 当前的版本包括DriverWorks、SoftICE及其它工具模块。DriverWorks:对于Windows NT下和 Windows 98与Windows 2000/XP共同支持的Win32驱动模型(WDM)设备驱动程序的开发提供完全的支持。DriverWorks中包含一个非常完善的源代码生成工具(DriverWizard)以及相应的类库和驱动程序样本,它提供了在C++下进行设备驱动程序开发的支持。SoftICE:SoftICE 是一个功能极其强大的内核模式调试器,它支持在配置一台单独的计算机或两台计算机下进行设备驱动程序的调试。DriverWorks提供了三个与USB直接相关的类:UsbLowerDevice,KUsbInterface和KUsbPipe类,用于实现USB设备操作。KUsbLowerDevice类用于逻辑设备的编程,KUsbInterface类用于接口的编程,KUsbPipe类用于管道的编程。最基本的例程有设备的启动,停止,卸载,读写,设备控制等例程 。
DriverStudio本身提供了一个设备驱动的框架,且可自动生成端点2的各种传输方式的源代码,故在此不讨论。对于设备控制命令传送的驱动则需根据实际应用具体实现如下:采用设备类请求函数
PURB BuildVendorRequest(
PUCHAR TransferBuffer,//为驱动程序存放传输数据的内存区
ULONG TransferBufferLength,//传输的字节数,对应于类请求的wLength
UCHAR RequestTypeReservedBits,//为类请求字节中的保留位
UCHAR Request,//具体请求数值,对应于类请求的bRequest
USHORT Value,//对应于类请求的wValue
BOOLEAN bIn=FALSE,//TRUE为输入:设备->主机,FALSE为输出:主机->设备
BOOLEAN bShortOk=FALSE,//TRUE表示设备传输的字节数,可少于指定的字节数
PURB Link=NULL,//为下一个传输的URB
UCHAR Index=0, //对应于类请求的wIndex
USHORT Function=URB_FUNCTION_VENTOR_DEVICE,//类别的请求
PURB pUrb=NULL//指向一存在的URB
);
实现的方法是对于读请求IOCTL_READ及写请求IOCTL_WRITE, 将相应的类别代码通过此成员函数传送给底层驱动,实现IO控制命令请求。代码实现如下:
NTSTATUS USBDRIVERDevice::DeviceControl(KIrp I)
{
NTSTATUS status;
PURB pUrb;
struct IOREQ* Ioctrl;
switch (I.IoctlCode())
{
case IOCTL_READ://处理IO读请求
PIRP pIrp=(PIRP)I;
Ioctrl=(struct IOREQ*)pIrp->AssociatedIrp.SystemBuffer;//取缓冲区数据
pUrb=m_Lower.BuildVendorRequest(NULL,0,0,0,Ioctrl->wvalue,TRUE,TRUE,NULL,
Ioctrl->wIndex,URB_FUNCTION_VENDOR_DEVICE);//类请求:读请求
if(pUrb==NULL)
status=STATUS_INSUFFICIENT_RESOURCES;
else
{status=m_Lower.SubmitUrb(pUrb);delete pUrb;}
break;
case IOCTL_WRITE:
PIRP pIrp=(PIRP)I;
Ioctrl=(struct IOREQ*)pIrp->AssociatedIrp.SystemBuffer; //取缓冲区数据
pUrb=m_Lower.BuildVendorRequest(NULL,0,0,1,Ioctrl->wvalue,TRUE,TRUE,NULL,
Ioctrl->wIndex,URB_FUNCTION_VENDOR_DEVICE); //类请求:写请求
if(pUrb==NULL)
status=STATUS_INSUFFICIENT_RESOURCES;
else
{status=m_Lower.SubmitUrb(pUrb);delete pUrb;}
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
if (status == STATUS_PENDING)
return status;
else
return I.PnpComplete(this, status);//成功,提交URB
}
另外,对于设备驱动的安装,还应修改设备配置文件中的安装程序类别和GUID:Class=USB,ClassGUID={36FC9E60-C465-11CF-8056-444553540000},否则将无法识别出是USB设备 。
2.4 PC端的应用程序设计
通过调用API函数编写PC的应用程序。函数有CreateFile(),CloseHandle(), DeviceIOControl(),ReadFile(),WriteFile()。其中DeviceIOControl()用于PC(主机)向DSP发送请求,ReadFile()和WriteFile()分别用于从USB设备中读出数据以及向USB设备中写入数据。在设计过程中必须注意的问题是:由于USB接口是主-从方式的接口,它的一切传输过程都必须通过主机向外设发送请求后才可以开始,所以在使用ReadFile()、WriteFile()读写数据之前,必须先通过DeviceIOControl()向USB设备发送请求,而且还须采用异步IO方式。例如,读数据的部分代码为:
struct IOREQ Ioctrl;//IO请求的结构
OVERLAPPED ov;//异步IO
memset(&ov,0,sizeof(OVERLAPPED));
ov.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//创建一事件
ResetEvent(ov.hEvent);//复位事件
unsigned long nBytes;
Ioctrl.wvalue=xx; Ioctrl.wIndex=xx;// 传送的标志位及要传输的字节数
bResult = DeviceIoControl(hDevice,IOCTL_READ,&Ioctrl,8,NULL,0,&nBytes,&ov);//启动 //读命令,
if (bResult != TRUE)
{
if(GetLastError()!=ERROR_IO_PENDING)//IO错误
{
MessageBox("请求数据传送失败! 已放弃", "错误");
return 0;
}
switch(::WaitForSingleObject(ov.hEvent,20))//等待20ms
{
case WAIT_OBJECT_0:
if(!::GetOverlappedResult(hDevice,&ov,&nBytes,TRUE))
return 0;
break;
case WAIT_TIMEOUT://超时错误
::CancelIo(hDevice);
return 0;
default:
return 0;
}
}
bResult = ReadFile(hDevice,…,&ov);// 从设备读数据
if (bResult != TRUE)
{…//同上}
3 系统测试及结果
继电器测试与保护装置的交流实验部分测试结果如下图所示。实验证明,利用USB传输响应时间完全达到设定要求,即使在有10路AD数据的情况下也反应灵敏,特别地,用USB传输方式,有效的解决了现场作业方便性的问题。
4 结束语
本文以一实际开发的项目为例,对以DSP芯片为控制器的USB传输系统具体的硬件和软件实现进行了阐述,特别地详述了适用于具体应用的设备驱动的开发,此设备驱动也已成功应用于USB接口的图像传输,具有一定的通用性。
参考文献
[1]周立功等. PDIUSBD12 USB固件编程与驱动开发[M]. 北京:北京航空航天大学出版社,2003,2
[2]武安河等. Windows 2000/XP WDM设备驱动程序开发[M]. 北京:电子工业出版社,2003,4
[3]施诺等[译]. Windows 2000 设备驱动程序设计指南[M]. 北京:机械工业出版社,2001,9