3.3 编程结构
简单编程需求可以理解为数据与函数运算并获得运算结果,但这对于一些有特殊运算需求的场合会显得特别无助,这时需要在编程中引入编程结构,使程序可以按照特定的需求运行。LabVIEW中的“结构”是传统文本编程语言中的循环和条件语句的图形化表示,包括For循环、While循环、定时结构、条件结构、事件结构、元素同址操作、平铺式顺序结构、公式节点、程序框图禁用结构、条件禁用结构、共享变量、局部变量、全局变量和反馈节点。图3-3-1简要描述了LabVIEW常用结构及使用场合。
图3-3-1 LabVIEW常用结构及使用场合
3.3.1 在程序框图中使用结构
1.添加结构
在LabVIEW的程序框图中使用“结构”,如同在前面板放置控件一样,在程序框图中放置程序结构即可。
【练习3-37】
参看如图3-3-2所示步骤,以For循环为例,掌握在程序框图中放置结构的基本方法,并尝试在程序框图中添加“结构”选板中的其他编程结构。
图3-3-2 在程序框图中添加结构
2.调整结构大小
【练习3-38】
继续上面的练习,参看如图3-3-3所示步骤,掌握调整已放置的For循环结构大小的方法(使得For循环中可以容纳更多的编程对象),并掌握禁用自动扩展功能的方法。尝试放置一个包含两帧的平铺式顺序结构,并调整结构第一帧的大小。该操作是否和调整For循环结构大小不同?
图3-3-3 调整结构大小
3.结构内放置对象
程序框图中仅放置一个空的程序结构是没有实际意义的,与数据、函数相结合才能发挥程序结构的效能。将对象拖曳到结构内和将结构包围在对象周围是两种常用的结构内放置对象的方法。
【练习3-39】
参看如图3-3-4所示步骤,编写程序,掌握在For循环内添加对象的2种基本方法。
图3-3-4 结构内添加对象
4.使用另一结构替换现有结构
LabVIEW中的有些编程结构是可以替换的,可将不适宜的结构替换为另一种结构,从而达到改善程序的执行效果或可读性的目的,结构间的替换关系如表3-3-1所示。
表3-3-1 结构间的替换关系
续表
【练习3-40】
继续上面的练习,参看图3-3-5将For循环结构替换为While循环结构。
图3-3-5 使用另一结构替换现有结构
5.删除结构但不删除其中的对象
【练习3-41】
继续上面的练习,以While循环为例,参看图3-3-6将While循环结构删除,但不删除其中的“加”函数、输入控件和显示控件,达到减少编程过程中的重复操作的目的。
图3-3-6 删除结构但不删除其中的对象
3.3.2 For循环与While循环
For循环和While循环是LabVIEW中使用频率极高的程序结构,两者均用于重复执行程序框图中的代码。For循环和While循环有6个共同点(见图3-3-7),下面将逐一讲解。
图3-3-7 For循环与While循环的共同点
1.For循环
For循环按照设定的次数重复执行循环内的代码,该代码称为子框图,如图3-3-8所示。
图3-3-8 For循环两种常用基本结构形式
除如图3-3-8所示的For循环基本形式外,For循环还提供了配置循环并行、移位寄存器功能形式,可以通过右击For循环结构边框→打开快捷菜单→选择相应命令实现。
图3-3-9 带并行实例接线端的for循环结构
1)指定For循环的执行次数
【练习3-42】
参看图3-3-10,编写程序实现指定10次循环的加法计算功能。
图3-3-10 指定For循环的执行次数
2)条件发生时停止For循环
【练习3-43】
参看图3-3-11,理解为For循环添加条件接线端的意义,并掌握设置方法。
3)使用For循环自动处理数组元素
【练习3-44】
参看图 3-3-12,理解数组连接至 For循环时输出隧道处理的方法,编写并运行程序得出输出隧道的运行结果。
【练习3-45】
参看如图3-3-12所示的基本程序,使用“高亮显示执行过程”工具调试,掌握为连线至循环的数组及数组输出隧道的具体执行过程。
图3-3-11 为For循环添加条件接线端
图3-3-12 For循环的自动处理数组元素
4)For循环的隧道
隧道用于For循环之间数据的传递,通过隧道将数据传入和传出For循环,而不进行额外处理。移位寄存器也是一种隧道,移位寄存器获取上一次循环的数据,并将数据传递至下一次循环。此外,当数组连接至 For 循环的输入隧道时,可以开启隧道的索引功能,每循环一次就可以读取数组的一个元素。While循环中也可以使用隧道功能。For循环的隧道用法如图3-3-13所示。
图3-3-13 For循环的隧道用法
2.While循环
与For循环功能类似,While循环也用于重复执行子程序框图中的代码。两者区别在于,While循环只有在满足设置的停止条件时才停止,否则不会停止运行。需要注意的是,无论是否满足停止条件,While循环至少会执行一次。
1)创建基本的While循环
基本的While循环结构的形式有两种,两者区别在于停止条件接线端的判断依据,While循环结构两种基本形式如图3-3-14所示。使用较多的形式是满足停止条件为真时While循环停止运行。
图3-3-14 While循环结构两种基本形式
【练习3-46】
参看图3-3-15,编写程序并理解While循环停止条件的概念。
图3-3-15 指定While循环的条件
2)调整执行定时
上一个例子,程序中While循环中函数运算执行的速度取决于计算机CPU的运算能力,简言之,CPU能“算”多快,这个程序就能“跑”多快。这意味着,计算机CPU的资源被这个程序集中占用了。这样做势必会影响其他应用程序的运行。为此,在不影响程序基本效率的情况下,可以适当控制程序执行的速度。
【练习3-47】
参看图3-3-16,为While循环添加“等待(ms)”函数。思考一下,对于For循环及其他结构,是否也可以使用延时函数?
图3-3-16 控制循环的定时
3)自动处理数组元素
将数组连线至While循环,设置“启用索引”功能实现数组中的每个元素依次参与While循环子框图的运算。For循环也支持“启用索引”功能。
【练习3-48】
参看图 3-3-17,编写程序并通过“高亮显示执行过程”工具掌握使用循环结构自动处理数组元素的方法。若要深入了解循环中的自动索引功能,可参看 labview\examples\Arrays\Arrays.lvproj范例。
图3-3-17 利用循环自动处理数组元素
4)累积离开循环数组中的数据
将While循环的输出隧道设置为连接模式时,可连接离开循环的数组。选择连接模式后,所有输入都按顺序组合成一个数组,维数和连入的输入数组一致。设置While循环输出隧道如图3-3-18所示。
图3-3-18 设置While循环输出隧道
输出隧道模式还有最终值模式和索引模式。最终值模式下,显示的是最后一次循环的输出值。索引模式则会创建一个比原维数加一维的数组。For循环也支持输出隧道模式设置。
5)使用移位寄存器和反馈节点在循环内传递数据
在While循环中想要传递数据则可以通过两种方法实现,一种是用移位寄存器,另一种是使用反馈节点。While 循环中使用移位寄存器的方法与For循环的用法相同。反馈节点也用于传递上一次执行的值,或在反馈节点每次执行时重置值,也起到了循环内传递数据的效果。循环内使用移位寄存器与反馈节点范例如图3-3-19所示。
图3-3-19 循环内使用移位寄存器与反馈节点范例
【练习3-49】
搜索并打开范例查找器的反馈节点—创建数组.vi,读懂在While循环内使用移位寄存器和反馈节点实现功能的方法。
6)转换While循环为For循环或定时循环
While循环与For循环之间可以相互转换(替换),转换后在功能上、细节上会有部分差异,如条件接线端和循环次数的取舍。此外,While循环可以替换为定时循环,但For循环不能替换为定时循环。
【练习3-50】
参看如图3-3-20所示步骤,将While循环转换为For循环或定时循环。
图3-3-20 转换While循环为For循环或定时循环
3.移位寄存器
移位寄存器可将上一次循环的值传递至下一次循环(While循环和For循环均适用),如图3-3-21所示。使用移位寄存器可以在循环之间将一个值传递至下一次循环,还可以将多个值传递至下一次循环。
此外,在循环的左侧可以创建层叠式移位寄存器,用于保存前面若干个循环的值,并将这些值传递至下一次循环,该方法常用于求相邻数据点的平均。
图3-3-21 使用移位寄存器在循环之间传递数据
1)将一个值传递至下一次循环
【练习3-51】
参看图3-3-22,以While循环为例编写程序并掌握移位寄存器将一个值传递至下一次循环的原理。
图3-3-22 使用移位寄存器将一个值传递至下一次循环
2)将多值传递至下一次循环
【练习3-52】
参看图3-3-23,以While循环为例编写程序并掌握移位寄存器将多个值传递至下一次循环的原理。
图3-3-23 使用移位寄存器将多个值传递至下一次循环
3)重置移位寄存器初始值
【练习3-53】
参看图3-3-24,以For循环为例编写程序并掌握重置移位寄存器初始值的方法。
图3-3-24 重置移位寄存器的初始值
4)将移位寄存器替换为隧道
隧道是将数据传入或传出结构的接线端。如果不需要将值从一个循环传递至另一个循环,可用隧道替换移位寄存器。
【练习3-54】
参看图3-3-25,以For循环为例编写程序并完成将移位寄存器替换为隧道的操作并再次转换,完成从隧道转换为移位寄存器的操作。
4.移位寄存器与反馈节点之间的替换操作
【练习3-55】
参看图3-2-26,掌握在两种不同情况下将反馈节点替换为移位寄存器的方法。
图3-3-25 寄存器替换为隧道
图3-3-26 寄存器与反馈节点之间的替换操作
3.3.3 执行部分代码的程序结构(条件、顺序、禁用)
LabVIEW中的条件结构、顺序结构、元素同址操作结构、禁用结构都含有多个子程序框图(子程序框图是指结构中包含的代码),可用于指定执行部分代码。
1.条件结构
条件结构是通过判读输入值实现代码执行的一种结构,包括两个或两个以上子程序框图(也称条件分支)。
【练习3-56】
参看图3-3-27,在程序框图中放置一个条件结构。
图3-3-27 放置条件结构
1)创建条件结构执行代码
条件结构可以指定两个或两个以上的分支,并根据结构的输入值执行程序。
【练习3-57】
参看图3-3-28,打开范例查找器的条件结构-选择器数据类型范例(Case Structure-Selector Data Types VI),理解并掌握创建由布尔按钮控制的条件结构执行代码的方法。
图3-3-28 条件结构-选择器数据类型范例
● 连接到分支选择器的字符串是区分大小写的。若要取消区分字符大小写,需将字符串值连接至分支选择器,右击条件结构的边框,打开快捷菜单,选择“不区分大小写匹配”选项。
● 若条件分支的值是字符串类型,需使用反斜线代码来表示特殊字符。例如,\\表示一个反斜线,\r表示回车。
2)输入条件分支的值
条件选择器标签,可以输入一个值或一组值的范围,用于更灵活适应编程时条件结构的分支响应条件。表3-3-2列出了在输入条件分支的值时,不同的值类型对应的范围说明。
表3-3-2 输入条件分支的值类型及范围说明
【练习3-58】
参看图 3-3-29,编写程序,掌握条件选择器标签输入值的操作方法并理解设置条件分支的值的意义。
图3-3-29 输入条件分支的值
3)条件结构上的未连线输出隧道
条件结构允许创建多个输入/输出隧道。条件结构的各个分支上都有输入隧道,但不一定每个分支都必须使用输出隧道。
【练习3-59】
参看图 3-3-30,理解条件结构上的未连线的输出隧道的含义,并掌握出现此情况时相应的处理方法。
图3-3-30 条件结构上的未连线输出隧道
4)使用条件结构处理错误
【练习3-60】
参看图3-3-31,编写程序并掌握使用条件结构处理“错误”的思路。
图3-3-31 使用条件结构处理错误
5)在条件结构之间添加分支
默认的条件结构只有真和假两个分支,对于需要使用多种分支条件响应的情况,可以为条件结构添加多个分支并为条件的分支排列顺序。
【练习3-61】
参看图3-3-32,编写程序并掌握为条件结构的分支重新排序的方法。
图3-3-32 添加条件分支及重新排序
6)指定条件结构的默认分支
当条件结构需要处理超出范围的值时,我们可为条件结构指定一个默认分支,而不必列出所有可能的输入值。例如,如果分支选择器的数据类型是整型,并且已指定1、2、3三个条件分支,则还须指定一个默认分支,输入数据为0、4或其他有效整型数时执行该默认条件分支。
【练习3-62】
参看图3-3-33,编写程序并执行该VI,理解设置默认分支的意义,并掌握2种设置默认分支的方法。
图3-3-33 设置默认分支
7)调换条件结构分支
条件结构的两个可见分支可以对调位置,该操作不影响其他分支及这些分支在快捷菜单中的显示。
【练习3-63】
参看图3-3-34,编写程序并掌握调换条件结构分支的方法。
图3-3-34 调换条件结构分支
8)条件结构分支移位
若连线至分支选择器的数据类型是非枚举的数值型,可将条件结构的可见分支移动到其他位置。
【练习3-64】
参看图 3-3-35,编写一个含有3个分支条件结构的程序并掌握条件结构分支移位的方法。
图3-3-35 条件结构分支移位
2.顺序结构
顺序结构包含一个或多个按顺序执行的子程序框图或帧。在顺序结构的每一帧中,数据依赖性决定了节点的执行顺序。
LabVIEW 顺序结构有两种形式:平铺式顺序结构和层叠式顺序结构。使用顺序结构需要注意部分代码会隐藏在结构中,程序调试时应考虑以数据流作为控制执行顺序的依据,而不能简单地以顺序结构的先后顺序作为控制执行顺序的依据。
顺序结构有以下特点。
① 使用顺序结构时,任何一个顺序局部变量都将会打破从左到右的数据流规范。
② 与条件结构不同,顺序结构的隧道只能有一个数据源,而输出可以来自任意帧。
③ 与条件结构类似,平铺式或层叠式顺序结构的所有帧都可以使用输入隧道的数据。
1)平铺式顺序结构
当平铺式顺序结构的帧都连接了可用的数据时,结构的帧按照从左至右的顺序执行。每帧执行完毕后会将数据传递到下一帧,这意味着某个帧的输入可能取决于另一个帧的输出。
【练习3-65】
参看图3-3-36,编写程序并熟悉平铺式顺序结构的基本使用规则。
图3-3-36 平铺式顺序结构
如果先将平铺式顺序结构转变为层叠式顺序结构,然后转变回平铺式顺序结构,那么LabVIEW会将所有输入接线端移到顺序结构的第一帧中,平铺式顺序结构进行的操作与层叠式顺序结构相同。
2)层叠式顺序结构
层叠式顺序结构将所有的帧依次层叠,因此每次只能看到其中的一帧,并且按照第 0 帧、第1帧直至最后一帧的顺序执行。
(1)层叠式顺序结构仅在最后一帧执行结束后返回数据。
(2)若为了节省程序框图空间,建议使用层叠式顺序结构。
(3)与平铺式顺序结构不同,层叠式顺序结构需使用顺序局部变量在帧与帧之间传递数据。(4)顺序结构可以保证执行顺序,但同时也阻止了并行操作。
【练习3-66】
参看图 3-3-37,将上一练习中的平铺式顺序结构转换为层叠式顺序结构,并掌握将平铺式顺序结构替换为层叠式顺序结构的基本方法及添加顺序局部变量的方法。
图3-3-37 层叠式顺序结构
3.禁用结构
禁用结构是阻止代码执行的一种程序结构,含有多个子程序框图,每次只编译和执行一个子程序框图,要执行的子程序框图在编译时决定。在程序调试时使用禁用结构,可以避免重复性的删除、恢复代码操作。
非活动子程序框图中的代码在运行时不执行也不编译。禁用结构可以用来禁用部分程序框图上的代码。
1)条件禁用结构
条件禁用结构有一个或多个子程序框图,LabVIEW在执行时根据子程序框图的条件配置只使用其中的一个子程序框图。条件禁用结构用于定义具体代码编译和执行的条件,可以根据条件进行配置(包括平台和其他用户定义符号等),执行一个子程序框图。编译时,LabVIEW不包括条件禁用结构中非活动子程序框图中的任何代码。
若VI的某段代码用于某个特定终端设备,可将这段代码放在条件禁用结构中,并将其配置为在某个特定终端上运行。条件禁用结构中可配置代码的终端包括 Windows、Mac、UNIX 系统和FPGA终端。受限篇幅原因,详细配置的符号、有效值可以查看LabVIEW帮助文档。
2)程序框图禁用结构
在程序框图禁用结构中,LabVIEW的编译不包括禁用子程序框图中的任何代码。
【练习3-67】
参看图3-3-38,打开\National Instruments\LabVIEW 2013\examples\Structures\Disable Structures\Disable Structures.lvproj,根据前面板中的文字完成相应的设置并运行程序框图禁用结构VI,掌握程序框图禁用结构的基本用法。
图3-3-38 程序框图禁用结构
3.3.4 事件结构
事件结构是一个等待事件发生并执行相应条件分支、处理该事件的编程结构。事件结构包括一个或多个子程序框图或事件分支,结构处理事件时,仅有一个子程序框图或分支执行。
1.事件结构的组成
图3-3-39为常用的事件结构形式(事件结构外加一个While循环),可以持续运行响应事件。
图3-3-39 常用的事件结构形式
2.添加事件分支
【练习3-68】
参看图3-3-40,为一个含有布尔按钮的事件结构添加一个“值改变”事件分支。
图3-3-40 添加事件分支
3.使用超时事件
使用一个超时事件可将事件结构配置为等待指定量的时间直到事件发生。
【练习3-69】
参看图3-3-41,设置超时时间为100ms的超时事件。
图3-3-41 使用超时事件
4.重排事件分支
【练习3-70】
参看图3-3-42,将事件结构的两个事件分支进行调整排序。
图3-3-42 重排事件分支
3.3.5 局部变量、全局变量
LabVIEW 的局部变量可从一个 VI 的不同位置访问前面板对象,全局变量可在多个 VI 之间访问和传递数据。
1.局部变量
无法访问前面板的某个对象或需要在程序框图节点之间传递数据时,可利用创建前面板对象的局部变量来实现。局部变量可对前面板上的输入控件或显示件进行数据读/写。写入一个局部变量相当于将数据传递给其他接线端,局部变量还可向输入控件写入数据和从显示控件读取数据。事实上,通过局部变量,前面板对象既可作为输入访问也可作为输出访问。
创建局部变量后,局部变量仅仅出现在程序框图上,在前面板上无显示。
【练习3-71】
参看图3-3-43,以数值输入控件为例,尝试用两种方法编写程序,掌握创建局部变量的方法。
2.全局变量
全局变量可在同时运行的多个 VI 之间访问和传递数据,全局变量属于内置的 LabVIEW 对象。创建全局变量时,LabVIEW会自动创建一个有前面板但无程序框图的特殊全局VI。在全局VI的前面板中添加输入控件和显示控件,能够定义全局变量的数据类型。该前面板实际是一个可供多个VI进行数据访问的“容器”。
图3-3-43 创建局部变量
【练习3-72】
参看图3-3-44,读懂程序并掌握全局变量的工作原理。
图3-3-44 全局变量
【练习3-73】
参看图3-3-45,掌握创建全局变量的基本方法。
3.变量的读/写操作
创建局部变量或全局变量后,就可以利用该变量进行数据的读/写操作了。默认情况下,创建的新变量是接收数据状态,呈现为显示控件。
变量可以由初始的“接收数据”配置为数据源“输出数据”、读取局部变量或读取全局变量。右击变量,打开快捷菜单,选择“转换为读取”命令,可将该变量配置为一个输入控件。节点执行时,VI将读取相关前面板输入控件或显示控件中的数据。
若需要变量从程序框图接收数据而不是提供数据,可右击该变量,打开快捷菜单,选择“转换为写入”命令。
程序框图中,读取局部变量、全局变量与写入局部变量、全局变量间的区别可以简单地类比为输入控件和显示控件间的区别。类似于输入控件,读取局部变量或读取全局变量的边框较粗;类似于显示控件,写入局部变量或写入全局变量的边框则较细。
图3-3-45 创建全局变量
4.局部变量和全局变量的使用注意事项
局部变量和全局变量是较为高级的LabVIEW概念。它们不是LabVIEW数据流执行模型中固有的部分。使用局部变量和全局变量时,程序框图可能会变得难以阅读,因此须谨慎使用。
错误地使用局部变量和全局变量,如将其取代连线板或用其访问顺序结构中每一帧中的数值,可能在VI中导致不可预期的行为。滥用局部变量和全局变量,如用来避免程序框图间的过长连线或取代数据流,将会降低执行速度。
1)局部变量和全局变量的初始化
若需对一个局部或全局变量进行初始化,则应在VI运行前将已知值写入变量,否则变量可能含有导致VI发生错误行为的数据。若变量的初始值基于一个计算结果,则应确保LabVIEW在读取该变量前先将初始值写入变量。写入操作与VI的其他部分并行可能引发竞争状态。
2)竞争状态
当两段或更多的代码并行执行,并且访问同一部分内存时会引发竞争状态。如果代码是相互独立的,将无法判断LabVIEW按照何种顺序访问共享资源。竞争状态随着程序运行的时间因素而改变,因此竞争状态具有一定的不确定性。操作系统、LabVIEW版本和系统中其他软件的改变均会引起竞争状态。
竞争状态会引起不可预期的结果。例如,两段独立的代码访问同一个队列,但是用户未控制LabVIEW访问队列的顺序,这种情况下将会引发竞争状态。简单地说,两段代码中的任何一段代码谁先执行谁后执行都是未知的。
【练习3-74】
参看图3-3-46编写程序,理解使用局部变量和全局变量时的竞争状态。
图3-3-46 使用局部变量和全局变量时的竞争状态