☰
PLC基本指令实战精讲:从梯形图逻辑到工程避坑指南
2026/10/23 3:52:39 网站建设 项目流程

1. 为什么还要死磕PLC基本指令

干了十几年工控,我见过太多人栽在“基本功”上。有人拿着西门子1200的编程手册翻来覆去地看,梯形图里一个自锁回路却写得漏洞百出;有人能把伺服参数调得飞起,结果设备一上电,一个简单的起保停都跑不起来。问题出在哪?不是他们不懂高级功能,是基本指令没吃透。

PLC基本指令,说白了就是编程语言里的“九九乘法表”。你乘法竖式再溜,口诀背不熟,算个账照样卡壳。接触器、继电器、定时器、计数器、比较指令、传送指令——这些东西看着简单,但真正到了现场,设备动作不对、信号丢失、时序错乱,十有八九都是这些“简单东西”没处理好。

这篇文章我打算把PLC基本指令从头到尾捋一遍。不是照搬手册那种念经式的讲法,而是结合我这些年在产线上踩过的坑、调过的设备、半夜被叫起来处理的故障,把每个指令背后的逻辑、使用场景、容易翻车的地方都讲清楚。不管你是刚入行的新手,还是干了几年但总觉得基础不牢的老手,看完应该都能有点收获。文章会以梯形图为主,因为这是现场最通用的表达方式,三菱、西门子、欧姆龙、台达,底层逻辑都相通。

2. 基本指令的底层逻辑:从继电器控制到梯形图

2.1 常开常闭不是“开关”,是条件判断

很多人初学PLC,把常开触点理解成“开关按下就通”,常闭触点理解成“开关按下就断”。这个理解不能说错,但太浅了。在PLC的扫描周期里,常开和常闭本质上是对位变量当前值的判断。

常开触点:如果这个位是1,条件成立,能流通过;如果是0,条件不成立,能流断开。 常闭触点:如果这个位是0,条件成立,能流通过;如果是1,条件不成立,能流断开。

你看,它跟物理开关没有必然关系。一个输入点X0接的是常开按钮还是常闭按钮,跟你在程序里用常开还是常闭触点,是两码事。我见过一个经典错误:现场急停按钮接的是常闭触点(安全规范通常要求急停用常闭,断线也能触发停机),结果有人在程序里又用了一个常闭触点X0。按钮没按的时候,物理回路是通的,X0为1,程序里的常闭触点断开,设备直接没法启动。他查了半天硬件,最后发现是程序里触点用反了。

注意:输入硬件是常闭还是常开,决定了PLC输入映像寄存器的初始状态。程序里用常开还是常闭,取决于你的控制逻辑需要。两者不要混为一谈。

2.2 能流的概念:梯形图的“电流”

梯形图看起来像电路图,但它没有真正的电流,只有“能流”——一种逻辑上的导通状态。能流从左母线出发,经过一系列触点条件,最终到达线圈或功能指令。能流能走通,线圈就动作;走不通,线圈就不动作。

这个“能流”是单向的,从左到右,不能倒流。所以梯形图里不允许出现回路,也不允许线圈直接接左母线(某些品牌允许,但不推荐)。理解能流的方向性,对后面理解自锁、互锁、置位复位非常关键。

2.3 扫描周期:为什么你的程序“慢半拍”

PLC是循环扫描执行的。一个扫描周期分三个阶段:输入采样、程序执行、输出刷新。输入采样阶段,把物理输入点的状态读进输入映像寄存器;程序执行阶段,从头到尾扫一遍程序,根据映像寄存器的值计算结果,写到输出映像寄存器;输出刷新阶段,把输出映像寄存器的值送到物理输出点。

这意味着什么?意味着你的程序里,输入信号的状态在整个扫描周期内是“冻结”的。如果在一个扫描周期内,某个输入变了,程序里看不到,要等下一个周期才更新。同样,输出线圈的状态也是在扫描周期末尾才真正输出。

