中级LabVIEW程式设计技巧与观念
2013-02-18
标签: LabVIEW

高科技產業需要好的生產設備作為進攻市場的利器。而如何控制生產設備的自動化則是決定此設備優劣的要件之一。

在一個公司考慮採用何種系統來控制生產機器,選擇多數日系機器都採用的PLC (Programmable Logic Controllers)系統(如三菱的FX2或A系列),或藉由PC介面(如ISA、PCI、GPIB等)來控制機器的系統,是最優先考慮的難題,因這兩種截然不同的控制系統需用不同的軟體及各有優劣,因此往往也很難下決定。

當初本公司在決定要採用PLC系統或是PC-based系統確實經過一番掙扎,由於大部分工程師的背景並非機械或控制科系出身,要想建立公司自行發展的技術基礎,無論是採用PLC系統或是PC-based系統均需重新學習。最後決定採用PC-based 的系統來作自動化控制,並使用軟體為National Instruments 公司的LabVIEW,主要著眼於其發展的彈性與潛力比PLC系統來的大,不僅可隨全世界的軟體進展而不斷改良,而使其應用範圍越來越廣,舉凡機械運動、儀器量測、網路通訊、影像處理…等均可應用,除此之外,更可支援active X的物件(objects)的插入連結應用於LV中,進而使LV的能力也越發強大到令人咋舌的地步。

在LabVIEW程式設計的技巧與觀念上,以下提供一些個人心得供大家參考:

if敘述的寫法

在LabVIEW中並沒有if的語法,這對寫慣了text-based程式語言的人來說,剛開始還真有點綁手綁腳,施展不開。解決的方法為所有的if判斷敘述均改用case結構來取代即可,原本就有寫過C語言的工程師很快就能上手了,但還是要花一小段時間來適應。

a=a+1的寫法

有沒有搞錯?這麼基本的寫法還要討論嗎?程度也未免太低了吧!沒錯,它是很簡單,但剛開始時也確實有些小困擾,因LabVIEW中,一個變數在一般情形下只能出現在block diagram中一次,將一個數值變數加一後再連回自己本身是最直覺的想法,但若如此,便會因data flow的方向相互牴觸的關係出現斷線,解決的方法有兩種:

1. 1.使用local variable,如:

2. 2.若是在迴圈中便可使用shift register來幫忙達成目的,如下圖所示,但記得shift register最好要給初值,否則初值將預設為0。

getch( )與kbhit( )﹔

對C語言有經驗的人一定很懷念這兩個函式:getch( )與kbhit( ),這兩個函式的功用差不多,差異是kbhit( )只會偵測到鍵盤是否有按鍵被按下,而getch( )則除了kbhit( )的功用外,還會傳回被按下按鍵的ASCII code。這兩個函式可讓程式暫停去等待某些鍵盤指令,非常好用也非常常用,例如:按下”y”或”Y”代表YES,”n”或”N”代表NO等等。

但一到LabVIEW中就發現,當程式執行時想要即時讀取鍵盤有何輸入並不是很方便,常用的做法是設一個numeric control或string control,用滑鼠去移動到control上及按滑鼠鍵,然後再由鍵盤輸入某些鍵,再按enter或移動滑鼠到toolbar上按下一個勾勾的按鍵,才能將鍵入的鍵值由程式讀入,對於只為了單純目的讓程式繼續的單擊動作(如按下”y”或”n”)而言,似乎太麻煩了,然而這類單擊動作顯然還是非常有需要的。

以下提供一個簡單的subVI:getch( ).vi,具有C語言中getch( )的功能,如此便很容易加到一般程式中了。當程式讀到鍵盤有按鍵被按下時,將按下的鍵值傳出後即結束。因其中有用到一個read keyboard.vi的subVI,使用時要注意在主程式呼叫getch( ).vi前,要先呼叫過Open Keyboard.vi,否則getch( ).vi中的Read Keyboard.vi會讀不到鍵盤,而主程式結束前要呼叫Close Keyboard.vi。

程式中有while 迴圈的結束方法

一般而言,當程式執行完所有的程式碼後就自動結束,這種情形也就沒什麼需要討論的。所以以下針對程式中有while 這種無限迴圈在程式裡面的情形作討論。

