基于DSP 的PCI 设备Windows 驱动程序设计
2020-10-13杨再明崔恒武曹洪龙
杨再明,崔恒武,曹洪龙
(1.92001 部队,青岛266000;2.苏州大学电子信息学院,苏州215006)
0 引言
在现代航空航天和航海领域,通常需要同时监测多个信号并根据各个信号的状态和时序进行工作状态监测,从而为整体系统正常运转提供保障。例如,采用便携式多路开关量信号检测仪同时对多路电磁继电器通断信号进行检测,很好的解决了多路开关量信号的并行实时检测和计时问题[1]。在这类仪器设计中主要采用上位机+下位机的系统模式,其中上位机通常采用Windows 系统的计算机,下位机为定制的信号采集板卡,考虑通信速率和实时性要求上位机和下位机采用PCI 协议通信的方式被广泛应用。该系统的软件开发主要分为上位机Windows 应用程序开发、下位机嵌入式软件开发和Windows 驱动开发三部分。对于Win⁃dows XP 或Windows 7 系统,微软公司(Microsoft)提供Windows 驱动程序开发包WDK(Windows Driver Kit)简化了Windows 驱动程序开发过程,可用于开发支持即插即用功能的WDM(Windows Driver Mode)驱动程序,实现上位机Windows 应用程序基于PCI 协议通过驱动访问下位机的功能[2-4]。
1 基于DSP的信号采集PCI设备
1.1 系统架构
为满足信号采集、处理等功能的实时性要求,数字信号处理器(DSP,Digital Signal Processor)被广泛应用在实时数字信号处理领域,TI TMS320C54x DSP 凭借其低功耗、高性价比的优点在信号采集PCI 设备领域广泛应用。图1 是基于TMS320C54x DSP 的信号采集PCI 设备系统架构框图,主要由上位机和下位机两部分组成,上位机和下位机PCI 协议进行通信,实现信号采集、实时分析、处理、存储和显示等功能。其中下位机为信号采集卡,其核心处理器可以选用TMS320C54x DSP,主要完成信号采集、实时分析处理。由于TMS320C54x DSP 本身不支持PCI 协议,需要协议转换芯片PCI2040 实现DSP 的HPI 接口与上位机(计算机)的PCI 接口之间相互转换。信号采集卡采用PCI金手指方式插入上位机主板上的PCI 插槽实现上位机和下位机的互联互通。
如图1 所示,上位机运行Windows 系统,具有可扩展性,支持新的硬件。为确保Windows 系统的健壮性和可靠性,Windows 系统从总体上分为内核模式和用户模式。Windows 系统内核工作在内核模式,可以访问底层硬件(访问物理映射内存、设备端口等),因此要增加新硬件则必须增加Windows 内核内容,模块化的驱动程序相当于Windows 内核的“积木”件,通过安装驱动程序可以实现Windows 内核对新硬件(例如图1的信号采集卡)的操作访问。用户应用程序(本例中为信号采集应用程序)工作在用户模式,无法直接访问底层硬件(本例为PCI 型的信号采集卡),需要安装驱动后通过驱动实现访问。
图1 基于DSP的信号采集PCI设备系统架构框图
1.2 驱动程序的工作方案
由图1 可见信号采集卡驱动程序在整个系统中起到联通作用,实现了运行在Windows 系统下的应用程序与信号采集卡(PCI 插卡)之间的通信,其具体的工作方案如图2 所示。在系统上电后驱动受限对其进行枚举识别,上位机用户应用程序启动后通过驱动可以查询有效的信号采集卡并获得访问句柄。由于TI TMS320C54x DSP 内部没有可固化用户DSP 程序的存储器,从节约成本和低功耗方面考虑选用HPI 自举模式由上位机在启用信号采集卡时灌DSP 程序,因此图2 驱动工作方案中主要分为初始化信号采集卡和数据采集2 部分。
当上位机用户应用程序要采集数据时,必须先通过驱动灌DSP 程序到信号采集卡。驱动在接收到灌DSP 程序的信息后,首先通过控制DSP 复位管脚电平触发DSP 硬件复位中断,通过查询DSP 复位管脚电平来确认信号采集卡是否成功进入HPI 自举模式;确认DSP 进入HPI 自举模式后,驱动通知上位机用户程序可以灌DSP 程序,并在接收到DSP 程序自举列表后按地址加载到DSP 的程序空间和数据空间;信号采集卡HPI 自举成功后将运行加载的DSP 程序采用中断方式通知驱动,驱动处理中断事务通过读取信号采集卡工作状态确认其初始化是否成功,记录且同时通知上位机用户应用程序。
用户应用程序接收到驱动通知确认信号采集卡正常工作后,可以发送采集命令通过驱动通知信号采集卡开启采集功能。为提高信号采集、处理和显示、存储的实时性,用户应用程序将启用事件(Event)监听线程,通过WaitForSingleObject 函数或WaitForMultipleOb⁃jects 函数监听与驱动共享的Event 状态实现实时处理;PCI 驱动程序在接收到采集命令后,通过中断服务函数(ISR)实时响应信号采集卡向驱动发送的中断,利用内核函数KeInitializeDpc 启用延迟过程调用例程(DPC)搬移数据提高ISR 响应的实时性;信号采集卡连续采集信号,且每完成一帧信号采集、分析、处理将发送中断通知驱动搬移数据。当驱动接收到停止采集命令后,通知信号采集卡停止数据采集进入低功耗状态。
图2 PCI驱动程序工作方案
2 基于WDM的PCI设备Windows驱动设计
2.1 基于WDM的Windows驱动程序基本框架
PCI 设备一般选用WDM(Windows Driver Mode)驱动程序类型支持即插即用,其入口函数为DriverEntry函数。Windows 内核中的I/O 管理器调用DriverEntry函数并通过形参pDO 传入驱动对象,形参RegistryPath为驱动在注册表中的键值[5],具体示例代码如下。代码中用Extern"C"修饰DriverEntry 函数是为了指示编译器该函数按C 语言的方式进行编译。
●AddDevice 回调函数,本例设置Pci2040AddDe⁃vice 为回调函数,主要负责创建设备对象、初始化设备扩展数据结构、创建符号链接和初始化DPC 处理函数,等等。
●DriverUnload 回调函数,本例设置Pci2040Un⁃load 为回调函数,即注册卸载例程,但在WDM 中卸载处理工作一般放在PNP 相关的回调函数中处理。
●IRP(I/O Request Packet)派遣函数,Windows 系统中I/O 管理器是用户模式和内核模式之间通信的桥梁,即工作在用户模式的应用程序发出I/O 请求时,由I/O 管理器捕获并转化为IRP 请求发送给驱动,驱动即调用对应的派遣函数进行处理。表1 中列出了一些主要IRP 派遣函数。
表1 WDM 驱动主要的IRP 回调函数
2.2 PCI设备驱动程序设计
开发基于WDM 的PCI 驱动程序主要是在WDM框架上实现IRP 派遣函数,实现应用程序、驱动和采集卡之间的相互通信。其中,应用程序可以通过Creat⁃File、ReadFile、WriteFile、DeviceIoCtrol 等函数与驱动程序进行交互,其对应的IRP 类型见表1。实现PCI 设备驱动程序的难点是驱动程序与采集卡之间的交互,重点是资源访问操作和中断处理,这需要在了解板卡硬件原理图的基础上实现。
以图1 为例,驱动对设备的访问相当于借助PCI2040 芯片转换进行HPI 访问,访问HPI 映射的物理存储空间需要调用内核函数READE_REGIS⁃TER_ULONG 和WRITE_REGISTER_ULONG 该系列函数,主要通过控制C54x DSP 的管脚状态实现表2 所示4 种模式的HPI 寄存器访问操作,进而封装成对HPI映射的物理存储空间的4 种访问方法[6]。
●DspMemReadNoInc 驱动函数,功能是读取指定C54x DSP 的RAM 地址的1 个16 位数据,具体操作方法是采用表2 中模式2 先向HPIA 寄存器写入地址,然后采用表2 中的模式3 读取HPID 的数据。
●DspMemWriteNoInc 驱动函数,功能是向C54x DSP 的RAM 地址写入1 个数据,具体操作方法是采用表2 中模式2 先向HPIA 寄存器写入地址,然后采用表2 中的模式3 向HPID 写入数据。
●DspMemReadInc 驱动函数,功能是读取指定C54x DSP 的RAM 地址的连续N(N≥1)个16 位数据,具体操作方法是采用表2 中模式1 先向HPIA 寄存器写入地址,然后采用表2 中的模式1 连续执行N 次HPID 读操作,可以读取从HPIA 中的地址开始的连续N 个16 位数据。
●DspMemWriteInc 驱动函数,功能是向C54x DSP的RAM 地址连续写入N(N≥1)个16 位数据,具体操作方法是采用表2 中模式1 先向HPIA 寄存器写入初始地址(目标地址-1),然后采用表2 中的模式1 连续执行N 次HPID 写操作,实现以目标地址为起始地址连续写入N 个16 位数据。
表2 C54x DSP HPI 访问方式
此外,可以通过调用内核函数WRITE_REGIS⁃TER_ULONG 该系列函数运用表2 中的工作模式0 向HPIC 寄存器写入数据,实现驱动触发DSP 的主机中断、配置HPI 通信方式等功能。
采集卡亦可以发送中断信号,WDM 驱动程序采用如下步骤处理中断事件:
(1)在IRP_MJ_PNP 的派遣函数Pci2040Pnp 中处理次级的IRP_MN_START_DEVICE 类型时,调用内核函数IoConnectInterrupt 将中断注册到系统,并指定中断服务函数(例如OnInterrupt)。
(2)自定义中断服务函数(例如OnInterrupt)实时处理中断,主要方法是读取中断标志,清除中断标志位,根据读取到的中断标志进行相应的处理。
(3)由于中断服务程序运行在高的设备中断请求级(DIRQL),执行时间不易过长,因此通常代码量尽量少,必要情况将不重要的代码放置DPC 函数中执行。DPC 函数运行在在相对较低的DISPATCH_LEVEL 级别,调用方法是在中断服务函数中利用内核函数IoRe⁃questDpc 调用在AddDevice 回调函数中初始化的DPC函数处理。
2.3 基于WDK的驱动实现
微软提供Windows 驱动开发套件(WDK,Windows Driver Kit),包括了WDM 驱动开发工具、例程和帮助文档,可用于用户开发Windows 驱动程序,其官方提供的最新版本为7.1,安装后可以编译链接C 源程序生成驱动。利用WDK 生成驱动程序的步骤如下:
(1)编写MAKEFILE 文件(无扩展名),内容固定不变,详细内容见WDK 例程。
(2)编写Sources 文件(无扩展名),定义驱动名称、类型和源程序文件,例如:
注意:MAKEFILE、Sources 和源程序文件在同一文件夹下。
(1)在开始菜单的“所有程序”中选择WDK 下的“Build Environments”,选择操作系统版本(例如Win⁃dows XP)下X86 Checked Build Environment 快捷方式进入命令行形式的工具窗口。
(2)利用CD 命令进入Sources 等文件所在目录(英文路径),然后执行build 命令,若无误将生成驱动程序pci2040Examples.sys。
3 PCI设备驱动测试程序
采用VC++开发测试程序如图3 所示,基于DSP的信号采集PCI 设备实现了对多路开关量信号的采集和状态变化判断。设计的驱动使Windows 系统可以识别该设备,并为测试程序提供了对该设备的读、写和中断响应等接口。设计图3 所示测试程序时,主要采用以下方法:
图3 基于DSP的信号采集PCI设备测试程序界面
●利用Windows API 函数SetupDiGetClassDevs 对该PCI 设备进行枚举。
●利用Windows API 函数DeviceIoControl 发起IRP 与驱动交互。
●利用Windows API 函数ReadFile 和WriteHan⁃dle 函数与驱动交互。
●开启独立线程采用Windows API 函数WaitForS⁃ingleObject 或WaitForMultipleObjects 接收驱动发送的事件中断,提高上位机的响应实时性。
4 结语
本文给出了基于WDM 的Windows 驱动程序设计方法,以基于DSP 的信号采集PCI 设备为例主要介绍了WDM 驱动程序设计框架、PCI 驱动程序设计过程和基于WDK 的驱动程序生成方法,并给出了进行开关量信号采集的PCI 设备测试程序实例,为开发基于WDM的PCI 驱动程序提供了参考。