这个机制导致两个常见问题:一是输入信号窄于一个扫描周期,可能被漏掉;二是程序里前后使用的同一个输入点,状态是一致的,不会因为物理信号变化而中途改变。理解这一点,才能理解为什么有些逻辑要放在中断里,为什么有些信号要用上升沿捕捉。

3. 触点与线圈指令:最基础也最容易翻车

3.1 常开常闭触点的实战用法

常开触点(LD/LDI、AND/ANI、OR/ORI)和常闭触点在程序里无处不在。但什么时候用常开,什么时候用常闭,很多人是凭感觉。我的经验是:从“条件成立”的角度去想,而不是从“开关状态”去想。

比如一个启动按钮,按下时信号为1,我要它触发启动,那就用常开触点。一个停止按钮,现场接的是常闭按钮,不按时信号为1,按下时信号为0,我要它在按下时切断运行,那就用常闭触点。再比如一个限位开关,平时不碰是0,碰到是1,我要“碰到就停”,那就用常开触点;我要“没碰到才允许运行”,那就用常闭触点。

这里有个口诀:你要什么条件成立,就用什么触点去判断。条件成立时该位为1,用常开;条件成立时该位为0,用常闭。别去管物理开关长什么样。

3.2 输出线圈与置位复位

输出线圈(OUT)是最常用的指令。能流通过时,线圈对应的位被置1;能流断开时,被置0。注意,是每个扫描周期都重新写一次。也就是说,如果某个扫描周期能流断了,线圈立刻变0,不管之前是什么状态。

置位(SET)和复位(RST)则不同。置位是能流通过时把位置1,之后即使能流断了,位保持1,直到遇到复位指令。复位同理。

什么时候用OUT,什么时候用SET/RST?我的原则是:如果这个输出在每个扫描周期都能明确计算出该通还是该断,用OUT;如果这个输出是事件触发的,需要保持状态,用SET/RST。

举个例子,一台电机的运行控制。如果逻辑是“启动按钮按下且没有停止信号且没有故障,电机运行”,那用OUT最合适,因为每个周期都能算出结果。但如果逻辑是“按下启动按钮,电机运行并保持;按下停止按钮,电机停止”,用SET/RST更直观,启动按钮用SET,停止按钮用RST。

但SET/RST有个坑:如果同一个位被多处SET和RST,最后执行的那条指令决定最终状态。我见过一个程序,同一个输出线圈在三个地方被SET,两个地方被RST,结果调试的时候动作完全乱套。后来全部改成OUT,逻辑一下子清晰了。

实操心得:新手阶段尽量用OUT,少用SET/RST。OUT强迫你每个周期都考虑完整条件,不容易出逻辑漏洞。等熟练了,再在合适的地方用SET/RST。

3.3 上升沿与下降沿:捕捉瞬间信号

上升沿(LDP、ANDP、ORP)和下降沿(LDF、ANDF、ORF)是基本指令里最容易被忽视、也最容易出问题的部分。

上升沿的作用是:当条件从0变1的那个扫描周期,能流通过一个周期,之后即使条件保持为1,也不再通过。下降沿同理,从1变0时通过一个周期。

为什么需要沿指令?因为PLC扫描周期很快,一个按钮按下去,可能持续几十毫秒到几百毫秒,这期间程序已经扫描了几百上千次。如果你用普通触点去触发一个计数或者一个动作,它会触发很多次。用上升沿,只触发一次。

我踩过的一个坑:用普通触点去触发一个数据传送指令,结果按钮按一下,数据被传送了几百次。虽然结果一样,但扫描周期被拖慢了,而且如果传送的目标地址在变化,就会出问题。后来改成上升沿触发,干净利落。

沿指令还有一个常见用法:配合SET/RST做单按钮启停。一个按钮,第一次按启动,第二次按停止。逻辑是:按钮的上升沿触发一个辅助继电器M0,M0的常开和电机的常闭做自锁,M0的常开和电机的常开做互锁……这个逻辑用沿指令实现起来很简洁,但初学者容易绕晕。我的建议是,先把普通自锁回路写熟,再玩单按钮启停。

