流水灯这题目,乍看像是单片机课的入门作业,但放到PLC里做,味道完全不一样。最近好几份《可编程控制器原理与应用》的综合实验报告都选了“基于S7-1200的PLC流水灯控制系统设计与仿真”这个题目。我接手这类问题时发现,它几乎把S7-1200最核心的几个能力全串起来了:位逻辑、定时器、数据传送、移位运算,再加上一套从I/O分配、硬件接线到仿真调试的完整工程流程。对自动化、机电类专业的学生来说,这是一道性价比极高的综合训练题;对刚转行做电气控制的新手,也是一条非常顺滑的上手路径。
这篇文章我不打算按教科书的方式讲,而是按实际做项目的顺序来写:先拆思路,再定硬件,然后写程序,最后仿真验证,把所有容易踩的坑提前标出来。你只要能跟着走完一遍,哪怕手里没有真实PLC,也能在软件环境里把一套完整流程跑通。
1. 项目整体思路拆解
1.1 这道综合实验题背后的真正考点
很多人以为流水灯的目标就是“让灯轮流亮”,于是上手就在网上抄一段梯形图。真这么做,这个实验就白做了。综合实验之所以叫“综合”,是因为它考的不是“会不会给某个输出点赋值”,而是下面这几层东西:
第一层是PLC的扫描执行方式。PLC不是真的“让灯自动往左走”,而是每个扫描周期根据程序算出输出状态,再刷新到输出点。流水效果靠的是“时间 + 状态记忆”,你得理解程序是一遍一遍重复执行的,而不是只有一次。这个认知不到位,后面所有程序都容易写成单片机那种顺序逻辑。
第二层是定时器的正确使用。S7-1200用的是IEC定时器,每一个TON/TOF实例都要配一个背景数据块,PT的时间单位是毫秒,且定时器不是在梯形图里“填个编号”就完事。这是和S7-200时代最大的区别,很多第一次接触S7-1200的人就倒在“我怎么找不到T37”这一步。
第三层是位和字节的关系。Q0.0到Q0.7这8个输出点,从数据层面看就是输出字节QB0。你完全可以通过传送、移位指令一次性改变整个字节的值,LED就会按二进制位亮灭。从“按位操作”跳到“按字节操作”,是一个很重要的思维转折点。
第四层才是仿真验证。TIA Portal自带的S7-PLCSIM可以模拟一台虚拟的S7-1200,不需要真实硬件就能看到程序运行结果。但它的操作逻辑和实物调试有差异,比如强制输入、监控变量、切换RUN/STOP这些步骤,若没搞清楚,程序写得再好也看不到现象。
1.2 三种主流实现方案怎么选
流水灯的控制逻辑,常见有三种做法。选哪一种,直接决定程序的结构和可扩展性。
方案一是定时器接力。用TON把多个定时器串起来,第一个定时器到点后触发第二个,第二个到点后触发第三个,以此类推。优点是逻辑直观,适合4路以内的简单演示;缺点是路数一多,梯形图会变得很冗余,而且每个定时器的复位逻辑要处理仔细,否则会出现灯“亮不灭”的情况。
方案二是移位指令。先用TON产生一个固定周期的脉冲,然后用ROL_B/ROR_B对存放灯态的一个字节做循环移位,每次脉冲到点就移一位,再把值送到输出字节。这个方案程序非常简短,8路流水灯只需要一段移位逻辑,方向也可以随时参数化,是最适合作为综合实验主方案的实现方式。
方案三是定时中断,也就是OB30周期性中断组织块。S7-1200支持按固定时间间隔调用中断程序,在里面用一个变量累加并输出,实现类似“扫描周期与中断周期分离”的高精度时序。这个方案定时最准,但结构相对复杂,适合做功能扩展,不太适合做成第一版代码。
| 方案 | 核心指令 | 适用路数 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|---|
| 定时器接力 | TON + 位逻辑 | 4~6路 | 思路直观,梯形图好理解 | 程序冗余,复位逻辑复杂 | 入门演示、理解时序 |
| 移位指令 | ROL_B / ROR_B | 8~16路 | 程序简短,方向可切换 | 需要理解字节位关系 | 综合实验主推方案 |
| 定时中断 | OB30 + SCL | 8~64路 | 时间精度高,扩展性好 | 结构复杂,不适合新手首发 | 调速、精准时序场合 |
我在实际做这个实验时,给初学者的建议是先拿方案一理解时序,再改用方案二作为交付版本。下面程序部分也按这个顺序讲。
2. 硬件方案与I/O分配设计
2.1 选型先把输出点数量定清楚
S7-1200的CPU型号有很多,综合实验里最常用的是CPU 1214C DC/DC/DC。第一个DC表示外部供电是24V直流,第二个表示输入支持直流信号,第三个表示集成输出是晶体管型。晶体管输出的优点是响应快,可以直接驱动小功率LED负载,逻辑电平是24V,控制指示类负载非常合适。
如果只是做四路流水灯,用更小的CPU 1212C也完全够,本体有6路数字量输出。但很多实验设计要求做到“八路流水”,此时1214C本体只有4路数字量输出,不够用,就需要扩展一块数字量输出模块SM1222 DQ8,这样就能提供额外8路输出。注意,如果你的实验报告里写了8个输出点,硬件配置里必须体现这块扩展模块,否则评委一眼就看出程序“跑不到真实硬件上”。
选型时还有一个容易忽略的坑:不要买成AC/DC/RLY的CPU。RLY表示继电器输出,它不是不能用,而是响应速度慢,频繁通断LED时触点寿命也不如晶体管,一般建议实验里优先选DC/DC/DC。
2.2 I/O地址分配表与接线细节
这个实验的输入输出点数不多,但分配表一定要工整。我常用的分配如下:
| 符号名 | 地址 | 类型 | 注释 |
|---|---|---|---|
| 启动按钮 | I0.0 | 输入 | 常开触点,按下启动 |
| 停止按钮 | I0.1 | 输入 | 常闭触点,按下停止 |
| 快速选择 | I0.2 | 输入 | 拨到ON时为快速模式 |
| 慢速选择 | I0.3 | 输入 | 拨到ON时为慢速模式 |
| 左向选择 | I0.4 | 输入 | 拨到ON时向左循环 |
| 右向选择 | I0.5 | 输入 | 拨到ON时向右循环 |
| 灯组L1 | Q0.0 | 输出 | 第1只LED |
| 灯组L2 | Q0.1 | 输出 | 第2只LED |
| 灯组L3 | Q0.2 | 输出 | 第3只LED |
| 灯组L4 | Q0.3 | 输出 | 第4只LED |
| 灯组L5 | Q4.0 | 输出 | 扩展模块输出 |
| 灯组L6 | Q4.1 | 输出 | 扩展模块输出 |
| 灯组L7 | Q4.2 | 输出 | 扩展模块输出 |
| 灯组L8 | Q4.3 | 输出 | 扩展模块输出 |
接线方面,LED灯一般要串限流电阻。晶体管输出为24V高电平,LED正向压降大约2V,工作电流取10到15mA比较安全,串一个1.5kΩ到2kΩ的电阻即可。计算很简单:R=(24-2)/0.012≈1.8kΩ,取标准值1.5kΩ或2kΩ都没问题。如果把8个LED的阴极并在一起接0V,阳极分别接到对应输出点,这样点亮逻辑就是“输出点为高电平时灯亮”。这种共阴接法最直观,也最不容易接错。
输入按钮的接法,推荐高电平有效:每个按钮一端接24V+,另一端接到对应输入点,输入公共端接0V。这样按下按钮,输入点得到高电平,程序里的常开触点就闭合。停止按钮要注意,外部接常开触点,程序里用常闭触点。这是安全习惯,如果你接反了,停止功能会变得很不可靠。
2.3 从四路到八路的扩展思路
四路和八路在程序上只差一个字节的宽度,但这里有硬件层面的限制。仿真环境S7-PLCSIM不会严格检查本体到底有几路输出,你给Q0.0到Q0.7连续定义8个点也能模拟。但真实1214C DC/DC/DC只有Q0.0到Q0.3四路集成输出,所以八路方案里Q4.0到Q4.3必须来自扩展模块SM1222。
扩展模块的地址是自动分配的,通常跟在CPU集成IO后面。仿真时PLCSIM也能模拟这个“扩展模块”,所以程序里直接用QB4作为扩展输出字节就行。最简单的方法是:程序里把灯态字节传送给QB4而不是QB0,这样8路输出就全在扩展模块上。
如果你手里只有本体四路输出,也可以把输出改成QB0的低四位,然后把移位初始值设为16#01,屏蔽高四位,程序仍然成立。这两种方式我都验证过,区别很小,关键是想清楚你的目标硬件是什么,地址不要张冠李戴。
3. 控制程序实现:从能转到会转
3.1 搭建运行框架:启动、停止、初始化
我习惯先把程序骨架搭出来,再往里填功能。OB1主程序只干一件事:调用FC1控制函数。FC1内部再分四段:运行标志、速度选择、移位脉冲、输出刷新。
运行标志的经典写法是“启动自锁 + 停止断开”。启动按钮I0.0的常开触点并联M0.0的常开触点,再串联停止按钮I0.1的常闭触点,最终驱动M0.0线圈。这样按下I0.0后M0.0自保持,按下I0.1后M0.0复位,整个流水逻辑都挂在M0.0这个总开关上。
初始化放在启动组织块OB100里。S7-1200在CPU启动时会执行OB100一次,非常适合做状态清零。我会在里面把灯态字节MB2传成16#01,也就是二进制2#00000001,同时把QB0清零。这个动作很关键,否则上次停机时遗留的灯态会影响下一次启动。
3.2 定时器接力法:四路顺序点亮的经典时序
定时器接力法的核心逻辑是:第一个定时器到点后,把控制权交给第二个定时器,第二个到点后交给第三个,依次往下。以四路为例,T1的定时时间一到,输出M0.2;M0.2又作为T2的启动条件;T2到点输出M0.3;T3到点输出M0.4;T4到点输出M0.5。
听起来简单,实际容易翻车。因为TON的Q在输入为真时会一直保持为真,不会自动断开。如果T2到点后T1还在计时保持,M0.2就一直为1,第一盏灯永远不灭。很多同学的梯形图就是在这里卡住的。
解决方式一:给前一个定时器加复位输入。比如T2的Q出来后,同时把T1的R输入置为1,让T1立刻复位。这种方式逻辑准确,但梯形图要额外增加一段网络。解决方式二:直接把定时器接力的每个“到点时刻”当成一个脉冲来用,在TON的Q上升沿时传送对应常数值到输出字节,而不是靠Q持续状态点亮。后者更接近现场做法。
我的建议是,定时器接力方案用来“看懂顺序逻辑”就够了,真正交付时直接用下面的移位指令方案,程序和调试都要清爽得多。
3.3 移位指令实现八路循环流水(主推方案)
这个方案把问题从“8个独立位”变成了“1个字节的移动”,程序量会大幅下降。
第一步,初始化。OB100里把MB2初始值设为16#01,也就是最低位为1。后续每一次循环移位,这个“1”就在字节里向左或向右移动,对应的灯就会依次点亮。
第二步,产生移位脉冲。用TON定时器,IN接运行标志M0.0,PT接速度时间。定时器每到达一次设定时间,它的Q会从0翻转为1。这个Q的上升沿就是“该动下一颗灯了”的信号。
第三步,循环移位。在LAD里用上升沿检测指令检测定时器Q的上升沿,然后执行ROR_B或ROL_B指令。ROR_B是循环右移,ROL_B是循环左移,方向由I0.4和I0.5选择。这里必须强调“循环”两个字:当字节里的1从Q0.7继续右移时,它会回到Q0.0,形成闭环。如果误用SHL_B或SHR_B,最高位就会被直接丢出去,灯流到边缘就全灭了。
第四步,输出刷新。把移位后的MB2传送到QB0或QB4。这个过程每个扫描周期都执行一次,但MB2只在移位脉冲到达时变化,所以输出不会乱跳。
核心梯形图逻辑可以参考下面的SCL伪码:
// 移位脉冲上升沿触发 IF #pulse_rising THEN IF "I0.4" THEN // 左向 #shift_value := ROL_B(IN := #shift_value, N := 1); ELSIF "I0.5" THEN // 右向 #shift_value := ROR_B(IN := #shift_value, N := 1); END_IF; END_IF; "QB0" := #shift_value;这段代码里,pulse_rising是定时器输出位经过上升沿检测后的单周期脉冲,shift_value是内部中间变量,对应MB2。
3.4 速度选择和方向选择如何参数化
速度不能靠改程序实现,应该做成外部开关随时可调。我常用的办法是:用三个MOVE指令,把不同的时间常量写到同一个定时时间变量里。
- I0.2快速选择为ON,传送T#300ms到DB1.SpeedTime。
- I0.3慢速选择为ON,传送T#800ms到DB1.SpeedTime。
- I0.2和I0.3都OFF,传送T#500ms到DB1.SpeedTime。
原理很简单:这三个MOVE从上到下依次执行,后执行的会覆盖先执行的。所以最终定时器读取PT时,拿到的就是最后一个满足条件的MOVE写入的时间值。如果两个速度开关同时ON,按程序扫描顺序,后写的慢速会覆盖快速,相当于慢速优先。我在设计说明里会写清楚这个特性,避免别人误以为硬件连接有误。
方向选择可以做得更灵活。I0.4为左向,I0.5为右向,两者都OFF时默认右向。方向开关直接决定执行ROL_B还是ROR_B。如果以后想加第三种模式,比如“左右来回”,可以再加一个状态位控制方向翻转,程序结构不需要大改。
4. 仿真验证流程:从代码到虚拟设备
4.1 工程配置:变量表、系统存储器、编译下载
仿真不是把程序写完点一下就完事,前面的工程配置要严谨。
打开TIA Portal新建项目,添加设备时选择S7-1200系列的具体CPU型号,比如CPU 1214C DC/DC/DC。添加后进入设备组态,在CPU属性里可以找到“系统与时钟存储器”选项卡,建议启用系统存储器字节和时钟存储器字节。系统存储器字节我一般设为100,时钟存储器设为101,这样MB100和MB101不会和程序的M0.0、MB2混用。
下一步是定义PLC变量表。把前面I/O分配表里的符号名全部输进去,地址一一对应。这一步很多人嫌麻烦直接跳过,直接用绝对地址“Q0.0”“I0.0”写程序。能跑,但后期调试会让你想骂人,因为一旦要改地址,满程序翻找绝对地址的滋味太难受了。花十分钟做好符号表,后面监控和修改都轻松。
全部写完编译,确保没有红色报错。
4.2 用PLCSIM强制输入并观察流水效果
编译通过后,点击“仿真”图标启动S7-PLCSIM。第一次启动会弹出选择接口的对话框,这时候必须选择PLCSIM,而不是电脑的真实网卡。如果你的TIA Portal版本和PLCSIM版本不一致,这一步会直接报错,所以安装软件时尽量保持版本统一。
点击下载,把项目下载到虚拟PLC。下载完成后PLCSIM窗口里会显示CPU状态。如果之前一直是STOP,需要手动点击RUN按钮,程序才开始扫描。这是新手常忽略的一步:程序下载了,但CPU没运行,灯当然不会动。
接下来是最关键的操作:在PLCSIM的IO选项卡里强制I0.0为TRUE。注意,这里不要在TIA的监控表里“修改”I0.0,因为监控表修改的是变量值,不一定能真正作用到输入映像区。PLCSIM的IO视图才是“模拟外部硬件信号”的正确入口。强制I0.0为TRUE后,你会看到M0.0变为TRUE,然后QB0的值按设定周期从16#01变成16#02、16#04、16#08……依次变化,对应的输出位会轮流点亮。
4.3 仿真与实物调试的几点差异
仿真毕竟是仿真,和实物调试有几个差异必须心里有数。
S7-PLCSIM不会检查你的CPU本体到底有几个物理输出点,你定义Q0.0到Q0.7都能模拟。但真实CPU上Q0.4到Q0.7不存在,所以前面说的硬件选型仍然要按实际情况来。
PLCSIM对模拟量、高速计数等功能的仿真能力有限,但数字量逻辑控制这类基础应用完全可以覆盖。流水灯实验用到的定时器、位逻辑、传送指令,仿真结果与实物基本一致,可信度很高。
PLCSIM可以配合TIA Portal里的HMI仿真做画面联调,如果你顺便做了一套触摸屏画面,也能直接看到虚拟按钮和虚拟LED的状态。这属于加分动作,不是实验必要项,但做出来很出效果。
5. 常见问题与排查技巧实录
5.1 输入强制无效、状态不刷新
这是仿真时最容易碰到的现象:在监控表里把I0.0改成TRUE,但程序里的启动标志并不动作。原因多半是强制的位置不对。监控表里的“修改”只能改程序运行时访问的变量,不能真正模拟外部输入信号。输入信号的变化必须通过PLCSIM的IO选项卡来强制,强制后你会看到输入变量前面出现一个特殊的标记,这时程序才会响应。
还有一种可能是你的启动自锁逻辑本身写错了,比如I0.1用的是常开触点而不是常闭触点,导致M0.0根本没法得电。排查时先看M0.0状态,如果是FALSE,再反推是哪一段把能流断掉了。
5.2 流水不走、只跑一次或全亮全灭
流水只走一次就全灭,十有八九是用了SHL_B/SHR_B而不是ROL_B/ROR_B。普通左移会把最高位直接移出字节,本来该循环回来的“1”消失了,字节变成0,灯自然全灭。换成循环移位后,位会从另一端补回来,效果就对了。
灯态快速闪完甚至全亮,通常是移位指令的使能条件接错了,每个扫描周期都触发移位。排查时把监控表打开,看MB2是不是在一个扫描周期内从1变成128。如果是,说明确实没有用脉冲上升沿限制,移位执行得太频繁了。把触发条件改成定时器Q的上升沿输出即可。
5.3 定时器时间不对、指令不执行
S7-1200的定时器是IEC定时器,不是S7-200时代的T37、T38。TON拉出来就是一个带背景数据块的功能块,背景DB不能共用,否则多个定时器会互相干扰。PT的数据类型是TIME,如果填的是整数500,指令直接报错或者运行行为异常,必须写成T#500ms这样的时间常量。
还有一个常见问题是定时器的IN输入前面没有能流,比如运行标志M0.0本来该接到TON的IN,结果中间漏了一条线或引错了触点,定时器永远不启动。排查时在PLCSIM中看定时器实例的Q和ET值,如果ET一直是0,说明IN端没有真正得电。
5.4 仿真器连接下载失败的几个经典原因
PLCSIM下载失败,最常见的三类原因:
一是PG/PC接口没选对。在线访问的接口必须明确指定为PLCSIM,而不是PN/IE、真实网卡或其他驱动。检查方法:右键项目里的PLC,选择“在线”,在弹出的接口设置里确认。
二是CPU状态处于STOP或断电状态。PLCSIM窗口里如果显示STOP,下载后记得点RUN。如果显示断电,点一下“电源”按钮恢复。
三是PLCSIM版本和TIA版本不匹配。这是安装环境问题,没法在软件里硬调,建议重新安装统一版本。社区里有一张版本匹配表,安装前可以先查一下。
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 输入强制无效 | 在监控表强制,而非PLCSIM的IO视图 | 在PLCSIM的IO选项卡强制 |
| 灯全亮或瞬间全灭 | 移位指令没有用脉冲触发 | TON输出加上升沿检测后再移位 |
| 流水只走一次 | 用了SHL/SHR | 换ROL_B/ROR_B |
| 定时器不动作 | PT类型错误或背景DB冲突 | 填T#500ms,每个定时器独立DB |
| 下载失败 | 接口没选PLCSIM | PG/PC接口选PLCSIM |
| CPU不运行 | 下载后仍是STOP | 点RUN按钮或启动组态为暖启动 |
6. 实验复盘与个人建议
这个实验做完后,我自己最大的体会是:流水灯本身并不值钱,值钱的是整套调试习惯。先搭运行框架,再加单点功能,最后参数化,整个过程就是工程现场的缩略版。哪怕以后你去调试伺服回零、步进电机点动、多工位装配线,思路和这个实验完全一致。
如果你是在做课程设计,我建议额外把“手动模式”加进去。比如I0.6为ON时,通过I0.7按钮手动步进一次,灯只走一位;I0.6为OFF时自动循环。这样一个简单的改动,就能让报告里的功能描述立刻多一个层次,评委也会觉得你不是只会抄程序。
最后分享一个小技巧:调试时不要只盯着面板上的LED,一定要用监控表以二进制形式观察QB0和MB2。有时候灯没亮不是逻辑错,而是你盯着“结果”看不出来,看“过程数据”一眼就知道问题出在哪一环。这个习惯,越早养成越有用。