LabVIEW如何方便地調用DLL文件
1.jpg (24.15 KB, 下載次數: 2)
下載附件 保存到相冊
圖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.jpg (17.07 KB, 下載次數: 0)
下載附件 保存到相冊
圖2 填寫被調用函數信息 <ignore_js_op>3.jpg (6.68 KB, 下載次數: 0)
下載附件 保存到相冊
圖3 CLN不同線程對比 通常情況下,除非使用的動態鏈接庫是多線程安全的,CLN 中選擇Run in any thread方式;否則必須選擇Run in UI thread方式。判斷一個動態鏈接庫是不是多線程安全的,需通過以下方法:如果一個動態鏈接庫的文檔中沒有明確說明它是多線程安全的,那麽就要當作是非多線程安全的;在可以看到動態鏈接庫源代碼的條件下,如果代碼中存在全局變量、靜態變量或者代碼中看不到有lock一類的操作,那麽這個動態鏈接庫也就肯定不是多線程安全的。4.jpg (13.76 KB, 下載次數: 0)
下載附件 保存到相冊
圖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。 <ignore_js_op>5.jpg (39.05 KB, 下載次數: 0)
下載附件 保存到相冊
<ignore_js_op>6.jpg (19.95 KB, 下載次數: 0)
下載附件 保存到相冊
<ignore_js_op>7.jpg (33.51 KB, 下載次數: 0)
下載附件 保存到相冊
<ignore_js_op>8.jpg (45.04 KB, 下載次數: 0)
下載附件 保存到相冊
表1 數據類型對比 第三頁用於為DLL設置一些回調函數,可以使用這些回調函數在特定的情形下完成初始化、清理資源等工作(圖5)。 <ignore_js_op>9.jpg (15.24 KB, 下載次數: 0)
下載附件 保存到相冊
圖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的部分匹配圖。 <ignore_js_op>10.jpg (20.07 KB, 下載次數: 0)
下載附件 保存到相冊
圖6 struct和cluster匹配 LabVIEW打包DLL文件 我們接下來學習如何使用LabVIEW來打包一個DLL文件。 首先我們編寫一個名為Scale.vi的程序,功能很簡單就是對輸入的數據乘上10,然後再輸出(圖7)。 <ignore_js_op>11.jpg (18.08 KB, 下載次數: 0)
下載附件 保存到相冊
圖7 scale.vi 必須在任務管理器中才能生成.dll文件。所以我們首先建立一個project,過程如下: 點擊File>>New Project: <ignore_js_op>12.jpg (20.14 KB, 下載次數: 0)
下載附件 保存到相冊
圖8 生成新項目 接著彈出是否將該VI添加到新項目的對話框: <ignore_js_op>13.jpg (13.71 KB, 下載次數: 0)
下載附件 保存到相冊
圖9 是否添加VI到新建項目 選擇Add,生成新的項目管理器,將其保存在需要的路徑下: <ignore_js_op>14.jpg (16.51 KB, 下載次數: 0)
下載附件 保存到相冊
圖10 項目管理器 右鍵單擊項目瀏覽器窗口中的Build Specifications,在快捷菜單中選擇New>>Shared Library(DLL),彈出對DLL文件進行設置的對話框。點擊Category>>Information,根據自己需求修改Build specification name和Target filename: <ignore_js_op>15.jpg (16.02 KB, 下載次數: 0)
下載附件 保存到相冊
圖11 Information頁面 點擊Source Files>>Project Files>> Scale.vi>> ,彈出對話框,直接用默認值,點擊OK: <ignore_js_op>16.jpg (18.16 KB, 下載次數: 0)
下載附件 保存到相冊
圖12 Define VI Prototype 點擊Destination>> Scale.dll,點擊 ,可選擇需要保存的路徑。然後再點擊Support Directory,這是指明了DLL支持文件的路徑(比如數據文件之類的放在哪個文件夾),選擇默認即可: <ignore_js_op>17.jpg (14.52 KB, 下載次數: 0)
下載附件 保存到相冊
圖13 Destination頁面 Category中的Source Files可供用戶對打包VI的屬性和密碼做一些設置;Advanced和Additional Exclusions可以做一些高級的設置,這些均按默認值即可。Version Information可讓用戶填寫版本號、名稱、版權、公司等信息: <ignore_js_op>18.jpg (19.1 KB, 下載次數: 0)
下載附件 保存到相冊
圖14 Version Information 點擊Run-Time Languages,可對支持語言進行選擇,默認即可。點擊Preview>>Generate Preview,可以預覽到結果: <ignore_js_op>19.jpg (16.45 KB, 下載次數: 0)
下載附件 保存到相冊
圖15 預覽生成 點擊Build,彈出生成狀態對話框: <ignore_js_op>20.jpg (13.28 KB, 下載次數: 0)
下載附件 保存到相冊
圖16 生成狀態框 點擊Done,生成完成,打開DLL文件保存的路徑查看: <ignore_js_op>21.jpg (28 KB, 下載次數: 0)
下載附件 保存到相冊
圖17 DLL文件保存路徑 LabVIEW調用DLL文件 LabVIEW可以方便地調用DLL文件,這些DLL文件可以是其他編譯工具,如VC,生成的。 LabVIEW可以直接通過CLN節點來調用DLL文件,以前面生成的Scale.dll文件為例。現有一個內部定時連續采集程序,通過調用該DLL文件,使讀取的值為實際采集值的10倍(圖18)。 <ignore_js_op>22.jpg (14.89 KB, 下載次數: 0)
下載附件 保存到相冊
圖18 連續采集程序 方法一 在程序框圖放入Call Library Function Node,雙擊彈出對話框。在Function頁面的Library name or path中給入生成的Scale.dll文件的路徑,Function name選擇Scale,其他選項默認。 <ignore_js_op>23.jpg (21.43 KB, 下載次數: 0)
下載附件 保存到相冊
圖19 Function頁面 由於是LabVIEW生成的DLL文件,在Parameters頁面不需要做改動,但是由於VI還有一路輸出,所以還需要添加一個參數y,作為DLL文件的輸出。 如果是C語言等非LabVIEW生成的DLL函數,需要將retuen type的type選項和Data type選項改成函數定義的參數類型,對於函數裏輸入的參數也都需要自行添加。Callbacks和Error Checking則不需要改動。 <ignore_js_op>24.jpg (25.39 KB, 下載次數: 0)
下載附件 保存到相冊
圖20 Parameters頁面 點擊“OK”,將生成的CLN的輸入段連接到DAQmx Read.vi,return type輸出連接到波形圖表上,即可實現采集值放大10倍的功能。 <ignore_js_op>25.jpg (15.98 KB, 下載次數: 0)
下載附件 保存到相冊
圖21 完成後的程序 方法二 LabVIEW中還有一種方法可以調用DLL文件,在VI的選項欄,依次選擇Tools——Import——Shared Library(.dll),彈出Import Shared Library對話框。 <ignore_js_op>26.jpg (33.08 KB, 下載次數: 0)
下載附件 保存到相冊
圖22 生成Import Shared Library對話框 選擇Create VIs for a shared library,點擊Next,在Shared Library(.dll) Files中輸入Scale.dll文件的路徑,Head(.h) File裏填寫頭文件的路徑。 <ignore_js_op>27.jpg (15.83 KB, 下載次數: 0)
下載附件 保存到相冊
圖23 選擇DLL文件路徑和頭文件路徑 點擊Next,如果DLL文件中依賴其他的一些DLL文件,需要在Include Paths中填寫這些文件的路徑。其他選項可以根據客戶需求設置,一般默認即可。這樣一直點擊Next到最後,選擇Open the generated library,點擊Finish。這樣可以生成一個.lvlib格式的庫文件,裏面包含了Scale.vi,這是將調用該DLL文件的方法封裝好的VI,只留下輸入和輸出接口,方便運用到LabVIEW的程序中。直接將Scale.vi拖放到剛才的連續采集中即可完成方法一的功能。 <ignore_js_op>28.jpg (14.56 KB, 下載次數: 0)
下載附件 保存到相冊
圖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文件所做的改動。1 #include 2 #include "NIDAQmx.h" 3 #include "Scale.h" 4 5 #define DAQmxErrChk(functionCall) if( DAQmxFailed(error=(functionCall)) ) goto Error; else 6 7 8 9 int main(void) 10 11 { 12 13 int32 error=0; 14 15 TaskHandle taskHandle=0; 16 17 int32 read; 18 19 float64 data[1000]; 20 21 char errBuff[2048]={‘\0‘}; 22 23 int i=0; 24 25 double x10=0; 26 27 28 29 30 31 /*********************************************/ 32 33 // DAQmx Configure Code 34 35 /*********************************************/ 36 37 DAQmxErrChk (DAQmxCreateTask("",&taskHandle)); 38 39 DAQmxErrChk (DAQmxCreateAIVoltageChan(taskHandle,"Dev1/ai0"/*Config correct device*/,"",DAQmx_Val_Cfg_Default,-10.0,10.0,DAQmx_Val_Volts,NULL));// 40 41 DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle,"",10000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,1000)); 42 43 44 45 /*********************************************/ 46 47 // DAQmx Start Code 48 49 /*********************************************/ 50 51 DAQmxErrChk (DAQmxStartTask(taskHandle)); 52 53 54 55 /*********************************************/ 56 57 // DAQmx Read Code 58 59 /*********************************************/ 60 61 DAQmxErrChk (DAQmxReadAnalogF64(taskHandle,1000,10.0,DAQmx_Val_GroupByChannel,data,1000,&read,NULL)); 62 63 64 65 printf("Acquired %d points\n",read); 66 67 68 69 for(i=0;i<1000;i++){ 70 71 Scale(data, &x10); 72 73 printf("the %d Value is : %f \n",i,x10); 74 75 76 77 } 78 79 Error: 80 81 if( DAQmxFailed(error) ) 82 83 DAQmxGetExtendedErrorInfo(errBuff,2048); 84 85 if( taskHandle!=0 ) { 86 87 /*********************************************/ 88 89 // DAQmx Stop Code 90 91 /*********************************************/ 92 93 DAQmxStopTask(taskHandle); 94 95 DAQmxClearTask(taskHandle); 96 97 } 98 99 if( DAQmxFailed(error) ) 100 101 printf("DAQmx Error: %s\n",errBuff); 102 103 printf("End of program, press Enter key to quit\n"); 104 105 getchar(); 106 107 return 0; 108 109 }
所以,使用LabVIEW不僅可以方便地調用各種編譯軟件生成的DLL文件,自己也能生成DLL文件供其他編譯軟件調用。這樣,用戶在編寫大型項目時更加靈活,也為熟悉C語言的工程師提供了巨大的方便。本文只對LabVIEW和VC相互調用DLL文件做了簡單的介紹,很多高級功能和技巧,用戶可以在實際運用中逐漸掌握。
LabVIEW如何方便地調用DLL文件