需要注意的是PCI9054 Base0、Base1固定设置为内存映射空间和I/O映射空间,用于PCI9054内部寄存器的访问,Base2、Base3用户自己根据EEPROM配置来设定为内存映射空间或者I/0映射空间。在驱动程序设计过程中,经常需要设置和查询PCI9054内部寄存器,访问PCI9054内部寄存器,要用到Base0或Base1空间,本设计中,对PCI9054内部寄存器访问利用Base1,即IO映射空间,比如:PCI9054 DMA通道0传输字节数寄存器偏移为0x8C,若要设定DMA通道0一次传输的字节数,可以通过初始化了的KIoRange对象m_IoRange0的成员函数outd来实现。
下面以主机由驱动程序向数据采集卡发送工作方式控制字为例,说明KMemoryRange成员函数访问硬件的方法,代码中m_MemoryRangeForB2为KMemoryRange对象,用于Base2空间的访问。
NTSTATUS DZDevice::DZDRIVER_IOCTL_Write_Handler (KIrp I)
{
NTSTATUS status = STATUS_SUCCESS;
PULONG pBuffer=(PULONG)I.IoctlBuffer();//输入参数
ULONG m_Offset=*pBuffer; //控制字寄存器在局部地址空间上的偏移
ULONG m_Data=*(pBuffer+2) ;//控制字
m_MemoryRangeForB2.outw(m_Offset,m_Data);//向寄存器写控制字
I.Information() = sizeof(ULONG); //本次操作传输的字节数
return status;
}
驱动程序控制字写函数中,首先取得从应用程序传递的偏移地址和控制字的指针,偏移地址和控制字存储在IRP的AssociatedIrp域中SystemBuffer指针指向的缓冲区,驱动程序先取得指向该缓冲区的指针,然后分别取得偏移地址和控制字,最后向该偏移地址写控制字,程序执行后,即向硬件上的寄存器写入了相应的控制字。
2)DMA方式读取FPGA上FIFO数据实现
DriverWorks提供了3个类KDmaAdapter、KDmaTransfer、KCommonDmaBuffer用于实现DMA操作,KDmaAdapter类用于建立一个DMA适配器对象,说明DMA通道特性和提供串行化访问的服务;KDmaTransfer类用于启动,控制DMA的传输以及DMA传输结束后数据由公用缓冲区拷贝靠应用程序数据缓冲区;KCommonDmaBuffer类用于申请系统提供的公用缓冲区。DriverWorks中,实现DMA传输过程如图3所示。

首先在设备启动例程(OnStartDevice)中创建一个KDmaAdapter类实例且在适配器对象描述表中正确描述适配器对象;创建一个KCommonDmaBufer类实例,调用该类的成员函数Initialize初始化公用缓冲区大小;创建一个KDmaTransfer类实例,并初始化为使用公用缓冲区作为DMA数据区。然后编写KDmaTransfer回调函数OnDamReady,回调函数中,先调用成员函数ByteRemaining(),判断数据是否传输完成。若完成,则调用函数Terminate()完成相应当IRP;未完成,则调用GetTransferDescripters(),获取当前传输数据的物理地址,传输字节数,然后进入StartDMA例程设置PCI9054的DMA寄存器,开始真正数据传输。当前段传输完成时,PCI9054的DMA中断控制器产生一个DMA中断,进入中断服务例程(ISR),中断服务例程中,首先判断是否为DMA通道的中断,然后禁止本次中断,再清除本次中断,最后连接到延时过程调用(DPC)。延时过程调用(DPC)中,调用KDmaTransfer类的Continue()成员函数,继续下一个段传输,直到DMA数据传输结束,完成该IRP。
3.3 驱动程序和应用程序之间的通信 驱动程序和应用程序之间的通信包括应用程序与驱动程序通讯和驱动程序与应用程序的通信。应用程序与驱动程序通信过程为,应用程序先用CreateFile函数打开设备,然后用DeviceIoControl和驱动程序通信, DeviceIoControl使用不同的命令字来调用驱动程序中的函数,包括从驱动驱动程序读取数据和写数据给驱动程序两种情况。也可以用ReadFile从驱动中读取数据或者用WriteFile写数据给驱动程序,当应用程序退出时,用CloseHandle关闭设备。
当驱动程序捕捉到特点事件发生时,应当通知应用程序,与应用程序通信,驱动程序和应用程序通信的方法主要有两种,DeviceIoControl异步调用和WIN32事件通知。DeviceIoControl异步调用时,驱动程序先将此IRP保存起来,然后调用I.MarkPending(),最后驱动程序返回STATUS_PENDING,当一个事件发生时,驱动程序再完成这个IRP;使用WIN32事件通知和应用程序通信时,应用程序首先创建一个事件,直接将该事件句柄传递给驱动程序,应用程序等待驱动程序发送事件消息。
4 结束语
数据采集是现实世界模拟信号到便于计算机处理的数字信号的第一步,数据采集卡是实现数据采集的关键设备。根据上述数据采集卡硬件,用DriverWorks成功开发了基于CPCI总线的数据采集卡驱动程序。为测试驱动程序工作正常与否,编写了上层测试应用程序,测试表明,驱动程序能够快速加载主机控制命令,通过DMA方式高速读取板卡上的数据,数据采集卡工作正常,传输速率符合要求。
参考文献
[1]武安河,邰铭,于洪涛.Window 2000/XP WDM设备驱动程序开发.北京:电子工业出版社,2003.
[2] 李贵山,陈金鹏著.PCI局部总线及其应用.西安:西安电子科技大学出版社. 2003.
[3]Waler Oney.Programming The Windows Driver Model.微软出版社,1999.
[4]PCI9054 Data Book .Version 2.1 PLX Technology, January 2000.
[5] Microsoft1Windows2000 驱动程序开发大全[M].北京:机械工业出版社,2001.