转:LabVIEW程式的利器(三):模组化VI 以及StateMachine进阶
时间:10-02
整理:3721RD
点击:
作者:陈柏龙
在这边看到好就转过来了,之前转载过两篇,大家反响好不错,说这些labview技巧很实用,就继续讲坐着的系列转载过来。
前两篇主要想传达一个写LabVIEW 程式的概念,也就是要将常用到的功能包成Sub VI 。写程式不再只是将所有程式码写进一个Loop 里面,而是开始写程式前会先规画好需要哪些程式功能,以及适合运用哪种Design Pattern 的架构。今天要谈的是更进阶的方法,不仅将程式模组化,连程式功能也一起加进去。
除了Data Flow 的概念,在LabVIEW 里面另外一个很重要的概念就是Shift Register 。一般初学者在学到Shift Register 的时候,只知道Shift Register 可以用来传递资料到下一次回圈,以及程式执行一开始要先对Shift Register 初始化,否则Shift Register 内部会保留上一次程式执行结束的资料。但其实Shift Register 只是LabVIEW 帮我们预先规画好的记忆体区块,我们可以随时随地去初始化、写入或读取里面的资料,进阶的用法就是Functional Global Variable 。
将上图的程式架构包成Sub VI就是一个可随时随地被呼叫使用的Functional Global Variable ,其有以下特点:
(1)只执行一次的Loop 。
(2)未初始化的Shift Register 。
(3) Enum Control和Case Structure 。
(4)禁止Reentrant execution 。
看起来Functional Global Variable 和一般常用的Local Variable 或Global Variable 的功能很像。但不同的是Functional Global Variable 本身就是一个Sub VI ,所以在VI Properties 中的Execution 可以去设定是否允许Reentrant execution ,而预设值是取消的,如下图。
在禁止Reentrant execution 的状态下, Sub VI 可以随时随地使用,但如果同时有两个人在呼叫它的时候,并不会同时写入资料,而是会排队等先呼叫的人执行完,另一个人才能进去使用它。如此可避免同时去读写资料而造成Race Condition ,甚至可以在程式里面增加功能去纪录资料是在何时何地被读写的。
接下来同样沿用前两篇的红绿灯程式来做为范例,在开始写程式之前,先规划一下会需要用到那些功能模组。首先是要有一个可以写入红绿灯状态并且可以读取显示的模组,另外就是要有一个计时功能的模组,设定好时间后,会等到时间到了再执行下一个动作。下面我会将这两个模组先写成Functional Global Variable ,如下图。
上图是Timing Module 的程式,在Reset 的状态中,会将输入的Wait Time (s) 以及将目前时间当作Start Time 写入Shift Register 中。
在Check 的状态中,每次会等待50ms ,并将目前的时间减掉Start Time 计算经过时间,再与一开始设定的Wait Time 相比较,输出为是否到达时间Done ?
上图是Traffic Light Module 的程式,其中Shift Register 内存放的是红绿灯的状态, State Control 指定执行的方法,最后会输出下一次要执行的状态Next State ,以及目前状态要等待的时间Wait Time (s ) 。当执行Read 状态时,会将Shift Register 中的资料读取出来并输出。
上图是红绿灯执行的流程,一开始会先初始化红绿灯,接下来亮红灯并停留2 秒,接着亮黄灯并停留1 秒,最后亮绿灯并停留3 秒。
看到这边大家应该会有似曾相识的感觉,没错这是State Machine 的写法,但是又会困惑为何回圈每次只执行一次。因为接下来要示范的是State Machine 的另外一种写法- Queued State Machine 。
主程式如上图所示,是由Queue 所架构而成的,其所传递的element 为主程式执行的状态Queue State 以及Traffic Light State 所包成的Cluster 。而在程式一开始就预先Enqueue element 进去一笔Write 的指令,将Traffic Light Module 状态设定为Start 。当程式执行Write 指令时,会将TL State 写入前面所写好的Traffic Light Module ,并且将Wait Time 设定于Timing Module 。再将Read 的指令以及Write 设定红绿灯的Next State 的指令先后排入Queue 里面。
接下来执行Read 指令时,会读取目前红绿灯的状态并且更新介面上的Indicator 。此时在Queue 里面还有一笔Write 的指令,是要将红绿灯的Next State 写入。但我们必须等Wait Time 到达时才能执行,所以就要使用Enqueue Element At Opposite End 将Wait 的指令插队排到Write 之前来执行。
而在Wait 的状态中,会去Check 时间是否到达Wait Time 了?如上图,如果时间还没到,同样将一笔Wait 的指令插队排入Queue 里面。当时间到达时,才不将Wait 的指令排入,此时先前排入Queue 里面Write 指令就会在下一次回圈执行。
整个程式的流程就会是Write (更新红绿灯) -> Read (读取红绿灯) -> Wait until time done -> Write …… 循环。而在这次的程式中所写的两个Functional Global Variable 除了可以储存资料外,它还可以将一些程式的功能或方法写进去,程式写起来较为弹性,像这次就把State Machine 给一并写了进去。
不得不说作者真的很强