摘 要:LabVIEW中的提供了調(diào)用共享庫函數(shù)的接口,但是一些現(xiàn)成的函數(shù)庫卻因?yàn)榻涌趨?shù)類型不同而不能在LabVIEW中使用。利用重新編寫動態(tài)鏈接庫的方法可以建立舊函數(shù)庫與LabVIEW聯(lián)系的通道,提高現(xiàn)有資源的利用率。本文以SDK2000圖像采集卡為例,介紹具體的實(shí)現(xiàn)過程。
關(guān)鍵字:LabVIEW;動態(tài)鏈接庫;CLF節(jié)點(diǎn)
1. 引言
實(shí)驗(yàn)室虛擬儀器工作平臺是美國國家儀器公司研制的一種通用程序開發(fā)系統(tǒng),以其強(qiáng)大的數(shù)據(jù)采集、數(shù)據(jù)處理、數(shù)據(jù)分析和儀器控制功能在現(xiàn)代測控領(lǐng)域中得到了廣泛的應(yīng)用。NI公司在推出LabVIEW語言同時,也推出了一系列的數(shù)據(jù)采集卡,但實(shí)際應(yīng)用中往往會用到第三方廠家生產(chǎn)DAQ卡,在這種情況下就需要使用LabVIEW提供的外部程序接口。
LabVIEW的“調(diào)用函數(shù)庫節(jié)點(diǎn)(CLF節(jié)點(diǎn))”提供了調(diào)用標(biāo)準(zhǔn)函數(shù)和用戶自定義函數(shù)的通用方法,對于LabVIEW不支持的硬件設(shè)備大部分采用這種方法進(jìn)行驅(qū)動。但是CLF節(jié)點(diǎn)也存在不足,使用中遇到最多的問題是參數(shù)類型不匹配。使用重寫動態(tài)鏈接庫的方法,一方面可以兼容舊函數(shù)庫的參數(shù)類型,另一方發(fā)面可以獲得LabVIEW提供的高級函數(shù)庫應(yīng)用。以下以SDK2000圖像采集卡為例,介紹重寫函數(shù)庫的基本原理和程序編寫過程。
2. 實(shí)現(xiàn)方案及方案論證
SDK2000圖像采集卡提供的Visual C++ IDE 程序開發(fā)包包含了外部程序調(diào)用的共享函數(shù)庫,但是有些函數(shù)不能直接用CLF節(jié)點(diǎn)進(jìn)行調(diào)用。主要有兩個原因:庫函數(shù)的參數(shù)類型與LabVIEW不兼容;圖形化語言對于一些底層操作不易實(shí)現(xiàn)。SDK2000圖像采集卡提供的庫函數(shù)含有LabVIEW不支持的數(shù)據(jù)類型,并且很多函數(shù)涉及到一些底層操作,如文件讀取和內(nèi)存管理。為了正確調(diào)用函數(shù)并返回有效數(shù)據(jù),解決這兩個問題是關(guān)鍵。
LabVIEW僅提供最基本的數(shù)據(jù)類型,雖然可以在CLF節(jié)點(diǎn)參數(shù)設(shè)置中選擇“Adapt toType”,但只能夠傳遞LabVIEW內(nèi)部使用的參數(shù)類型而已,而共享庫函數(shù)使用上百種非標(biāo)準(zhǔn)數(shù)據(jù)類型。參數(shù)類型不匹配可分為兩種情況:
● 非標(biāo)準(zhǔn)數(shù)據(jù)類型定義。很多程序和函數(shù)用到一些非標(biāo)準(zhǔn)的類型定義,例如它們常常用char、short和long的類型定義代替Windows API使用的BYTE、WORD和DWORD。這種情況下要正確調(diào)用這些程序和函數(shù),必須找到這些參數(shù)在LabVIEW中同等的數(shù)據(jù)類型。
● 以結(jié)構(gòu)或者類作為參數(shù)。一些程序和函數(shù)使用結(jié)構(gòu)或者類作為參數(shù),但是LabVIEW并不能夠識別這些參數(shù)的數(shù)據(jù)結(jié)構(gòu)。為了正確調(diào)用這些庫函數(shù),LabVIEW提供了兩種解決辦法:使用CIN節(jié)點(diǎn)或者重寫函數(shù)庫對這些函數(shù)進(jìn)行重新封裝,使這些函數(shù)的輸入輸出參數(shù)能夠符合LabVIEW的標(biāo)準(zhǔn)。
SDK2000提供的共享函數(shù)庫使用了很多LabVIEW不支持的參數(shù)類型,如RECT、VIDEOSTREAMINFO等。為了正確調(diào)用這些函數(shù),必須用CIN節(jié)點(diǎn)或者重寫函數(shù)庫的方法對這些函數(shù)進(jìn)行重新封裝。相對而言,重新編寫動態(tài)鏈接庫方法比使用CIN節(jié)點(diǎn)更為常用。因?yàn)镃IN代碼直接嵌入到VI程序代碼里,對于編程器的限制比較高,所以一般不采用CIN節(jié)點(diǎn)。
重新編寫動態(tài)鏈接庫的另一個原因,是為了獲得LabVIEW提供的高級函數(shù)庫應(yīng)用。LabVIEW提供了在代碼開發(fā)環(huán)境下的高級函數(shù)庫,這些函數(shù)是針對于LabVIEW使用的數(shù)據(jù)類型,如下面所介紹的NumericArrayResize()函數(shù)。NumericArrayResize()函數(shù)用于動態(tài)改變數(shù)組的大小,不過只適合于LabVIEW使用的數(shù)組結(jié)構(gòu)。LabVIEW高級函數(shù)庫還包含了一些底層操作,如文件讀寫與內(nèi)存分配等等,不存在參數(shù)類型不同和底層操作困難的問題。
3. 軟件編程
由于需要重寫的函數(shù)比較多,這里僅以重寫保存DIB圖像數(shù)據(jù)函數(shù)為例,說明重寫函數(shù)庫的編寫過程。新函數(shù)GetDib()對原函數(shù)DSStream_GetCurrentDib()進(jìn)行了封裝,其作用是向原函數(shù)傳遞有效參數(shù)并返回LabVIEW能識別的數(shù)據(jù)。使用的編程環(huán)境為Visual C++6.0。
第一步:分析目標(biāo)函數(shù)的參數(shù)類型
SDK2000開發(fā)包中對獲得當(dāng)前圖像的DIB數(shù)據(jù)函數(shù)的聲明為:
HRESULT DSStream_GetCurrentDib(int iCardID,BYTE* pBuffer,long* pSize)
pBuffer指向預(yù)分配的內(nèi)存,值為NULL時,pSize得到保存圖像需要的內(nèi)存大小,若pBuffer不為NULL,函數(shù)將DIB圖像數(shù)據(jù)保存到從pBuffer開始,長度為(*pSize)的內(nèi)存區(qū)域。CLF節(jié)點(diǎn)不能直接調(diào)用該函數(shù),因?yàn)長abVIEW編程環(huán)境下沒有提供內(nèi)存管理機(jī)制,并且CLF節(jié)點(diǎn)不能把指向預(yù)分配內(nèi)存的指針傳遞給該函數(shù),所以必須對這個函數(shù)進(jìn)行重新封裝。
圖像的DIB數(shù)據(jù)是非數(shù)字型的數(shù)據(jù),為了返回LabVIEW能夠識別的數(shù)據(jù),可以選擇字符串或者單字節(jié)數(shù)組作為數(shù)據(jù)的載體,但是由于數(shù)據(jù)中包含了十進(jìn)制的0,所以只能用單字節(jié)數(shù)組作為載體,并且為了能夠動態(tài)改變數(shù)組的大小,必須以數(shù)組的句柄作為傳遞參數(shù)。因?yàn)樵贚abVIEW提供的高級函數(shù)庫中,所有改變數(shù)組、字符串大小的函數(shù)都是針對于句柄進(jìn)行的。
第二步:編寫動態(tài)鏈接庫
在VC中使用MFC Application(dll)建立一個名字為MySDK2000的工程,然后在MySDK2000.cpp中鍵入以下代碼:
typedef struct
?。?int32 dimSize;
uInt8 elt[1];
?。?TD1;//TD1的數(shù)據(jù)結(jié)構(gòu)能被LabVIEW和新鏈接庫識別
typedef TD1 **TD1Hdl;
extern "C" __declspec(dllexport) long GetDib(TD1Hdl BitMapinfo)//BitMapinfo為數(shù)組的句柄
?。?long pp=0;
hr=DSStream_GetCurrentDib(m_iCardID, NULL, &pp);//得到保存圖像需要的內(nèi)存大小
if (NumericArrayResize(uB,1L,(UHandle*)&BitMapinfo,pp))//改變數(shù)組物理大小
return 0;
?。ǎ狟itMapinfo)->dimSize = pp;//改變數(shù)組大小的標(biāo)志
BYTE* lpdst; // 指向緩存DIB圖像的指針
lpdst = (*BitMapinfo)->elt;
hr=DSStream_GetCurrentDib(m_iCardID, lpdst, &pp);//將當(dāng)前圖像的DIB數(shù)據(jù)保存到內(nèi)存中
BITMAPINFO* pInfo = (BITMAPINFO*)lpdst;//位圖文件頭指針
return pInfo->bmiHeader.biSize;//返回位圖信息頭的數(shù)據(jù)長度
}
編寫過程的幾點(diǎn)說明:
● 為了正確使用LabVIEW中的數(shù)據(jù)類型,必須在MySDK2000.cpp中手工加上#include “extcode.h”的聲明。頭文件”extcode.h”中定義了CIN和外部子程序所用到的基本數(shù)據(jù)類型和許多函數(shù)等,用以解決某些常量和數(shù)據(jù)類型與系統(tǒng)頭文件的沖突。
● 為了能夠在VC中調(diào)用LabVIEW的函數(shù)庫,必須把CINTools目錄下LabVIEW.lib包含在工程里。使用NumericArrayResize()函數(shù)用以動態(tài)改變數(shù)組的長度,它的功能和WIN32函數(shù)LocalAlloc()具有相似的功能。它的函數(shù)聲明如下:
MgErr NumericArrayResize(int32 typeCode, int32 numDims,
UHandle *dataHP,int32 totalNewSize);
● 如果內(nèi)存分配失敗,函數(shù)返回錯誤代碼。如果成功,還須修改數(shù)組結(jié)構(gòu)中的dimSize,因?yàn)榇撕瘮?shù)不能自動修改此值。
其它代碼的編寫類似于VC環(huán)境下的開發(fā)。代碼完成后,構(gòu)件并產(chǎn)生最終的目標(biāo)文件MySDK2000.dll。
第三步:在LabVIEW中調(diào)用動態(tài)鏈接庫
配置CLF節(jié)點(diǎn)的各項(xiàng)參數(shù)。第一個選項(xiàng)為函數(shù)庫文件名和路徑,選擇剛才編譯的文件MySDK2000.dll;第二個選項(xiàng)為函數(shù)名,選擇GetDib;第三項(xiàng)設(shè)置返回和傳遞參數(shù)。具體設(shè)置如下表:
其它選項(xiàng)保持默認(rèn)設(shè)置。程序框圖如下:
為了弄清楚CLF運(yùn)行時傳遞參數(shù)和返回參數(shù)的原型,可以在CLF節(jié)點(diǎn)上右鍵彈出菜單中選擇“Creat.c File…”,然后在文本編程環(huán)境下察看它的參數(shù)原型。這些參數(shù)原型其實(shí)就是LabVIEW使用到的數(shù)據(jù)結(jié)構(gòu),如上面說看到的數(shù)組結(jié)構(gòu)TD1,TD1為LabVIEW一維數(shù)組的數(shù)據(jù)結(jié)構(gòu)。
4. 結(jié)論
通過對原有函數(shù)的封裝,函數(shù)將圖像卡采集的DIB數(shù)據(jù)以數(shù)組形式返回給LabVIEW,LabVIEW將對這些數(shù)據(jù)進(jìn)行進(jìn)一步的處理,如二值化、邊緣分析等等。實(shí)踐證明,通過重寫動態(tài)鏈接庫的方法,第三方DAQ設(shè)備可以更協(xié)調(diào)地使用于LabVIEW開發(fā)環(huán)境中。
參考文獻(xiàn)
[1]. Using External Code in LabVIEW,National Instruments Corporation,2001.
[2]. David J.Kruglinski,著,潘愛民 王國印,譯. Visual C++ 技術(shù)內(nèi)幕(第五版)[M].北京:清華大學(xué)出版社,2002.
[3]. 楊樂平,李海濤,等. LabVIEW高級程序設(shè)計[M]. 北京:清華大學(xué)出版社,2003.
[4]. 林康紅,唐海峰,奉玲,等. 動態(tài)鏈接庫DLL在虛擬儀器中的應(yīng)用[J].計算機(jī)應(yīng)用,2002,5:56-57.
[5]. 陶以政,潘振顯.怎樣在LabVIEW中調(diào)用Win32動態(tài)鏈接庫[J].計算機(jī)應(yīng)用,2000,20(6):72-73.
[6]. 張輝,郁凱元,龍濤,等.基于LabVIEW軟件的動態(tài)鏈接庫和數(shù)據(jù)采集[J].儀表技術(shù),2002,6:36-37.