简介:这份《PLC实验 八段LED编程实验 交通红绿灯》文档面向电气工程及其自动化等专业学生与PLC初学者,定位为课程设计、实验复现和梯形图/HMI入门的参考材料,重点解决S7-1200在TIA Portal V13下的编程、HMI组态与仿真验证问题。压缩包共1个docx文件,约1.62MB,以实验指导书形式组织,涵盖实验目的、设备材料、操作步骤、思考题与反思总结,便于按模块查阅。内容从PLC变量表设置、启动停止与接通延时定时器控制、0—8循环显示及16进制段位码MOVE指令,延伸到KTP700触摸屏八段LED图形组合、变量关联和按钮事件设计;同时给出智能交通灯自动/手动控制、PROFINET连接与IP配置、HMI通行/黄灯时间设定、定时器编号冲突排查,以及用三个八段LED显示学号的扩展思路。已有449人学习,适合需要复现项目、理解PLC与HMI数据交互或完成课程作业的读者参考。
1. 实训台上那台 S7-1200 上电之后
实训台上那台 S7-1200 刚上电的时候,多数人觉得八段 LED 编程实验是最简单的一题——编码表背下来,MOVE 指令一送,数字就出来了。真按下启动按钮才发现,卡住人的从来不是编码表:两个 TON 被复制粘贴成了同一套背景数据,节拍全乱;HMI 上那八个矩形死活不跟着段码变;交通红绿灯的手自动切换一按下去,南北东西四个方向的灯同时亮。这套 PLC 实验资料把两个经典题目放在一起——八段 LED 实验和交通红绿灯,用 TIA Portal V13 画梯形图、用 KTP700 触摸屏做 HMI、最后靠仿真把结果跑出来。它适合电气工程及其自动化方向正在做课程设计的人,也适合手里有 PLC 但只会点亮一个指示灯的初学者。下面按我拆这套资料的顺序讲:先把硬件和变量铺平,再分别啃两块程序,最后说哪些地方能改成自己的模板。
2. 项目全景:S7-1200、KTP700 与 TIA Portal V13 的分工
这套资料不是单一程序,而是两个独立实验共用一套软硬件基线。看懂基线,后面两个实验的代码才有地方落。
2.1 一条 PROFINET 线把 PLC 和触摸屏绑在一起
硬件侧只有三样东西:一台装了 TIA Portal V13 和 WinCC Advanced 的编程电脑、一个 S7-1200 实验箱、一块 KTP700 触摸屏。CPU 型号在资料里写得很明确,CPU 1214C DC/DC/DC,订货号 6ES7 214-1AG40-0XB0,DC/DC/DC 的意思是 24V 直流供电、24V 直流输入、晶体管直流输出——最后这一项很关键,晶体管输出才能直接驱动实验箱上的 LED 段,继电器输出型做八段数码管扫描会明显拖尾。
设备组态的顺序不能颠倒,先加 PLC 再加 HMI,最后连线:
1. 新建项目 -> 添加新设备 -> PLC -> CPU 1214C DC/DC/DC -> 6ES7 214-1AG40-0XB0 2. 添加新设备 -> HMI -> KTP700 Basic PN(7 寸) 3. 网络视图 -> 用 PROFINET 把 HMI 的以太网口拖到 PLC 的绿色网口上 4. PLC 属性 -> PROFINET 接口 -> IP 192.168.0.200 / 255.255.255.0 5. HMI 属性 -> PROFINET 接口 -> IP 192.168.0.201 / 255.255.255.0 6. 编译 -> 下载 PLC -> 下载 HMI两个 IP 必须同网段且不能冲突,这一点在仿真阶段会被反复验证。HMI 的连接参数里,"伙伴站"选 PLC 的名称而不是 IP,这样以后改地址不用重新配连接。实际接线时实验箱上的 PROFINET 口只有一个,电脑和 HMI 要么走交换机,要么先把程序下进 PLC 再拔线接屏。
提示:DC/DC/DC 型的 Q 点是源型输出,接共阴数码管时中间一般要串限流电阻,实验箱上如果已经有,就别再外接,否则段亮度会明显偏暗。
2.2 变量表决定后面八成的返工量
资料里第一句实验步骤就是"根据所需要的输入输出设置 PLC 变量表",这句话被很多人跳过,结果后面改到怀疑人生。原因是 S7-1200 的绝对地址和符号名是两套东西,梯形图里显示%I0.0还是"启动按钮"取决于你有没有在变量表里定义。定义完之后,改地址只改变量表,程序里的符号自动跟着走。
八段 LED 实验用到的 I/O 极少,一个启动、一个停止,输出一个字节就够:
| 名称 | 地址 | 数据类型 | 用途 |
|---|---|---|---|
| 启动按钮 | I0.0 | Bool | 常开,启动循环显示 |
| 停止按钮 | I0.1 | Bool | 常闭串入,终止循环 |
| 段码输出 | QB0 | Byte | Q0.0~Q0.7 对应 a~g、dp |
| 当前数字 | MW10 | Int | 0~9 循环计数 |
| 节拍脉冲 | M0.0 | Bool | 1s 单脉冲 |
交通灯实验的 I/O 稍微多一点,而且地址分配必须一次定死,后面 PLC 程序和 HMI 动画都引用它:
| 名称 | 地址 | 说明 |
|---|---|---|
| 启动按钮 | I0.0 | 启动系统 |
| 手自动开关 | I0.1 | 0 自动,1 手动 |
| 南北通行走 | I0.2 | 手动模式下有效 |
| 东西通行走 | I0.3 | 手动模式下有效 |
| 南北红灯 | Q0.0 | 输出 |
| 南北黄灯 | Q0.1 | 输出 |
| 南北绿灯 | Q0.2 | 输出 |
| 东西红灯 | Q0.3 | 输出 |
| 东西黄灯 | Q0.4 | 输出 |
| 东西绿灯 | Q0.5 | 输出 |
这张表要打印出来贴在显示器边上。资料原文在描述自动流程时,把"南北红灯 Q0.0"和"南北方向绿灯 Q0.5"写串过位置,照着文字直接敲地址,最后南北绿灯会挂到东西方向去,现象是自动模式一进入就两边同时绿。我后面会单独说这个坑。
2.3 两块程序的骨架完全不同
虽然共用一套硬件,八段 LED 和交通红绿灯的程序结构不是一个路子。八段 LED 是"查表 + 输出":一个整数在 0~8 之间循环,每换一个数字就去段码表里取一个字节,MOVE 到 QB0,剩下的交给硬件。它的技术难点在节拍生成和 HMI 上的八个独立图形怎么绑变量。
交通灯是"状态推进":任何时刻只有一个状态有效,状态之间靠时间条件迁移,每个状态负责一组输出的通断。它的难点在互锁和手自动切换时的全复位,一旦漏了复位,切换瞬间就会双方向绿灯。
资料里两个实验都是新建一个 FC 块写逻辑,再在 OB1(Main)里把 FC 拖进去调用。这种做法对课程设计够用,但有个副作用:FC 没有静态存储区,定时器只能靠全局背景 DB 或者 M 区做记忆,逻辑一多就乱。我一般会让八段 LED 用 FC 配全局 DB,交通灯用 FB 配多重实例,稳定性差一个量级,这一点放到最后一章展开。
3. 八段 LED 实验:编码表、1s 节拍与 MOVE 输出
这块程序总共不到二十个程序段,但每个程序段都有明确的职责,拆开看比整体贴一遍有价值。
3.1 先定共阴共阳,再谈编码表
八段数码管的八个段是 a、b、c、d、e、f、g、dp,从 Q0.0 到 Q0.7 一一对应。共阴管的公共端接地,某一段给高电平就亮;共阳管公共端接 24V,某一段给低电平才亮。实验箱上通常是共阴,如果不是,所有段码取反即可,逻辑一个字都不用改。
数字 0~9 的共阴段码,注意 dp 位藏在最高位,做数字显示时一律为 0:
| 数字 | 点亮段 | 二进制(dp g f e d c b a) | 十六进制 |
|---|---|---|---|
| 0 | a b c d e f | 0011 1111 | 16#3F |
| 1 | b c | 0000 0110 | 16#06 |
| 2 | a b d e g | 0101 1011 | 16#5B |
| 3 | a b c d g | 0100 1111 | 16#4F |
| 4 | b c f g | 0110 0110 | 16#66 |
| 5 | a c d f g | 0110 1101 | 16#6D |
| 6 | a c d e f g | 0111 1101 | 16#7D |
| 7 | a b c | 0000 0111 | 16#07 |
| 8 | a b c d e f g | 0111 1111 | 16#7F |
| 9 | a b c d f g | 0110 1111 | 16#6F |
共阳管把这十个值取反就行,0 变成 16#C0,1 变成 16#F9,依次类推。这张表建议直接做成 DB 里的常量数组,而不是散落在各个 MOVE 指令的 IN 参数里。散着写的后果是:想加一个字母 A~F,得翻遍十几个程序段。数组写法:
// DB 名:LED_Data,类型为全局 DB // 静态变量:SEG_CODE : ARRAY[0..9] of Byte // 初始化时把这十个十六进制值填进去 // 取段码(SCL 写法,梯形图里对应一条 MOVE) #"LED_Data".SEG_CODE[#digit] := 16#3F; // 仅示意索引可以动态给 #segByte := #"LED_Data".SEG_CODE[#digit]; %QB0 := #segByte; // 一次性刷新八个段这里#digit是 0~9 的整数,SEG_CODE是十元素字节数组,索引越界会直接触发区域长度错误,运行时会跳到 OB121,所以后面必须有一句范围限制。%QB0 := #segByte这一句等价于梯形图里的 MOVE 指令盒:IN 接数组元素,OUT 接 QB0,EN 使能就执行。一次写整个字节,比写八条Q0.x := ...干净得多,也不会出现"改了 a 段忘了改 g 段"的半截显示。
3.2 1s 节拍:TON 自复位与计数
资料里写的是"接通延时定时器,PT 设为 1s,1s 后自动接通"。这句话背后有两种实现,差别很大。
第一种是每个数字一个定时器,0 到 1 用一个、1 到 2 用一个,一共九个。这就是很多人复制粘贴出事的地方——粘贴过来的 TON 会复用同一个背景 DB,编译能过,但九个定时器实际上是一个,节拍全乱了。
第二种是我推荐的:一个 TON 自复位,产生连续的 1s 单脉冲,再接一个加法。结构如下:
// 单脉冲发生器:定时器到点 -> 输出一个扫描周期的脉冲 -> 自复位 #iPulse := #tonTimer.Q; // 上一次的到点信号作为本次脉冲 #tonTimer(IN := NOT #tonTimer.Q, // 复位后立刻重新计时 PT := T#1S);这段是 SCL 里调用 IEC 定时器实例的写法,梯形图里对应一个 TON 指令盒,IN 前面串一个常闭触点(自己的 Q),PT 填T#1S,Q 输出到一个 M 点。逻辑是:计时满 1 秒,Q 置位,下一扫描周期 Q 的常闭点断开,IN 变 0,定时器复位,Q 立刻掉,再下一周期重新开始计时。这样每 1 秒产生一个只持续一个扫描周期的脉冲,用它去驱动计数最稳。
// 计数与循环:0~8 循环 IF #iPulse THEN IF #curDigit >= 8 THEN #curDigit := 0; ELSE #curDigit := #curDigit + 1; END_IF; END_IF; // 停止优先:停止按钮断开时清零并复位输出 IF NOT "停止按钮" THEN #curDigit := 0; %QB0 := 16#00; END_IF;#curDigit是静态变量,写在 FB 的 Static 区里最省事。>= 8而不是>= 9,是因为资料的实验要求是 0~8 循环显示,8 后面直接回 0。如果要做 0~9,把常数改成 9 就行,这也是我建议把上限定成一个常量#maxDigit的原因,改需求的时候只动一处。
需要提醒的是脉冲的时序。#iPulse只在一个扫描周期内为真,如果这段计数逻辑被放在 OB1 里一个条件跳转的分支后面,脉冲可能被吃掉。梯形图里同样的问题:脉冲 M 点如果被多个程序段以置位/复位方式改写,最终值取决于程序段顺序。我的习惯是脉冲点只在生成它的那个程序段里写,其他程序段只读。
3.3 启动停止用串联,但顺序有讲究
资料里说"通过启动按钮与停止按钮串联实现控制",在梯形图里就是一个自锁回路。这一点上有一个细节值得说:启动用常开触点、停止用常闭触点,两者串在一条回路上,再并一个自锁支路。
// 启动自锁(梯形图的等价 SCL) #runFlag := ("启动按钮" OR #runFlag) AND "停止按钮";这里"停止按钮"按常闭接线,物理上是常闭点,程序里直接写常开触点(符号名非取反),按下时信号变 0,回路断开。如果你用的是实验箱上的常开按钮,就必须在程序里写成NOT "停止按钮",否则一上电就跑。
注意:仿真时最容易翻车的就是这一步。PLCSIM 里没有真实接线,按钮是手动置位的,常开常闭全靠你在程序里写对,实物上一按就停、仿真里却停不下来,八成是这里差了取反。
自锁标志#runFlag为真时,才允许脉冲推动计数;为假时把#curDigit清零并把%QB0写 0。这个"停止时清零"的动作很多人不做,结果停止后再启动,数码管从上次的数字接着往下跳,看起来就像程序没复位。严格讲这是设计选择,但对课程设计来说,从 0 开始更符合"按下启动,LED 按顺序循环显示"的实验要求。
3.4 HMI 上的八个矩形:动画怎么绑才动得起来
触摸屏这边,资料的做法是"添加矩形图形组合成八段 LED 形状,添加静态文本,并进行动画设计"。具体拆开是三步。
第一步,摆图形。KTP700 分辨率 800×480,把八个矩形拉成标准七段形状:a 段横条在上,b、c 是右上和右下两竖,d 是中间横,e、f 是左下和左上两竖,g 是底部横,dp 是右下角一个小方块。八个矩形各自独立成对象,不要用组合功能合并——合并之后动画只能整体绑一个变量,就没法逐段控制了。
第二步,绑动画。选中一个矩形,属性里的"动画"→"外观",添加一个事件,变量选QB0对应的位,比如 a 段就绑"段码输出".%X0。位访问的写法在 TIA Portal 里是变量名加.0到.7,如果变量表里QB0定义成 Byte,直接在动画的变量选择框里展开就能看到每一位。前景色跟随:值为 1 时设成亮红或亮绿,值为 0 时设成深灰。这比用"可见性"做闪烁效果好,因为段位不消失,只是颜色变暗,看起来才像真正的数码管。
第三步,控制按钮。启动和停止各放一个按钮,事件配"按下时置位变量"和"释放时复位变量",分别指向M10.0这类中间点,再由 PLC 程序把这些中间点和物理 I0.0、I0.1 并联。这样在没接实物按钮的仿真环境里也能操作。资料里还有一条要求是"在触摸屏上输入 6,LED 会输出 6",实现方式是在画面上放一个 IO 域,变量绑一个MW12,模式设为"输入/输出",PLC 程序里加一条:当MW12在 0~9 之间时,直接把它送到#curDigit并停止自动循环。
这一步的坑在于采集周期。HMI 变量默认采集周期可能是 1 秒,八段 LED 每秒换一个数字,视觉上刚好卡在采集边界,会出现"跳过某个数字"的错觉。把QB0的采集周期改成 100ms,问题就消失。另外动画绑的必须是 PLC 变量而不是 HMI 内部变量,如果绑错了,PLC 输出在变、画面纹丝不动。
4. 交通红绿灯实验:状态推进、互锁与手自动切换
从八段 LED 转到交通灯,最大的变化是逻辑从"查表输出"变成"状态机"。前者错了只是显示错,后者错了可能出现南北东西同时放行的逻辑冲突。
4.1 第一段程序永远是全复位
资料里写"启动时先初始化,把各个灯输出复位",这一句必须落到程序的最前面,而且优先级最高。原因很实际:PLC 断电重启后 Q 点状态不确定,上一次运行停在南北绿灯,重新上电如果直接进自动流程,可能出现南北绿灯和东西绿灯同时为 1 的瞬间。
// 程序段 1:全复位,优先级最高 IF NOT "启动按钮" OR NOT "手自动开关" THEN ; // 占位,实际业务在下面判断 END_IF; // 统一的复位入口 #resetAll := (NOT "启动按钮") OR #handAutoSwitching; IF #resetAll THEN %Q0.0 := FALSE; // 南北红 %Q0.1 := FALSE; // 南北黄 %Q0.2 := FALSE; // 南北绿 %Q0.3 := FALSE; // 东西红 %Q0.4 := FALSE; // 东西黄 %Q0.5 := FALSE; // 东西绿 #state := 0; END_IF;#resetAll把"没启动"和"正在切换手自动"两种情况合并成一个复位条件,是这套程序里最省事的一个设计。#state := 0是状态机归零,保证复位后一定从初始状态重新开始。注意这里用的是普通赋值而不是置位复位指令,因为全复位是每个扫描周期都要维持的状态,用S/R反而会在条件消失后留下残余。
梯形图里对应的是六个并联的复位线圈,每个 Q 点一个。如果想省程序段,可以用一条MOVE 0 -> QB0加一条MOVE 0 -> QB1,因为六个灯正好落在 Q0.0~Q0.5,两个字节就能全清。前提是 Q0.6、Q0.7 没被别的东西占用。
4.2 自动模式:四个状态加两个时间条件
自动流程的实质是一条环形状态链:南北绿 → 南北黄 → 东西绿 → 东西黄 → 回到南北绿。每个状态的保持时间由 HMI 上设定的绿灯时间和黄灯时间决定,绿灯时间长、黄灯时间短。
用一个 CASE 结构写,比资料里那种"每个状态一段梯形图 + 各自一个定时器"清晰得多:
CASE #state OF 0: // 初始/停止 IF #runFlag AND NOT "手自动开关" THEN #state := 1; #stepTimer(IN := FALSE, PT := T#1S); // 复位定时器 END_IF; 1: // 南北绿,东西红 %Q0.2 := TRUE; %Q0.3 := TRUE; %Q0.0 := FALSE; %Q0.1 := FALSE; %Q0.4 := FALSE; %Q0.5 := FALSE; #stepTimer(IN := TRUE, PT := #greenTime); IF #stepTimer.Q THEN #stepTimer(IN := FALSE, PT := #greenTime); #state := 2; END_IF; 2: // 南北黄,东西红 %Q0.2 := FALSE; %Q0.1 := TRUE; #stepTimer(IN := TRUE, PT := #yellowTime); IF #stepTimer.Q THEN #stepTimer(IN := FALSE, PT := #yellowTime); #state := 3; END_IF; 3: // 东西绿,南北红 %Q0.1 := FALSE; %Q0.3 := FALSE; %Q0.2 := FALSE; %Q0.0 := TRUE; %Q0.5 := TRUE; %Q0.3 := TRUE; #stepTimer(IN := TRUE, PT := #greenTime); IF #stepTimer.Q THEN #stepTimer(IN := FALSE, PT := #greenTime); #state := 4; END_IF; 4: // 东西黄,南北红 %Q0.5 := FALSE; %Q0.4 := TRUE; #stepTimer(IN := TRUE, PT := #yellowTime); IF #stepTimer.Q THEN #stepTimer(IN := FALSE, PT := #yellowTime); #state := 1; END_IF; END_CASE;#greenTime和#yellowTime都是Time类型,从 DB 里读,DB 里的值由 HMI 的 IO 域写进来。整个状态机只用一个定时器实例#stepTimer,每次状态切换时先把IN置 FALSE 复位,下一状态再重新置 TRUE。这是复用单个 TON 的标准做法,好处是不会出现"两个状态各自的定时器同时在跑"的问题。
每个状态里的输出赋值我写成逐位置位复位而不是一次性 MOVE,是因为状态 1 和状态 3 的灯组合不同,硬编码更直观。如果追求干净,可以给每个状态准备一个输出字节常量,一个 MOVE 完事,和八段 LED 的段码表思路一致:
// 状态 1 的输出字节:Q0.0~Q0.5 = 0 0 1 0 0 1 // 南北绿(Q0.2) + 东西红(Q0.3) = 2#00001100 = 16#0C %QB0 := 16#0C;状态 2 南北黄加东西红是16#0A,状态 3 南北红加东西绿是16#21,状态 4 南北红加东西黄是16#11。四个常量摆出来,整个路口的状态一眼看到底,比六条赋值语句好审。
4.3 手动模式与切换瞬间的那一下复位
手动逻辑很短:I0.1为高电平时进手动,I0.2为高电平就南北绿加东西红,I0.3为高电平就东西绿加南北红。真正的难点在切换的那一瞬间——从自动切到手动,上一个自动状态可能停在"南北黄",你按下"东西通行",如果不先复位,南北黄灯还亮着,东西绿灯一起亮,就成了黄绿同亮。
做法是把切换动作本身当成一个事件。用一个边沿检测抓I0.1的跳变,跳变时置一个只维持一个扫描周期的复位标志:
// 手自动切换检测:上升沿和下降沿都触发一次全复位 #rTrig(CLK := "手自动开关"); #fTrig(CLK := "手自动开关"); #handAutoSwitching := #rTrig.Q OR #fTrig.Q;#rTrig和#fTrig是两个R_TRIG/F_TRIG实例,Q 只在跳变的那一个扫描周期为真。#handAutoSwitching拿去喂 4.1 里的#resetAll,就能保证切换瞬间所有灯灭一下再按新模式点亮。视觉上会闪一下,但这恰恰是正确行为——真实路口的模式切换也要清空当前相位。
手动模式下还有一个互锁要补。如果I0.2和I0.3同时为高(有人手贱两个都按下去了),按顺序执行的话后面的会覆盖前面的,最后是东西绿。更稳妥的做法是加一个优先判断:
IF "南北通行" AND NOT "东西通行" THEN %QB0 := 16#0C; // 南北绿 + 东西红 ELSIF "东西通行" AND NOT "南北通行" THEN %QB0 := 16#21; // 东西绿 + 南北红 ELSE %QB0 := 16#00; // 都不按或都按,全灭 END_IF;这段的判断顺序不重要,重要的是AND NOT这个互斥条件。两个都按时全灭,比随便选一个方向通行安全。
4.4 HMI 画面:指示灯颜色动画与 IO 域的时间设定
交通灯的 HMI 比八段 LED 简单,元素少,但变量关联更容易出错。画面上要放的东西是:六个圆形指示灯(或用矩形旋转成横竖条)、一个启动按钮、一个手自动切换开关、两个手动通行按钮、两个 IO 域(绿灯时间、黄灯时间)。
指示灯的动画配置和八段 LED 一样,选"动画 → 外观",变量绑对应的 Q 位,1 时前景色为灯色,0 时为暗灰。这里有个细节:绿灯和黄灯的颜色建议用同一个圆形对象做,靠动画切换颜色,而不是放两个圆叠在一起控制可见性。叠放的做法在仿真里看不出来,实际屏上会因为刷新不同步出现重影。
IO 域的配置要点是模式。资料里写"选择输入输出模式和显示格式",这个模式选错是高频问题:设成"输出"模式时,操作员在屏上点了没反应,值永远是 PLC 写下来的;设成"输入"模式时,屏上的值能改,但 PLC 那边的变化不回显。必须是"输入/输出",并且加上限幅:
绿灯时间 IO 域: 变量:交通灯_DB.HMI_GreenTime(Time 类型) 模式:输入/输出 格式:十进制,单位 s 限值:最小值 5s,最大值 60s限幅这一步不能省。仿真时手动输入一个 0,定时器 PT 设成T#0S会立即到点,状态机在同一个扫描周期内连跳四步,看起来像"程序跑飞了"。加上限值后,屏上根本输不进去。
5. 避坑与排查:五个真实翻过车的地方
这部分是我拆这套资料时踩过、也在别人代码里见过的问题,按现象、原因、解决三段写。
现象一:复制粘贴后,所有程序段的灯以同一个节奏闪。原因是从第一个程序段复制梯形图到后面几段时,TON 指令盒的背景 DB 被一起复制,九个定时器实际上是同一个实例。S7-1200 的定时器不是编号资源,而是背景数据块,同一个 DB 被多处调用时后写的参数会覆盖前面的,表现为所有节拍都跟着最后一个 PT 走。解决方式是删除所有复制来的定时器,从右侧指令树重新拖入,每个 TON 单独建背景 DB;或者干脆改成"单个定时器自复位 + 计数"的写法,从根上消除重号的可能。
现象二:自动模式下南北绿灯和东西绿灯同时亮。原因不是逻辑写错,是地址表抄错。资料原文描述自动流程时,前后出现过"南北红灯 Q0.0"和"南北方向绿灯 Q0.5"这样的表述,和前面定义的 Q0.2 南北绿、Q0.5 东西绿对不上。照着文字敲,南北绿灯被挂到了东西绿灯的地址上,两个方向自然一起亮。解决办法是自己先画一张地址分配表,把六个 Q 点和三个颜色两个方向的矩阵填满,写程序时只查表不看文字。这个习惯在真实项目里同样管用,因为现场接线和图纸不一致是常态。
现象三:HMI 上指示灯颜色纹丝不动,PLC 那边 Q 点亮得好好的。三种可能,按概率排:动画绑的是 HMI 内部变量而不是 PLC 变量;变量绑对了但采集周期是 1 秒,肉眼看不出变化;动画的触发条件写成了"等于 1"但变量类型是 Bool,比较永远不成立。排查顺序是先在 HMI 上放一个最朴素的 IO 域直接显示QB0的值,能显示说明通信通,问题在动画配置;不能显示说明连接或变量地址有问题。这个"先放个 IO 域看看"的动作,我每次调屏都做。
现象四:PLCSIM 里 PLC 跑得好好的,HMI 仿真连不上。原因是 V13 的 HMI 仿真需要通过 PLCSIM 的虚拟网卡,而 HMI 连接参数里的伙伴地址还写着 192.168.0.200。PLCSIM 默认只监听本机回环,不响应外部网段的连接。解决方式是把 HMI 连接改成指向 PLCSIM 实例,或者在 PLCSIM 的设置里确认虚拟网卡已经启用,并保证两边 IP 在同一网段。如果时间紧,直接用 HMI 的内部变量做画面演示、PLC 单独用监控表验证逻辑,两边分开调通再合并,比死磕联调快。
现象五:屏上输入 6,数码管还是照着自己循环。原因是自动循环和手动输入两套逻辑同时在写#curDigit,后执行的那个覆盖前面的。资料里的要求是"在触摸屏上输入 6,LED 会输出 6",隐含条件是输入优先。做法是加一个模式位:检测到 HMI 输入值变化(用!=比较上一周期的值),就置位"手动输入模式",此模式下自动计数被IF挡掉,只有停止按钮能把它清回自动。两套逻辑写同一个变量时,一定要有一个明确的优先级,靠程序段顺序决定优先级是最不可靠的做法。
6. 二次开发:把段码表和状态机抽成能复用的模板
这两块程序如果只是交作业,写成什么样都行。但如果想留在自己手里当模板用,我建议做三件事,都是我在后面几个项目里反复用到的改法。
第一件,把八段 LED 封装成 FB。建一个 FB 叫LED_Display,输入参数是Digit : Int、Enable : Bool、CommonAnode : Bool,输出参数是SegByte : Byte,Static 区放段码数组ARRAY[0..15] of Byte(顺便把 A~F 也填上)。内部逻辑就三行:范围限制、查表、共阳时取反。调用时%QB0 := #ledDisplay.SegByte,一行搞定一个数码管。要做三个数码管显示学号,就建三个多重实例,各自喂不同的Digit,再配三个输出字节,原来的"每个数码管一个函数块"就变成了同一份代码的三次调用,改编码表只改一处。
第二件,把交通灯做成带参数的状态机 FB。现在这套程序把绿灯时间、黄灯时间写死在 DB 里,路口一变就得改程序。改成 FB 之后,把GreenTime、YellowTime、AllRedTime(全红清空时间)、DirectionCount(两相位还是四相位)做成输入参数,状态编号用CASE配合数组索引。四相位路口的左转相位,就是往状态数组里多插两个状态,逻辑不动。这个改法的验证方法很直接:把GreenTime设成 3 秒、YellowTime设成 1 秒,跑十分钟,看状态是否严格按 1→2→3→4→1 循环,任何一个状态停留时间偏离就是定时器复位没做干净。
第三件,把两个实验的 HMI 画面拆成可复用的面板块。八段 LED 的八个矩形做成一个 Faceplate,接口变量是一个 Byte,拖到画面上绑不同的输出字节就是不同的数码管;交通灯的六灯组合同样做成 Faceplate,接口是一个 Byte。这样以后做课程设计以外的东西,比如一个简单的流水线状态监控,直接拖块改绑定就行,不用再画一遍矩形。
下面这张表是我自己用的改造优先级,按投入产出排:
| 改造项 | 工作量 | 收益 | 建议时机 |
|---|---|---|---|
| 段码表进数组 | 半小时 | 加字符、换共阳只改一处 | 立刻做 |
| LED 封装成 FB | 1 小时 | 多位数码管零成本复制 | 第二次用到时做 |
| 交通灯参数化 | 2 小时 | 换路口只改参数 | 要做第二个路口时做 |
| HMI 面板块 | 1 小时 | 画面复用,绑定不出错 | 画面元素超过 10 个时做 |
改造完一定要回归验证,我的做法是保留一份原始程序做对照:同一组输入序列分别灌进原始版和改造版,用监控表逐周期比对六个 Q 点的状态,连续跑满两个完整循环没有偏差才算通过。这个比对动作看着笨,但它抓到过我自己改参数化时漏掉的全红时间——改造版在黄灯转红灯的瞬间,两个方向各有 100ms 的全暗,肉眼完全看不出来,只有逐周期比对才暴露。
从那以后我每次动别人的 PLC 程序,都强制先导出一份原始变量表和历史状态序列做基线,改完再跑一遍比对,不管改动看起来多小。这套 PLC 实验资料的价值不在于程序本身多复杂,而在于它把"变量规划、定时器实例、HMI 绑定、模式切换"这几个以后会反复遇到的问题,压缩在两个能一下午跑通的小题目里。希望帮到你。
本文还有配套的精品资源,点击获取