4. 定时器与计数器:时间与数量的控制核心

4.1 定时器:不只是“延时”

定时器(T)是PLC里使用频率最高的功能指令之一。不同品牌的定时器类型和精度不同,但基本原理一致:有一个设定值(SV),一个当前值(PV),一个计时条件。计时条件成立时,当前值开始累加(或递减),到达设定值后,定时器输出触点动作。

三菱的定时器T0-T199是100ms精度,T200-T245是10ms精度;西门子的TON、TOF、TP类型各有用途。这里不展开品牌差异,重点讲使用逻辑。

定时器最常见的用法是延时启动、延时停止、闪烁、超时报警。但有几个细节容易被忽略:

第一,定时器的计时条件是能流。能流通过时计时,能流断了就复位(普通定时器)。所以如果你希望定时器在某个条件成立后开始计时,条件消失后保持当前值,那要用保持型定时器,或者用SET指令把计时条件锁住。

第二,定时器的设定值可以是常数,也可以是寄存器。用寄存器做设定值,可以在触摸屏上修改,灵活性高很多。但要注意寄存器的数据类型和范围,别设了一个超出定时器量程的值。

第三,多个定时器级联可以实现长时间延时。比如一个定时器最大只能设3276.7秒,你要延时两小时,可以用第一个定时器计时60秒,它的输出触发第二个定时器再计时60秒,两个定时器的输出串联作为最终条件。级联的时候注意,第一个定时器的输出要用上升沿触发第二个定时器的计时条件,否则第二个定时器会一直计时。

注意:定时器的当前值在计时条件断开后是否复位,取决于定时器类型。普通定时器会复位,保持型定时器不会。用之前一定要确认清楚,否则会出现“时间到了但没动作”或者“时间没到就动作了”的诡异现象。

4.2 计数器:上升沿是灵魂

计数器(C)用来对事件计数。基本要素:计数输入、复位输入、设定值、当前值。计数输入每来一个上升沿,当前值加一(或减一),到达设定值后输出触点动作。

计数器的核心是上升沿。如果你用普通触点去触发计数,一个信号持续期间,计数器会每个扫描周期加一次,瞬间就溢出了。所以计数器的计数输入必须用上升沿,或者用脉冲信号。

我见过一个包装机项目,用光电开关计数产品数量。光电开关的信号持续几十毫秒,程序里用普通触点计数,结果一个产品被计了几十次。后来改成上升沿,问题解决。但还有第二个问题:光电开关抖动。机械振动导致光电开关在短时间内多次通断,上升沿也捕捉到了多次。最后在程序里加了一个小延时滤波,或者用计数器配合定时器做防抖,才彻底稳定。

计数器的复位也很关键。有些计数器是复位端有信号时当前值归零,有些是复位端从0变1时归零。用之前查手册,别想当然。

4.3 定时器和计数器的组合应用

定时器和计数器组合,可以实现很多有意思的功能。比如:一个定时器计时1秒,它的输出触发计数器计数,计数器计到60,就是1分钟;再计到3600,就是1小时。这种组合在需要长时间计时但单个定时器量程不够时非常有用。

另一个经典应用是闪烁电路。两个定时器互相触发,一个控制亮的时间,一个控制灭的时间,就能做出任意频率和占空比的闪烁效果。比用系统自带的闪烁继电器灵活得多。

还有一个我常用的技巧:用计数器做设备运行时间统计。设备运行时,一个定时器每秒产生一个脉冲,计数器对这个脉冲计数。计数器的当前值就是运行秒数,换算一下就是小时数。这个数据可以显示在触摸屏上,提醒维护人员按时保养。

5. 数据传送与比较指令:让程序“会算账”

5.1 传送指令:MOV不是简单的赋值

