中级LabVIEW程序设计技巧与观念 I
if叙述的写法
a=a+1的写法
getch( )与kbhit( ) 平行循环执行时的同步性
程序中有while 循环的结Occurrence 的功能及使束方法
巢状if结构的改良
用方法
State Machine 的观念及写法
高科技产业需要好的生产设备作为进攻市场的利器。而如何控制生产设备的自动化则是决定此设备优劣的要件之一。
在一个公司考虑采用何种系统来控制生产机器,选择多数日系机器都采用的PLC (Programmable Logic Controllers)系统(如三菱的FX2或A系列),或藉由PC接口(如ISA、PCI、GPIB等)来控制机器的系统,是最优先考虑的难题,因这两种截然不同的控制系统需用不同的软件及各有优劣,因此往往也很难下决定。
当初本公司在决定要采用PLC系统或是PC-based系统确实经过一番挣扎,由于大部分工程师的背景并非机械或控制科系出身,要想建立公司自行发展的技术基础,无论是采用PLC系统或是PC-based系统均需重新学习。最后决定采用PC-based 的系统来作自动化控制,并使用软件为National Instruments 公司的LabVIEW(以下简称LV),主要着眼于其发展的弹性与潜力比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的结构具有更多的弹性与优点,叙述如下:
1. 1. 因使用enum type做为各步骤的说明,因此可将state machine
中各case的title均写成文字叙述,这也使得程序的叙述性及可读性较好。
2.
3.
4.
2. 比较容易将流程图写成程序,也可很容易调整流程中各步骤的顺序,当程序出现多个路径时(如若选YES时去做task 1,选NO时去做task 2之类或更复杂的情况),只要在最后选择next state时稍加改变即可,换个角度来看,也就是很容易达成其它程序语言中的goto的效果,却能保持程序结构的完整性。
3. 因为有一个error state专门处理各state传来的error或
exception的讯息,使得程序设计时,可集中也较容易处理Error Handler的讯息。而当处理完成时,又可选择回到哪一个state去,或是走到close state来结束这个State Machine的循环。
4. 在最后有一个close state,也就是在此不论程序执行有无错误发生,最终都会到这个state进行一定结束程序的步骤,使程序可以顺利结束。如此一来,便不致因错误发生时,因某些关闭动作没有做而产生意想不到的结果或”挂”在那里了。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- igat.cn 版权所有 赣ICP备2024042791号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务