三菱 PLC 编程入门 2
目录
上一篇文章《三菱 PLC 编程入门 1》 介绍了 PLC 编程的基本知识。本文实现一个跑马灯的小例子。

微信扫码查看视频
第一步,从 0 到 7,依次亮灯。
灯的亮和灭,用到输出继电器 Y
。
Y3 := TRUE; // 3 号灯亮
Y3 := FALSE; // 3 号灯灭
依次亮灯,可以用定时器 TON
(延时开)。
// 1秒后,0号灯亮
TON0(IN := TRUE, PT := T#1s, Q := Y0);
// 如果0号灯亮,1秒后,1号灯亮
TON1(IN := Y0, PT := T#1s, Q := Y1);
// 如果1号灯亮,1秒后,2号灯亮
TON2(IN := Y1, PT := T#1s, Q := Y2);
// ...
上面的程序,能实现依次亮灯。但是有个问题,关灯不好实现。
举例说明。
// 1秒后,0号灯亮
TON0(IN := TRUE, PT := T#1s, Q := Y0);
// Y0 熄灭
Y0 := FALSE
结果是灯不亮。因为程序是循环执行的,第一行执行完,立即执行第二行(灯灭)。当接操作 Y
时,新的状态会覆盖旧的状态。
为了解决这个问题,可以利用辅助继电器 M
。
// 1秒后,M0 开。这是灯点亮的第一个条件(第二个条件在第二步,依次关灯)
TON0(IN := TRUE, PT := T#1s, Q := M0);
另外,间隔时间是写死的。为了方便调整,可以用一个变量表示。
// 延迟时间,单位:ms
iDelay := 500;
总结一下,第一步可以这样实现。
// 参数配置 Configuration
// 延迟时间,单位:ms
iDelay := 500;
// 开关打开
X0 := TRUE;
// 实现 Implementation
// iDelay 毫秒后,M0 开
TON0(IN := X0, PT := INT_TO_TIME(iDelay), Q := M0);
// 如果 M0 开,iDelay 毫秒后,M1 开
TON1(IN := M0, PT := INT_TO_TIME(iDelay), Q := M1);
// 如果 M1 开,iDelay 毫秒后,M2 开
TON2(IN := M1, PT := INT_TO_TIME(iDelay), Q := M2);
// ...
TON3(IN := M2, PT := INT_TO_TIME(iDelay), Q := M3);
TON4(IN := M3, PT := INT_TO_TIME(iDelay), Q := M4);
TON5(IN := M4, PT := INT_TO_TIME(iDelay), Q := M5);
TON6(IN := M5, PT := INT_TO_TIME(iDelay), Q := M6);
TON7(IN := M6, PT := INT_TO_TIME(iDelay), Q := M7);
第二步,从 7 到 0,依次灭灯。
灭灯要用到计时器 TP(持续开)。
要计算灯的持续点亮时间 $s_i$,$i=0,1,…,n-1$,其中 $i$ 是灯的编号,$n$ 是灯的总数。令 $d$ 代表间隔时间。我们有 $s_i = (n + i)\cdot d$。
// iDelay * 9 毫秒后,M15 关,即 7 号灯灭的条件
TP7(IN := TRUE, PT := INT_TO_TIME(iDelay * 9), Q := M15);
// iDelay * 10 毫秒后,M14 关,即 6 号灯灭的条件
TP6(IN := TRUE, PT := INT_TO_TIME(iDelay * 10), Q := M14);
// ...
TP5(IN := TRUE, PT := INT_TO_TIME(iDelay * 11), Q := M13);
TP4(IN := TRUE, PT := INT_TO_TIME(iDelay * 12), Q := M12);
TP3(IN := TRUE, PT := INT_TO_TIME(iDelay * 13), Q := M11);
TP2(IN := TRUE, PT := INT_TO_TIME(iDelay * 14), Q := M10);
TP1(IN := TRUE, PT := INT_TO_TIME(iDelay * 15), Q := M9);
TP0(IN := TRUE, PT := INT_TO_TIME(iDelay * 16), Q := M8);
最后输出。
// 输出
Y0 := M0 AND M8;
Y1 := M1 AND M9;
Y2 := M2 AND M10;
Y3 := M3 AND M11;
Y4 := M4 AND M12;
Y5 := M5 AND M13;
Y6 := M6 AND M14;
Y7 := M7 AND M15;
这样一来,实现了依次亮灯和灭灯。但是有个问题,循环不起来。原因是,计时器和继电器的状态没有重置。
第三步,状态重置。
// 状态重置
// tReset 是 TON 的一个实例
// M100 是重置标签,TRUE - 执行重置,FALSE - 不执行重置
// 这段代码的作用是,保证每个周期(iDelay * 16 毫秒)重置一次状态
tReset(IN := TRUE, PT := INT_TO_TIME(iDelay * 16), Q:= M100);
IF M100 THEN
// 重置 TON
TON0(IN := FALSE);
TON1(IN := FALSE);
TON2(IN := FALSE);
TON3(IN := FALSE);
TON4(IN := FALSE);
TON5(IN := FALSE);
TON6(IN := FALSE);
TON7(IN := FALSE);
// 重置 TP
TP0(IN := FALSE);
TP1(IN := FALSE);
TP2(IN := FALSE);
TP3(IN := FALSE);
TP4(IN := FALSE);
TP5(IN := FALSE);
TP6(IN := FALSE);
TP7(IN := FALSE);
// 重置 M
M0 := FALSE;
M1 := FALSE;
M2 := FALSE;
M3 := FALSE;
M4 := FALSE;
M5 := FALSE;
M6 := FALSE;
M7 := FALSE;
// 重置“自身”
// 保证一个周期重置一次
M100 := FALSE;
tReset(IN := FALSE);
END_IF;
结合这三个步骤,就实现了跑马灯的效果。
但是,还有个小问题。代码的灵活性不高。比如,如果要改变灯的数量,那么需要更改代码,以及重新测试。如果项目复杂,那么会增加维护和测试成本。
接下来(下一篇),我们考虑用模块化的方式,实现上述功能。目的是使得代码更容易测试和维护。