传送指令(MOV)把源操作数的值复制到目标操作数。看起来简单,但有几个点要注意。

第一,数据类型要匹配。源是16位整数,目标是32位浮点数,直接传送会出错。有些品牌会自动转换,有些不会。用之前确认清楚。

第二,传送是每个扫描周期都执行的。如果源数据在变化,目标数据也跟着变。如果你希望只在某个条件成立时传送一次,要用上升沿触发。

第三,传送指令可以传送常数、寄存器、位组合。比如把常数100传送到一个数据寄存器D0,或者把输入端子X0-X7的状态传送到一个字节寄存器。后者在读取拨码开关或者多位输入时很有用。

我踩过的一个坑:用MOV指令把一个寄存器的值传送到另一个寄存器,结果两个寄存器地址有重叠,传送后源数据被覆盖了。后来查手册才发现,某些品牌的数据寄存器是连续编址的,D0和D1是独立的,但如果你用32位传送指令DMOV,它会占用D0和D1两个寄存器。如果你同时还在用D1做其他事情,就会冲突。

实操心得:用32位指令时,一定要确认它占用了哪些寄存器,避免地址冲突。我习惯在程序里给32位数据预留连续的寄存器区域,并且做好注释。

5.2 比较指令:程序的分岔路口

比较指令(CMP、>、<、=、<>等)用来判断两个数值的大小关系,条件成立时输出触点动作。它是程序实现分支逻辑的核心。

比较指令的常见用法:判断计数器当前值是否到达设定值、判断模拟量是否超限、判断两个数据是否相等。比如一个温度控制系统,温度值存在D0,设定值存在D100,用比较指令判断D0>D100时启动冷却,D0<D100-5时停止冷却,就能实现一个简单的回差控制。

比较指令的坑在于数据类型和符号。16位有符号整数的范围是-32768到32767,如果你比较的两个数超出了这个范围,或者一个是无符号数一个是有符号数,结果可能出乎意料。比如30000和-30000比较,在无符号看来30000大,在有符号看来-30000小。用之前确认数据的类型。

另一个坑是浮点数比较。浮点数有精度问题,两个理论上相等的浮点数,实际可能差一个极小值。所以浮点数比较通常不用“等于”,而是判断差值是否小于一个很小的数。比如判断D0和D100是否相等,用|D0-D100|<0.001来代替D0==D100。

5.3 数据运算指令:加减乘除的边界

加减乘除指令(ADD、SUB、MUL、DIV)在PLC里也是基本指令。但PLC的运算和计算机的运算一样,有溢出和精度问题。

加法指令,两个16位数相加,结果可能超过32767,溢出后结果会变成负数。如果你没注意,程序就会出错。解决办法是用32位运算,或者提前判断是否可能溢出。

除法指令,除数为0会报错。有些品牌会直接停机,有些会给出错误标志。程序里一定要做除零保护,在除法之前判断除数是否为0。

乘法指令,两个16位数相乘,结果可能很大,必须用32位存放。三菱的MUL指令,结果自动占用两个连续寄存器,低16位在指定寄存器,高16位在下一个寄存器。用的时候要注意。

我个人的习惯是:只要涉及运算,尽量用32位指令。虽然占用的寄存器多,但溢出风险小很多。现在的PLC寄存器资源一般够用,没必要为了省几个寄存器冒风险。

6. 程序控制指令:让逻辑有“章法”

6.1 主控指令:MC和MCR

主控指令(MC、MCR)用来控制一段程序的执行与否。MC指令相当于一个总开关,MCR是总开关的复位。在MC和MCR之间的程序,只有当MC条件成立时才执行。

主控指令的好处是减少重复条件判断。比如一台设备有手动和自动两种模式,手动模式下执行一段程序,自动模式下执行另一段。如果不用主控,每一条指令前面都要加一个模式判断触点,程序冗长且容易出错。用主控,把模式判断放在MC指令里,后面的程序就干净了。

