1. 项目概述:从“指令”到“逻辑”的桥梁
干了十几年自动化,从三菱FX系列玩到西门子S7-1500,再到现在各种国产品牌,我越来越觉得,PLC编程的核心,其实就藏在这些看似枯燥的“指令”里。很多人觉得PLC编程就是画梯形图,照着说明书拖指令,这没错,但只对了一半。另一半,是你得真正理解每一条指令背后的“脾气”和“边界”。今天,我们不聊那些宏大的系统架构,也不讲复杂的通讯协议,就沉下心来,掰开揉碎了聊聊“PLC编程指令”这个最基础、也最核心的东西。无论你是刚入门的新手,还是想梳理知识体系的老手,希望这篇从实战角度出发的梳理,能帮你把手里零散的指令卡片,搭建成坚固可靠的程序大厦。
简单说,PLC编程指令就是PLC的“单词”和“语法”。你用这些“单词”(指令)按照“语法”(编程语言,如梯形图、结构化文本)组合起来,形成一段段“句子”(程序段),最终告诉PLC:“当这个按钮被按下(输入条件),你就去启动那个电机(输出动作),并且记下它已经运行了多久(数据处理)”。指令的质量和使用的恰当与否,直接决定了程序的稳定性、效率和可维护性。接下来,我会从设计思路、核心指令解析、实操编程要点到常见问题排查,带你完整走一遍。
2. 核心设计思路:指令不是孤岛,是系统的一部分
很多人学指令是孤立地学,LD是取,OUT是输出,MOV是传送,然后呢?在实际项目中,指令从来不是单独生效的。我的思路是,把指令放在三个维度里看:信号流、数据流和时间流。
2.1 信号流:逻辑控制的骨架
这是最直观的一层,对应着基本的位逻辑指令。你的核心任务是用指令搭建一条清晰的“电路”路径。
- 取指令(LD/LDN):这不是简单的“读取”,而是逻辑路径的起点。LD取常开,意味着“当这条路径导通时”;LDN取常闭,意味着“当这条路径断开时”。选择哪一个,取决于你的物理接线(常开/常闭触点)和逻辑设计(是检测“有信号”还是“无信号”动作)。
- 输出指令(OUT):这是逻辑路径的终点,但绝不是结束。OUT驱动一个线圈(Q点),但这个线圈的状态会反过来影响其他用到它的触点。这里最常见的坑是双线圈输出:同一个线圈在程序的不同位置被OUT,PLC以最后一个扫描周期执行的结果为准,这会导致逻辑混乱,是调试中的噩梦。
- 中间继电器(M点)与置位/复位(SET/RST):这是构建复杂逻辑的关键。用M点存储中间状态,比如“自动模式已启动”、“报警已应答”。SET/RST指令比OUT更可控,SET后状态自保持,直到被RST,非常适合做状态标志位和单按钮启停控制。
实操心得:画梯形图时,在脑子里同步想象一条电流的流向。从左侧母线(电源正极)开始,经过一系列触点(开关)的条件判断,最终能否流到线圈(负载)。这条“电流”路径必须清晰、无冲突。对于复杂的联锁,我习惯先用M点做出所有的互锁和条件状态,最后再用一个统一的网络去OUT最终的执行机构,这样逻辑清晰,便于调试。
2.2 数据流:让PLC拥有“记忆”和“智慧”
如果说信号流是开关控制,数据流就是大脑运算。涉及定时、计数、比较、运算和传送。
- 定时器(TON/TOF/TP)与计数器(CTU/CTD):这是顺序控制的基石。TON(接通延时)用的最多,但参数设置是关键。比如一个TON定时器,你设定PT=1000,单位是ms还是100ms?这取决于PLC的时基。三菱FX的T0-T199是100ms时基,设定1000就是100秒!务必查手册。
- 比较指令(CMP、>、<、=)与运算指令(ADD、SUB、MUL、DIV):用于工艺判断。例如,当实际温度(存储在D100)大于设定温度(D200)时,关闭加热器。这里的数据类型必须一致,整数和浮点数不能直接比较,需要转换。
- 传送指令(MOV)及其变种:这是数据搬运工。但MOV只是拷贝,源数据不变。在涉及批量数据初始化、配方调用时,会用成块传送(BMOV)或浮点数传送(EMOV)。西门子S7-1200/1500里灵活的“MOVE”指令能自动处理不同类型的数据,但也要注意溢出问题。
2.3 时间流:理解扫描周期,指令执行不再“玄学”
所有指令都在PLC的扫描周期内执行。一个周期包括:读取输入、执行程序、更新输出。这意味着:
- 同一周期内,后面的指令可以读到前面指令执行的结果。比如你用MOV把10传给D0,紧接着在同一周期用CMP判断D0是否等于10,结果是“真”。
- 输入的变化不会立即影响输出,必须等到下一个扫描周期。对于高速脉冲捕捉或精确定时,这就需要用到“立即指令”(如LDI、OUTI)或中断功能。
- 复杂的数学运算指令(如浮点运算、三角函数)可能占用多个扫描周期,如果你在运算结果未出来前就去使用它,会得到错误值。这时需要用“运算完成”标志位或延时来确保数据就绪。
理解这三流合一,你选用指令时就不再是机械地套用,而是会思考:我这个操作,主要是处理开关信号、进行数据运算,还是对时序有严格要求?从而选择最合适、最高效的指令组合。
3. 核心指令集深度解析与选用指南
市面上PLC品牌众多,指令名称和符号略有差异,但核心思想相通。下面我以最常见的类别,结合不同品牌(如西门子、三菱、汇川)的实例进行对比解析。
3.1 位逻辑指令:构建安全逻辑的基石
这是使用频率最高的指令群,安全是第一位。
| 指令类别 | 典型指令(西门子) | 典型指令(三菱) | 核心功能与要点 | 常见应用场景与避坑 |
|---|---|---|---|---|
| 触点与线圈 | I0.0,Q0.0,M0.0 | X0,Y0,M0 | 构成逻辑电路的基本元素。 | 急停回路必须使用常闭触点(NC)接入,并在程序中使用常开指令(LD)判断断开状态,实现“断线安全”。 |
| 置位/复位 | SET,RST(S7-200 SMART) /S,R(S7-1200/1500) | SET,RST | 具有保持功能的输出。SET后状态锁存,直至被RST。 | 用于设备运行状态、模式标志、报警锁存。切记要成对使用并在合适的地方复位,避免状态“卡死”。 |
| 边沿检测 | P(上升沿),N(下降沿) | LDP,LDF,ANDP,ANDF等 | 捕捉信号变化瞬间,常用于启动、计数、消抖。 | 消抖:用定时器配合边沿检测,避免机械触点抖动误触发。注意:边沿检测指令在每个扫描周期都会比较当前值和上一周期值,要确保其直接连接在触点后,中间不要有复杂逻辑。 |
| 取反 | NOT | INV | 对逻辑结果取反。 | 简化逻辑表达。例如,当“非自动模式”时执行手动操作。使用时要格外小心,容易让逻辑变得晦涩。 |
注意事项:在构建复杂的启保停、联锁、互锁电路时,我强烈建议使用顺序功能图(SFC)的思维来设计,然后用基本的位逻辑指令去实现。先画出状态转移图,明确每个状态的条件和输出,再翻译成梯形图,这样程序结构会清晰得多,也易于调试和维护。
3.2 数据处理与运算指令:程序智能化的关键
这部分指令让PLC从“继电器替代品”变成了“工业控制器”。
传送指令:
- MOV:万金油,但要注意数据长度。西门子S7-300/400的MOVE指令需要指定长度(如MOVE_B、MOVE_W、MOVE_DW)。三菱的
MOV K100 D0是将十进制100传到D0。 - 块传送:用于初始化数据块或复制数组。例如,开机时将一组预设参数(配方)从DB块传送到工作区。
- 交换指令:如三菱的
XCH D0 D1,可用于数据排序或算法中。
- MOV:万金油,但要注意数据长度。西门子S7-300/400的MOVE指令需要指定长度(如MOVE_B、MOVE_W、MOVE_DW)。三菱的
比较指令:
- 除了大于、等于、小于,还有范围比较(如三菱的
ZCP指令,判断一个值是否在某个区间内)。这在温度控制、液位控制中非常有用。 - 浮点数比较要小心:由于浮点数精度问题,避免直接用
=判断相等,而应判断两数差的绝对值是否小于一个极小值(如|D0 - D1| < 0.001)。
- 除了大于、等于、小于,还有范围比较(如三菱的
算术运算:
- 整数运算:注意溢出。16位整数(-32768~32767)相加可能超过范围,导致结果错误。必要时使用32位整数(双字)运算。
- 浮点数运算:涉及小数和复杂计算时使用。执行速度比整数慢。在运动控制中计算位置、速度时必不可少。
- 自增/自减:如
INC、DEC,常用于循环计数。在FOR/NEXT循环中作为步进计数器。
3.3 定时与计数指令:顺序与流程的控制核心
定时器:
- 接通延时(TON):最常用。输入导通后,定时器开始计时,时间到则输出导通。输入断开,定时器立即复位。
- 断开延时(TOF):输入导通时,输出立即导通;输入断开后,定时器开始计时,时间到则输出断开。常用于设备停机后的延时吹扫、冷却。
- 脉冲定时器(TP):输入导通的瞬间,输出立即导通一个固定宽度的时间脉冲,与输入信号持续时间无关。可用于生成固定宽度的脉冲信号。
- 关键参数:预设值(PT)和时基。务必根据手册设置正确的时间单位(1ms, 10ms, 100ms, 1s)。
计数器:
- 加计数器(CTU):CU端每收到一个上升沿,当前值加1,当前值>=预设值(PV)时,输出触点导通。
- 减计数器(CTD):CD端每收到一个上升沿,当前值减1,当前值<=0时,输出触点导通。
- 加减计数器(CTUD):具备加和减计数功能。
- 应用技巧:计数器复位端(R)要谨慎使用。通常用外部按钮或特定条件复位。如果将复位端一直导通,计数器将无法计数。高速计数需使用专用的高速计数器(HSC)指令和硬件端子。
3.4 程序控制指令:提升程序结构的利器
这类指令用于优化程序流程,增强可读性。
- 跳转与标签(JMP/LBL):满足条件时跳过一段程序不执行。可用于调试(跳过某些功能)或实现模式选择。滥用会导致程序逻辑碎片化,难以跟踪,建议仅在必要时使用。
- 子程序调用(CALL/RET):将重复使用的功能(如气缸控制、报警处理)封装成子程序(FC/FB),使主程序简洁。FB带有背景数据块,可以保存状态,非常适合控制多台同类设备(如多个电机)。
- 循环指令(FOR/NEXT):用于重复执行一段代码特定次数。例如,初始化一个数组的所有元素。注意循环体内的扫描时间,避免导致整个扫描周期过长。
- 结束指令(END):表示主程序结束。之后的指令不被执行。有些PLC是隐含的,有些需要手动添加。
4. 实战编程:从指令到稳定程序的进阶之路
懂了指令,不等于能写出好程序。下面我以一个经典的“小车自动往返控制”为例,展示如何综合运用指令。
控制要求:小车由一台电机驱动,在A、B两点间自动往返。按下启动按钮,小车从A点向B点运行,到达B点(限位开关B动作)后自动返回A点,到达A点(限位开关A动作)后再自动向B点运行,如此循环。按下停止按钮,小车完成当前行程后停在当前位置(或立即停止,根据安全要求定)。要求有必要的互锁和状态指示。
4.1 第一步:定义I/O与变量(以西门子S7-1200风格为例)
| 符号 | 地址 | 注释 |
|---|---|---|
Start | I0.0 | 启动按钮(常开) |
Stop | I0.1 | 停止按钮(常闭) |
LS_A | I0.2 | A点限位(常开,碰到导通) |
LS_B | I0.3 | B点限位(常开,碰到导通) |
Motor_Forward | Q0.0 | 电机正转(向B点) |
Motor_Reverse | Q0.1 | 电机反转(向A点) |
Auto_Running | M0.0 | 自动运行模式标志 |
Cmd_To_B | M0.1 | 命令:向B点运行 |
Cmd_To_A | M0.2 | 命令:向A点运行 |
4.2 第二步:程序设计与指令实现(梯形图)
网络1:启动/停止与运行模式控制
I0.0 I0.1 M0.0 --| |-------|/|-------(S) // 启动按钮按下,且停止按钮未按下,置位运行标志 M0.0 --| |-------(R) // 停止按钮按下,复位运行标志这里用了置位复位指令,而不是普通的线圈输出,是为了实现“启动按钮点动后,运行状态保持”。
网络2:自动往返逻辑判断
M0.0 I0.2 M0.2 --| |-------| |-------(S) // 自动运行中,到达A点,置位“去B点”命令 M0.0 I0.3 M0.1 --| |-------| |-------(S) // 自动运行中,到达B点,置位“去A点”命令 M0.1 I0.2 --| |-------| |-------(R) // 正在去B点途中,到达A点(起点),复位“去B点”命令 M0.2 I0.3 --| |-------| |-------(R) // 正在去A点途中,到达B点(终点),复位“去A点”命令这是核心逻辑。利用限位信号和命令标志的互锁,实现状态的自动切换。注意,这里
M0.1和M0.2在大部分时间是互斥的。
网络3:电机输出驱动(带互锁)
M0.1 M0.2 Q0.1 --| |-------|/|-------( ) // “去B点”命令有效,且“去A点”命令无效,则正转 M0.2 M0.1 Q0.0 --| |-------|/|-------( ) // “去A点”命令有效,且“去B点”命令无效,则反转这里加入了电气互锁(用对方的常闭触点串联),确保正反转输出不会同时有效,保护电机和驱动器。
网络4:停止逻辑处理(完成当前周期后停止)
I0.1 M0.0 // 如果停止按钮按下,且处于自动运行模式 --| |-------| |----[MOV 0, MW10] // 可以置位一个“请求停止”标志(这里用MW10存储) M0.1 I0.2 // 如果正在去B点,且到达了A点(说明已完成回程) --| |-------| |----[R M0.0] // 复位运行标志,停止 M0.2 I0.3 // 如果正在去A点,且到达了B点(说明已完成去程) --| |-------| |----[R M0.0] // 复位运行标志,停止这是“完成当前周期停止”的实现。如果需要急停,则可以在网络1的停止回路中直接复位
M0.0,并立即复位M0.1和M0.2,同时断开电机输出。
通过这个案例,你可以看到,基本的位逻辑指令(LD、OUT、SET、RST)、比较判断(隐含在触点逻辑中)和简单的数据传送(MOV)是如何协同工作,完成一个看似复杂的自动控制任务的。关键在于将控制要求分解为清晰的状态和条件,并用合适的指令去实现状态转移和输出。
5. 高级指令应用与性能优化
当基础指令玩熟后,就需要一些“高级货”来提升程序的效率和优雅度。
5.1 移位与循环指令:处理流水线与状态步进
- 应用场景:装配流水线上,工件每前进一个工位,对应的操作(如拧螺丝、打标)就需要跟进。可以用一个位序列(如
MB0)来表示工位状态,用移位指令(如SHL_B)在检测到步进信号时,将整个状态序列左移或右移一位。 - 注意事项:移位指令会移出数据位,需要根据工艺决定是补0还是循环填入。循环移位指令(如
ROL、ROR)则用于需要周期性轮询的场景。
5.2 表指令与寻址:批量数据处理的利器
- 先入先出(FIFO)/后入先出(LIFO):用于缓存数据。例如,在包装线上,扫描枪读到多个产品条码,PLC需要按读取顺序依次处理,就可以用FIFO指令队列存储。
- 间接寻址与指针:这是实现灵活程序的关键。例如,你需要对DB块中从DBW0到DBW100的50个温度值进行平均。你可以写一个循环,使用指针
P#DBX0.0并每次增加2个字节(一个字)的偏移量来遍历所有数据。这避免了写50条相同的加法指令。L P#DBX0.0 // 加载起始地址指针 T MD20 // 存入地址寄存器 L 0 // 清空累加器 T MD24 // 存入求和结果 L 50 // 循环次数 Next: T MB30 // 循环计数器 L MD24 L DBW [MD20] // 间接读取DB块中的数据 +R // 浮点数相加 T MD24 L MD20 L P#2.0 // 指针增加2个字节 +D T MD20 L MB30 LOOP Next // 循环 L MD24 L 50.0 /R // 求平均 T DBW200 // 存储平均值注意:间接寻址功能强大但容易出错,务必确保指针地址在有效范围内,避免访问非法内存导致PLC停机。
5.3 字符串与通讯指令:人机交互与系统联网
- 字符串处理:
CONCAT(连接)、LEFT、RIGHT、MID(截取)、FIND(查找)等指令,用于处理来自触摸屏、扫码枪、上位机的文本信息。 - 通讯指令:如西门子的
TSEND_C、TRCV_C,三菱的RS指令,用于实现PLC与变频器、仪表、机器人、其他PLC之间的数据交换。这是实现工厂信息化的基础。调试通讯的核心是确保双方波特率、数据位、停止位、校验位等参数完全一致,并理解清楚数据交换的映射关系。
6. 常见问题、调试技巧与避坑指南
这部分是我多年调试经验的浓缩,很多都是手册里不会写的“血泪教训”。
6.1 指令执行结果不符合预期?按这个流程查
- 查电源与接线:这是所有问题的第一步。用万用表量一下输入点电压是不是24V?输出点有没有负载短路?特别是传感器是NPN还是PNP接法?别笑,至少30%的“程序问题”根源在这里。
- 查PLC运行状态:PLC是否在RUN模式?有没有致命错误导致STOP?查看诊断缓冲区。
- 查程序扫描:在线监控程序,看逻辑流是否按你预想的路径执行。重点监控:
- 关键的条件触点(如启动、停止、限位)的物理状态和程序状态是否一致。
- 定时器/计数器的当前值是否在变化?预设值对吗?
- 数据寄存器的值是否正确?有没有被其他地方意外改写?(这就是变量地址重叠或重复复位的典型症状)。
- 查指令细节:
- 操作数地址对吗?你操作的是
DB1.DBX0.0还是M0.0? - 数据类型匹配吗?试图把
INT传给REAL指令? - 边沿指令用的对吗?是不是放错了位置,导致每个周期都触发?
- 双线圈问题:全局搜索同一个输出点(如
Q0.0),看是否在多个网络中被输出。
- 操作数地址对吗?你操作的是
6.2 典型问题速查表
| 问题现象 | 可能原因 | 排查思路与解决方法 |
|---|---|---|
| 输入点有信号,但程序里看不到 | 1. 输入滤波时间过长。 2. 硬件组态中地址分配错误。 3. 输入点损坏。 | 1. 在线修改输入滤波时间(通常可设为0.1ms~12.8ms)测试。 2. 核对硬件组态与实际模块位置、地址。 3. 短接该输入点公共端和信号端,看程序状态变化。 |
| 输出点程序已导通,但外部无输出 | 1. 输出点未强制或未下载。 2. 输出点被其他逻辑(如急停、互锁)断开。 3. 输出回路断路或负载过大。 4. 输出点损坏。 | 1. 在线强制该输出点,观察外部指示灯和电压。 2. 监控整个输出逻辑路径,查找隐蔽的断点。 3. 检查接线和负载。 4. 更换备用点测试。 |
| 定时器不计时或不准 | 1. 定时器使能条件不满足或瞬间导通。 2. 预设值(PT)单位错误(如把ms当成s)。 3. 定时器编号与时基不匹配。 4. 扫描周期过长,影响精度。 | 1. 确保使能条件持续导通。 2. 仔细查阅手册,确认定时器分辨率。 3. 核对定时器编号范围(如T0-T199是100ms)。 4. 优化程序,减少扫描时间,或使用高精度定时中断。 |
| 数据运算结果错误 | 1. 数据类型不匹配(如整数除法舍去小数)。 2. 数据溢出(超过数据范围)。 3. 运算指令未执行完成就使用结果。 4. 地址重叠,数据被意外覆盖。 | 1. 使用转换指令(如INT_TO_REAL)统一类型。2. 改用更大范围的数据类型(如DINT、REAL)。 3. 使用指令的“完成位”或添加一个扫描周期延时。 4. 检查数据块和变量定义,确保地址唯一。 |
| 通讯指令失败 | 1. 物理连接(线缆、端口)问题。 2. 通讯参数(波特率、站号等)不一致。 3. 通讯缓冲区被其他任务占用。 4. 指令使能条件或管脚参数错误。 | 1. 检查接线,更换端口测试。 2. 逐项核对双方所有通讯参数。 3. 确保同一时刻只有一个通讯任务激活。 4. 在线查看指令的错误代码(Error),根据代码查手册。 |
6.3 提升程序质量的几个习惯
- 规范命名与注释:不要再用
M0.0、D100。给每一个触点、线圈、数据变量起一个见名知意的符号名,如Auto_Start_Cmd、Tank_Level_Actual。在每个网络开头写上注释,说明这段逻辑的目的。三个月后,你和你的同事都会感谢这个习惯。 - 模块化与复用:把反复使用的功能,比如一台电机的启停、正反转、调速,封装成一个功能块(FB)。下次用到时,直接实例化一个,传入不同的IO地址和参数即可。这极大减少了重复劳动和出错几率。
- 善用状态机编程:对于顺序流程,使用状态机(用枚举变量表示状态)比单纯用一堆互锁的位逻辑要清晰十倍。状态转移图一目了然,调试时只需关注当前状态和转移条件。
- 预留调试接口:在关键变量附近,增加一些用于调试的
M点,可以手动强制或复位,模拟某些条件。在正式交付前,再将这些调试点屏蔽或删除。 - 理解扫描周期,善用立即指令和中断:对于需要快速响应的信号(如高速计数、急停),不要依赖主程序扫描,要使用硬件中断(OB40等)或立即输入/输出指令来处理。
指令是砖瓦,思维是蓝图。掌握每一条指令的细节是基本功,而如何将它们有机地组合起来,构建出稳定、高效、易维护的控制程序,才是真正的编程艺术。这个过程没有捷径,就是多看、多写、多调试、多总结。每次解决一个诡异的故障,你对指令的理解就会更深一层。最后,保持好奇心,新的PLC品牌、新的指令集出现时,去学一学,你会发现核心思想都是相通的,只是表达方式不同罢了。