1引言
随着计算机技术和微电子技术的发展,各种性能优良的数据采集卡不断出现,从而为测量控制领域所需的实时、高效的信号采集和处理提供了可能。当前,利用计算机强大的计算能力和丰富的软硬件资源来组织仪器系统的虚拟仪器技术得到了迅猛发展,并且已经成为研究热点[1-3]。基于数据采集系统的虚拟仪器系统是成本最低和应用最广的虚拟仪器组建方案,数据采集是该类系统最核心的问题[4]。我们在开发物理化学虚拟仪器系统时,采用了由众人精密测控技术公司研制的基于工业标准的12位32通道ISA数据采集卡PS-2104,由于该卡没有配套的驱动程序,所以面临端口访问如何实现这一棘手问题。我们在实际过程中经过探索,发现采用Windows下的动态链接库技术(DLL),将对端口寄存器的读写操作封装起来作成DLL可以解决该问题。DLL作成并且经过适当配置后,在VB语言环境开发虚拟仪器应用软件时就可象使用Windows API函数一样来使用端口操作函数,大大方便了后续软件模块开发。本文对基于DLL技术实现化学虚拟仪器的端口操作的技术细节进行了详细介绍。
2采用DLL技术的必要与可能
在Windows系统中,应用程序在多任务环境中共享内存资源,因此,静态链接往往导致的内存资源浪费,为了解决这一问题,广泛地采用了DLL技术。Windows本身通过DLL为所有的应用程序提供API函数和资源支持,Windows本身使用的所有库也都是DLL[5]。DLL不象一般Windows应用程序必须有一个消息循环随时准备接受消息,DLL是不接受任何消息的。DLL实际上是一种基于Windows操作系统的可被其他应用程序或DLL调用的函数集合组成的可执行模块,但其本身并不能运行[6]。广义地讲,可将DLL视为对Windows操作系统的扩展。
VB以其开发的易学好用和强大的用户界面设计能力倍受程序员的青睐,是微软公司推
出的开发Windows应用程序的主力平台之一。我们在开发物理化学虚拟仪器软件时也采用VB平台。但其不能直接对寄存器进行操作,没有直接的输入输出端口操作语句,而Windows也未提供访问I/O端口的函数。所以要实现这一功能,必须借助其他编程语言,而C语言或汇编语言具备较强的对硬件操作的能力,所以可以通过在VC环境下编制出对硬件I/O端口访问的动态链接库(DLL),然后在VB中调用。
3实现端口操作的动态链接库
3.1 VC++6.0中开发的对端口操作的DLL步骤
(1)启动VC++6.0,在IDE中选择File/New,再选择Projects标签,选择Win32 Dynamic Link Library选项,在Project 中输入DLL的名字MySample ,单击OK进入Step1,默认所有其余选项, 单击FiNIsh进入Step2,完成DLL框架设置。
(2)选择File/New,再选择Files标签,选择c/c++ source file选项,选中Add to project复选框,在 File中输入DLL实现文件的名字MySample,单击OK,编写端口操作函数代码实现文件MySample.CPP。
(3)选择File/New,再选择Files标签,选择Text file选项,确保选中Add to project复选框,在 File中输入DLL模块定义文件名MySample.DEF,单击OK,编写模块定义文件MySample. DEF。
(4)选择Build/Execute,生成所需的DLL文件,将MySampleDebugMySample.dll拷贝到Windows目录下的system子目录中,即可供VB调用。
3.2 VC++6.0中开发的对端口操作的DLL代码
3.2.1 函数代码实现文件MySample.CPP
其中包含了两个必需函数LibMain()和Wep(),前者是DLL的主入口点,当DLL首次装入时被调用,完成DLL的初始化工作;后者是DLL的结束函数,当所有调用该DLL的程序结束时被调用,负责DLL被卸出前的必要清除工作(如释放系统资源等)。由于此处开发的DLL函数仅仅是实现对I/O端口的读写,故不需要为其进行初始化和收尾清除工作,在这两个函数中只加一条“return 1;” 语句以表示调用成功即可[7]。函数LibMain()和Wep()前加入关键词“FAR”和“PASCAL”,分别用来声明远程调用和定义参数传递与堆栈清除时所使用的协议。
#include "windows.h"
#include "conio.h"
int FAR PASCAL LibMain
( HANDLE hinstance, //hinstance为 DLL的句柄
WORD wDataSeg, //wDataSeg为数据段的注册器值
WORD wHeapSize, //wHeapSize为模块定义文件中定义的堆大小
LPSTR lpszCmdline //lpszCmdline为指向命令行参数的远指针
)
{
return 1;
}
int FAR PASCAL WEP(int fSystemExit)
{
return 1;
}
//以下函数从I/O端口读一个字节
int _stdcall inpvb(unsigned short port_adr)
{
int value;
value=_inp(port_adr);
return(value);
}
//以下函数输出一个字节到I/O端口
void _stdcall outpvb(unsigned short port_adr, short value)
{
_outp(port_adr,value);
}
3.2.2模块定义文件MySample.DEF
该文件指定应用程序模块的各种属性
LIBRARY MySample //动态链接库名称
DESCRIPTION 32 bit DLL for input/output port operation //描述动态链接库的用途
CODE PRELOAD MOVEABLE DIACARDABLE //设置代码段属性
DATA PRELOAD MOVEABLE SINGLE //设置数据段属性
HEAPSIZE 1024 //设置局部堆的大小
EXPORTS //定义导出函数
WEP @1
inpvb @2
outpvb @3
4 VB中使用DLL实现端口操作
VB应用程序要调用DLL函数,必须先进行声明。DLL函数必须在标准全局模块或窗体层中声明。对无返回值的写端口函数应声明为Sub格式,对有返回值的读端口函数应声明为Function格式,同时应注意C语言和VB数据类型的对应关系,以使VB声明语句中参数、返回值的类型应与DLL中相应参数、返回值的类型一致[8]。声明后的DLL函数的使用如同VB本身的函数,以下举一例说明(数据采集)。
在标准模块中加入以下两条声明:
Public Declare Function inpvb Lib "MySample" (ByVal port_adr As Integer) As Byte
Public Declare Sub outpvb Lib "MySample" (ByVal port_adr As Integer, ByVal value As Integer)
在定时触发的数据采集事件模块中加如下代码:
address = 256 '结合计算机I/O端口分配情况为采集卡分配基地址(与跳线应一致)
Call outpvd(address, 32 +channel) '选择通道(调用自己建立的DLL函数)
Call outpvd(address + 1, 0) '启动A/D转换(调用自己建立的DLL函数)
Do While (inpvd(address + 1) = 1) '循环读取转换状态直到A/D转换已经完成
Loop
H = inpvb(address + 2) '读转换后高8位
L = inpvb(address + 3) '读转换后低4位(此时位8位)
Result = H * 16 + L / 16 '合成12位数据
5结束语
将对端口寄存器读写操作封装起来作成DLL,就使得在VB语言环境开发虚拟仪器应用软件时使用端口操作函数如同使用Windows API函数,所以极大地方便了后续软件开发。此外,由于DLL函数是在程序运行过程中动态装入的,所以程序代码的冗余度大大降低,运行效率大大提高。




