摘要:介绍了利用DriverWorks开发基于PCI总线的DSP运动控制卡的Windows驱动程序的详细过程和方法,实现了在Windows环境下上位机和DSP运动控制卡之间的高速通信。
关键词:PCI;DSP;WDM
1 引言
随着数控技术与装备的发展,特别是在通用微机数控领域,以PC+运动控制卡模式的开放式运动控制系统已经越来越引起人们的重视。而DSP以其功能强、速度快、编程和开发方便等特点,逐渐成为运动控制卡开发的主流处理器,其与PCI总线结合可以实现上下位机的高速通讯,能够满足控制系统对实时性和精度的要求[1]。
PCI总线是一种高性能32/64位地址数据线复用的外部总线,其与CPU无关,与时钟频率也无关,可以适用于各种平台,支持多处理器和并发工作。在33MHz总线速度下,访问时间只需要60ns,数据传输速率最高可达132Mb/s,此外其还有自动识别和配置外设的功能,使所有与PCI兼容的设备实现真正的即插即用。PCI总线正以其优良性能和可适应性迅速取代原先的ISA总线的主导地位,成为现代微机的主流总线[2]。
WDM(Windows Driver Model),即Win32驱动程序模型,是微软公司为当前主流操作系统Win98、Win2000和WinXP等的驱动程序设计的一种构架。它和传统的Win3.x、Win95和Win98等使用的VXD型驱动是完全不同的体系结构,其可支持电源管理、自动配置和热插拔等功能,是微软公司力推的全新驱动程序模式,旨在通过提供一种灵活的方式来简化驱动程序的开发,在实现对新硬件支持的基础上减少并降低必须开发的驱动程序数量和复杂性[3]。本文介绍的程序就是一种WDM驱动程序。
2 C28x DSP芯片和PCI总线专用接口芯片简介
近年来,TI公司推出了新一代的DSP芯片——TMS320C28x,它是到目前为止C2000系列中性能最强大的一代产品。与以前的C24x系列DSP相比,C28x系列具有更高的性能,进一步增强了芯片的接口能力和嵌入功能,并拓宽了数字信号处理器的应用领域,即从原先的普通电机数字控制拓展到现在的高端多轴电机控制[4]。它的出现,为高性能、高精度和高集成度的电机控制器的实现提供了优越的解决方案。
连接到PCI总线上的设备可分为主控设备和目标(从)设备两类。主控设备可以控制总线,驱动地址、数据和控制信号;目标设备不能启动总线操作,只能依赖于主控设备从其中读取数据或向其传送数据。主控设备芯片价格一般比较高,而目标设备芯片价格则比较便宜,并且目标设备芯片较简单,易于操作。
目前两大PCI专用接口芯片生产商是AMCC公司和PLX公司[5]。AMCC公司的PCI接口芯片主要有S5920、S5933、S5935等。S5920是目标设备芯片;S5933既可设置为主控设备芯片,又可设置为目标设备芯片;S5935是S5933的改进版。PLX公司在PCI接口芯片的设计生产中首屈一指,其目标设备芯片产品主要有PCI9030、PCI9050和PCI9052,PCI9050和PCI9052可以应用于ISA卡转接PCI的设计中,PCI9030是专门为嵌入式系统设计的;主控设备芯片产品主要有PCI9054、PCI9080和PCI9060,PCI9060是最早的32位主控设备芯片,PCI9080是PCI9060的后继产品,而PCI9054则是一种性价比很高的主控芯片。
由于主控设备一般比较复杂,并且价格昂贵,同时,为了缩短开发周期,在运动控制卡硬件的设计中,本文采用PLX公司的目标设备PCI9052作为PCI总线接口芯片。PCI9052是继PCI9050之后推出的低成本、低功耗、32位PCI总线接口芯片,其可与多种局部总线相连,并且支持相对慢的局部总线在PCI总线上的突发传输速率达到132MB/s。PCI9052兼容PCI V2.1协议特性;支持复用或非复用的8/16/32位局部总线;支持4个局部片选信号和5个局部地址空间,这些片选和空间的范围可由串行EEProM或主控制器独立编程;支持两个来自局部总线的中断,可生成一个PCI中断;所有控制、地址和数据信号都由PCI9052直接生成,用于驱动PCI和局部总线,不用额外驱动电路;支持局部总线等待状态,附加的LRDYi#握手信号可用于产生各种等待状态[6]。
上位机和基于DSP的运动控制卡通过PCI9052实现控制命令和运动轨迹数据的传输。具体来说,为了满足上下位机大量的高速数据的交换,采用共享双端口存储器实现局部总线接口,这样上位机将一些控制命令和数据通过PCI9052发送至双口RAM,并同时发送中断信号给DSP的外部中断引脚XINT2,DSP响应中断,读取双口RAM中的信息以控制电机的运行。同样,DSP将电机的一些运行参数发送至双口RAM,并同时发送中断信号给PCI9052的外部中断引脚LINTi1,由上位机读取相关参数进行分析计算,从而决定控制策略。当上下位机同时对双口RAM中的同一单元写操作时,将会发生访问冲突,这时可以由其内部仲裁逻辑决定优先权,而不需额外的处理程序。
3 PCI总线设备驱动程序的实现
为了实现上位机和DSP之间的通信,必须在Windows下开发出适用的WDM驱动程序,这也是开发基于PCI总线的DSP运动控制卡的关键之一。对于开发WDM驱动程序,目前有以下三种常用开发环境或它们的组合[2]:
(1)直接使用Windows DDK;
(2)使用DriverStudio;
(3)使用Windriver。
采用DDK开发难度较大,有很多通用的基础性的工作要做。使用DriverStudio开发难度则低一些,其工具软件已经做了很多基础性的工作,也封装了一些细节,开发者只要专心的去做需要的操作。Windriver几乎没有难度,可以很容易的进行驱动程序的开发,但只能开发与硬件相关的驱动,事实上开发者所编写的只是定制和调用它提供的通用驱动,因此执行效率不是很高。在这里,本文采用NuMega公司开发的DriverStudio中的DriverWorks工具包进行WDM驱动程序的设计,实现对PCI设备的配置空间、I/O端口和内存的访问以及对设备中断的处理。由于DriverWorks以工具栏的形式嵌入到VC中,因此可以更方便的用来进行开发和调试驱动程序。
3.1 生成WDM代码框架和配置信息
打开VC,出现DriverStudio在VC中嵌入的工具栏,利用其上的DriverWizard可以快速的生成WDM驱动程序框架,它包括两个工程:一个是驱动程序工程,另一个是测试应用程序工程。在驱动程序工程中,如果在向导页选择了SystemManaged的DPC类型,将产生3个类:从基类KDriver派生的PCI9052类、从基类KPnpDevice派生的PCI9052Device类和从基类KDriverManagedQueueEx派生的PCI9052Device_DriverManagedQueue类,如果选择DriverManaged的DPC类型,就只有前两个类。PCI9052类负责驱动程序的装载和卸载,处理的内容包括对应的DDK中的DriverEntry、AddDevice和Unload例程。PCI9052Device类负责PCI设备的功能实现,主要包括设备的Pnp处理、电源管理、分发例程处理和中断处理等,主要的功能代码编写都在该类中。如果没有对分发例程进行串行化,就没有类PCI9052Device_DriverManagedQueue。
3.2 配置空间的访问
在硬件设计时,根据板卡的具体功能已经确定了PCI9052的配置信息内容,并且烧写到EEPROM中,每次上电初始化时,进行自动配置。所以设计驱动程序时不提供对PCI配置空间、9052局部配置寄存器和EEPROM的操作,以确保板卡功能固定,不受应用程序干扰。但是,如果需要访问配置空间则可以通过类KPciConfiguration来操作。在WDM中,访问PCI设备配置空间通过向PCI总线发送读写配置空间的IRP来实现,该类封装了这些操作,使配置空间的操作非常方便。
3.3 I/O端口的访问
在这里,PCI配置寄存器提供的基地址寄存器1固定用于PCI9052芯片的操作寄存器I/O映射地址。类KIoRange封装了对I/O端口的操作。对PCI设备而言,首先需要在OnStartDevice函数中取得I/O资源,然后初始化。例如:
KIoRange m_IoPortRange1;//创建实例
status = m_IoPortRange1.Initialize(pResListTranslated,//转换后的资源列表指针pResListRaw,//原始的资源列表指针 PciConfig.BaseAddressIndexToOrdinal(1) //‘1’代表分配的I/O资源在PCI基地址的序数,PCI设备最多可以有6个内存或I/O资源空间);
参数pResListTranslated和pResListRaw可以在KpnpDevice::OnStartDevice中分别通过KIrp::TranslateResources和KIrp::AllocatedResources来得到。
在WDM中,对于I/O端口,系统将其看成寄存器,因此必须将I/O资源分配成可由系统访问的非分页内存,函数Initialize可以完成该工作。
初始化成功与否可以通过检查返回值来判断。
if (NT_SUCCESS(status)) //成功初始化
eLSe {Invalidate();return status;} //未成功初始化,错误信息在status中
成功初始化后,就可以调用KIoRange类的成员inb、outb、inw、outw、ind和outd从端口读或写一个字节、字和双字数据。如:
m_IoPortRange1.inb(ULONG ByteOffset,//以字节为单位的地址偏移量;PUCHAR Buffer,//指向输入数据的指针;ULONG Count //输入数据的字节数);
在驱动程序停止运行之前,一定要卸载资源,即通过对I/O端口调用成员函数Invalidate实现。
3.4 内存的访问
PCI配置寄存器提供的基地址寄存器0固定用于PCI9052芯片的操作寄存器内存映射地址。类KMemoryRange封装了对PCI设备的映射内存的访问。在WDM中,驱动程序得到的只是PCI总线配置机构分配的物理内存,这在驱动程序中是无法使用的,必须将其转换为系统可以访问的非分页内存,在DDK中调用函数MmMapIoSPACe。在KMemoryRange类的成员函数Initialize中实现这个转换。例如:
status = m_MemoryRange0.Initialize(pResListTranslated,pResListRaw,PciConfig.BaseAddressIndexToOrdinal(0));
以上函数的参数、意义和用法与I/O端口的操作基本相同,类KMemoryRange的其他成员函数操作也与I/O端口的操作基本相同。
3.5 中断的处理
在DriverWorks中,将中断的处理封装成类KInterrupt和宏MEMBER_ISR。例如:
public:
DEVMEMBER_DISPATCHERS //自动声明用KDevice继承来的分发例程
MEMBER_ISR(pci9052Device,Isr);//声明连接的中断服务例程Isr
BOOLEAN Isr(void);//声明中断服务例程函数
protected:
KInterrupt m_pci9052Interrupt;//中断变量
这样,中断服务程序的实现形式为:
BOOLEAN pci9052Device::Isr(void)
{if (FALSE) //检验是否为硬件设备产生的中断
{return FALSE;//不是硬件设备产生的中断,返回FALSE}
//这里编写具体的中断服务程序,即上下位机通信程序
return TURE;//是硬件设备产生的中断,返回TRUE
最后,在OnStartDevice函数中,调用类KInterrupt的成员函数InitializeAndConnect初始化中断变量和调用LinkTo连接中断例程。如下:
status = m_pci9052Interrupt.InitializeAndConnect(pResListTranslated,LinkTo(Isr),this );那么,中断服务程序就可以正常的工作了。
3.6 驱动程序的安装及调试
Win98、Win2000和WinXP是依靠一个扩展名为INF的文本文件来控制与安装驱动程序的相关信息的。DriverWorks已经在所建工程的driver目录下生成了一个设备安装文件。在这里,只需将该文件中Strings部分双引号中的提示改为相应的内容即可生成本设备的安装文件。当通过修改过的INF文件和编译后生成的.sys系统文件成功安装了驱动程序后,还应编写用户应用程序仔细测试驱动程序代码的可靠性和安全性。
4 结论
传统的驱动程序都是采用DDK开发,难度较大,并且开发效率也不高。而采用NuMega公司的DriverStudio中的DriverWorks工具包则可以简化驱动程序的开发过程,提高开发效率。实践表明,DriverStudio并没有通过牺牲系统性能来换取驱动程序的快速开发。
参考文献
[1] 鲍伟,张崇巍.PCI总线技术在运动控制卡中的应用[J].仪器仪表学报,2005,26(8):334~336(340).
[2] 尹勇,李宇.PCI总线设备开发宝典[M].北京:北京航空航天大学出版社,2005.
[3] Walter Oney. Programming the Microsoft Windows Driver Model[M]. Microsoft Press,2003.
[4] 徐科军,张瀚,陈智渊.TMS320X281X DSP原理与应用[M].北京:北京航空航天大学出版社,2006.
[5] 曹宁,汪飞.高速PCI总线接口卡的开发[J].电子技术应用,2004(7):26~29.
[6] PCI9052 Data Book[Z].PLX Corporation,2001.
作者简介:许春康(1982-),男,硕士,研究方向为数控技术。