1. 控制系统的整体架构设计与方案选型
1.1 为什么选S7-300+WinCC这个组合
做三货物自动售货控制系统设计,摆在面前的第一道题不是怎么编程序,而是选什么平台。我之前见过不少学生和刚入行的工程师,一上来就纠结“要不要换S7-1200”“是不是用精简面板组态更方便”。我的观点很直接:如果这个项目是课程设计、毕业设计,或者面向教学演示、存量产线改造,S7-300搭配WinCC依然是目前最稳妥的组合,没有之一。
S7-300虽然是西门子的经典中型PLC,已经不算最新产品线,但它的存量市场非常庞大,很多工厂、实训室、老旧产线里还在稳定运行。你打开西门子官方手册和技术论坛,S7-300的案例、库文件、FAQs几乎要多少有多少,遇到问题时随便一搜就能找到对应的解决方案。这一点对初学者极其友好,因为你踩的坑大概率别人早就踩过了。
WinCC作为上位机组态软件,和S7-300之间的通讯是“原配”关系,走的是西门子自家的S7协议,不需要额外买驱动,不需要写一堆底层通讯代码。你只需要在WinCC里添加SIMATIC S7 Protocol Suite驱动,配置好PLC的IP地址或者DP地址,变量就能直接对上。
三货物版的控制系统比单货物版多出了货物选择、多路输出、找零状态管理这些逻辑,但又比全尺寸售货机(动辄十几个货道、带制冷和触摸屏)简单得多,正好卡在“能学到完整设计流程”和“不被复杂度淹没”的平衡点上。
1.2 三货物版控制系统的功能边界
很多初学者拿到这个题目之后,第一反应是“三个货物?那不是很简单嘛,三个输出口各接一个电机,搞定收工”。实际做下来你会发现,真正的难点根本不在出货,而在“钱”的处理上。
自动售货控制系统本质上是一个有限状态机:待机、投币检测、金额累计、货物选择、出货执行、找零结算、故障报警。三货物版本对每一个状态都要有完整的逻辑闭环,只是在货物选择部分从“多选一”简化成了“三选一”,在I/O规模和变量数量上做了减法。
以我这次做的设计为例,系统的功能边界如下:
- 支持两种硬币投币识别(例如5角和一元的硬币),能够实时累加金额并显示在面板上;
- 三个货道各自独立,选择对应货物后,只有金额充足才允许出货;
- 出货动作由PLC输出控制直流电机转动,将货物推落到取货口;
- 出货完成后自动扣减金额,余额累计继续购买;
- 按下找零按钮后,系统按最少硬币数原则退币;
- 光眼检测卡货等异常状态并声光报警。
这套功能逻辑如果拆开看,每一个都不难,但放在一起就需要你认真规划数据结构和程序结构。整个过程做下来,基本上能把PLC编程中的位逻辑、定时器、计数器、比较指令、数据块操作全都过一遍。
1.3 设计方案的总体流程
系统启动后先复位所有输出,进入待机状态。当投币检测传感器检测到有硬币投入,PLC读取当前硬币面值并累加到余额变量里,WinCC通过变量连接同步显示余额变化。用户按下某个货物选择按钮(或者在WinCC画面上点击货物图标),PLC先比较“余额是否大于等于货物单价”,条件满足则启动对应货道电机。电机运转一定时间后停止,货物掉落经过取货口光眼确认后,系统自动扣款并更新库存数量。如果用户不再继续购买,按下找零按钮,PLC按照预设的硬币面额组合计算退币数量,驱动退币电磁执行机构。整个过程通过HMI和指示灯实时反馈状态。
在实际设计时,我建议把PLC程序和WinCC组态并行开发,不要等PLC写完再去做上位机,那样会很浪费时间。WinCC的变量表和PLC的符号表在功能上是一一对应的,你先在PLC里把符号表建好,后面WinCC的变量全部从这里直接映射,效率和准确度都能提升不少。
2. 硬件配置与I/O分配详解
2.1 S7-300核心模块选型
S7-300是一个模块化系统,不像S7-1200那样CPU自带集成IO,S7-300的每个模块都需要单独选型。对于三货物版自动售货机,我不建议选太大规模的配置,够用就好,留出10%到20%的余量就可以。
我这次选用的模块配置如下:
| 模块 | 型号推荐 | 说明 |
|---|---|---|
| 电源模块 | PS 307 5A | 给CPU和IO模块供电,前端接220V交流 |
| CPU模块 | CPU 314C-2PN/DP | 自带DI 24点/DO 16点,集成PN口和DP口,通讯方便 |
| 数字量输入模块 | SM 321 DI16 | 扩展投币、选择按钮、光眼等输入点 |
| 数字量输出模块 | SM 322 DO16 | 扩展货道电机、退币电磁铁、报警灯输出点 |
有人会问,CPU 314C的集成IO已经不少了,为什么还要扩展SM321和SM322?原因很简单:集成IO虽然够用,但如果你把所有信号都压在CPU自带端子上,一旦某个点烧掉,修起来特别麻烦。而且扩展模块可以让你把“控制类信号”和“执行类信号”分开布局,接线整齐,排查问题的时候也更容易分区域处理。
CPU 314C-2PN/DP这个型号我一定要多说一句,它自带两个通讯口,一个PROFINET口,一个DP口。做WinCC通讯时,直接用PN口走以太网,把PLC和装WinCC的电脑接到同一个交换机或者直连网线就能通讯,非常方便。如果以后想挂远程IO站或者变频器,DP口也还有富余。
2.2 I/O点位的规划原则
三货物版系统的I/O分配看起来简单,但规划得好不好直接影响后续编程和接线。我见过太多人上来就乱接,最后程序写完发现点位乱了,又回去改接线,费时费力。
正确的做法是先列I/O清单,再动手接线。我这次的I/O分配如下:
输入信号(DI):
- I0.0:五角硬币投币检测
- I0.1:一元硬币投币检测
- I0.2:货道1出货光眼确认
- I0.3:货道2出货光眼确认
- I0.4:货道3出货光眼确认
- I0.5:货物选择按钮1
- I0.6:货物选择按钮2
- I0.7:货物选择按钮3
- I1.0:找零按钮
- I1.1:卡货故障复位按钮
输出信号(DO):
- Q0.0:货道1电机正转
- Q0.1:货道2电机正转
- Q0.2:货道3电机正转
- Q0.3:退币电磁铁
- Q0.4:运行指示灯(绿色)
- Q0.5:故障报警灯(红色)
- Q0.6:蜂鸣器
这里有一个容易忽略的细节:货道电机的限位问题。直流减速电机如果通电时间太长,会把货物推过头甚至卡死。我实际设置的是通电2秒后自动停止,并结合光眼信号判断货物是否掉落。如果2秒后光眼还没检测到货物,判定为卡货,立即停止电机并报警。
2.3 投币识别与找零机构的硬件处理
投币识别在最简单的三货物系统里可以不做光电识别的复杂算法,直接用机械币道配合传感器即可。比如5角和一元硬币在币道里的直径不同,可以分别在币道不同位置安装光电传感器,硬币滚过哪个传感器就累计对应面值。这种方式虽然不能做到“全币种识别”,但对于课程设计和教学演示来说完全足够。
配套的找零机构可以用“退币电磁铁+多币道”的方案。当PLC计算需要退几枚5角硬币时,会输出脉冲信号驱动电磁铁打开对应币道的挡板,硬币掉落到退币口。这里要特别注意:电磁铁不能长时间通电,否则线圈会发热烧毁,一般用0.5秒的短脉冲即可。
3. PLC程序结构:梯形图核心逻辑逐段拆解
3.1 数据结构与变量规划
S7-300编程我建议用Step 7软件,它支持LAD(梯形图)、FBD(功能块图)、STL(语句表)三种语言。三货物版程序规模不算大,用LAD完全能表达清楚,也比STL更直观,方便后面答辩或者交接的时候给人讲。
程序结构上用“主程序OB1 + 子功能FC”的方式组织,这样每个功能独立,调试时可以单独测试某个模块,不用一跑就是整个程序。我规划的功能块如下:
- FC1:投币处理和余额计算
- FC2:货物选择和出货控制
- FC3:找零计算和退币执行
- FC4:库存管理与故障报警
全局变量使用数据块DB来保存,比如DB1里放余额、货物单价、库存数量、出货状态等。用DB而不是M区存放业务数据,好处是数据组织清晰、断电保持可控,而且WinCC直接读DB变量特别方便,地址一目了然。
DB1的内容大致如下:
- DB1.DBD0:当前余额(实数,单位元)
- DB1.DBD4:货道1单价
- DB1.DBD8:货道2单价
- DB1.DBD12:货道3单价
- DB1.DBW20:货道1库存
- DB1.DBW22:货道2库存
- DB1.DBW24:货道3库存
- DB1.DBX30.0:出货中标志位
3.2 投币检测与金额累加
投币检测的梯形图逻辑并不复杂,核心是边沿检测。当I0.0或I0.1从0变1,说明有一枚硬币投入,这时候把对应的面值加到余额上。注意这里一定要用上升沿,不能用电平。如果用电平触发,硬币卡在传感器前后持续产生信号,金额会被反复累加,从1块变10块。
以五角硬币为例(FC1局部):
A I0.0 // 读取五角投币传感器 FP M0.0 // 上升沿检测 JCN M001 // 没检测到上升沿就跳转 L DB1.DBD0 // 加载当前余额 L 0.5 // 五角面值 +R T DB1.DBD0 // 存回余额 M001: NOP 0如果你习惯用LAD拖拽,逻辑是一样的:I0.0串一个P(上升沿),后面接ADD指令,把0.5加到DBD0里。为什么我比较推荐DBD0用实数?因为单价可能是2.5元、3元这种带小数的值,用整数就还得乘以10或者100来换算,多一层逻辑还容易算错。直接实数运算,画面显示和比较指令都省事。
3.3 货物选择与出货执行
三货物版的出货逻辑核心是“比较 + 互锁”。用户按下选择按钮,PLC判断这个货道是否还有库存、余额是否充足,条件全部满足才启动电机。如果余额不足,可以选择闪烁提示或者通过WinCC弹窗提示用户继续投币。
我写这段逻辑时特别加了一个“出货中状态位”做防重入保护。目的很简单:在电机运转期间,即使再按其他按钮,程序也不会重复执行出货动作。这个保护在真实设备上极其重要,因为按钮被多次触发,或者WinCC脚本被重复点击,都会导致设备同时启动两个电机,故障风险极高。
下面以货道1的逻辑为例说明(FC2节选):
- 启动条件:选择按钮I0.5按下并产生上升沿;DB1.DBW20(库存1)> 0;DB1.DBD0(余额)>= DB1.DBD4(单价1);出货中标志位DB1.DBX30.0 = 0
- 满足条件后:置位出货中标志位,置位Q0.0货道1电机输出
- 同时调用TON定时器,设定2秒,2秒到后复位Q0.0
- 读取取货口光眼确认信号I0.2:如果为1,说明货物掉了,做扣减余额和库存的处理;如果为0,判定卡货,置位报警标志并触发Q0.5报警灯和Q0.6蜂鸣器
这里有一个我踩过好几次的坑:光眼确认信号不能放在定时器结束后才检测,应该在定时器运行期间持续检测,否则会出现“货物已经掉落但光眼信号只是一瞬间的短脉冲,等定时器结束时已经错过了”这种问题。正确的做法是:在定时器到点之前的整个过程中,只要光眼有一次为1,就记录到M区的一个“出货成功”标志里,定时器结束后再检查这个标志进行扣款。
3.4 找零计算与退币执行
找零计算是很多人容易写乱的地方,核心是贪心算法的PLC实现:先用最大面额的硬币去凑找零金额,不够再用小面额补。三货物版一般只需要退5角硬币,所以逻辑简化成“需要退的枚数 = 找零金额 / 0.5”。
找零按钮按下后,PLC先把余额里的整数元部分登记为“应退金额”,然后拿应退金额除以0.5得到需要退的5角硬币枚数,存放在DB里。退币执行时不能一次性把所有硬币同时弹出,这样会卡币。正确做法是用一个定时器循环:每弹出一枚硬币(电磁铁通电0.5秒,然后断电),等一秒钟,再弹下一枚。这个节奏可以通过比较指令和计数器实现,也可以用循环指针来实现,推荐用计数器方式,逻辑更清晰。
退币完成后,一定记得把余额清零,并复位“找零中”状态位。别小看这一步,如果不复位,下一次投币时找零机构的输出还会保持在通电状态。
3.5 库存管理和故障报警
三货物版本的库存管理属于锦上添花,但对于卖点和答辩加分非常有用。库存数量在初始化时预设,比如每个货道放10瓶饮料,每次出货成功扣1,当库存为0时,该货道的选择按钮失效,甚至在WinCC画面上直接显示“售罄”状态。
故障报警这里我做了三级处理,不算复杂,但很实用:
- 轻微提示:WinCC画面文字提示“余额不足”,不产生声光报警;
- 一般故障:卡货,红灯常亮、蜂鸣器间断鸣响,需要操作人员到现场处理并按下复位按钮;
- 严重故障:投币口卡币,程序立刻封锁所有投币和操作逻辑,持续报警等待维护。
4. WinCC上位机组态与脚本实现
4.1 WinCC变量建立与通讯连接
WinCC组态的第一步不是画画,而是建立变量连接。在变量管理里添加“SIMATIC S7 Protocol Suite”通道,然后在通道下新建连接,填写PLC的IP地址(如果走PN口通讯,就是CPU的IP),机架号和槽号按照S7-300的标准配置填(通常机架0,槽2)。
我在这个项目里用到的WinCC变量主要有这几类:
| WinCC变量 | 对应PLC地址 | 数据类型 | 用途 |
|---|---|---|---|
| Balance | DB1.DBD0 | 浮点数 | 当前余额 |
| Price1 | DB1.DBD4 | 浮点数 | 货道1单价 |
| Stock1 | DB1.DBW20 | 无符号16位 | 货道1库存 |
| Vending_State | M10.0 | 二进制 | 出货中状态 |
| Fault_Flag | M10.1 | 二进制 | 故障标志 |
WinCC读S7-300的DB块变量时,地址格式要写对。实数对应DBD,16位整数对应DBW,位对应DBX。我在实际项目里见过不少人把实数地址写成DBW,导致画面显示乱码或者一直闪烁跳变,其实是变量类型和地址没对应上。
通讯连接建好后,可以先在WinCC的变量模拟器里测试变量变化,确认链路没问题,再开始做画面。
4.2 画面设计与基本交互功能
WinCC画面设计这块,核心原则是“信息不过载”。三货物版要显示的信息其实不多:当前余额、三个货物的图片和价格、库存状态、出货按钮、找零按钮、故障状态指示。把这些元素排好就行,不要堆太多的装饰和特效,运行稳定性优先。
我做的画面布局是上下两栏:
- 顶部一行显示余额、运行状态、故障状态;
- 中部三个货物卡片横排,每个卡片上有货物名称、价格、库存数量、购买按钮;
- 底部一个大的“找零”按钮和“故障复位”按钮。
货物的图片可以用WinCC的图形编辑器直接画矩形加文字,也可以导入PNG图片。我建议直接画简洁图块,加载速度快,也不容易出现图片路径丢失的问题。
交互按钮的用途不同,实现方式也不同。购买按钮如果直接在按钮属性里做“按”和“释放”动作,很容易造成误触发。我推荐用C脚本实现购买逻辑,按钮事件选择“单击”触发,这样按下抬起才算一次有效点击。
4.3 C脚本实现二次确认功能
WinCC的C脚本可以放在按钮的事件里运行。之前有个朋友问过我:“我在WinCC画面里点出货按钮,不小心多点了两下,结果直接出了两瓶饮料,怎么防?”这就是典型的缺少二次确认的案例,对于实际的售货机来说,虽然按钮误触不算大问题,但在工业控制里,点击“启动”就是切换一台几百千瓦的电机,必须有二次确认。
在自动售货系统里,我给购买按钮加了一个二次确认弹窗脚本,效果是:用户点击购买按钮后,不是直接启动,而是先弹出一个对话框显示“确认购买XX商品?”,点击“是”才执行置位和复位动作。这样一来既能防止误触,也能给用户一个确认的机会。
C脚本核心逻辑如下:
#include "apdefap.h" void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName) { int nMsgRet; nMsgRet = MessageBox(NULL, "确认购买商品1?将扣除相应金额。", "购买确认", MB_YESNO | MB_ICONQUESTION); if (nMsgRet == IDYES) { // 触发PLC置位,启动出货 SetTagBit("Start_Vend1", 1); Sleep(50); SetTagBit("Start_Vend1", 0); } }这里最关键的技巧是:置位之后延时几十毫秒再复位,做一个短脉冲给PLC。我之前见过有人写脚本直接对整个变量做“赋值1然后又赋值0”,结果PLC那边根本接收不到脉冲,因为两行代码执行得太快,通讯周期还没跟上。加一个Sleep就能让通讯系统把状态切换完整传过去。
4.4 数据归档和报表
这部分在基础三货物版里不一定要求,但如果做毕业设计或者实际商业化部署,建议把每天的出货记录、金额收入、故障事件都归档到WinCC里。WinCC自带变量归档功能,在变量管理里把需要记录的变量选中,勾选“归档”,系统就会按设定周期把数据写入归档数据库。
归档最大的用处是做故障追溯。调试期间我遇到过一台售货机半夜自己报警,看PLC程序看不出问题,后来通过WinCC归档查历史趋势,发现是凌晨的电压波动导致投币传感器误触发了一次。这种问题如果没有历史数据,根本无从排查。
5. 联调测试方法与常见问题排查
5.1 仿真调试与实物调试的衔接
三货物版控制系统调试阶段,我非常推荐先做离线仿真,再做实物联动。仿真阶段不需要把整套硬件都接好,用Step 7自带的PLCSIM就能模拟PLC运行。但要注意:PLCSIM和WinCC之间通过变量连接时,需要在WinCC里把连接属性设置为“PLCSIM模拟通讯”,否则WinCC连不到仿真PLC。
调试顺序建议如下:
- 先用PLCSIM单步调试FC1投币逻辑,用变量表强制输入I0.0、I0.1,观察DB1.DBD0余额是否正确累加;
- 再调试FC2出货逻辑,强制置位I0.5选择按钮,观察Q0.0是否按时序动作、定时器是否准确;
- 接着调试找零逻辑,强制把余额改成3.5元,按下找零按钮,观察退币计数器是否按0.5元递减;
- 以上逻辑验证通过后,接入WinCC,首先做变量通讯测试,确认画面数值随PLC变量变化;
- 最后接实物,按照I/O表逐点核对每根线的状态,再做完整的投币-购买-出货-找零联动测试。
5.2 常见问题速查表
实际联调中,我遇到或者见过的问题主要集中在通讯和时序两方面,整理成表方便大家对照排查:
| 故障现象 | 可能原因 | 排查方法 |
|---|---|---|
| WinCC画面数值不刷新 | PG/PC接口设置错误;变量地址类型不匹配 | 在WinCC里用变量模拟器测试,确认变量能读写;检查TCP/IP通讯是否连通 |
| WinCC连不上PLC | 未添加S7 Protocol Suite驱动;IP不在同一网段 | 先Ping通PLC地址,再检查连接配置的机架号和槽号 |
| 投币金额重复累加 | 用了电平触发而不是上升沿处理 | 检查FP上升沿指令是否有效,确保每投一枚只触发一次 |
| 点击购买按钮无反应 | C脚本置位时间太短、PLC通讯周期未捕获 | 增加Sleep延时(建议50毫秒以上);检查是否选中“单击”事件 |
| 货物已掉出但系统未扣款 | 光眼信号在定时器结束时已消失 | 增加“出货成功”锁存标志,在定时器运行期间检测到光眼就置位,定时结束再判断 |
| 退币数量不对 | 找零算法顺序错误 | 先除以大面额,再取余数,依次递减;模拟各金额值验证 |
| 报警频繁误报 | 光眼被灰尘遮挡或安装角度偏 | 调整光眼灵敏度,增加滤波延时(如持续0.3秒才确认卡货) |
5.3 调试中的实战心得
快做完这个项目的时候,我踩了一个特别典型的坑,写在这里给大家提个醒:S7-300的定时器资源有限,如果你用的是老型号CPU比如CPU 314,定时器数量是有限制的。三货物版如果每个货道用一个TON,加上找零节奏控制和光眼延时滤波,整体定时器数量很容易超过软定时器可分配上限。解决方法是把定时器做成“复用”模式,即用一个TON做时间基准,然后用比较指令去分出多个时间段,或者用FB块在里面定义多实例定时器。
另外一个心得是关于WinCC画面刷新率的。三货物版的画面刷新率没有必要设太高,200毫秒或者500毫秒完全够用,刷新率太高反而会增加CPU负载,特别是你同时开了变量归档的时候,运行久了WinCC会变得越来越卡。我之前试过100毫秒刷新,运行一晚上后WinCC的内存占用飙升到2个G起步,改成500毫秒后稳定很多。
还有一个被很多人忽视的细节:WinCC和PLC的时钟同步。如果WinCC电脑和PLC的时间不一致,归档数据和报警记录的时间戳就会对不上,后面做故障复盘的时候会很痛苦。建议在WinCC里启用时间同步功能,以PLC时间为准,每次系统启动时自动校准。
6. 项目扩展思路与商业化方向
这个三货物版做到这里,主体功能已经完整了,但如果你还有余力,我建议在下面几个方向做扩展:
第一是支付方式升级。现在的三货物版只支持硬币,商业上肯定不够。可以考虑在系统中预留4G通讯模块接口,通过HTTP接口调用云端支付API,把“投币支付”扩展成“扫码支付”。PLC端只需要新增几个命令字变量和状态位,WinCC端增加一个二维码显示区域,复杂逻辑放在网关里,而不是放在PLC里,这样改动最小。
第二是远程监控和数据管理。S7-300走PROFINET以后,可以配合WinCC的Web发布功能,做一个远程监控页面,通过手机浏览器就能实时看到每个货道的库存和故障状态。运维人员不需要亲自跑到机器前面就能解决70%以上的排查工作,这在散布式自动售货网点部署中特别实用。
第三是多机联网管理。如果你有不止一台售货机,可以做成“一台PLC控制一台售货机 + 一台WinCC服务器统一管理多台”的结构。WinCC支持多连接,每台PLC的变量可以在同一个画面里分区域显示,这样所有的在线状态、营业数据、故障信息都在一个平台里汇总。
我记得有一次给一个实训基地做整套售货机教学系统,用的就是S7-300三货物版的架构扩展出来的五机联网方案。每个学生小组负责维护其中一台机器的程序逻辑和画面,所有机器通过交换机连到教师机的WinCC监控站。学生既能看到自己那一台的实时数据,也能对比其他组的程序写法和画面布局,教学效果比单机演示好得多。
如果你是想把这个项目直接用在毕业设计或者课程作业里,还可以考虑加上“电机转速控制”这一层:货道电机如果使用变频器或者模拟量模块输出,可以在投放不同重量货物时调节推送力度,避免冲力太大把易碎商品砸坏。虽然三货物版用不到,但答辩时提出来,说明你考虑了实际工况,印象分会好很多。