
前阵子有人问我手里那台西门子S7-200PLC还能做什么有意思的练手项目。我直接推荐去做自动售货机五种货物那种。别觉得售货机是单片机的地盘放在教学、实训或者小微型自动售货机改造场景里S7-200这套方案一点不过时。做完这个项目你对输入输出接线、定时器计数器、状态互锁、梯形图模块化这套东西会有比刷十遍例题都深的理解。这篇文章没有打算把完整程序一页一页贴出来而是把我从硬件选型、I/O规划、四段核心逻辑写到现场调试的完整链路走一遍重点讲清楚为什么这样做以及哪些地方最容易翻车。1. 为什么一个停产多年的PLC还能把这台售货机盘活1.1 项目从哪里来很多人听到“自动售货机”第一反应是单片机或嵌入式板卡。确实市面上量产的售货机为了压缩成本普遍用专用控制板甚至工控机加安卓系统。但这不意味着PLC方案没有价值。在自动化教学、职业竞赛、设备改造演示、小型创业型售货机验证这些场景里PLC有一套不可替代的优势逻辑清晰、抗干扰强、学员容易上手而且S7-200在实训室里实在太常见了。我做的这一版目标很明确一个支持五种货物的自动售货机控制项目。面板上有五个选货按钮分别对应五个货道投币器负责接收硬币每个货道有一个电机或推杆用来把货物推出来每路还配了缺货检测。控制要求也不复杂投币后显示余额按下选货按钮如果钱够且货道有货就启动出货机构出完货自动找零如果钱不够提示继续投币如果货道空了选货按钮无效且对应的指示灯会亮。这个需求放到现在的S7-1200或者Smart 200上做起来更舒服但为什么我用S7-200因为很多实验室和二手市场还在大量流通这个型号存量设备非常多。你去看不少学校的PLC实训台用的还是CPU224XP或者CPU226。与其纠结“这型号是不是太老了”不如把它当做一个成熟稳定的工具把控制逻辑吃透。1.2 S7-200的资源能不能扛住自动售货机先盘一下控制需求到底有多少输入输出。五种货物五个选货按钮是5个输入投币器给出脉冲信号算1个输入每个货道一个缺货检测又是5个输入出货机构还需要一个通用的出货完成信号用来确认货物掉没掉出来算是1个输入。加一起一共12个输入。输出这边五个货道各一个电机或推杆驱动信号就是5个输出找零或者退币机构还要1个输出再加一个蜂鸣器或者报警灯总共7个输出。这样一算S7-200家族的CPU224XP就很合适14个数字量输入、10个数字量输出还自带2路模拟量输入以后想加温度或者重量传感器都方便。CPU221虽然便宜但只有6个输入4个输出明显不够用。CPU226则是16输入16输出也绰绰有余只是价格高一些。还有一个容易被忽略的点CPU224XP自带两个通讯口一个用于下载程序和调试另一个可以留给触摸屏或组态软件。做这种带监控需求的小项目通讯口数量比点数还关键。1.3 五货道售货机到底要控哪些对象我把这个售货机拆成几个控制对象来理解。第一是投币器本质是一个脉冲信号源。每投一枚硬币输出端会产生一个短暂的脉冲PLC要做的就是准确捕捉这个脉冲并累加金额。第二是货道驱动机构有电机推杆式也有螺旋弹簧式。螺旋弹簧那种电机转一圈就掉一罐饮料推杆式则靠行程控制两种方式在PLC程序里差别不大就是输出一个动作信号并持续一定时间。第三是缺货检测我习惯用反射式光电传感器装在货道最前端有货时传感器输出一种状态没货时输出另一种状态。第四是出货完成检测通常是一个红外对射或微动开关安装在出货口货物掉落时会短暂触发。这些对象串联起来就是一个完整的闭环收钱、判断、出货、反馈、找零。PLC的价值就在中间那部分“判断”上你要把各种边界情况都想清楚程序才算是真正能用。2. 硬件搭建从按钮到货道电机的I/O规划2.1 五组货物与价格表设计五种货物的价格不能乱设因为涉及找零逻辑。我这边设计成1号货道卖可乐3元2号货道卖雪碧3元3号货道卖矿泉水2元4号货道卖薯片5元5号货道卖饼干4元。为了程序方便所有金额都以“角”为单位可乐就是30角矿泉水就是20角薯片就是50角。为什么用角而不是用元因为PLC的整数运算最直接你不想在程序里处理浮点数。如果用浮点去算0.5元找零麻烦不说一旦触及浮点比较还会出现精度问题。用角之后所有比较和减法都是整数运算速度飞快逻辑也直观。比如 VW0 存余额比较 VW0 30就是判断能不能买可乐。价格表并不需要做成真正的数据块直接在比较指令里写死就行。因为只有五个货道逻辑量不大。如果你想做二十种货物的通用售货机那就要考虑把价格放到V区或者数据块里做成索引寻址的查表结构。我这个项目从简但会在程序结构上预留升级空间。2.2 I/O分配表我最终的I/O分配如下这个表看起来简单但接线和写程序都要对着它来输入点功能说明I0.0投币脉冲硬币识别器输出每枚硬币一个脉冲I0.11号选货按钮可乐I0.22号选货按钮雪碧I0.33号选货按钮矿泉水I0.44号选货按钮薯片I0.55号选货按钮饼干I1.01号货道缺货检测光电传感器无货时输出有效I1.12号货道缺货检测同上I1.23号货道缺货检测同上I1.34号货道缺货检测同上I1.45号货道缺货检测同上I1.5出货完成信号出货口红外对射或微动开关输出点功能说明Q0.01号货道电机驱动推杆或螺旋弹簧Q0.12号货道电机同上Q0.23号货道电机同上Q0.34号货道电机同上Q0.45号货道电机同上Q0.5找零/退币机构电磁铁或退币电机Q0.6报警/蜂鸣器卡货或严重故障时提醒2.3 接线环节的NPN/PNP和电源问题这里我要重点强调很多新手在接线阶段就直接翻车。S7-200 CPU224XP的数字量输入可以接NPN型传感器也可以接PNP型传感器关键在于公共端的接法。比如你手头硬币识别器是NPN开集电极输出那么输入公共端M要接24V正极信号端接PLC输入点这样传感器导通时把输入点拉到低电平程序里读到的就是有脉冲如果是PNP输出公共端接0V信号端还是接输入点。问题在于很多人不看手册想当然地按一种方式接结果传感器信号死活不来。我见过最惨的情况是电源接反直接把传感器的输出口烧了。所以接线前一定先确认三样东西传感器类型是NPN还是PNP、输出状态是有货有效还是无货有效、工作电压是12V还是24V。这三种组合排列下来有八种情况你不能赌运气。电源容量也要留足。五个货道电机虽然不会同时转但启动瞬间电流会叠加在同一个24V开关电源上。我实测过普通小型推杆电机启动电流能到2A五个同时堵转的话瞬间电流能冲到10A以上。所以我建议给控制回路单独用一个5A以上的开关电源PLC和传感器用一路电机和电磁铁用另一路。这个成本不高却能省掉后面一大堆疑难杂症。3. 核心控制程序投币、选货、出货、找零四段逻辑3.1 金额累计与价格比较投币累计是整个程序的地基。我之前第一版程序里直接在扫描周期里用上升沿指令去捕捉投币脉冲结果硬币投快一点就丢数投十块钱只显示八块。问题出在S7-200的扫描周期一般十几到几十毫秒而硬币识别器的脉冲宽度可能只有几个毫秒扫描程序根本来不及响应。正确做法是利用CPU224XP的高速计数功能或者中断输入。I0.0这个点本身可以配置为高速计数器HSC0的输入也可以用作上升沿中断。我最终选择的是中断方式把I0.0配置为上升沿中断中断程序里执行金额累加。这样硬币不管投得多快脉冲都不会丢。用STL指令表来看核心逻辑大概是这样的LD I0.0 EU I 10, VW0这段的意思是检测到I0.0有上升沿给VW0这个余额寄存器加10也就是加1元。如果你不放心快脉冲可以进一步在OB1里修改SM0.0相关的系统配置把I0.0关联到中断事件0。实际测试下来连续快速投币十几个VW0的数值一次不差。价格比较也很简单我对每个货道写一段比较逻辑LD I0.1 // 有人按1号选货按钮 EU LDW VW0, 30 // 余额是否大于等于30角 ALD M10.0 // 1号货道允许购买标志这个允许购买标志后面还要跟缺货检测、系统忙状态等条件串联起来最终才决定是否真的让电机动作。这样设计的好处是每一层条件都可以单独监控排查问题时不用满屏找触点。3.2 货物选择的互锁与缺货判断五个选货按钮放在面板上位置离得很近操作员完全可能不小心同时按下两个。如果不做互锁就会出现一次购买动作里两个货道同时弹货的情况不仅乱账还可能造成机械卡死。我的互锁思路是引入一个“系统忙”标志位比如M20.0。当任何一个货道开始执行出货动作时M20.0置位此时其它选货按钮全部失效直到这次出货和找零流程完全结束M20.0才复位。在梯形图里每个选货支路的前面都串一个M20.0的常闭触点程序写起来很符合直觉。缺货判断也不复杂。I1.0对应1号货道缺货传感器如果它有效说明这个货道没有货了。选货条件里要串联这个点的常闭。同时我还让缺货状态直接驱动一个面板指示灯这样顾客不用按下去之后才发现没货一眼就能避开空货道。这里有个细节货道空了之后PLC必须锁定该货道即使按钮被按了也不产生任何动作。但在实际项目里只让按钮失效还不够最好在组态或者数码管上给出“此货道缺货”的提示。后面接MCGS的时候我就是把I1.0到I1.4直接映射到画面上用红色圆点显示。3.3 出货时序与完成信号出货动作不能一直是ON状态否则电机会一直转轻则空转发热重则把货道结构顶坏。所以每次出货都给一个固定的定时时间我这边设置的3秒。3秒内如果收到出货完成信号就提前结束出货动作3秒到点还没收到完成信号就要按卡货故障处理。梯形图里的时序是选货条件满足后置位Q0.01号货道电机同时启动T37定时器定时3秒。接着检测I1.5出货完成信号。这里的I1.5是公共信号安装在出货口不管哪个货道出货货物经过出货口时都会触发一次。因为前面已经做了互锁同一时间只会有一个货道动作所以公共完成信号不会造成错乱。如果T37定时时间到I1.5还没来说明货物卡在货道里或者电机堵转了。这时候先把Q0.0复位再置位一个故障标志M30.0同时输出Q0.6报警。我实际测试中发现不能一报警就永久停机所以我在程序里做了两次尝试第一次失败后等5秒自动再出一次货如果第二次仍然没有完成信号才彻底停机报警。这步逻辑虽然多花了一点编程时间但在真实售货机上非常实用毕竟偶尔一次货物没掉到位重试一下就能解决。3.4 找零/退币的执行逻辑找零逻辑的关键也是避开浮点运算。假设你在4号货道买了5元的薯片投入了10元余额VW0是100角货道价格是50角。计算找零金额就是100减50等于50角也就是应该退5枚一元硬币。实现方式有两种。一种是直接用整数除法指令把找零金额除以10得到退币数。另一种是循环减法每退一枚硬币做一次减10直到找零金额小于10。我第一版用的是循环减法虽然用除法更“正统”但循环减法在程序里更容易看出过程调试时还可以用状态表观察余额逐步减少的整个过程。退币执行机构是一个电磁铁或者退币电机每动作一次就退一枚硬币。所以输出Q0.5必须做成脉冲形式置位0.2秒然后复位0.3秒这是一个完整的退币周期。我写了一个FOR循环结构来发送指定次数的脉冲。S7-200的FOR/NEXT指令嵌套不复杂注意不要把循环体里的定时器搞成多实例冲突就行。如果投币金额刚好等于货价找零金额为0这步直接跳过不做任何退币动作程序直接回到待机状态。4. 调试实录最容易翻车的六个现场问题4.1 投币脉冲丢计数一开始在实训台上用模拟按钮当作投币脉冲程序跑得干干净净。一旦接到真实硬币识别器问题立刻暴露投三枚一元硬币VW0经常只有20有时候甚至是10。我用状态表监视VW0又把硬币识别器输出接到示波器上看波形发现脉冲宽度只有2毫秒左右而普通扫描周期是18毫秒。扫描程序很可能在两次扫描之间错过了整个脉冲。解决办法就是前面说的把投币信号从中断扫描改为硬件中断捕捉。S7-200对I0.0的上升沿中断响应是微秒级的完全能覆盖这种短脉冲。改完之后哪怕我连续快速投币十几次VW0都一次不差。这个经历给我的教训是凡是用到外部脉冲信号先看脉冲宽度再决定用扫描方式还是中断方式。4.2 多个货道同时动作调好互锁之前我在面板上故意同时按两个按钮做压力测试结果两个货道的电机同时转了。从用户角度讲这是绝对不可接受的。排查程序时我发现问题不在选货逻辑而在出货完成信号的处理上。两个货道同时置位意味着公共的I1.5完成信号不知道应该算谁的。我最后在选货条件之前加了一个“系统忙”全局标志同时把选货输出做成一个字节寄存器并用译码方式驱动。也就是说五个货道不是各自独立的Q0.0到Q0.4直接输出而是先归到一个“选货结果”变量里再通过译码输出。这样即使用户手再快同一时刻也只能有一个货道动作。4.3 电机堵转烧触点第一版我直接用PLC的继电器输出点驱动货道电机跑了不到一周Q0.1的触点就烧黑了。原因很简单电机堵转时电流很大继电器触点频繁通断拉弧特别严重。后来我把每一路电机都改成通过中间继电器再驱动中间继电器触点容量更大并且在电机线圈两端并联续流二极管。PLC这边只负责输出一个24V的小电流信号控制中间继电器线圈彻底和电机主回路隔离开。改完之后连续跑了一个多月再没出现触点烧蚀问题。4.4 缺货检测误报我用的反射式光电传感器装在货道前端调试时发现一个很有意思的现象货道里明明有货传感器偶尔会报缺货。观察半天才发现是货品外包装反光导致的。黑色包装比银色包装更容易吸光银色包装会在某个角度产生强烈反光让传感器以为前方有物体遮挡输出状态就不稳定。解决方案不复杂调整安装角度让传感器避开货品反光最强的区域同时在程序里对缺货信号做延时滤波比如信号持续300毫秒以上才认定为真的缺货。这种问题只在现场才会遇到纯看程序是看不出来的。4.5 找零机构把PLC电源拉垮退币电磁铁动作电流很大我的电源容量又刚好只够PLC和传感器用结果每次退币的瞬间PLC都出现一次短暂复位触摸屏也跟着闪一下。用万用表量24V电源发现退币动作时电压跌到只有16V左右。判断是电源容量不够我把退币机构独立接到第二路24V电源上和PLC控制回路完全分开。如果现场只有单路电源至少要把电容加大或者换成功率更大的开关电源不能指望一个24V 2A的小电源去扛几个电机和电磁铁。4.6 电源共地问题这个坑比较隐晦。我把PLC、传感器、电机、退币电磁铁都接到同一个开关电源的24V正极但传感器地和电机驱动器的地没有完全回到电源的同一个端子导致现场出现了奇怪的信号干扰按钮偶尔失灵投币计数偶发多计。后来我检查接线发现所有0V线虽然都接到电源端子但有的接在端子排左边有的接在右边导线上存在压差。把所有0V线统一汇到一个接地铜排再单独引一根粗线回到电源0V端子干扰立刻消失了。这类共地问题在工控现场非常典型。5. 程序架构升级从一把梭到模块化5.1 主程序加子程序的划分第一版程序我把所有逻辑都堆在OB1里大概两百多行梯形图看着就像一盘散沙。改一个选货逻辑要翻好几页还容易把别的地方改坏。写完能跑但后续维护很痛苦。第二版我把程序按照功能拆成了几个子程序SBR_0负责系统初始化SBR_1负责投币计数SBR_2负责选货判断SBR_3负责出货时序SBR_4负责找零退币。主程序OB1只负责调用这些子程序以及处理一些全局的状态转换。这样一来谁出了问题就进对应的子程序看十分钟就能定位。每个子程序内部也尽量保持输入、输出清晰。比如SBR_2选货判断输入是五个按钮和余额VW0输出是允许购买标志位和“系统忙”置位请求。虽然S7-200没有像高级语言那样严格的函数参数但用V区变量约定好接口后可读性会好很多。5.2 用状态位替代散乱的M点程序里M点用多了以后会发现一个麻烦不知道这个M点是干什么用的甚至重复使用。后来我做了重命名规范和状态分配表把M20.0到M20.3定义为一个状态字节的状态位。M20.0是待机M20.1是投币累计M20.2是出货执行M20.3是找零退币。程序流转时当前状态只能有一个为ON这样排查起来非常方便。推动状态机流转的核心是事件。比如投币脉冲触发时如果当前处于待机状态就切到投币累计状态当选货完成时从投币累计切到出货执行状态。这种写法让程序的逻辑层级更清晰后续加断电记忆的时候只需要保存状态字节和相关数据变量就行。5.3 增加故障自恢复和统计信息卡货故障不能一出就永远死机所以我加了自动重试机制。M30.0是第一次卡货标志M30.1是第二次卡货标志。第一次卡货时复位出货输出等5秒再自动触发一次同样的出货动作如果仍然卡货M30.1置位Q0.6报警输出常亮提示管理员过来处理。统计信息方面我把每一笔销售的货物编号和金额都累加到V区变量里。每台售货机一共有多少个V区可以存这些数据CPU224XP的V区是8K字节存几千笔交易记录没问题也可以定期通过通讯读出。这些数据接上MCGS组态之后就能直接看到当日销售额、各货道销量排行对运营来说非常有用。6. 接上MCGS组态把售货机变成监控对象6.1 通讯设置与变量关联S7-200和MCGS通讯硬件上一般用USB-PPI电缆或者CP5611卡加Profibus电缆。我这边用的是USB-PPI线在MCGS里选择“西门子S7-200”驱动然后设置通讯口参数。需要注意S7-200的PPI默认波特率是9.6kbpsMCGS设备窗口里的波特率、校验位、从站地址必须完全一致。CPU224XP的站地址默认是2如果多个PLC组网要改地址才能区分。变里关联是整个组态的核心。MCGS里新增数据对象时数据源类型要选择寄存器索引比如VW0对应PLC的VW0变量这样MCGS才能通过PPI协议读写。我实际把VW0余额、M20状态字节、Q0.0到Q0.4货道状态、I1.0到I1.4缺货状态全部映射了出来在画面上做成了模拟售货机面板。顾客在触摸屏上也能看到哪几个货道缺货以及当前余额是多少。6.2 运行监视和统计数据MCGS画面我做了三个页面。第一页是运行监控显示五个货道的货物图片、库存状态、实时余额、出货状态相当于一个虚拟售货机面板。第二页是销售统计MCGS通过读写V区数据对象把当日销售额、各货道销量用表格和曲线展示出来。第三页是故障记录符合条件的报警事件会写入MCGS的报警数据库带时间戳。这里有个实际经验MCGS读VW0这类字变量没有问题但如果要直接监视Q点和I点状态一定要用正确的字节索引方式。比如Q0.0对应Q0.0在MCGS的数据对象里要选位对象否则读出来是字节数据判断逻辑会很别扭。6.3 后续还能怎么扩展这台售货机项目做完后我一直在想还能往哪里延伸。比较自然的扩展有三个方向。第一个方向是把单一投币改成扫码支付PLC侧不需要改变太多只要在外部加一个支付网关支付成功后输出一个模拟投币信号给PLC即可。这样一来机械和电机部分完全复用只动一下外围硬件。第二个方向是加网络通讯。S7-200本身没有以太网口但可以用一个串口转以太网模块把PPI协议封装成TCP再对接上位机或云平台。这样就能远程查看各货道的库存和销售数据。不过这件事要注意S7-200的PPI协议是西门子私有协议网关配置要仔细一些。第三个方向是把这套逻辑移植到S7-200 Smart或S7-1200上。因为核心的状态机和时序思想是通用的到了新平台只需要换指令写法。我在Smart上重新实现过一遍I/O点数可以更紧凑程序运行速度也明显快很多。如果你本来就是学新平台看完这篇再去对照新指令手册改写会顺手很多。最后再分享一个小技巧我在调试这个项目的时候习惯把PLC的状态表保持在监控界面把关键变量VW0、M20状态字节、Q0.0到Q0.6全部拉进去配合MCGS画面一起看。故障出现时先看状态字节停在哪一步再看对应的输入输出点大概率几分钟就能锁定问题。别小看这些习惯项目能不能快速收尾往往就取决于这些细节。