1. 1.程式在執行時,按下上方tool bar中的紅色圓形停止鍵。這個方法看起來方便但實際上並不理想,理由是你並沒有真正在程式中寫使程式結束的程式碼,而是用LabVIEW本身去終止你的程式,程式的data flow將停止在你按下停止鍵那一刻的地方。

2. 2.稍微改良的簡便方法,是用一個Boolean Control(如stop)來連在迴圈中的停止符號前,按下此control 即停止迴圈,而結束程式。如下圖:

要注意的是,當按下stop時,程式並不是馬上停止,而是要等到下一次重複迴圈時,做完迴圈內的動作才會停止。

3. 3.用一個Boolean Control(如stop)來連在迴圈中的一個case,在true的情形中放進Functions> Application Control中的Stop,當按下此Control時強迫此程式停止。

不過使用這個方法要注意一點,就是其實用Stop這個vi來終止程式的話,和用tool bar中的紅色圓形停止鍵的效果是一樣的,也就是由LabVIEW硬生生地將程式終止,若在程式結束前有些所謂結束動作,例如關閉某些介面檔案的stream或channel,或是讓儀器機械手臂復歸到某狀態或位置等,那麼就可能無法執行了。

4. 但若程式中有兩個(以上)的平行迴圈均需停止才能結束程式的話,一般的方法為在一個迴圈的停止符號前連一個Boolean control,如2.中所述,而在其他迴圈的停止符號前連上此Boolean control的local variable即可。如下圖所示:

此法看來甚為理想,但實際上仍有其缺點,這是由於LV的while loop設計為當迴圈的停止符號收到停止訊號時,並不會立即停止迴圈,必須要執行完下一次在迴圈中的程式碼後才會停止迴圈,這在某些情形下會發生預期外的結果。例如,我的程式中有兩個while loop,第一個每1秒重複一次,第二個則每1小時重複一次,當我按下Boolean control同時給兩個迴圈停止訊息,希望立即終止程式時,第一個迴圈最壞的情形在2秒內便停止了,但第二個迴圈可能要超過1小時才停止!

那要如何解決這種現象呢?可以考慮使用上述3.的方法,或請參考Occurrence 的功能及使用方法中的說明。

Occurrence 的功能及使用方法

當程式在等待某種事件發生,如甲事件,然後立即去做乙事件,常用的寫法為用一個while loop去等待甲事件的發生,若甲事件的發生了,則終止此等待迴圈去做乙事件,但為了增進程式效率,通常這個等待迴圈中會加上delay一段時間,若delay的時間短,如一秒以下,甲事件發生到乙事件開始的時間差可能可以在容忍範圍內,但若delay的時間長,如一小時,那麼甲事件發生到乙事件開始的時間差顯然不符合要求。這時就可以利用Occurrence 的功能了。

Occurrence 的icon在Functions> Advanced> Synchronization中可找到,是LabVIEW中同步控制的功能之一。這邊用下面的例子來說明,有兩個平行迴圈,左邊的每一秒計時一次,右邊的每10秒計時一次,希望當stop鍵按下時,兩個迴圈同時終止。若用前述local variable的方法來控制,結果是當第一個迴圈停止了,但第二個迴圈卻要等10秒才停止。而以下這個程式則可達到同時結束的目的。當stop鍵按下時,由第一個迴圈Set Occurrence產生一個訊息,傳出第一個迴圈外由Generate Occurrence產生Occurrence的訊號,再傳到每一個迴圈中的wait on occurrence去產生true訊號已終止迴圈。順帶一提,使用wait on occurrence可在其ms timeout(-1)的connector上連上一數字作為delay的時間,可不用再使用其他的delay功能。

平行迴圈執行時的同步性

當程式中有數個平行迴圈同時進行時,大部分的情形是希望各平行迴圈每次開始時均能夠同步,可避免一些出乎預期的狀況發生。常用有兩種方法敘述如下﹔

1. 1.使用Wait Until Next ms Multiple﹔

在迴圈中加入Wait Until Next ms Multiple的icon並均連上相同的常數做為delay的時間長短,並於程式執行時,同時啟動這兩個迴圈,如此可確保之後各迴圈每次開始時均同步開始,如下圖所示﹔

