labview對dll的調用

LabVIEW調用DLL文件

  LabVIEW支持通過調用DLL文件的方式與其它編程語言混合使用。比如,在實際的工程項目中,用戶可以用C++語言實現軟件的運算部分,並把這些功能構建在DLL文件中,然後再使用 LabVIEW編寫程序的界面部分,並通過調用編寫好的DLL來調用運算部分的功能。

  LabVIEW 中是通過Call Library Function Node(CLN)節點來完成DLL文件調用的。創建一個新的VI,右擊程序框圖,在Functions Palette中依次選中Connectivity——Libraries&Executables工具欄即可找到該節點(圖1)。

圖1 Call Library Function Node

  將節點放置在程序框圖中,雙擊會出現它的配置對話框,共有四頁。第一頁用於填寫被調用函數的信息(圖2)。Library name or path需給出DLL文件名和路徑,操作系統路徑下的DLL文件,直接輸入文件名也可調用,否則必須輸入全路徑。在這裏已經給出名字的DLL是被靜態加載到程序中的,也就是說當調用了這個DLL的VI被裝入內存時,DLL同時被裝入內存。LabVIEW也可動態加載DLL,只要勾選上Specify path on diagram的選項即可。選擇了這個選項,在 Library name or path中輸入的內容就無效了,取而代之的是CLN 節點多出一對輸入輸出,用於指明所需要使用的DLL的路徑。這樣,當VI被打開時,DLL不會被裝入內存,只用程序運行到需要使用這個DLL中的函數時,才把其裝入內存。Function name是需要調用的函數的名稱,LabVIEW會把DLL中所有的暴露出來的函數都列出,用戶只要在下拉框中選取即可。Thread欄用於設定哪個線程裏運行被調用的函數。用戶可以通過 CLN 節點的配置面板來指定被調用函數運行所在的線程。CLN 的線程選項非常簡單,只有兩項: Run in UI thread和Run in any thread。LabVIEW的程序框圖上直接可以看出一個 CLN節點是選用


圖2 填寫被調用函數信息

的什麼線程。如果Run in UI thread,節點顏色是橙色的;Run in any thread則是淺黃色的(圖3)。

圖3 CLN不同線程對比

  通常情況下,除非使用的動態鏈接庫是多線程安全的,CLN 中選擇Run in any thread方式;否則必須選擇Run in UI thread方式。判斷一個動態鏈接庫是不是多線程安全的,需通過以下方法:如果一個動態鏈接庫的文檔中沒有明確說明它是多線程安全的,那麼就要當作是非多線程安全的;在可以看到動態鏈接庫源代碼的條件下,如果代碼中存在全局變量、靜態變量或者代碼中看不到有lock一類的操作,那麼這個動態鏈接庫也就肯定不是多線程安全的。

  選擇了Run in any thread方式,LabVIEW會在最方便的線程內運行動態鏈接庫函數,且一般會與調用它的VI在同一個線程內運行。因爲LabVIEW是自動多線程的語言,它也很可能會把動態鏈接庫函數分配給一個單獨的線程運行。如果程序中存在沒有直接或間接先後關係的兩個CLN節點,LabVIEW很可能會同時在不同的線程內運行它們所調用的函數,也許是同一函數。對於非多線程安全的動態鏈接庫,這是很危險的操作。很容易引起數據混亂,甚至是程序崩潰。

  選擇Run in UI thread方式,因爲LabVIEW只有一個界面線程,所以如果所有的CLN設置都是界面線程,那麼就可以保證這些CLN調用的函數肯定全部都運行在同一線程下,肯定不會被同時調用。對於非多線程安全的動態鏈接庫,這種方式就保證了它的安全。

  讓我們回到配置對話框第一頁,Calling convention用於指明被調用函數的調用約定。這裏只支持兩種約定:stdcall和C call。它們之間的區別在於,stdcall由被調用者負責清理堆棧,C call由調用者清理堆棧。這個設置錯誤時,可能會引起LabVIEW崩潰,也就是說如果LabVIEW調用DLL函數時出現異常,首先應該考慮這個設置是否正確。(Windows API一般使用的都是stdcall;標準C的庫函數大多使用C call。如果函數聲明中有類似__stdcall這樣的關鍵字,它就是stdcall的。)第二頁是函數參數的配置(圖4)。


