上个月刚收尾的一套配料称重设备,三菱FX3U做主控,16个原料仓位,按产品配方自动落料。这套程序最核心的部分就是16仓位配方管理,从需求梳理到最后调试交付,前后改了两版,中间还踩了几个印象很深的坑。今天把这套配方程序的整体思路、寄存器规划、关键指令段和调试过程整理出来,给正在做同类设备的工程师一个参考。
这套设备其实不算复杂,但配方程序一旦出问题,后果非常直接:配比错、批次报废、客户现场停机。所以我把这部分的可靠性和操作性放在第一位。如果你也在做配料、分装、多工位参数切换这类项目,这篇记录应该能帮你少走不少弯路。
1. 项目背景与需求拆解:设备要什么,程序才写什么
写程序最怕客户只说一句“做个配方功能”,然后就撒手不管。配方功能听着简单,但具体到现场就全是细节。所以我习惯先把工艺拆清楚,再对着拆解结果确定程序架构。
1.1 工艺现场:16个仓位到底怎么来的
这台设备是一条小型配料线,核心是16个原料仓,每个仓下面有独立的落料阀门和称重模块。生产时按照产品要求,把对应仓位的原料依次放进中间秤,达到目标重量后关闭阀门,进入下一仓,最后混合出料。
关键点在于:不同产品对应的16个仓参数完全不同。每个仓至少有三个参数要管,目标值、粗加料提前量、允许误差。粗加料提前量,就是快到目标重量时提前关阀的提前量,用来抵消阀门关闭滞后和落料冲击带来的过冲;允许误差,则是判定这一仓投料是否合格的容差范围。
所以一台设备要生产多个产品,就是在切换16组参数。16个仓乘以每仓3个参数,一共48个参数,这还只是最基础的。实际项目里还会加上每仓的投料顺序、提前量修正系数、超差后的补料次数,参数会更多。
1.2 原始方案是怎么被推翻的
设备厂原来的方案很直接,触摸屏上放48个数值输入框,换产品时工人手动改。听着也能用,但实际上问题很大。
首先是效率问题。48个参数挨个改一遍,熟练工人也要十五到二十分钟,期间设备得停机等待。其次是出错率问题。参数多,现场噪音大,工人看错行、输错数字是家常便饭,尤其粗加料提前量这类参数,改错一个数字,整批料的重量精度就废了。最头疼的是责任界定,出问题后厂商说是工人改错了,工人说是触摸屏自己跳的,两边扯皮。
所以设备厂最终提的需求很明确:参数不能靠人肉输入,必须做进配方里,工人只需要选产品号,剩下的事情交给PLC。
1.3 最终需求清单:写进程序前先写清楚
这个项目我梳理出来的需求清单是这样的:
- 支持多个产品配方,现场暂定16个产品,但程序内部要预留扩展空间。
- 每个配方包含16个仓位、每仓3个配方参数,即48个数据字。
- 一键切换配方,切换动作要快,且不能影响配料过程中正在执行的动作。
- 配方数据必须掉电保持,不能因为断电就清空。
- 支持在HMI上修改参数并保存回配方,但要有防误操作机制。
- 配方数据要有校验逻辑,加载时发现数据异常立刻报警,不能带着错误数据生产。
需求看起来不多,但每一条落到实处都是程序结构设计上的约束。比如“不能影响配料过程”,就逼着我在主流程里做互锁逻辑;再比如“预留扩展空间”,又逼着我用变址和偏移量来计算存储地址,而不是傻傻地把16组配方挨个写死。
2. 硬件选型与寄存器规划:FX3U如何承载16组配方
为什么选三菱FX3U而不是别的型号,这是设备方早期就定下来的事情,但我自己评估完之后也认可这个选择。FX3U在这个场景下其实很合适,原因有三个。
2.1 为什么是FX3U,而不是FX3G或者更大系列
FX3U在三菱小型PLC里属于性能比较能打的那一档。FX3G虽然是更便宜的替代品,但D寄存器数量、变址修饰、FOR/NEXT循环这些指令的支持上,FX3U更完整。后面如果这台设备要接入称重仪表走MODBUS,FX3U的通讯扩展能力也更从容。
再有一点是I/O点数。这套设备本身I/O不算多,但带了4路模拟量输入和2路模拟量输出,还要预留几个备用点位。FX3U-32M基本单元再加上两个扩展模块,点数正好覆盖,还有余量。选型的时候我习惯留至少10%的余量,不然电气装配阶段发现缺一个输入点,改起来就难受了。
2.2 寄存器分区方案:D区怎么分配
FX3U的D寄存器总量不少,但用起来必须规划清楚,不然程序写到后面就成浆糊了。我这套程序的D区分配是这样的:
| 地址范围 | 用途 | 说明 |
|---|---|---|
| D0-D49 | 系统公共区 | 秤状态、阀门状态、班产累计、手动/自动标志 |
| D50 | 目标产品号 | HMI写入,范围1-16,这是配方切换的入口 |
| D51 | 当前产品号 | PLC内部维护,切换完成后更新 |
| D52-D59 | 偏移计算中间变量 | 计算配方的存储起始地址 |
| D60-D99 | 子程序局部变量 | 校验累加器、循环计数器等 |
| D100-D147 | 当前配方工作区 | 16仓位×3参数,主流程直接读取这一块 |
| D148 | 当前配方校验和 | 加载校验时使用 |
| D150-D199 | 称重数据区 | 当前秤的毛重、净重、目标量、落差量等 |
| D500起 | 配方存储区 | 每个配方49字,最多可放26个产品配方 |
这里有个很关键的设计思路:当前配方工作区D100-D147是主流程唯一会去读取的数据区域。配方切换本质上是把存储区的数据成批搬进D100-D147,主流程根本不需要知道自己正在生产什么产品,它只需要按照D100-D147里的参数去做控制。
这样做的好处是程序解耦,配方管理逻辑和配料控制逻辑互不干扰。后期要改配方逻辑,不会碰到主控制程序。
2.3 掉电保持设置:最容易忽略的细节
FX3U的D区默认有一块锁存区域,但范围不是你想要的那么多。默认情况下D512-D7999是锁存区域,而D500-D511并不在锁存范围内。
我这套程序配方存储区从D500开始,如果直接把D500-D511的数据当配方存储区用而不做任何设置,那就完蛋了,掉电之后前12个字的数据全丢。解决方案是在PLC参数里的“软元件”页面,把D500-D1299全部设为锁存区域。
这里的教训是:不要想当然认为“D区都是断电保持的”。FX3U的D0-D511默认是非锁存的,除非你改参数。而且这个设置不在程序里,在PLC参数里,很多人改了程序忘了改参数,现场一断电,之前保存的配方全部归零。
另外一个细节是FX3U的电池。锁存区域靠电池供电维持,电池寿命一般五到七年,但设备在高温高湿环境会缩短。三菱有个特殊继电器M8016,电池电压低的时候会置ON,这个信号一定要接到HMI报警画面上,不然电池没电了,配方数据悄悄丢失,排查起来非常被动。
3. 配方数据结构设计:产品号、仓位号与地址的映射关系
配方程序的核心,说白了就是一张表:产品号在表中对应哪一段D寄存器,偏移量怎么算。这个搞清楚了,程序里那些指令就是机械执行。
3.1 配方表的结构:每仓位3参数,每个配方49字
先看一下单个配方的内部结构。每个仓位3个参数,16个仓位就是48个字。再加上一个校验字,所以每个配方占49个D寄存器。
| 相对偏移 | 含义 | 示例 |
|---|---|---|
| 0 | 1号仓目标值 | D100,单位g |
| 1 | 1号仓粗加料提前量 | D101,单位g |
| 2 | 1号仓允许误差 | D102,单位g |
| 3 | 2号仓目标值 | D103 |
| 4 | 2号仓粗加料提前量 | D104 |
| 5 | 2号仓允许误差 | D105 |
| ... | ... | ... |
| 45 | 16号仓目标值 | D145 |
| 46 | 16号仓粗加料提前量 | D146 |
| 47 | 16号仓允许误差 | D147 |
| 48 | 校验字 | D148 |
为什么校验字要占一个字放在最后面,而不是单独开一块区域?因为这样可以直接把整个49字一起搬进工作区,加载完成之后用同一个基地址做校验,逻辑非常顺。
校验算法用的是经典的和校验:把前面48个字的数值全部累加,得到一个总和,存到第49个字里。加载的时候重新算一遍前48字的累加和,和校验字比对,一致就说明数据没被破坏。
3.2 地址映射:产品号如何换算成存储区偏移
配方存储区从D500开始,每个配方49字,那么产品号n的起始地址就是:
起始地址 = 500 + (n - 1) × 49
举个例子:
| 产品号 | 起始地址 | 结束地址 | 校验字位置 |
|---|---|---|---|
| 1 | D500 | D548 | D548 |
| 2 | D549 | D597 | D597 |
| 3 | D598 | D646 | D646 |
| 16 | D1235 | D1283 | D1283 |
为什么要用变址寄存器而不是直接把16个地址全部写死?因为这个配方表一旦定义好,后期要扩展产品数量,只需要改一个常数。比如程序里用K49这个常数乘以产品号,将来每个仓位参数从3个变成4个,那只需要把K49改成K65,存储区间自动整体平移,不需要逐条改程序。
当然这个常数改动要非常谨慎,因为配方区已经存进去的数据位置会全部变化。我一般会在程序开头做一个常量定义区,把这个K值放到一个D寄存器里方便统一修改,同时用注释标清楚这个数跟配方结构强相关,不能随便动。
3.3 主流程是怎么读取工作区的
主控制程序读取的不是D500后面的大表,而是D100-D147这块工作区。比如1号仓的配料逻辑大概是:目标值是D100,粗加料提前量是D101,允许误差是D102。配料开始后,称重数值到了D100-D101的位置,粗加料电磁阀关闭,进入慢加料;到了D100附近开始精调;最终落在D100±D102的范围内,判定合格。
这套结构的优点非常明显:主流程完全感知不到配方的存在。它只是按D100-D147当前的内容执行控制逻辑。至于D100-D147的内容是从哪个产品配方里加载进来的,那是配方管理程序的事情,两者之间通过工作区解耦。
后期如果要加一个功能,比如“临时修改当前配方的某个参数但不保存”,也只需要在HMI上修改D100-D147对应的字,主流程立刻生效。方便得很。
4. 核心程序逻辑实现:加载、保存、校验的完整流程
接下来是重头戏,实际的梯形图逻辑。我按加载、保存、校验三个部分来讲,每段都会附上关键指令的写法,以及我为什么这么写。
4.1 配方加载流程:一键切换到底发生了什么
先明确触发方式。HMI上放一个“切换配方”按钮,对应PLC的M100。按下按钮后,PLC检测到上升沿才执行加载,而不是一直执行。
用上升沿的原因很朴素:M100如果一直是ON,程序就会反复执行加载逻辑,BMOV指令不断往外搬数据,还可能把正在运行的参数改掉。用PLS指令把按钮脉冲化,一次性执行,干净利落。
核心指令段是这样写的:
LD M100 PLS M101 // 范围校验:产品号必须在1-16之间 LD M101 ZCP K1 K16 D50 M20 ANI M22 AND M20 // 范围OK,继续执行加载 MOV D50 D60 SUB D60 K1 D60 MUL D60 K49 D52 MOV D52 Z0 BMOV D500Z0 D100 K49 CALL P0逐行解释一下。
第一步,把D50里的目标产品号读出来,减1,再乘以49,得到偏移量。这个偏移量送进变址寄存器Z0。变址寄存器的意义在于,D500Z0这个地址,会随着Z0的值动态变化。Z0=735时,D500Z0实际指向D1235,正好是16号产品的起始地址。
第二步,BMOV是成批传送指令,一次把49个字从配方存储区搬到工作区。这里注意,BMOV的源地址D500Z0是变址后的地址,目标地址D100是工作区起点,传送数量K49。
第三步,CALL P0是调用校验子程序。加载完成后必须马上校验,防止搬过来的数据本来就是坏的。
校验子程序P0的逻辑是这样的:
P0: MOV K0 D70 MOV K0 Z1 FOR K48 ADD D100Z1 D70 D70 INC Z1 NEXT CMP D70 D148 M30 // M30: 相等, M31: D70>D148, M32: D70<D148这段是标准的求和校验。Z1从0循环到47,每次把D100Z1对应地址的值累加到D70。循环结束后D70就是前48个字的累加和,再用CMP和D148比较。比较结果放到M30-M32,M30导通代表校验通过。
校验通过后,把D50的目标产品号更新到D51当前产品号,同时复位加载状态。校验不通过,M30不导通,D51保持原来的产品号,同时置位一个校验错误报警M33,HMI上弹窗提示操作工。
这里有一个非常重要的细节:校验不通过时,不会中断当前正在使用的配方。也就是说,旧参数继续生效,设备可以保持运行,只是HMI会提示配方数据异常。如果校验不通过就直接清空工作区,设备立刻失控,那才是真正的灾难。
4.2 配方在线修改与保存:调试模式下的安全机制
配方保存的逻辑比加载稍微复杂一点,因为涉及“防误操作”。我的做法是给保存操作加三重保险。
第一重保险是“调试模式”标志。M130是调试模式开关,只有设备进入调试模式,保存按钮才有效。普通操作模式下,HMI上的保存按钮是灰色的,PLC侧也不响应。
第二重保险是二次确认。HMI上点“保存配方”按钮后,会弹出一个确认框提示“确认将当前参数保存到产品号X的配方中?”,操作工必须再点一次“确认”,才真正触发M102。
第三重保险是脉冲触发。和加载一样,保存也用上升沿触发,执行完成后自动复位,防止按钮卡住导致反复写入。
保存的核心指令段:
LD M102 PLS M103 ANI M130 // 不在调试模式,禁止保存 LD M103 AND M130 // 先计算当前工作区D100-D147的累加和 MOV K0 D70 MOV K0 Z1 FOR K48 ADD D100Z1 D70 D70 INC Z1 NEXT MOV D70 D148 // 计算目标地址偏移并写回 MOV D50 D60 SUB D60 K1 D60 MUL D60 K49 D52 MOV D52 Z0 BMOV D100 D500Z0 K49和加载相比,保存多了一步:先把工作区的48个参数累加求和,结果写入D148,然后再整体BMOV回配方存储区。这样保存之后,配方区的校验字一定是和当前48个参数匹配的。
这个顺序不能反。如果先搬运参数再算校验和,BMOV搬过去的49个字里,校验字是上一次的旧值,跟新参数不匹配,下次加载的时候一定会校验报错,把自己给坑了。
4.3 数据校验与容错:不能只做加法,还得做上限保护
和校验虽然简单,但有一个天然弱点:如果两个参数同时发生相同数值的错误,比如一个参数从100变成101,另一个参数从200变成199,累加和不变,校验就漏过去了。
大多数情况下这无所谓,因为D区数据损坏通常是整片异常,不太会出现“两个参数刚好互补”的巧合。但如果项目对安全性要求高,我会再加上两个保险。
一是给每个配方增加一个固定的特征字。比如在第48个字的位置存一个固定的十六进制数0x5A5A,加载时校验这个字,如果特征字不对,说明配方区根本不是预期格式,直接报警。特征字在程序初始化的时统一写入。
二是给参数做上下限检查。加载完成之后,逐个判断48个参数是否在工艺允许的范围内,比如目标值不能为0,不能大于设备最大量程。任何一个参数越界,都按校验失败处理。这个检查用区间比较指令ZCP逐项做就行,虽然程序长一点,但能拦住大部分异常情况。
项目刚交付的时候我总觉得校验逻辑做到“累加和一致”就够了,直到有一次现场真的出现了一个参数被写成0的情况,是和校验发现不了的。从那以后,特征字和上下限检查就成了我配方程序的标配。
5. HMI画面设计与联调细节
程序写得再好,人机界面一塌糊涂,现场照样没法用。这套项目的HMI用的是三菱GT系列触摸屏,和FX3U走编程口直接通讯。画面设计上没有做得很花哨,但每个元素都有明确的功能定位。
5.1 配方画面布局:操作工与调试员各司其职
HMI上我分了三层画面。
第一层是“生产操作画面”,给操作工用的。上面只有一个产品号选择输入框(D50)、一个当前产品号显示框(D51)、一个“切换配方”按钮(M100),以及当前配方校验状态指示灯。操作工要做的动作就三个:选产品号、按切换、看状态,没有任何多余的参数入口。
第二层是“配方参数画面”,给调试员用的。16个仓位分成4页显示,每页4个仓位,每个仓位显示3个参数,对应D100-D147里的值。画面上有保存按钮、加载按钮、调试模式开关。这一层默认隐藏,需要通过权限密码才能进入。
第三层是“配方状态画面”,给我自己调试用的。实时显示D52偏移量、Z0变址值、D70累加和、D148校验字、M30-M32校验结果位。现场排查问题的时候,这一页能告诉我程序到底在哪一步出了问题。
5.2 地址对接与数据格式
HMI和PLC地址对接时,最容易踩的坑是数据类型不一致。三菱GT Designer里数值输入/显示有“16位整数”和“32位整数”选项。我配方里的参数全部是16位带符号整数,所以D100-D147的输入框全部选16位。
但有一个地方需要特别注意:D52-D53是MUL乘法指令的32位结果区,D52是低16位,D53是高16位。当偏移量小于32767时,D53始终为0,所以画面状态里显示D52就够。但你不要因为这个就去把D52的显示改成16位无符号,因为偏移量算出来永远是正数,用16位整数显示没问题,只是在做边界测试时发现,如果产品号被错误写入一个32767以上的大数,Z0就会变成负数,BMOV的源地址直接跑到D500之前。所以范围校验永远不能省。
5.3 为什么不用HMI自带的配方功能
用GT Designer做HMI的人都知道,触摸屏本身也自带配方功能,可以建立配方表、批量写入PLC。但我在这个项目里坚持用PLC侧做配方管理,主要考虑了四点:
| 对比项 | HMI自带配方 | PLC侧配方程序 |
|---|---|---|
| 数据存储位置 | HMI内部存储 | PLC锁存D区 |
| 换屏迁移 | 配方数据要重新录入 | PLC程序不动,换屏只做画面 |
| 校验逻辑 | 基本没有 | 可以加和校验、特征字、范围检查 |
| 互锁逻辑 | 很难实现 | 可以无缝接入主流程互锁 |
| 调试观察 | 类似黑盒 | 在线监视一目了然 |
最关键的是换屏问题。现场万一触屏坏了,临时换一块同型号的新屏,HMI自带的配方数据可能因为存储格式不一致读不出来。而PLC侧方案里,配方数据全在PLC里,新屏只要通讯地址对上,画面重画一遍就能恢复使用,工厂不会因为一块触摸屏的损坏导致整套配方丢失。
6. 调试中踩过的坑与避坑建议
每个项目都是踩坑踩过来的,这套配方程序也不例外。整理几个印象最深的,给大家做个参考。
6.1 产品号被写入0或超大数,配方区直接错乱
第一次联调的时候,我在HMI上无意中把产品号输入框清空了,结果D50变成0。程序里的偏移计算是(D50-1)×49,算出来是-49,变址寄存器Z0变成负数,BMOV D500Z0读出来的数据是D500之前的地址内容,整个工作区瞬间变成一团乱码。要不是校验子程序拦住了,设备直接按错误参数投料,后果不堪设想。
这个问题的根子在于:HMI输入框允许输入任意数值,而程序没有做输入边界限制。后来我在程序里加了ZCP K1 K16 D50 M20的范围校验,把0和大于16的数值全部拦在加载逻辑之外,并且HMI输入框也设置了最小值和最大值1到16。两边一起做限制,即使有误操作,程序也不会执行异常逻辑。
6.2 切换配方时设备正在运行,参数瞬间变化
这个坑是设备方在试生产时发现的。工人趁着一批料正在配料的过程中,随手切换了产品号。D100-D147被瞬间更新,当前批次的称重目标值突然变了,一批料直接报废。
解决方式是加互锁:配料进行中标志M120为ON时,M101(加载脉冲)被M120的常闭触点串联阻断,切换动作执行不了。HMI上再做一个条件,M120为ON时切换按钮置灰,操作工看得到原因。
不过完全禁止切换也有问题,如果设备刚好处于批次间隔,切换动作还得等配料结束才能做,工人会觉得麻烦。后来我改成“预切换”模式:配料过程中允许选择产品号并暂存,但真正的BMOV传送要等本批次结束后自动执行。这样既不干扰生产,也不耽误换产。
6.3 误保存把好配方覆盖了
有一次在现场调试配比参数,技术员改了几个参数试生产,觉得效果不错,本来想“保存一下”,结果手一抖点到了旁边的“加载配方”按钮,把刚才调好的参数全部覆盖回旧值。这还不是最惨的,最惨的是有人在保存时连点两下确认,把没改完的半成品参数也存进了配方。
我的解决思路是场景分离:保存按钮只出现在配方参数画面,并加上调试模式标志和二次确认。加载按钮放在生产操作画面,两个按钮不在同一个页面,极大减少误触概率。另外,PLC侧无论加载还是保存,都用脉冲触发且执行完自动复位,即使画面按钮状态异常,也不会重复执行。
6.4 锁存区边界问题导致的配方数据丢失
这个必须单独拿出来讲。项目初期有客户反馈,配方数据偶尔会丢,而且丢得很奇怪,都是前两组的配方内容丢,后面的配方安然无恙。
查到最后发现,配方存储区从D500开始,而FX3U默认的锁存区域是从D512开始的。D500-D511这12个字根本没在锁存范围内,断电就丢。因为每个配方占49字,前两个配方的后半段正好越过D511,表现就是“部分配方丢失”。
如果设备经常停电或者有人直接拉闸,这个问题几乎必现。排查方法也很简单,在PLC参数里查看软元件锁存范围,把D500-D1300全部设为锁存。这里特别提醒一句:不同版本的GX Works/GX Developer,锁存范围设置的位置和确认方式略有不同,改完之后一定要在线读取PLC参数确认,不能只看编程软件界面上的勾选。
6.5 配方数据正常,校验却报警的离奇现象
最后一个小坑,校验偶尔误报。明明HMI上看到的参数都是正常的,加载时却报校验失败。查了半天发现,问题出在HMI的数值显示格式上。画面里某个参数框用了“显示小数点”功能,显示的是10.5,实际PLC里存的是105。操作工在HMI上把10.5改成11.0,PLC收到的是110,看起来正常,但操作工以为改了1个单位,实际改了5个单位,配方数据本身没问题,人的判断被画面误导了。
这提醒我,配方参数类的数值显示,要么全部不带小数点,要么小数位固定一致,千万不能混合格式。否则现场的人看着很正常,数据的实际含义已经偏了。
这套16仓位配方程序整体开发下来,最大的体会是:配方功能的核心不在“存数据”,而在“管数据”。地址规划、校验机制、互锁逻辑、防误操作,每一样都比单纯执行一条BMOV指令要花更多心思。项目交付之后,客户那边换产品从原来的人工改参数20分钟,变成现在30秒一键切换,而且再没出现过错改参数导致的批次报废。我做配方程序这些年,慢慢形成了一个习惯:凡是和配方相关的地址,一律在程序开头集中定义,旁边写清楚偏移计算规则。程序将来交到别人手里维护,人家一眼就能看明白结构,不用靠猜。