但注意,這並不表示各迴圈所執行的次數也一樣,如上例,若Task A 執行一次費時少於100ms,而Task B執行一次費時大於100ms,可確定的Task A的迴圈每執行過一次必為100ms,Task B的迴圈每執行過一次必為100ms的整數倍,因此有可能甲迴圈執行了兩次或多次,但乙迴圈只執行了一次而已。若要各迴圈執行的次數也相同的話,便需利用下面第2.中所敘述的功能了。

2. 2.使用Rendezvous﹔

Rendezvous為集合地、會合點的意思,這個功能可在Functions> Advanced> Synchronization中找到,使用時要先Generate Rendezvous給固定個數的迴圈,而各迴圈中則要加入Wait at Rendezvous這個icon。當程式執行時,即使有某迴圈先結束,也會等到使用這個Rendezvous的所有迴圈均結束後,然後再一起同步重新執行迴圈。注意,要結束程式前要記得Destroy Rendezvous。使用這個方法便可確定各迴圈每個迴圈必定同時開始,且各迴圈執行的次數也一定相同。下面是一個簡單的例子,可注意到各迴圈所執行的次數均相同,且迴圈中delay的時間長短並不會影響到迴圈執行次數,反正各迴圈等待的時間必相同,就是耗時最長的那個迴圈的等待時間啦。

巢狀if結構的改良

if…else的語法往往造成了複雜的結構,如C語言中:

if (a==FALSE) b=TRUE

else if (c==FALSE) b=TRUE

else if (d==FALSE) b=FALSE ….;

這樣寫出來似乎很厲害 (因別人要花點時間才能了解如何做邏輯判斷),但也有可讀性不佳的缺點。尤其寫在LabVIEW中,因其是graphic language之故,可讀性更差(看起來還真是”巢狀”啊)。例如下例,要看完並了解程式如何邏輯判斷,恐怕要花上一段時間(因false的case中也可能藏有程式碼),另一個缺點是程式編譯完後的VI檔較佔硬碟空間。

雖然有時如同上圖的巢狀結構無法避免,但當判斷的條件增多時,可考慮將數個條件以Build Array的方法再加上case結構來改寫,自己再做個真值表來決定各case中的內容即可。下圖為一個寫法的示意圖。

要注意的小地方是Boolean Array並不能直接連上case結構,要將之轉成數字後才可以,因此程式中用了一個Boolean Array To Number的功能。

State Machine 的觀念及寫法

先讓我們來看看State Machine的標準寫法吧。基本上一個State Machine也祇不過是一個迴圈內加上一個case結構而已。每當迴圈重做(iterate)一次時,就只做眾多case中的一個,再用enum type的constant決定下一次迴圈重做時所要進入的case。

下面舉一個簡單例子,說明有三件順序性的工作用State Machine方法所寫出的結果。

State Machine依其用法可視作是sequence的變形,但是比單用sequence的結構具有更多的彈性與優點,敘述如下:

  • 因使用enum type做為各步驟的說明,因此可將state machine中各case的title均寫成文字敘述,這也使得程式的敘述性及可讀性較好。
  • 比較容易將流程圖寫成程式,也可很容易調整流程中各步驟的順序,當程式出現多個路徑時(如若選YES時去做task 1,選NO時去做task 2之類或更複雜的情況),只要在最後選擇next state時稍加改變即可,換個角度來看,也就是很容易達成其它程式語言中的goto的效果,卻能保持程式結構的完整性。
  • 因為有一個error state專門處理各state傳來的error或exception的訊息,使得程式設計時,可集中也較容易處理Error Handler的訊息。而當處理完成時,又可選擇回到哪一個state去,或是走到close state來結束這個State Machine的迴圈。
  • 在最後有一個close state,也就是在此不論程式執行有無錯誤發生,最終都會到這個state進行一定結束程式的步驟,使程式可以順利結束。如此一來,便不致因錯誤發生時,因某些關閉動作沒有做而產生意想不到的結果或”掛”在那裡了。

可能会用到的工具/仪表
本站简介 | 意见建议 | 免责声明 | 版权声明 | 联系我们
CopyRight@2024-2039 嵌入式资源网
蜀ICP备2021025729号