圖4 配置函數的參數

  DLL和LabVIEW之間傳遞參數,最常用的三種數據類型是數值、數值型數組和字符串。C語言中經常把指針或者數據的地址在函數間傳遞,在32位操作系統中,可以使用int32數值來表示指針。因此,當需要在LabVIEW中傳遞指針數據時,可以使用I32或U32數值類型來表示這個地址類型的數據。但是,64位的程序中,數據的地址只能使用I64或U64來表示。這樣,如果一個調用了DLL函數的VI,並且函數參數中有地址型數據,使用固定數據類型的數值來表示地址,就要準備兩份代碼。解決方法是使用LabVIEW中的新的數據類型Pointer-sized Integer。這個數據類型的長度在不同的平臺上會自動使用32位或64位長度。如果在C語言函數參數聲明中有const關鍵字,可以選中Constant選項。布爾類型在DLL函數和LabVIEW VI之間傳遞沒有專有的數據類型,是利用數值類型來傳遞的。輸入時先把布爾值轉變爲數值,在傳遞給DLL函數;輸出時再把數值轉爲布爾值。對於數組的傳遞,LabVIEW只支持C數據類型中的數值型數組,傳遞數組類型需要注意的的是“Array Format”要選擇“Array Data Pointer”。這個設置中還有其他兩個選項,帶有“Handle”的參數類型都是表示LabVIEW定義的特殊類型的。在第三方的DLL中不會使用到數組參數作爲輸出值時,要記得爲輸出的數組數開闢空間。開闢數據空間的方法有兩種:第一種方法,創建一個長度滿足要求的數組,作爲初始值傳遞給參數,輸出數的數據就會被放置在輸入數組的所在的內存空間內。第二種方法是直接在參數配置面板上進行設置。在Minimum size中寫入一個固定的數值,LabVIEW就會按此大小爲輸出的數組開闢空間。在 Minimum size 中選擇函數的其它數值參數,而不是固定數值。這樣LabVIEW會按照當時被選擇的參數值的大小來開闢空間。字符串與使用與數組是非常類似的,實際上在C語言中字符串就是一個I8數組。

在NI軟件的安裝路徑下打開當前使用版本的LabVIEW文件夾,通過 examples\dll\data passing\Call NativeCode.llb找到簡單數據類型在LabVIEW與C之間的對應關係。部分常見關係見表1。

表1 數據類型對比

  第三頁用於爲DLL設置一些回調函數,可以使用這些回調函數在特定的情形下完成初始化、清理資源等工作(圖5)。


圖5 設置回調函數

  如果爲Reserve選擇了一個回調函數,那麼當一個新的線程開始調用這個DLL時,這個回調函數首先被調用。可以利用這個函數爲新線程使用到的數據做初始化工作。線程在使用完這個DLL之後,它會去調用Unreserve中指定的回調函數。Abort中指定的函數用於VI非正常結束時被調用,也就是讓一個程序在運行完前停止。這些回調函數的原型在Prototype for these procedures中列出,必須要由DLL的開發者按照特定的格式實現。如果使用的DLL不是專爲LabVIEW設計的,一般不會包含這樣的回調函數。

  第四頁是錯誤處理方式,用戶可根據需要選擇相應的錯誤檢查級別。

  另外還需要注意的是,C語言中的struct在LabVIEW中可以使用cluster來表示,但有時需要作出相應的調整。這是因爲在C語言中,struct的字節對齊是可以進行設置的,這就決定了其各元素的存放地址的可變性。C語言中的對字節對齊數可通過#pragma pack指令或在工程屬性中進行指定。而在LabVIEW的cluster中,所有元素只能是1字節對齊的,所以如果要和C語言中非1字節對齊的struct對應,需要做出一些調整。比如,對於C語言中2字節對齊的struct,第一個元素如果是I8型的,在LabVIEW的cluster中第一個元素對應不變,但不能緊挨着放第二個元素,必須留一個無意義的空位。C語言的struct其實也是如此,只不過沒有表現出來。所以爲了方便,如果自己用C語言生成DLL文件供LabVIEW調用最好將struct都設爲1字節對齊。C語言的struct中可以嵌套數組,但是這和LabVIEW中含有數組元素的cluster是不一樣的,LabVIEW中需要將數組中的元素都拆開放入cluster中。

  如果C語言的struct中含有一個指針,LabVIEW中的cluster只能用一個U32數值(32位系統上,64位系統上使用U64)來表示指針的地址,而不能將指針所指向的內容放到Cluster中去。如果聲明的是指向struct的指針,才能在LabVIEW中使用cluster與之對應。CLN節點的配置面板中,沒有一個專門命名的“struct”或者“cluster”參數類型,應選擇“Adapt to Type”就可以了。如果參數的類型就是結構而非指針,考慮到C函數參數的壓棧順序,把一個結構體作爲參數傳給函數,相當於把結構中每個元素分別作爲參數傳遞給函數。圖6爲C語言中struct和LabVIEW中cluster的部分匹配圖。


