第5章 边 沿 触 发 边沿触发指上升沿触发和下降沿触发,捕捉的是BOOL型变量从高电平到低电平或从低电平到高电平变化的瞬间。由于PLC是采用循环扫描的工作方式,扫描周期一般是毫秒级,因此当操作人员按下按钮,或者接近开关、光电开关等传感器检测到工件,虽然这些操作 时间很短,但PLC已经执行了成千上万次指令。有时只需要 指令执行一次,例如使用光电开关进行产品计数,光电开关检测到一件产品,只需要计一次数即可。由于各种客观因素的影响,光电开关检测到一件产品,PLC的计数指令可能已经执行了很多次,计数结果显然是不准确的。如果只捕捉到信号的变化, 即光电开关捕捉的信号从无到有这一瞬间,才计一次数,也就是执行一次指令,则就能实现正确的计数,这就是边沿触发,捕捉的是电平的变化。 注意: 边沿触发捕捉的是BOOL型变量的变化,其他类型的变量不存在边沿触发的概念。 5.1基本概念 5.1.1上升沿 上升沿(R_TRIG)指BOOL型变量从低电平变化为高电平的那一瞬间, 梯形图中的实现如图51所示。 图51上升沿梯形图 图51中的梯形图与图41中的最大不同在于: 只在变量xLabel1从FALSE变为TRUE的瞬间,变量xResults才为TRUE,并持续一个扫描周期。假设变量xLabel1为PLC的输入点,接入按钮的常开点; 变量xResults为PLC的输出点,接指示灯,只有在按下按钮的瞬间,指示灯才会亮。图41中的梯形图,只要按下按钮,指示灯就一直亮。所以,上升沿的实质是 捕捉BOOL型变量从低电平到高电平的变化,或者从无到有的变化,取的是“变化”; 而常开常闭点,取的是“结果”。 IEC 611313标准制定了专门的功能块R_TRIG(R是英文RISE的首字母, 意为上升,TRIG意为触发)来实现上升沿。如果调用功能块来实现图51中的上升沿触发,则如图52所示。 图52功能块实现上升沿 图52中的梯形图与图51中的梯形图是完全等价的,它采用功能块实现了上升沿触发。当功能块R_TRIG检测到输入引脚CLK上的变量xLabel1发生从低电平到高电平的变化时,输出引脚Q变为高电平,并持续一个扫描周期。因此,变量xResults也变为高电平,并持续一个扫描周期。功能块R_TRIG的作用 就是捕捉它的输入引脚CLK的上升沿。 上升沿的实质是把变量当前扫描周期的值与上一个扫描周期的值进行比较,如果上一个扫描周期的值 为FALSE,而本次扫描周期的值为TRUE,则捕捉到一次上升沿变化。因此,要想捕捉到上升沿,必须存储上一个扫描周期的值。回顾 2.6.3节,功能块和函数的区别就是功能块有存储空间,因此实现上升沿 必须调用功能块。使用功能块必须实例化,也就是要定义功能块型变量。图52中的R_TRIG_0就是定义的功能块型变量。 功能块型变量在CODESYS中与BOOL型、INT型、结构体等一样,是一种数据类型。而在西门子博途 中,称为背景数据块。实现上升沿的代码如下: VAR R_TRIG_0 : R_TRIG;//上升沿功能块实例化 END_VAR R_TRIG_0(CLK := xLabel1,Q => xResults); 从这段代码不难看出,功能块调用的格式如下: 实例名(VAR_IN := <变量或表达式>,VAR_OUT=><变量或表达式>); 功能块调用就是分别使用运算符“:=”和运算符“=>”来获取功能块的输入和输出,它们之间用“,”隔开,调用结束,以“;”结束。所以调用功能块的实质 是一系列赋值操作。调用功能块的目的 就是对于给定的输入值获取其输出值。如果要在其他语句中使用功能块的输入和输出,调用格式为“实例名.输入/输出”。因此,图51中的梯形图也可以用如下代码表示。 VAR R_TRIG_0 : R_TRIG; END_VAR R_TRIG_0(CLK := xLabel1); IF R_TRIG_0.Q THEN xResults := TRUE;//检测到上升沿时,输出一个扫描周期 ELSE xResults := FALSE; END_IF 这段代码使用IF…ELSE…END_IF语句实现图51中的梯形图程序。从代码可以看出,对于功能块的调用,ST语言 还是很灵活的,既可以在功能块的输入引脚和输出引脚上直接填写变量,也可以使用“.” 图53SoMachine中调 用输入助手 引用输入引脚和输出引脚的值,再通过赋值语句传递相应的变量。这段代码中,IF…ELSE…END_IF语句的判断条件R_TRIG_0.Q,就是使用实例名R_TRIG_0来调用功能块的输出引脚Q。对于不需要的引脚,在调用的时候也可以省略。ST语言在实现上升沿的时候要比梯形图略显烦琐,如果不想手动输入,SoMachine还提供了输入助手功能。 首先,在需要输入代码的空白处右击,弹出的快捷菜单如图53所示。 然后,选择“输入助手”命令,弹出“输入助手”对话框,如图54所示。 图54“输入助手”对话框 选择“功能块”,右侧会出现调用功能块选项,单击Standard前面的“+”, 展开Standard列表,在展开的列表中选择Trigger前面的“+”,展开Trigger列表,如图55所示。 图55SoMachine中输入助手对话框中的边沿触发 选择“R_TGIG”,弹出“自动声明”对话框,即调用R_TRIG的对话框,如图56所示。自动声明 是指为调用的R_TRIG功能块输入/输出引脚分配变量。 图56SoMachine中定义R_TRIG对话框 在图56中的“名称(N):”文本框中填写实例名,从“范围(S):”“标志(F):”“对象(O):”“初始化(J):”“地址(A):”等描述不难看出, CODESYS中把功能块的实例名看作一种数据类型。因此,在CODESYS中,功能块的实例名 简称功能块型变量。对于功能块型变量的命名,可以按照3.4.4节介绍的变量命名原则来命名,也可以使用简单的方式 命名为“R1”。 图57输入助手自动建立的代码 单击“确定”按钮,系统就自动生成功能块R_TRIG的调用格式,如图57所示。 在SoMachine中,所有的功能块和函数调用都可以采用这种方式辅助输入。 5.1.2下降沿 下降沿(F_TRIG)是指BOOL型变量从高电平变化为低电平的那一瞬间,使用功能块F_TRIG(F是英文单词FALL的首字母, 意为下降)实现,下降沿在梯形图中的实现方法如图58所示。 图58下降沿梯形图 用ST语言实现下降沿,代码如下: VAR F_TRIG_0 : F_TRIG;//下降沿功能块实例化 END_VAR F_TRIG_0();//调用功能块 F_TRIG_0.CLK := xLabel1; xResults := F_TRIG_0.Q ;//检测到下降沿时,输出一个扫描周期 调用下降沿功能块的代码中采用了另一种方式, 不是在调用功能块时将变量赋值给输入/输出,而是直接用赋值表达式实现。 可见,功能块的调用是非常灵活的。采用这种方法时, 应尽量把赋值语句和调用功能块的语句放在一起,不然调试时非常麻烦。 通过对边沿触发功能块的学习,可以发现,ST语言中调用功能块是非常灵活的。 调用功能块在ST语言中应用非常广泛,类似梯形图中的各种指令。希望读者能够 在学习边沿触发的过程中掌握调用功能块的方法,后续章节还会继续介绍如何调用功能块。 5.1.3西门子博途中的边沿触发 西门子博途也提供了R_TRIG和F_TRIG功能块实现边沿触发。R_TRIG和F_TRIG功能块是IEC 611313中制定的标准功能块,凡是符合IEC 611313标准的PLC,都可以使用这两个功能块来实现边沿触发。 由于西门子博途中功能块的调用,涉及背景数据块,与CODESYS平台的PLC略有不同。 下面介绍西门子博途中边沿触发的实现方法。 1. 如何调用 在西门子博途(以下简称博途)中调用各种功能块非常方便,它提供了指令窗口,可以把指令直接拖曳到编辑区,如图59所示。 直接把图59中的R_TRIG拖曳到代码编辑区,会自动弹出“调用选项”对话框,如图510所示。 图59博途中的指令窗口 图510博途中的分配背景数据块 选择“单个实例”,名称为“R_TRIG_DB”,这在博途 中称为分配背景数据块,实质 是分配一块存储空间。“R_TRIG_DB”就是为这块存储空间取的名字, 类似于CODESYS中的功能块型变量,也就是实例名。可根据需求更改名称,只需要符合博途 图511博途自动生成的上升沿代码格式 的命名规范即可,具体可参考博途编程手册。此处更改名称为“R1”,单击“确定”按钮,在代码编辑区会自动生成上升沿代码格式,如图511所示。 从图511可以看出,系统已经自动生成调用格式,只需要添加变量即可, 并且系统自动为R1添加了变量标识符。 博途中的背景数据块的实质也是变量。 默认单个实例是全局变量; 填入变量后,代码如下: "R1"(CLK := #xLabel1); #xResults := "R1".Q; 当然,也可以使用IF…ELSE…END_IF语句,实现代码如下: "R1"(CLK := #xLabel1); IF"R1".Q THEN #xResults := TRUE; ELSE #xResults := FALSE; END_IF 2. 如何避免产生大量背景数据块 边沿触发是PLC编程中经常需要使用的,一个项目中可能需要很多边沿触发。在博途中,每调用一次 边沿触发 就会建立一个背景数据块,因此会产生大量的背景数据块,使得程序结构混乱, 影响程序的可读性,并且严重浪费PLC的系统资源 。那么如何避免这个问题呢?博途 提供了多重背景的解决方案,可以利用FB本身的背景数据块, 保存边沿检测需要的数据。 下面介绍如何使用多重背景。在图510的“调用选项”对话框中选择“多重实例”,需要注意的是,博途 默认是选择“单个实例”,如图512所示。 图512定义多重实例 图512中,“接口参数中的名称”栏中的内容就是定义的实例名,可以使用默认值,也可以根据需要更改。这里更改为 “R1”,单击“确定”按钮,系统就在当前FB的STATIC中 即静态变量中,自动建立一个名称为“R1”,数据类型为R_TRIG的变量,如图513所示。 图513定义的多重背景 类似地,需要多少个变量就拖曳多少个上升沿进来。注意一定要选择“多重实例”,如图514所示。 从图514可以看出,系统同样为R1自动添加了变量标识符,由于R1是在FB内建立的,所以是局部变量。这样充分利用了FB的存储空间存储上升沿功能块所需要的数据,减少了背景数据块。在博途中,所有的功能块调用都可以按照这种 方式操作,以减少背景数据块的数量。但是此种方法只适用在FB中调用边沿触发的情况,在FC中调用边沿触发是无法使用多重实例的。如果需要在FC中调用,可以使用参数实例来解决,例如在博途中新建FC块FC1,然后调用R_TRIG功能块,出现“调用选项”对话框时,选择“参数实例”,如图515所示。 对比图512和图515可以看出,在FC中调用功能块时是没有“多重实例”选项的。接口参数中的名称更改为“R1”,然后 单击“确定”按钮。系统就自动在当前FC的InOut即输入/输出变量中建立一个名称为“R1”,数据类型为R_TRIG的变量,如图516所示。 图514FB中调用多个上升沿触发功能块 图515FC中选择参数实例 图516FC中建立的参数实例 与多重背景一样,需要多少个变量就建立多少个实例,注意一定要选择“参数实例”,如图517所示。 图517FC中建立多个参数实例 由于FC并没有存储空间,所以无法实现边沿触发,参数实例的目的 是利用其他FB的存储空间。新建FB块,命名为“FB1”,然后在FB1中调用FC1,如图518所示。 从图518可以看出,只需要在调用FC的FB中建立R_TRIG类型的静态变量即可,这就是参数实例的意义,如图519所示。 可以看出,参数实例的目的是利用其他FB的存储空间,为没有存储空间的FC服务。参数实例 可以看作是一个接口,用来建立FB和FC之间的联系。 注意: 博途 中的FB就是功能块,FC就是函数。本例中调用的FB和FC就是用户自定义功能块和自定义函数。 图518在FB中调用FC 图519为调用的FC分配变量 5.2边沿触发与逻辑运算的综合应用 边沿触发在PLC编程中应用广泛,下面结合前面章节介绍的逻辑运算语句和IF语句,并结合具体的例子 讲解它们的具体应用。 5.2.1启动保持停止 启动保持停止梯形图是PLC梯形图的基础,是梯形图原理最好的体现,在PLC编程中 应用广泛。由于现场存在各种不可预知的干扰因素,为了最大限度 地减少误操作,或者出于程序编写的便利性,可以使用边沿触发。例如,启动信号来自其他功能块的输出,功能块的输出一直是高电平信号,因此启动信号需要使用上升沿触发,如图520所示。 图520上升沿触发的启动保持停止梯形图 图520中的梯形图是采用上升沿触发的启动保持停止梯形图。需要再次强调,为了设备安全,停止信号应该接到按钮的常闭点上,因此程序中应当使用常开触点。为了演示NOT逻辑,图520的梯形图使用了常闭触点,即停止信号接到停止按钮的常开点上。 该梯形图使用ST语言实现的代码如下: VAR xStart : BOOL; xStop : BOOL; xRun : BOOL; R_TRIG_0 : R_TRIG; END_VAR R_TRIG_0(CLK := xStart); xRun := (R_TRIG_0.QORxRun ) AND(NOT xStop); 与4.5.1节中 的代码相比,本例用R_TRIG_0.Q代替了启动信号,即变量xStart,而R_TRIG_0.Q正是变量xStart的上升沿。 当然,也可以使用IF…ELSE…END_IF语句实现,代码如下: R_TRIG_0(CLK := xStart); IF ( (R_TRIG_0.QORxRun ) AND(NOT xStop) ) THEN xRun := TRUE;//启动 ELSE xRun := FALSE;//停止 END_IF 5.2.2单按钮启停 4.5.3节用逻辑关系实现了单按钮启停功能。从程序代码可以看出,利用逻辑关系实现单按钮启停功能, 十分烦琐。单按钮启停功能也可以使用XOR运算实现。 回顾前面介绍的异或运算,是比较两个逻辑变量的值,如果值相同,运算结果为FALSE; 如果值不同,则运算结果为TRUE。因此,比较输入和输出的值就可以实现单按钮输出功能。代码如下: VAR xIN : BOOL;//输入按钮 xOUT : BOOL;//输出继电器 R1 : R_TRIG; END_VAR R1(CLK := xIN); xOUT := R1.Q XOR xOUT; 以上是使用逻辑表达式实现的单按钮启停,使用IF…ELSE…END_IF语句实现的代码如下: R1(CLK := xIN); IF (R1.Q XOR xOUT ) THEN xOUT := TRUE; ELSE xOUT := FALSE; END_IF 变量xIN分配的地址接入按钮,变量xOUT分配的地址接入继电器,按下按钮,变量xIN的值为TRUE,变量xOUT的值为FALSE。根据异或的运算规则, 输出变量xOUT的值为TRUE,继电器吸合; 再次按下按钮,变量xIN的值为TRUE, 变量xOUT的值此时为TRUE,根据异或的运算规则, 此时输出变量xOUT的值为FALSE,继电器释放。如此反复,就实现了单按钮启停功能。 此程序中取的是变量xIN的上升沿,读者可以思考并自行模拟如果不采用上升沿会发生什么结果。 如果不采用上升沿,按下按钮时,变量xOUT变为TRUE,由于按下按钮的时间,远远大于PLC的扫描周期, 会进行多次XOR运算。第一次运算时,变量xIN的值为TRUE,变量xOUT的值为FALSE,经过XOR运算后,变量xOUT的值变为TRUE; 然后会进行第二次运算,此时变量xIN的值为TRUE,而变量xOUT的值 也为TRUE,运算结果为FALSE; 然后进行第三次运算,此时变量xIN的值为TRUE, 变量xOUT的值为FALSE,运算结果为FALSE,因此输出变量xOUT的值会在FALSE和TRUE之间不停地切换。 如果取变量xIN的上升沿,只在第一次运算的时候R1.Q的值为TRUE,以后每次运算中, R1.Q为FALSE,XOR运算的结果一直为TRUE,因为按下按钮后只有一次上升沿触发。 5.2.3逻辑运算实现边沿触发 边沿触发的原理是比较变量在相邻两个扫描周期的值。如果上一个扫描周期的值为TRUE,而本次扫描周期的值为FALSE,那么就捕捉到此变量的一次下降沿; 如果上一个扫描周期的值为FALSE,而本次扫描周期的值为TRUE,则 捕捉到此变量的一次上升沿,这也是功能块F_TRIG和R_TRIG的工作原理。所以,要想捕捉到边沿信号,必须存储变量在每个扫描周期的值,至少要存储相邻两个扫描周期的值,这也是 为什么边沿触发功能块需要实例名,并分配背景数据块的原因。 理解了边沿触发的原理,完全可以通过编写程序来实现边沿触发,程序代码如下: VAR xLabel_Last : BOOL;//变量xLabel在上一个扫描周期的值 xLabel : BOOL;//用于捕捉边沿信号的变量 xLabel_R : BOOL;//上升沿信号 xLabel_F : BOOL;//下降沿信号 END_VAR IF (xLabel_Last= FALSE) AND (xLabel) THEN//和上一个扫描周期的值比较 xLabel_R := TRUE;//上升沿 ELSE xLabel_R := FALSE; END_IF; IF (xLabel_Last) AND (xLabel= FALSE) THEN//和上一个扫描周期的值比较 xLabel_F := TRUE;//下降沿 ELSE xLabel_F := FALSE; END_IF xLabel_Last := xLabel; //保存当前扫描周期的值 第一个IF…ELSE…END_IF语句中,变量xLabel在上一个扫描周期 的值为FALSE,而在本次扫描周期的值为TRUE,捕捉到它的上升沿,变量xLabel_R在一个扫描周期内值为TRUE。第二个IF…ELSE…END_IF语句中,变量xLabel在上一个扫描周期的值为TRUE,而在本次扫描周期的值为FALSE,捕捉到它的下降沿,变量xLabel_F在一个扫描周期内值为TRUE。 注意: 利用逻辑语句实现边沿触发依赖PLC的循环扫描原理。在程序的最后,执行语句“ xLabel_Last:=xLabel;” 保存当前扫描周期内变量xLabel的值。在下一个扫描周期,变量Label_Last的值,就是变量xLabel在上一个扫描周期的值。如果把语句“xLabel_Last:=xLabel;”放到第一个IF…ELSE…END_IF语句的前面,则无论如何也无法实现边沿触发功能。 5.2.2节的单按钮启停程序也可以使用下面的代码实现。 xOUT := (xIN AND NOT xLabel) XOR xOUT; xLabel := xIN; 以上代码利用上升沿的逻辑运算式代替了上升沿功能块R_TRIG。 5.3注意事项 边沿触发在PLC中的应用非常广泛。前文提到过,IEC 611313标准是推荐标准,并不是强制标准,因此各家PLC在符合标准的前提下,都有自己的特色。在三菱GX Works3中,既有标准的边沿触发功能块R_TRIG和F_TRIG, 其用法与CODESYS平台PLC一样; 也有三菱PLC独有的边沿触发功能块——PLS功能块和PLF功能块,如图521所示。 图521三菱GX Works3中的PLS功能块和PLF功能块 图521中是PLS和PLF在梯形图中的表示形式,PLS是上升沿触发,PLF是下降沿触发。从图中可以看出,它们都没有分配实例名,所以是函数。熟悉三菱PLC的读者,对这两个功能块应该不陌生。当输入引脚EN捕捉到上升沿,PLS 功能块的输出引脚d会输出一个扫描周期的高电平信号; 当输入引脚EN捕捉到下降沿, 图522GX Works3中边沿触发的 ST语言代码实现 PLF功能块的输出引脚d 会输出一个扫描周期的高电平信号。可见,它们的用法与R_TRIG和F_TRIG类似。在三菱GX Works3中,使用这两个 功能块实现边沿触发的ST语言代码如图522所示。 从图522可以看出,在三菱GX Works3中,这两个功能块用函数调用的形式实现。在变量xLabel的上升沿,变量xResults1的值变为TRUE,并持续一个扫描周期; 在变量xLabel的下降沿,变量xResults2的值变为TRUE,并持续一个扫描周期。 PLS和PLF两个功能块是三菱PLC独有的,主要目的是兼容自家的产品。 为了和自己既有的PLC兼容, 许多PLC都有类似的指令。它们在ST语言中的使用方法,无非 是调用函数和调用功能块,但是这些PLC特有的指令无法进行跨平台移植。所以笔者建议,尽量使用标准的R_TRIG和F_TRIG功能块来实现边沿触发。