边沿检测的R_TRIG和F_TRIG都是功能块,每调用一次就要有一个独立的实例来记住上一次的CLK状态,如果你在FOR循环里反复调用同一个R_TRIG实例,它只会记住最后一次循环的输入状态,前面的全部丢失。这是最常见的写法错误。
在VAR区声明两个数组,一个存信号,一个存触发器实例。信号的数组可以是BOOL,触发器的数组就是R_TRIG。声明语法是:alarm : ARRAY[0..200] OF BOOL;,alarmTrig : ARRAY[0..200] OF R_TRIG;,索引变量用INT声明。F_TRIG同理,把R_TRIG换成F_TRIG就行。
循环里先调用触发器实例,再用它的.Q输出判断是否发生了边沿。写法是alarmTrig[i](CLK := alarm[i]);,然后IF alarmTrig[i].Q THEN里面写你要执行的逻辑。注意调用的时候是实例名[索引](CLK := 输入),不是实例名(CLK := 输入[索引]),括号位置不一样,前者索引定位到具体的实例,后者还是共用同一个实例。
调用和判断不能写在同一行。有人写成Rtrig(alarme[i].state)然后紧接着IF Rtrig(alarme[i].state) THEN,这样功能块被调用了两次,第二次调用时上一次的边沿状态已经被覆盖了,Q永远为TRUE。正确做法是调用一次,用.Q读结果。

数组边界写死,ARRAY[0..200]的循环就写FOR i := 0 TO 200 DO,不要写TO 201或者TO 250。越界访问R_TRIG数组不会触发编译错误,运行时可能读到相邻变量的内存,Q输出会莫名其妙地置位,状态机会乱跳。CODESYS有隐式边界检查函数CheckBounds,可以在开发阶段启用,上线前再关掉,性能有影响。
如果输入信号来自物理按钮或者继电器触点,边沿检测之前先做去抖。50ms的TON延时滤波放在R_TRIG前面,信号稳定50ms后再送入CLK,不然一次按键的机械抖动会被识别成多次上升沿,数组里存进重复值。R_TRIG的实例数组本身不解决抖动问题,它只负责单次扫描周期内的状态记忆。
结构体数组里套R_TRIG也可以,先声明一个TYPE,里面放BOOL信号和R_TRIG实例,然后用ARRAY OF结构体,调用的时候写myStruct[i].trig(CLK := myStruct[i].signal);,再用myStruct[i].trig.Q判断。这种写法比平行数组好管理,信号和它对应的边沿实例绑在一起,不会出现数组长度不一致的问题。

免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删