圖6 struct和cluster匹配

LabVIEW打包DLL文件

我們接下來學習如何使用LabVIEW來打包一個DLL文件。

  首先我們編寫一個名爲Scale.vi的程序,功能很簡單就是對輸入的數據乘上10,然後再輸出(圖7)。


圖7 scale.vi

  必須在任務管理器中才能生成.dll文件。所以我們首先建立一個project,過程如下:

點擊File>>New Project:


圖8 生成新項目

  接着彈出是否將該VI添加到新項目的對話框:


圖9 是否添加VI到新建項目

  選擇Add,生成新的項目管理器,將其保存在需要的路徑下:


圖10 項目管理器

  右鍵單擊項目瀏覽器窗口中的Build Specifications,在快捷菜單中選擇New>>Shared Library(DLL),彈出對DLL文件進行設置的對話框。點擊Category>>Information,根據自己需求修改Build specification name和Target filename:

圖11 Information頁面

  點擊Source Files>>Project Files>> Scale.vi>> ,彈出對話框,直接用默認值,點擊OK:


圖12 Define VI Prototype

  點擊Destination>> Scale.dll,點擊 ,可選擇需要保存的路徑。然後再點擊Support Directory,這是指明瞭DLL支持文件的路徑(比如數據文件之類的放在哪個文件夾),選擇默認即可:


圖13 Destination頁面

  Category中的Source Files可供用戶對打包VI的屬性和密碼做一些設置;Advanced和Additional Exclusions可以做一些高級的設置,這些均按默認值即可。Version Information可讓用戶填寫版本號、名稱、版權、公司等信息:


圖14 Version Information

點擊Run-Time Languages,可對支持語言進行選擇,默認即可。點擊Preview>>Generate Preview,可以預覽到結果:


圖15 預覽生成

點擊Build,彈出生成狀態對話框:


圖16 生成狀態框

點擊Done,生成完成,打開DLL文件保存的路徑查看:


圖17 DLL文件保存路徑

LabVIEW調用DLL文件

  LabVIEW可以方便地調用DLL文件,這些DLL文件可以是其他編譯工具,如VC,生成的。

  LabVIEW可以直接通過CLN節點來調用DLL文件,以前面生成的Scale.dll文件爲例。現有一個內部定時連續採集程序,通過調用該DLL文件,使讀取的值爲實際採集值的10倍(圖18)。


圖18 連續採集程序

方法一

  在程序框圖放入Call Library Function Node,雙擊彈出對話框。在Function頁面的Library name or path中給入生成的Scale.dll文件的路徑,Function name選擇Scale,其他選項默認。


圖19 Function頁面

  由於是LabVIEW生成的DLL文件,在Parameters頁面不需要做改動,但是由於VI還有一路輸出,所以還需要添加一個參數y,作爲DLL文件的輸出。

  如果是C語言等非LabVIEW生成的DLL函數,需要將retuen type的type選項和Data type選項改成函數定義的參數類型,對於函數裏輸入的參數也都需要自行添加。Callbacks和Error Checking則不需要改動。


圖20 Parameters頁面

  點擊“OK”,將生成的CLN的輸入段連接到DAQmx Read.vi,return type輸出連接到波形圖表上,即可實現採集值放大10倍的功能。