但主控指令有个限制:MC和MCR必须成对使用,不能交叉,不能嵌套超过规定层数。三菱的MC指令可以嵌套8层,但实际项目中我很少超过2层。嵌套太多,程序可读性急剧下降,调试的时候找问题很痛苦。

6.2 跳转指令:CJ的取舍

跳转指令(CJ)用来跳过一段程序不执行。和主控指令不同,跳转是直接跳过,被跳过的程序完全不扫描,连输出都不刷新。

跳转指令的典型应用是故障处理。设备正常运行时,执行主程序;出现故障时,跳转到故障处理程序,主程序不执行。这样可以确保故障时设备立即停止,不会因为主程序还在扫描而出现意外动作。

但跳转指令也有风险:被跳过的程序里的输出线圈,状态保持不变。如果你跳转前某个输出是ON,跳转后它还是ON,不会自动变OFF。所以跳转前一定要把该关的输出关掉,或者用跳转指令配合SET/RST来管理输出状态。

我一般不建议新手用跳转指令,因为容易出逻辑漏洞。如果确实需要条件执行,用主控指令更安全,因为主控指令在条件不成立时,内部的输出线圈会被强制复位。

6.3 子程序与中断:进阶但必要

子程序(CALL、SRET)和中断(EI、DI、IRET)严格来说不算“基本指令”,但在实际项目中几乎离不开。子程序用来把重复的逻辑封装起来,减少程序长度;中断用来处理需要快速响应的信号,比如高速计数、位置捕捉。

子程序的使用要点:调用子程序时,子程序内部的输出状态在返回后是否保持,取决于品牌和设置。有些品牌在子程序返回后,输出保持;有些会复位。用之前确认清楚。

中断的使用要点:中断程序要尽可能短。中断程序执行期间,主程序暂停。如果中断程序太长,会影响主程序的扫描周期,导致其他信号响应变慢。我一般把中断程序控制在几十条指令以内,只做最紧急的处理,复杂逻辑放到主程序里。

7. 常见问题与排查技巧实录

7.1 输入信号不动作,程序里却显示为ON

这种情况通常是输入滤波时间的问题。PLC的输入点有滤波电路,用来消除抖动。滤波时间一般是10ms左右,但有些品牌可以设置。如果输入信号太窄,小于滤波时间,PLC就捕捉不到。

排查方法:用编程软件监控输入映像寄存器,看信号是否变化。如果物理输入点有信号但映像寄存器不变,就是滤波或硬件问题。如果映像寄存器变了但程序里不动作,就是程序逻辑问题。

另一个可能是输入公共端接线。有些PLC的输入点是分组共阳或共阴的,公共端没接对,整组输入都不工作。我见过一个项目,输入模块的公共端接错了,导致8个输入点全部失效,查了一下午。

7.2 输出线圈动作了,但设备不动作

输出线圈动作,说明程序逻辑没问题。设备不动作,问题在输出侧。排查顺序:输出映像寄存器是否有信号、输出模块指示灯是否亮、输出端子是否有电压、中间继电器是否动作、接触器是否吸合、电机是否得电。

常见原因:输出模块的公共端没接、输出模块的保险丝烧了、输出端子螺丝松动、中间继电器线圈烧了、接触器线圈烧了。我遇到最多的是输出端子螺丝松动,特别是振动环境下的设备,运行一段时间后螺丝就松了,接触不良。

7.3 定时器时间不准

定时器时间不准,通常是扫描周期的影响。定时器的计时是基于扫描周期的,如果扫描周期波动大,定时器的时间就会有误差。比如一个100ms精度的定时器,设定值10,理论上是1秒。但如果扫描周期是20ms,实际计时可能是1秒±20ms。

解决办法:对时间精度要求高的场合,用中断定时或者高速定时器。有些品牌有专门的高精度定时器,不受扫描周期影响。另外,尽量缩短程序长度,减少扫描周期波动。

7.4 计数器误计数

