做三菱PLC项目这些年,我总结了一个规律:程序写到一定复杂度,真正决定效率的不是那几个常开常闭触点,而是功能指令用得好不好。尤其是在设备联调、上位机对接、数据统计这种场景里,SUM、BON、DECO、ENCO、ZRST这五条指令出现的频率极高。它们单独拿出来都不难,但很多新手只会用MOV和SET/RST,一遇到“统计某段寄存器里有多少个报警位”“把一个按钮编号翻译成对应输出点”就卡住了。这篇文章我就把这五条指令的用法、原理、实战案例和踩坑经验一次说透,建议你收藏后跟着敲一遍。
1. 这五条指令到底在帮你解决什么
1.1 从“继电器思维”切换到“字处理思维”
PLC程序最开始是继电器逻辑,一个X对应一个开关,一个Y对应一个接触器,这种思维非常直观。但当设备到了三十个点位、五十个报警、八个工位之后,你再一个一个点位去SET和RST,程序会变得又长又乱,而且很容易漏。
这时候就要引入“字”的概念。三菱FX系列里,一个D寄存器是16位,一个32位寄存器就是两个连续的D组合起来。SUM、BON、DECO、ENCO、ZRST这五条指令,本质上就是在“位”和“字”之间快速切换的工具。它们不是用来替代基本逻辑的,而是帮你把一批位当做一个整体来处理,程序量能减少很多。
我见过不少工程师把几十个报警用几十个触点逐个去统计,程序写出来几百行,后来换成SUM一条指令就解决了。这就是思维方式的差异。
1.2 五条指令的分工速览
先看一张表,心里有个轮廓再往下看:
| 指令 | 核心功能 | 典型场景 |
|---|---|---|
| SUM | 统计16/32位中ON状态的数量 | 报警点数、合格品计数、信号强度判断 |
| BON | 检测指定某一位的状态并输出到线圈 | 解析状态字、把寄存器某一位变成M点 |
| DECO | 把数值解码成对应编号的位ON | 工位选择、多路输出控制 |
| ENCO | 把位的位置编码成数值 | 按钮编码、工位号提取、优先权判断 |
| ZRST | 对一段连续区间批量复位 | 一键复位、数据区清零、设备停止时清状态 |
从这张表能看出来,SUM和BON偏“统计和解析”,DECO和ENCO偏“编码和解码”,ZRST则是收尾清理的利器。后面我会一个一个拆开讲。
2. 指令逐个拆解:格式、原理、实例
2.1 SUM:统计16位里有多少个“1”
SUM的指令格式是:
SUM S DS是源,可以是字元件,也可以是位元件;D是结果寄存器,存放ON的数量。
如果源是字元件D0,那么SUM就是统计D0这16位里为ON的位数。例如,D0 = H00FF,也就是低8位全部为ON,高8位全部为OFF,那么执行SUM D0 D10之后,D10的值就是8。
如果源是位元件,比如SUM X0 D10,那么统计的是从X0开始的16个连续输入点X0到X15里ON的个数。这一点特别常用,比如你有一组传感器输入,想判断当前有几个工位在运行,直接一条SUM就出来了。
32位模式使用DSUM,源跨越两个寄存器,统计结果最大是32。注意,DSU M的源如果是位元件,需要从指定的位开始连续32个位,比如DSUM X0 D10,统计的是X0到X31。
这条指令最需要注意的地方是结果范围。16位模式下,结果只能是0到16,不会超过这个值。不要以为D10能装下很大的数就乱用,它会按16位源来计算。实际项目里,我用SUM做报警统计时,会先把报警点整理到连续的M区,然后一次性统计,非常方便。
2.2 BON:按位取状态,做定位判断
BON的格式是:
BON S D n意思是:检测源S的第n位是ON还是OFF,然后把结果输出到D。D一般是位元件,比如M、Y。
举个例子:
BON D0 M10 K5如果D0的第5位(bit5)为ON,则M10为ON;如果为OFF,则M10为OFF。D0本身的值不会被改变,它只做只读检测。
这条指令特别适合解析通讯数据。伺服驱动器、变频器、智能仪表通过MODBUS或CC-Link通讯返回的状态字,一般是按bit定义的,比如bit0是准备好、bit1是运行中、bit2是报警。你用BON直接把每一位拆到M点,触摸屏上就能直接显示状态,不需要写一堆WAND和CMP去比较。
32位模式对应DBON,可以检测32位寄存器里的任意一位,n的范围是0到31。实际使用中,我见过有人把BON当成位判断的“瑞士军刀”,什么状态都往BON上靠,其实它的正确定位是“把寄存器里的某一位拎出来变成独立信号”,用于减少通讯协议处理和梯形图判断逻辑。
2.3 DECO:把数字翻译成位
DECO的格式是:
DECO S D n它做的事情是:把源S的低n位看作一个数值,然后让目标D开始的第“这个数值”个位变为ON,其他位都变为OFF。
这里需要区分目标D是位元件还是字元件,两种情况差别很大。
先看位元件的情况。如果D是M或Y这类位元件,n可以取1到8。例如:
DECO D0 M100 K4源D0的低4位如果数值是5,那么M105变为ON,而M100到M115这16个位里其他15个全部变为OFF。
再看字元件的情况。如果D是D寄存器,那n固定取0。例如:
DECO D0 D1 K0这条指令把D0的低4位数值解码到D1的16个bit中。如果D0低4位是5,那么D1的bit5变为ON,也就是D1 = H0010。
DECO最有价值的地方是,它一条指令就代替了十几个比较指令。比如你要根据工位号D0去选择8个气缸动作,用一堆CMP去判断D0等于0、等于1、等于2,程序写得很痛苦。用DECO D0 M0 K3,直接让M0到M7里对应位置为ON,后面每路动作就用对应的M点去控制,梯形图清爽很多。
2.4 ENCO:把位的位置编码成数字
ENCO和DECO正好相反,格式是:
ENCO S D nS是源位元件,D是目标字元件,n是编码位数。它会在从S开始的2的n次方个位里,找到编号最大的那个ON位,然后把这个编号写到D的低n位。
举例:
ENCO M100 D0 K4源范围是M100到M115,一共16个位。如果M103、M110、M115都是ON,那么ENCO会取编号最大的M115,对应的位置编号是115减100等于15,所以D0的结果就是15,也就是二进制的1111。
如果只有M103为ON,则D0等于3。
这里有个容易踩的坑:如果所有位都是OFF,D0的结果是0;如果只有第0位,也就是M100为ON,D0的结果也是0。这两种情况的结果看起来一样,程序里无法区分。所以使用ENCO时,最好先加一条判断,确认源范围内至少有一个位ON,再去取编码结果。
ENCO在按键抢答、工位输入合并这种场景下非常实用。多个按钮同时按下时,它会自动取编号最大的那一个,你可以利用这个特性做优先权处理,不用额外写联锁。
2.5 ZRST:区间批量复位
ZRST的格式是:
ZRST D1 D2它会把从D1到D2这段区间内的同类元件全部复位。D1和D2可以是位元件,也可以是字元件,但必须类型相同,不能M和D混着来。
例如:
ZRST M0 M50会把M0到M50这51个位全部变为OFF。
再比如:
ZRST D0 D99会把D0到D99这100个寄存器的值全部清零。
这条指令最大的价值就是“一键复位”。设备停止时,你需要把一批中间继电器、状态标志、计数器、数据寄存器全部清干净,用ZRST一次搞定。以前用RST逐条写,几十个RST放在程序里既不美观也容易漏。ZRST本身没有32位版本,但你可以对连续的字范围操作,例如ZRST D100 D199,这相当于把100个16位寄存器全部清零,对32位计数器来说,一次性清零连续两个寄存器即可。
ZRST对定时器T和计数器C也能起作用。例如ZRST T0 T5,可以让定时器线圈和当前值都复位。但要注意计数器C200以后是32位计数器,复位的区间范围要按32位计数器所在寄存器对的个数去推算,最好查阅对应系列手册。
3. 实战:三条指令组合成一套完整的控制逻辑
3.1 案例一:8工位转盘用DECO + ENCO做工位选择
我做过一个8工位转盘设备,每个工位有一个到位传感器,操作台上有8个按钮,工人按下某个按钮就要求转盘停到对应工位,并且把该工位的夹具松开。
第一步是按钮编码。8个按钮接在X0到X7上,执行:
ENCO X0 D0 K3按下X5,D0等于5;按下X7,D0等于7。如果同时按下多个,它自动取编号最大的那个,这符合“优先靠后”的操作习惯。
第二步是工位选择输出。执行:
DECO D0 M100 K3D0等于5时,M105变为ON,这个M点直接驱动工位5夹具松开。M100到M107分别对应8个工位的选择状态,梯形图里逻辑非常清晰。
第三步是转盘动作完成后清理。转盘到位并结束动作后,执行:
ZRST M100 M107 ZRST D0 D0把所有工位选择位和工位号清零,为下一次运行做准备。这三条指令合在一起,替代了我原来写的二十多行比较和复位逻辑,而且调试时通过监控M100到M107,一眼就能看到当前选择了哪个工位,非常直观。
3.2 案例二:用SUM统计报警点位,配合ZRST做一键复位
设备上有一组32个报警点,分布在M0到M31。触摸屏首页要显示“当前报警数量”,同时按钮按下后要一键清除所有报警。
统计数量用32位SUM:
DSUM M0 D200因为源是M0开始的连续32个位,D200里就是当前报警总数。触摸屏直接读取D200显示,不需要在触摸屏里写任何脚本。
一键复位用ZRST:
X20 ----| |----( PLS M500 ) M500 ----| |----------------------------[ ZRST M0 M31 ]---X20是触摸屏或面板上的复位按钮,按下后M500产生一个扫描周期的脉冲,ZRST把M0到M31全部复位。这里用PLS脉冲是有讲究的:如果不用PLS,ZRST会在整个按钮按下期间反复执行,虽然结果一样,但会让程序扫描周期变长;用PLS只执行一个扫描周期,逻辑更干净。
这个案例最实用的一点是,报警点必须整理在连续区间内。如果报警点散布在M0、M10、M200,那SUM没法一次统计,ZRST也没法安全复位。所以我在项目初期分配M区时,会专门划出一段连续区间给报警,比如M0到M63,所有报警统一映射到这里。这样后面统计和复位都会非常省事。
3.3 案例三:用BON拆设备状态字,对接触摸屏和上位机
伺服驱动器通过MODBUS返回一个状态字到D100,bit定义如下:bit0是准备好,bit1是运行中,bit3是报警,bit7是原点回归完成。如果靠上位机或触摸屏去解析这个字,每次都要做位运算,通讯压力大而且不直观。
我在PLC里用四条BON指令把它拆成四个独立M点:
M8000 ----| |----[ BON D100 M200 K0 ]--- M8000 ----| |----[ BON D100 M201 K1 ]--- M8000 ----| |----[ BON D100 M202 K3 ]--- M8000 ----| |----[ BON D100 M203 K7 ]---M200是准备好,M201是运行中,M202是报警,M203是原点回归完成。触摸屏直接读M200到M203,显示状态指示灯,非常简单。上位机如果要用LabVIEW或者其他组态软件读,也只需要读这几个M点,不需要去解析MODBUS寄存器里的bit。
这个思路也可以反过来用。你有一批散乱的输入点要发给上位机,先用BMOV把M区的16个位组合成一个字,上位机收到一个寄存器就能知道16个点位的状态,这就是状态字压缩。上位机界面里的“按位显示”就是这个原理,比如Grafana面板上的一些告警项,也是通过类似方式把位状态展开的。
4. 现场最容易踩的坑,我帮你总结成了“排错清单”
4.1 操作数范围设错,程序编译不过去
这是新手最容易犯的问题。DECO和ENCO的n不是随便取的,n决定了参与运算的位数量是2的n次方。比如DECO中n取4,目标位区就是16个位,从M100到M115;n取8,目标位区就是256个位,从M100到M355。如果写成DECO D0 M100 K8,那就要求M100所在区间能覆盖256个连续位,一旦超出该软元件类型的最大范围,GX Works编译时会直接报错。
还有一种情况是程序能编译过,但运行时行为不对。比如ENCO的源位范围不够,你写的源是M10 K8,理论上源范围是M10到M265共256个位,但FX3U的M区可能被前面用了很多,后面的位牵连到了其他程序,导致编码结果异常。所以用这类指数型指令前,先数一数自己划定的位区间是否完整。
4.2 索引变址与ZRST连用,区间关系可能被“翻转”
ZRST支持用变址寄存器Z去修饰D1或D2。例如:
ZRST D0Z0 D10Z1如果Z0当前值为5,Z1当前值为3,那实际复位范围是D5到D13,这个没问题。但如果Z0改成5,Z1改成1,实际范围就变成了D5到D11,还是正常的。
问题出在D1的最终地址大于D2的情况。比如Z0当前值为10,Z1当前值为0,范围是D10到D10,这没毛病。可一旦你用循环程序去修改Z0,让它大于D2的最终地址,三菱的ZRST不会帮你自动交换区间,它只会按从D1到D2的方向执行,结果就变成只复位D1一个地址,或者干脆没有达到预期效果。
所以我的习惯是,只要ZRST参与了变址,就在执行前用比较指令判断Z0和Z1的关系,确保D1最终地址不大于D2。如果两个端点都是固定地址,那只要D1的编号小于等于D2就可以。
4.3 仿真通了,现场不行:扫描周期与输入滤波差异
GX Works2的仿真功能对这几条指令的运算结果模拟得很准确,但现场和仿真最大的区别在于物理信号的真实时序。
我之前做过一个抢答器项目,用ENCO X0 D0 K3去编码8个抢答按钮,GX Works仿真里一切正常,但现场调试时按钮按下后编码结果偶尔会跳变。后来查了很久,发现是两个按钮被同时按下时,它们之间的时间差只有几毫秒,而PLC的输入滤波默认时间是10毫秒左右。在滤波窗口内,X输入状态可能瞬间出现两个ON,ENCO取编号最大的那一个,所以结果会出现“跳号”现象。
解决方法是两种。第一种是调整PLC输入滤波时间,X0到X7统一设置成1毫秒或更短。第二种是在ENCO后面加一个“确定键”联锁,只有按下确认按钮才把编码结果锁存到D寄存器,这样即使输入有抖动,也不会影响最终工位号。
4.4 常见问题速查表
| 现象 | 可能原因 | 处理办法 |
|---|---|---|
| SUM结果明显偏大 | 源位区间里有未使用的位常ON | 把多余位手动置OFF,或改用只包含有效位的字寄存器 |
| DECO输出位置不对 | n取错,实际区间不是2的n次方 | 按2的n次方反推区间,检查编译范围 |
| ENCO结果和按钮位置对不上 | 多个按钮同时ON,取的是最大编号 | 增加联锁或按需求调整优先级 |
| ZRST执行后仍有位为ON | 复位后程序马上又SET了该位 | 检查逻辑顺序,确认复位指令在置位逻辑之后 |
| 通讯状态字解析错误 | BON的n写错,位编号从0开始 | 先看手册的bit定义,再从0开始编号 |
这张表基本覆盖了我这几年现场调试遇到的主要问题。你如果遇到其他奇怪现象,优先打开监控模式,把源寄存器的当前值和目标M点的状态逐个对照,通常几分钟就能定位。
5. 除了以上案例,这几条指令还能这样用
5.1 状态字压缩与远程监控
现在很多项目都要接MES、SCADA或者自建监控平台。上位机读PLC点位,点位越多,通讯负担越重。如果现场有几百个输入点,每个点都单独建通道,上位机配置量非常大。
我的做法是用MOV指令把连续M区的16个位打包成一个字,再用MODBUS去读这个字。例如MOV M0 D100,就把M0到M15打包到了D100。上位机收到D100后,按bit展开就能得到16个点位状态。如果需要做更复杂的统计,比如监控面板上显示“当前有几个报警”,那就用SUM对打包后的字再统计一次,上位机只需要读D200这一个寄存器就够了。
BON则是反过来用,当上位机把状态字写过来时,PLC端用BON拆成M点。这种双向处理的思路,在对接LabVIEW、组态王、Grafana这类工具时特别实用。
5.2 步进电机与HMI联动中的典型用法
控制步进电机时,经常要做速度档位选择和当前位置判断。比如设备上有8个速度档位,物理上是一个8位拨码开关接到X0到X7,用ENCO X0 D0 K3就能把档位号直接读出来,然后通过比较档位号去调用对应的脉冲频率参数。如果不用ENCO,就得写8行CMP去判断档位。
ZRST在步进屏控制里也常用。设备停机时,需要把当前脉冲累计寄存器、方向标志、运行中标志、暂停标志全部清掉,一条ZRST把连续分配区的寄存器和M点一次性复位,避免下次启动时残留数据导致误动作。
和HMI联动时,触摸屏上如果有很多报警指示灯,对应的M点建议全部放在一个连续区段。这样ZRST一键复位报警灯时,只需要一条指令,不需要在触摸屏脚本里逐个复位。这个设计在项目前期分配软元件地址时就要想好。
5.3 FX系列和Q系列的区别
这套指令在FX和Q系列里都支持,Q系列的功能更强大,操作数范围更大。比如Q系列中,软元件的编号范围和FX不同,M区、D区的可用地址更多,DECO和ENCO的位数范围也可能有细微差异。换平台时不能只改PLC型号,必须把指令手册翻一遍,确认软元件编号、操作数边界、双字指令对应关系。
另外,Q系列里有些指令支持文件寄存器、支持多维数组访问,ZRST对特定存储区可能还需要额外的操作。我的建议是,项目从FX迁移到Q系列时,先用小程序把每一条指令在模拟环境里跑一遍,确认结果和FX一致后再批量移植。
最后再分享一个小技巧
我每次教徒弟用这五条指令,都会让他们做一个小练习:用X0到X7接8个按钮,用Y0到Y7接8个指示灯,要求按任意按钮时只有对应指示灯亮,同时用D0显示当前按钮编号。这个练习把ENCO、DECO、ZRST全部用上了,一小时内基本能掌握核心用法。
我个人的体会是,功能指令不是背得越多越厉害,而是要在项目里真正用上一次,才会形成肌肉记忆。这篇文章里提到的几个案例,建议你在GX Works里逐个敲一遍,甚至故意把n写大、把范围写错,多看几次报错信息,你就真正理解了。下次做项目再遇到类似需求,你会回来感谢这几个指令的。