摘 要:LabVIEW中的提供了调用共享库函数的接口,但是一些现成的函数库却因为接口参数类型不同而不能在LabVIEW中使用。利用重新编写动态链接库的方法可以建立旧函数库与LabVIEW联系的通道,提高现有资源的利用率。本文以SDK2000图像采集卡为例,介绍具体的实现过程。
1. 引言
实验室虚拟仪器工作平台是美国国家仪器公司研制的一种通用程序开发系统,以其强大的数据采集、数据处理、数据分析和仪器控制功能在现代测控领域中得到了广泛的应用。NI公司在推出LabVIEW语言同时,也推出了一系列的数据采集卡,但实际应用中往往会用到第三方厂家生产DAQ卡,在这种情况下就需要使用LabVIEW提供的外部程序接口。
LabVIEW的“调用函数库节点(CLF节点)”提供了调用标准函数和用户自定义函数的通用方法,对于LabVIEW不支持的硬件设备大部分采用这种方法进行驱动。但是CLF节点也存在不足,使用中遇到多的问题是参数类型不匹配。使用重写动态链接库的方法,一方面可以兼容旧函数库的参数类型,另一方发面可以获得LabVIEW提供的函数库应用。以下以SDK2000图像采集卡为例,介绍重写函数库的基本原理和程序编写过程。
2. 实现方案及方案论证
SDK2000图像采集卡提供的Visual C++ IDE 程序开发包包含了外部程序调用的共享函数库,但是有些函数不能直接用CLF节点进行调用。主要有两个原因:库函数的参数类型与LabVIEW不兼容;图形化语言对于一些底层操作不易实现。SDK2000图像采集卡提供的库函数含有LabVIEW不支持的数据类型,并且很多函数涉及到一些底层操作,如文件读取和内存管理。为了正确调用函数并返回有效数据,解决这两个问题是关键。
LabVIEW仅提供基本的数据类型,虽然可以在CLF节点参数设置中选择“Adapt toType”,但只能够传递LabVIEW内部使用的参数类型而已,而共享库函数使用上百种非标准数据类型。参数类型不匹配可分为两种情况:
● 非标准数据类型定义。很多程序和函数用到一些非标准的类型定义,例如它们常常用char、short和long的类型定义代替Windows API使用的BYTE、WORD和DWORD。这种情况下要正确调用这些程序和函数,必须找到这些参数在LabVIEW中同等的数据类型。
● 以结构或者类作为参数。一些程序和函数使用结构或者类作为参数,但是LabVIEW并不能够识别这些参数的数据结构。为了正确调用这些库函数,LabVIEW提供了两种解决办法:使用CIN节点或者重写函数库对这些函数进行重新封装,使这些函数的输入输出参数能够符合LabVIEW的标准。
SDK2000提供的共享函数库使用了很多LabVIEW不支持的参数类型,如RECT、VIDEOSTREAMINFO等。为了正确调用这些函数,必须用CIN节点或者重写函数库的方法对这些函数进行重新封装。相对而言,重新编写动态链接库方法比使用CIN节点更为常用。因为CIN代码直接嵌入到VI程序代码里,对于编程器的限制比较高,所以一般不采用CIN节点。
重新编写动态链接库的另一个原因,是为了获得LabVIEW提供的函数库应用。LabVIEW提供了在代码开发环境下的函数库,这些函数是针对于LabVIEW使用的数据类型,如下面所介绍的NumericArrayResize()函数。NumericArrayResize()函数用于动态改变数组的大小,不过只适合于LabVIEW使用的数组结构。LabVIEW函数库还包含了一些底层操作,如文件读写与内存分配等等,不存在参数类型不同和底层操作困难的问题。
3. 软件编程
由于需要重写的函数比较多,这里仅以重写保存DIB图像数据函数为例,说明重写函数库的编写过程。新函数GetDib()对原函数DSStream_GetCurrentDib()进行了封装,其作用是向原函数传递有效参数并返回LabVIEW能识别的数据。使用的编程环境为Visual C++6.0。
步:分析目标函数的参数类型
SDK2000开发包中对获得当前图像的DIB数据函数的声明为:
HRESULT DSStream_GetCurrentDib(int iCardID,BYTE* pBuffer,long* pSize)
pBuffer指向预分配的内存,值为NULL时,pSize得到保存图像需要的内存大小,若pBuffer不为NULL,函数将DIB图像数据保存到从pBuffer开始,长度为(*pSize)的内存区域。CLF节点不能直接调用该函数,因为LabVIEW编程环境下没有提供内存管理机制,并且CLF节点不能把指向预分配内存的指针传递给该函数,所以必须对这个函数进行重新封装。
图像的DIB数据是非数字型的数据,为了返回LabVIEW能够识别的数据,可以选择字符串或者单字节数组作为数据的载体,但是由于数据中包含了十进制的0,所以只能用单字节数组作为载体,并且为了能够动态改变数组的大小,必须以数组的句柄作为传递参数。因为在LabVIEW提供的函数库中,所有改变数组、字符串大小的函数都是针对于句柄进行的。
第二步:编写动态链接库
在VC中使用MFC Application(dll)建立一个名字为MySDK2000的工程,然后在MySDK2000.cpp中键入以下代码:
typedef struct
{ int32 dimSize;
uInt8 elt[1];
} TD1;//TD1的数据结构能被LabVIEW和新链接库识别
typedef TD1 **TD1Hdl;
extern "C" __declspec(dllexport) long GetDib(TD1Hdl BitMapinfo)//BitMapinfo为数组的句柄
{ long pp=0;
hr=DSStream_GetCurrentDib(m_iCardID, NULL, &pp);//得到保存图像需要的内存大小
if (NumericArrayResize(uB,1L,(UHandle*)&BitMapinfo,pp))//改变数组物理大小
return 0;
(*BitMapinfo)->dimSize = pp;//改变数组大小的标志
BYTE* lpdst; // 指向缓存DIB图像的指针
lpdst = (*BitMapinfo)->elt;
hr=DSStream_GetCurrentDib(m_iCardID, lpdst, &pp);//将当前图像的DIB数据保存到内存中
BITMAPINFO* pInfo = (BITMAPINFO*)lpdst;//位图文件头指针
return pInfo->bmiHeader.biSize;//返回位图信息头的数据长度
}
编写过程的几点说明:
● 为了正确使用LabVIEW中的数据类型,必须在MySDK2000.cpp中手工加上#include “extcode.h”的声明。头文件”extcode.h”中定义了CIN和外部子程序所用到的基本数据类型和许多函数等,用以解决某些常量和数据类型与系统头文件的冲突。
● 为了能够在VC中调用LabVIEW的函数库,必须把CINTools目录下LabVIEW.lib包含在工程里。使用NumericArrayResize()函数用以动态改变数组的长度,它的功能和WIN32函数LocalAlloc()具有相似的功能。它的函数声明如下:
MgErr NumericArrayResize(int32 typeCode, int32 numDims,
UHandle *dataHP,int32 totalNewSize);
● 如果内存分配失败,函数返回错误代码。如果成功,还须修改数组结构中的dimSize,因为此函数不能自动修改此值。
其它代码的编写类似于VC环境下的开发。代码完成后,构件并产生终的目标文件MySDK2000.dll。
第三步:在LabVIEW中调用动态链接库
配置CLF节点的各项参数。个选项为函数库文件名和路径,选择刚才编译的文件MySDK2000.dll;第二个选项为函数名,选择GetDib;第三项设置返回和传递参数。具体设置如下表:
其它选项保持默认设置。程序框图如下:
为了弄清楚CLF运行时传递参数和返回参数的原型,可以在CLF节点上右键弹出菜单中选择“Creat.c File…”,然后在文本编程环境下察看它的参数原型。这些参数原型其实就是LabVIEW使用到的数据结构,如上面说看到的数组结构TD1,TD1为LabVIEW一维数组的数据结构。
4. 结论
通过对原有函数的封装,函数将图像卡采集的DIB数据以数组形式返回给LabVIEW,LabVIEW将对这些数据进行进一步的处理,如二值化、边缘分析等等。实践证明,通过重写动态链接库的方法,第三方DAQ设备可以更协调地使用于LabVIEW开发环境中。
免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。