计数器误计数,前面说过,主要是上升沿和抖动的问题。解决办法:计数输入用上升沿,同时在硬件或程序里加滤波。硬件滤波可以在输入点并联电容,程序滤波可以用定时器做延时确认。

还有一个容易被忽略的原因:计数器的复位条件。如果复位条件在计数过程中短暂成立,计数器会归零,然后重新计数。如果复位信号有抖动,计数器就会反复归零。用上升沿触发复位,或者给复位信号加滤波。

7.5 程序修改后设备动作异常

程序修改后设备动作异常,最常见的原因是修改了程序但没重新下载,或者下载了但没复位PLC。有些品牌修改程序后需要断电重启才生效,有些需要切换运行模式。

另一个原因是寄存器的初始值。程序修改后,如果新增了寄存器,它的初始值可能是0,也可能是随机值。如果程序里没有初始化,就会出问题。我习惯在程序开头用一段初始化程序,把关键寄存器赋初值,确保每次上电状态一致。

7.6 常见问题速查表

现象可能原因排查方法解决措施
输入不动作滤波时间过长、公共端接错监控输入映像寄存器调整滤波时间、检查公共端
输出不动作输出模块故障、端子松动检查输出指示灯和端子电压更换模块、紧固端子
定时器不准扫描周期波动监控扫描周期用中断定时、缩短程序
计数器误计数无上升沿、信号抖动监控计数输入加上升沿、加滤波
程序修改后异常未下载、未复位、寄存器未初始化确认下载状态、检查寄存器重新下载、复位、加初始化
通信失败站号冲突、波特率不匹配检查通信参数统一站号和波特率
模拟量漂移干扰、接地不良检查屏蔽线和接地加屏蔽、改善接地

8. 从基本指令到稳定程序的几个习惯

写了这么多年程序,我越来越觉得,PLC编程的难点不在指令本身,而在习惯。同样的指令,不同的人写出来,稳定性天差地别。分享几个我坚持了很多年的习惯。

第一个习惯:每个输出线圈只出现一次。不管是OUT还是SET/RST,同一个输出位在程序里只写一次。如果逻辑复杂,用辅助继电器M做中间状态,最后统一输出。这样调试的时候,只要监控这个输出位,就能知道它为什么通、为什么断,不用满程序找。

第二个习惯:注释写到“为什么”而不是“是什么”。比如一个常开触点X0,注释写“启动按钮”是“是什么”,写“按下时允许设备启动”是“为什么”。过半年再看程序,前者你可能忘了当时怎么想的,后者一看就明白逻辑意图。

第三个习惯:关键信号加滤波。按钮、限位、光电开关,这些机械或光电信号都有抖动。程序里加一个10ms到50ms的延时确认,能避免很多误动作。别指望硬件滤波能解决所有问题,软件滤波更灵活。

第四个习惯:程序分段,每段一个功能。手动程序、自动程序、报警程序、通信程序,分开写,用主控或跳转隔开。别把所有逻辑混在一起,调试的时候你会疯。

第五个习惯:上电初始化。程序开头用一段初始化程序,把关键寄存器、计数器、定时器复位,确保每次上电状态一致。这个习惯帮我省了无数次“上次运行状态残留”的麻烦。

这些习惯看起来简单,但坚持下来不容易。我见过太多程序,功能都能实现,但可读性差、可维护性差,换个人接手就抓瞎。基本指令是砖,习惯是水泥,两者结合才能盖出稳固的房子。

最后说一个我自己的教训。早年做一个项目,程序写得很“聪明”,用了大量SET/RST和跳转,逻辑绕来绕去。调试的时候自己都绕晕了,设备动作时对时错。后来全部推倒重来,用最笨的OUT和自锁回路重写,程序长了一倍,但一次调试通过,运行三年没出过逻辑问题。从那以后我就信一个理:在工控领域,简单可靠永远比聪明复杂重要。基本指令用好了,比什么高级功能都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询