圖21 完成後的程序

方法二

LabVIEW中還有一種方法可以調用DLL文件,在VI的選項欄,依次選擇Tools——Import——Shared Library(.dll),彈出Import Shared Library對話框。


圖22 生成Import Shared Library對話框

  選擇Create VIs for a shared library,點擊Next,在Shared Library(.dll) Files中輸入Scale.dll文件的路徑,Head(.h) File裏填寫頭文件的路徑。


圖23 選擇DLL文件路徑和頭文件路徑

  點擊Next,如果DLL文件中依賴其他的一些DLL文件,需要在Include Paths中填寫這些文件的路徑。其他選項可以根據客戶需求設置,一般默認即可。這樣一直點擊Next到最後,選擇Open the generated library,點擊Finish。這樣可以生成一個.lvlib格式的庫文件,裏面包含了Scale.vi,這是將調用該DLL文件的方法封裝好的VI,只留下輸入和輸出接口,方便運用到LabVIEW的程序中。直接將Scale.vi拖放到剛纔的連續採集中即可完成方法一的功能。


圖24 完成的程序

VC調用LabVIEW生成的DLL文件

  剛纔介紹了LabVIEW調用DLL文件的方法,使用VC調用LabVIEW生成的DLL文件也很簡單。還是以之前生成Scale 的DLL文件爲例,不同的是採集電壓程序使用的是C語言的例程,但和LavVIEW實現的功能相同。

  首先將先前生成Scale DLL文件時,路徑下所有的文件全部複製粘帖到C語言例程的文件夾下。打開連續採集程序,點擊狀態欄的Project——Settings,在Project Settings對話框中加載入Scale.lib的靜態鏈接庫。

  在程序中鍵入#include "Scale.h",以便引入該DLL函數。下面是C程序的代碼,功能是有限點採集電壓,通過Scale.dll文件實現採樣值放大10倍的功能。加粗部分是因爲調用DLL文件所做的改動。

#include

#include "NIDAQmx.h"

#include "Scale.h"

#define DAQmxErrChk(functionCall) if( DAQmxFailed(error=(functionCall)) ) goto Error; else

int main(void)

{

int32 error=0;

TaskHandle taskHandle=0;

int32 read;

float64 data[1000];

char errBuff[2048]={'\0'};

int i=0;

double x10=0;

/*********************************************/

// DAQmx Configure Code

/*********************************************/

DAQmxErrChk (DAQmxCreateTask("",&taskHandle));

DAQmxErrChk (DAQmxCreateAIVoltageChan(taskHandle,"Dev1/ai0"/*Config correct device*/,"",DAQmx_Val_Cfg_Default,-10.0,10.0,DAQmx_Val_Volts,NULL));//

DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle,"",10000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,1000));

/*********************************************/

// DAQmx Start Code

/*********************************************/

DAQmxErrChk (DAQmxStartTask(taskHandle));

/*********************************************/

// DAQmx Read Code

/*********************************************/

DAQmxErrChk (DAQmxReadAnalogF64(taskHandle,1000,10.0,DAQmx_Val_GroupByChannel,data,1000,&read,NULL));

printf("Acquired %d points\n",read);

for(i=0;i<1000;i++){

Scale(data[i], &x10);

printf("the %d Value is : %f \n",i,x10);

}

Error:

if( DAQmxFailed(error) )

DAQmxGetExtendedErrorInfo(errBuff,2048);

if( taskHandle!=0 ) {

/*********************************************/

// DAQmx Stop Code

/*********************************************/

DAQmxStopTask(taskHandle);

DAQmxClearTask(taskHandle);

}

if( DAQmxFailed(error) )

printf("DAQmx Error: %s\n",errBuff);

printf("End of program, press Enter key to quit\n");

getchar();

return 0;

}

所以,使用LabVIEW不僅可以方便地調用各種編譯軟件生成的DLL文件,自己也能生成DLL文件供其他編譯軟件調用。這樣,用戶在編寫大型項目時更加靈活,也爲熟悉C語言的工程師提供了巨大的方便。本文只對LabVIEW和VC相互調用DLL文件做了簡單的介紹,很多高級功能和技巧,用戶可以在實際運用中逐漸掌握。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章