PLC编程入门:从基础指令到实战应用,掌握工业自动化核心语言
1. PLC指令:从“开关”到“大脑”的工业语言
如果你刚接触工业自动化,看着控制柜里那些方方正正的PLC(可编程逻辑控制器),可能会觉得它很神秘。但说穿了,它的核心工作就是“听”输入信号(比如按钮、传感器),“想”逻辑关系(就是你写的程序),然后“指挥”输出信号(比如电机、阀门)。而“想”这个过程,靠的就是一套特定的语言——PLC指令。这就像你用中文的“主谓宾”造句来指挥别人做事一样,PLC指令就是工程师用来指挥机器动作的“工业造句法”。
很多人,包括一些刚入行的朋友,容易把PLC编程想得过于复杂,或者过于简单。过于复杂,是觉得它像C++、Java那样深奥;过于简单,是以为就是画几个梯形图的“启保停”。实际上,PLC指令是介于两者之间的一种存在:它语法极其简单、直观(尤其是梯形图),但组合起来却能构建出控制整条产线、整个车间的复杂逻辑。今天,我们不谈高深的算法库、通信协议,就回归最本质的东西,把那些最常用、几乎每个程序都离不开的指令掰开揉碎了讲清楚。无论你是用西门子、三菱、欧姆龙还是国产的汇川、信捷,这些基础指令的思想都是相通的,掌握了它们,你就拿到了打开PLC世界大门的钥匙。
2. 基础逻辑指令:构建程序的“砖瓦”
如果把一个PLC程序比作一栋大楼,那么基础逻辑指令就是砌墙的砖块。它们直接对应着最基本的“与”、“或”、“非”逻辑关系,是构成所有复杂控制的基础。这部分指令学不好,后面的程序就像建在沙滩上的城堡。
2.1 LD、LDN、OUT:程序的起点与终点
LD (Load):这是所有梯形图程序的“起手式”。它的作用很简单:读取一个输入触点(比如I0.0)或者一个内部继电器(比如M0.0)的当前状态(ON或OFF,1或0),并将这个状态加载到当前逻辑运算的“栈顶”或理解为当前逻辑行的起点。你可以把它看作一个“开关检查员”,它去查看某个开关是不是按下了。
LDN (Load Not):和LD是孪生兄弟,作用相反。它读取指定触点的状态,然后取“反”后再加载。也就是说,如果那个触点是OFF(0),LDN加载的结果就是ON(1)。这常用于需要“常闭”条件触发的场景,比如“安全门未关闭时报警”。
注意:不同品牌的PLC,常闭触点的画法和指令可能不同。在梯形图上,常闭触点通常显示为“||/||”。有些品牌直接用LD指令加载常闭触点地址,系统会自动识别为取反逻辑;有些则明确使用LDN指令。编程前务必查阅对应品牌的手册。
OUT (Out):这是逻辑行的“终点”和“执行者”。它把前面一系列逻辑运算的最终结果,输出到一个线圈上。这个线圈可以控制一个实际的输出点(比如Q0.0,连接着一个继电器),也可以是一个内部的辅助继电器(M点)或者一个定时器/计数器的线圈。OUT指令就是“下令”,根据前面的逻辑是真是假,来决定让这个线圈得电(ON)还是失电(OFF)。
一个最经典的例子——电机启保停控制:假设启动按钮接I0.0(常开),停止按钮接I0.1(常闭),电机接触器线圈由Q0.0控制。 梯形图逻辑通常表现为:
网络1 |--| |---|/|---( )---| | I0.0 I0.1 Q0.0 | | | |--| |----------------| | Q0.0 |用指令表(IL)来写就是:
LD I0.0 // 检查启动按钮是否按下 OR Q0.0 // 或上电机自锁触点 ANDN I0.1 // 与上停止按钮(常闭,故用ANDN) OUT Q0.0 // 输出结果到电机线圈这段程序清晰地展示了LD作为起点,经过OR(或)、ANDN(与非)的逻辑组合,最终由OUT执行输出的完整流程。这里有个关键点:OR Q0.0实现了“自锁”。当I0.0按下后,Q0.0得电,其常开触点闭合,即使I0.0松开了,电流仍能通过Q0.0自身的触点保持流通,这就是“保持”功能。而ANDN I0.1意味着,只有当停止按钮I0.1(物理上是常闭触点,正常时导通)没有被按下时,通路才完整。
2.2 AND、ANI、OR、ORI:逻辑的串联与并联
单个条件往往不够,我们需要组合条件。
AND (And):串联一个常开触点。意思是“而且”,只有当前面的逻辑结果和这个触点的状态都为“1”时,整体结果才为“1”。在梯形图上,就是两个触点的串联。ANI (And Not):串联一个常闭触点。只有当前面的逻辑结果为“1”而且这个触点的状态为“0”时,整体结果才为“1”。
OR (Or):并联一个常开触点。意思是“或者”,当前面的逻辑结果或这个触点的状态有一个为“1”时,整体结果就为“1”。在梯形图上,就是从一个触点后面并出去一条支路。ORI (Or Not):并联一个常闭触点。
应用场景:两地控制一台电机。地点A的启、停按钮为I0.0和I0.1,地点B的启、停按钮为I0.2和I0.3。电机Q0.0。 逻辑可以写为:(I0.0 OR I0.2) AND (I0.1 AND I0.3的非逻辑)。用指令可以灵活组合。这体现了AND用于串联必要条件(两个停止按钮都必须满足),OR用于并联充分条件(任意一个启动按钮均可)。
2.3 SET 与 RST:状态的“锁定”与“解锁”
这是比OUT更强大的输出指令,也是理解PLC记忆功能的关键。
SET (Set):置位指令。当SET前面的逻辑条件从“0”变为“1”(即上升沿)时,它会把指定的线圈(可以是输出Y/Q,也可以是内部继电器M)强制变为“1”(ON)状态。最关键的是,即使SET前面的条件随后又变成了“0”,这个被置位的线圈也会一直保持为“1”状态,直到被RST指令复位。这就像一个带自锁的开关,按一下开,就永远保持开。
RST (Reset):复位指令。当RST前面的逻辑条件从“0”变为“1”时,它会把指定的线圈强制变为“0”(OFF)状态,并同样保持,直到被SET指令置位。它也可以用来复位定时器(T)、计数器(C)的当前值,以及数据寄存器(D)的内容。
SET/RST 与 OUT 的核心区别:
- 保持性:OUT指令的输出状态完全依赖于其前面逻辑运算的即时结果。前面通,它就有输出;前面断,它就没输出。而SET/RST具有“锁存”功能,一旦执行,状态就保持,不受前面逻辑后续变化的影响(除非遇到相反的指令)。
- 编程灵活性:SET和RST可以成对出现,但不必在同一个程序段或同一个扫描周期内。你可以在程序的任何地方,用任何条件去SET一个标志位,然后在很远的地方,用另一个完全不同的条件去RST它。这在设计复杂的状态机、故障报警复位、模式切换时极其有用。
- 优先级:当同一个线圈在同一扫描周期内既被SET又被RST时,通常后执行的指令有效。但为了避免这种逻辑混乱,良好的编程习惯是确保对同一个线圈的SET和RST条件互斥。
实战案例:一个简单的报警指示灯控制。当故障信号I0.4到来时(上升沿),报警灯Q0.1要点亮并保持闪烁,直到维修人员按下复位按钮I0.5。
网络 10: 报警触发与锁定 LD I0.4 // 故障信号 SET M0.0 // 置位报警标志位,M0.0将一直为1 网络 11: 报警复位 LD I0.5 // 复位按钮 RST M0.0 // 复位报警标志位,M0.0将变为0并保持 网络 12: 报警灯输出(闪烁) LD M0.0 // 检查报警是否激活 AND SM0.5 // SM0.5是PLC系统自带的1秒时钟脉冲(0.5秒ON,0.5秒OFF) OUT Q0.1 // 输出到报警灯,实现闪烁在这个案例中,SET M0.0和RST M0.0完美实现了状态的锁定和解除,而报警灯的具体动作(闪烁)则由M0.0的状态和时钟脉冲组合控制。这种将“状态”和“动作”分离的编程思想,让程序结构更清晰,更容易维护。
3. 定时与计数指令:让程序拥有“时间感”和“数量感”
机器控制离不开时间和数量。定时器让PLC能“等待”,计数器让PLC能“数数”。它们是实现顺序控制、流程节拍、产量统计的核心。
3.1 定时器指令:TON, TOF, TP
不同品牌的定时器分类和名称略有差异,但思想一致。以常见的西门子S7-200/1200系列为例:
TON (On-Delay Timer):接通延时定时器。这是用得最多的定时器。当TON的使能输入端(IN)为“1”时,定时器开始计时,当前值(Txxx)从0开始增加。当当前值达到预设值(PT)时,定时器的状态位(Txxx)变为“1”,其常开触点接通。只要IN端保持为“1”,定时器状态就保持为“1”。当IN端变为“0”时,定时器立即复位,当前值清零,状态位变为“0”。
典型应用:电机星三角启动。按下启动按钮后,先以星形(Y)运行,延时8秒后,自动切换到三角形(△)运行。
LD I0.0 // 启动按钮 AN I0.1 // 停止按钮(常闭) = M10.0 // 启动中间继电器 LD M10.0 TON T37, 80 // 启动一个100ms基时的定时器T37,预设80*100ms=8秒 LD M10.0 AN T37 // 启动后,定时未到 = Q0.0 // 输出星形接触器 LD M10.0 A T37 // 启动后,定时已到 = Q0.1 // 输出三角形接触器TOF (Off-Delay Timer):断开延时定时器。当IN端从“0”变为“1”时,定时器状态位立即变为“1”,但不计时。当IN端从“1”变为“0”时,定时器才开始从0开始计时。计时达到预设值PT后,状态位才变为“0”。在计时过程中,状态位保持为“1”。
典型应用:走廊照明灯。按下开关灯亮,松开开关后,灯延时30秒自动熄灭。
TP (Pulse Timer):脉冲定时器。当IN端从“0”变为“1”时,定时器状态位立即变为“1”,并开始计时。无论IN端保持“1”多久,只要计时达到预设值PT,状态位就立即变为“0”,输出一个宽度为PT的固定脉冲。如果IN端在计时结束前就变为“0”,状态位也立即变为“0”。
重要心得:定时器的“时基”或“分辨率”(如100ms、10ms、1ms)非常关键。它决定了定时器的精度和最大定时范围。选择一个8秒的延时,用100ms时基的定时器,预设值就是80;如果用1ms时基,预设值就是8000。预设值不能超过该类型定时器的最大值(通常是32767)。在需要高精度定时的场合(如几十毫秒),要选用更小时基的定时器。
3.2 计数器指令:CTU, CTD, CTUD
计数器用来对输入脉冲的上升沿进行计数。
CTU (Count Up):加计数器。每个CU(计数脉冲输入端)的上升沿,计数器的当前值(Cxxx)加1。当当前值大于或等于预设值(PV)时,计数器的状态位(Cxxx)变为“1”。复位输入R为“1”时,计数器当前值清零,状态位复位。
CTD (Count Down):减计数器。装载输入LD为“1”时,将预设值PV装入当前值。每个CD(计数脉冲输入端)的上升沿,当前值减1。当当前值减到0时,计数器的状态位变为“1”。
CTUD (Count Up/Down):加减计数器。同时具备加计数和减计数功能,有两个脉冲输入端CU和CD,以及复位端R和装载端LD。当前值在-32768到32767之间变化。
实战案例:流水线产品数量统计与批量打包。 假设每个产品经过传感器I0.6产生一个脉冲。
网络 20: 产品计数 LD I0.6 // 产品检测传感器 EU // 上升沿检测指令,每个产品只计一个数 CTU C10, 100 // 加计数器C10,预设值100 网络 21: 满100个打包并复位计数 LD C10 // 当C10状态为1(计数值>=100) S Q0.2, 1 // 置位打包机构动作 TON T38, 50 // 打包动作时间5秒 LD T38 // 打包时间到 R Q0.2, 1 // 复位打包机构 R C10, 1 // 复位计数器C10,当前值清零,为下一批计数准备这里的关键是使用了EU(上升沿检测)指令来确保每个产品只触发一次计数,避免传感器抖动或产品停留导致重复计数。CTU指令的R端复位,通常由一个独立的条件(如打包完成)触发,确保计数周期清晰。
4. 数据处理与比较指令:程序中的“算盘”和“裁判”
PLC不仅要处理开关量,还要处理数字。比如读取温度传感器的数值(0-27648对应0-100℃),或者设定一个速度参数。这就需要数据处理指令。
4.1 数据传送指令:MOV
这是最基础的数据操作指令。功能是将一个数据从源地址“复制”到目标地址。源数据可以是常数、输入映像区、输出映像区、变量存储区等,目标地址通常是变量存储区或输出映像区。
应用:设定参数。例如,将常数1000传送到数据寄存器D100,作为某个速度环的预设值。
LD M1.0 // 当某个初始化条件满足时 MOV 1000, D100 // 将常数1000传送到D100应用:数据转移。将模拟量输入通道AIW0读到的值,传送到变量区VD200进行后续处理。
LD SM0.0 // SM0.0是常ON的特殊继电器 MOVW AIW0, VW200 // 将AIW0(字)传送到VW2004.2 比较指令:CMP (>, >=, =, <=, <, <>)
比较指令用于比较两个数值(可以是整数、双字整数、实数)的大小或相等关系。比较结果为真时,其触点接通。
比较指令通常以“触点”形式出现在梯形图中,如CMP== D10, K50表示比较D10是否等于常数50。在指令表中,可能是LDW= D10, 50。
应用:温度控制。假设温度值存储在VD300中。
网络 30: 温度过低加热 LDW< VD300, 2000 // 温度值 < 2000 (假设对应50℃) = Q0.3 // 开启加热器 网络 31: 温度过高报警 LDW> VD300, 2500 // 温度值 > 2500 (假设对应62.5℃) S M1.1, 1 // 置位高温报警标志应用:区间判断。判断一个产量值D200是否在合格范围内(1000到2000之间)。
LDW>= D200, 1000 // D200 >= 1000 AW<= D200, 2000 // 并且 D200 <= 2000 = M2.0 // 置位合格标志4.3 数学运算指令:ADD, SUB, MUL, DIV
基本的四则运算指令。需要注意的是数据类型要匹配(字节、字、双字、实数),以及运算结果可能溢出。
应用:计算平均值。连续采集5次压力值,存储在D101-D105,求平均值存到D110。
LD M3.0 // 计算使能 +I D101, D110 // D110 = D101 +I D102, D110 // D110 = D101 + D102 +I D103, D110 // 累加... +I D104, D110 +I D105, D110 // D110 = 总和 MOV 5, D120 // 除数5 /D D110, D120 // D110 = 总和 / 5 (注意:整数除法会舍去小数!)踩坑提醒:整数除法会丢失余数!如果需要小数结果,必须使用实数(浮点数)运算。即,先将整数转换为实数(使用ITD, DTR指令),然后用实数除法指令
DIV_R进行计算,最后结果才是带小数的平均值。这是新手常犯的错误。
5. 程序控制与高级功能指令
这些指令用于优化程序结构、处理复杂逻辑和特殊功能。
5.1 跳转与标签指令:JMP, LBL
JMP(跳转)和LBL(标签)配合使用,可以实现程序的无条件或条件跳转。当JMP指令前面的逻辑条件满足时,程序将跳转到指定的LBL标签处继续执行,中间的指令被跳过。
作用:用于程序的分支选择、跳过某些无需执行的程序段(如设备未上电时的初始化)、或者构建循环。滥用跳转会严重破坏程序的可读性和扫描周期逻辑,务必谨慎使用。通常,用子程序调用是更好的结构化选择。
5.2 子程序调用指令:CALL, SBR, RET
这是实现结构化、模块化编程的关键。将特定的功能(如手动模式、自动模式、报警处理、配方调用)编写成独立的子程序(SBR),在主程序中通过CALL指令来调用。子程序执行完毕后,通过RET指令返回主程序。
优点:
- 模块化:程序结构清晰,易于理解和维护。
- 可重用:相同的功能(如气缸动作)只需编写一次子程序,多次调用即可。
- 节省内存:主程序扫描时,只扫描被调用的子程序,未被调用的不扫描。
5.3 上升沿/下降沿检测指令:EU, ED
这是两个极其重要但容易被忽略的指令。它们不直接操作数据,而是检测其前面逻辑状态的变化沿。
EU (Edge Up):上升沿检测。当其前面的逻辑状态从“0”变为“1”时,EU指令输出一个单扫描周期的脉冲“1”,之后即使前面逻辑保持为“1”,EU输出也变回“0”。ED (Edge Down):下降沿检测。当其前面的逻辑状态从“1”变为“0”时,ED指令输出一个单扫描周期的脉冲“1”。
为什么重要?因为很多操作我们只想执行一次。比如前面计数器的例子,如果不加EU,传感器信号持续为“1”的期间,每个扫描周期计数器都会加1,导致计数错误。加上EU后,只有信号从无到有的那个瞬间计数一次。
另一个典型场景:单按钮启停控制。用一个按钮(I0.0)控制一盏灯(Q0.0)的亮和灭。按一次亮,再按一次灭。
LD I0.0 EU // 取按钮的上升沿 = M0.0 // M0.0是一个单脉冲 LD M0.0 XOR Q0.0 // 对Q0.0的状态取反(异或操作) = Q0.0 // 输出结果回Q0.0这里,EU指令确保了每次按下按钮,只产生一个瞬间脉冲M0.0,用这个脉冲去触发Q0.0的状态翻转。如果没有EU,按钮按下的整个期间,Q0.0会在每个扫描周期都翻转一次,导致状态完全不可控。
6. 指令应用中的常见陷阱与最佳实践
掌握了指令的语法只是第一步,在实际项目中灵活、正确地运用它们,才是真正的挑战。这里分享一些从坑里爬出来的经验。
6.1 双线圈输出:逻辑的“精神分裂”
这是PLC编程的大忌。指在同一个程序扫描周期内,对同一个输出线圈(Y/Q)或内部线圈(M)进行了多次OUT、SET或RST操作。由于PLC是顺序扫描执行的,写在后面的指令会覆盖前面的指令结果,导致程序行为与你的梯形图逻辑看起来完全不符。
错误示例:
网络1: 自动模式启动电机 LD I0.0 // 自动启动 = Q0.0 网络2: 手动模式启动电机 LD I1.0 // 手动启动 = Q0.0在这个程序里,无论I0.0和I1.0是什么状态,Q0.0最终只受网络2的控制,因为网络2后执行。网络1的逻辑完全失效。
解决方案:
- 集中输出:将对同一个线圈的所有控制逻辑,集中到一个程序段中,用“或”的关系合并条件。
LD I0.0 // 自动启动条件 O I1.0 // 或 手动启动条件 AN I0.1 // 与 停止条件(常闭) = Q0.0 // 集中输出 - 使用中间标志位:在不同的程序段中,将控制逻辑输出到不同的中间继电器(M点),最后再用这些M点的逻辑组合去驱动最终的输出线圈。
网络1: LD I0.0 = M10.0 // 自动启动标志 网络2: LD I1.0 = M10.1 // 手动启动标志 网络3: LD M10.0 O M10.1 AN I0.1 = Q0.0 // 集中输出 - 使用SET/RST:在条件分散且需要保持的场合,用SET和RST指令。确保置位和复位条件互斥,且逻辑清晰。
网络1: LD I0.0 SET Q0.0 // 自动启动,置位 网络2: LD I1.0 SET Q0.0 // 手动启动,也能置位(条件可或) 网络3: LD I0.1 RST Q0.0 // 停止条件,复位
6.2 扫描周期与信号抖动:看不见的“时间幽灵”
PLC程序是循环执行的,一个循环称为一个扫描周期(包括输入采样、程序执行、输出刷新)。这个特性会导致一些时序问题。
问题1:用同一个物理按钮的常开触点,既作为启动条件,又作为停止条件,并且没有使用边沿检测。由于机械按钮按下时会有数毫秒到数十毫秒的抖动,在抖动期间,PLC可能已经扫描了多个周期,导致一次按下被误判为多次动作。
对策:务必对来自物理按钮、行程开关等机械触点的信号进行去抖动处理。最简单有效的方法就是使用EU或ED指令,只检测稳定后的状态变化沿。更复杂的可以用定时器实现软件滤波。
问题2:“竞态条件”。在两个不同的程序段,一段用条件A的上升沿SET一个标志M,另一段用条件B的上升沿RST同一个标志M。如果A和B在同一个扫描周期内同时为真,那么最终M的状态取决于SET和RST指令在程序中的先后顺序。后执行的指令会覆盖先执行的。
对策:仔细规划程序逻辑,尽量避免对同一地址的置位/复位条件在理论上可能同时发生。如果无法避免,需要明确设计优先级,或者引入中间状态进行互锁。
6.3 数据类型的匹配与转换:小心“数值失真”
这是数据处理中最常见的错误。比如,将一个16位整数(范围-32768~32767)直接传送到一个32位实数地址,或者用整数除法指令去做平均值计算。
黄金法则:在进行任何数学运算或比较前,确保参与运算的两个操作数数据类型完全一致。
操作流程:
- 读取源数据:明确源数据是什么类型?是来自模拟量输入的16位整数(INT),还是来自编码器的32位双整数(DINT),或者是触摸屏输入的实数(REAL)?
- 选择指令:根据目标操作选择正确的指令。加法有整数加法
ADD_I、双整数加法ADD_DI、实数加法ADD_R。 - 必要转换:如果类型不一致,先用转换指令。例如,将整数转换为双整数
I_DI,再将双整数转换为实数DI_R,然后进行实数运算。 - 存储结果:将结果存放到足够大、类型匹配的存储区。实数运算的结果一定要存到实数地址(如VDxxx)中。
例如,计算 (D100 + D102) / 2 的平均值,并保留小数:
LD SM0.0 // 常通,持续计算 ITD D100, VD200 // 将整数D100转换为双整数,存到VD200(实际占用VD200-VD203) DTR VD200, VD204 // 将双整数转换为实数,存到VD204 ITD D102, VD208 // 转换D102 DTR VD208, VD212 // 转换为实数 +R VD204, VD212 // 实数相加,结果在VD212 MOVR 2.0, VD216 // 将常数2.0以实数形式存入VD216 /R VD212, VD216 // 实数除法,平均值在VD216 MOVR VD216, VD220 // 将最终平均值存到VD220虽然看起来步骤多了,但这保证了计算的精确性。在工业控制中,一个参数的小数点后误差,可能导致整批产品不合格。
6.4 保持性与初始化:设备重启后的“记忆”
有些数据,比如生产累计数量、设备运行时间、当前配方号,需要在PLC断电重启后还能保持。而有些数据,比如临时运算中间值、单次循环的状态标志,则需要每次上电都清零。
如何设置?
- PLC硬件组态/系统块设置:在编程软件中,通常有专门的“保持性寄存器”设置区域。你可以指定哪些数据区(如MB0-MB13, VB0-VB199)在断电后由PLC的超级电容或电池保持。务必根据实际需要设置,不要盲目扩大保持区范围,因为这会增加电池负荷和潜在的数据错误风险。
- 软件初始化:在程序的开头(通常放在主程序的第一段),使用一个仅在上电第一个扫描周期为ON的特殊继电器(如西门子的SM0.1,三菱的M8002)来对需要清零的变量进行复位操作。
LD SM0.1 // 首次扫描为ON MOV 0, VD100 // 清零累计产量 MOV 0, VD104 // 清零临时变量 R M10.0, 16 // 复位M10.0开始的16个位 - 保持性数据的恢复判断:对于保持性数据,上电后可能需要判断其有效性。例如,可以设置一个“上电确认”标志,在设备正常停机时将其置位并保持。上电后,如果发现这个标志是置位的,说明上次是正常关机,保持的数据有效;如果发现是复位的,说明可能是异常断电,保持的数据可能不可靠,需要进行默认值加载或报警提示。
指令是PLC编程的基石,但比记住所有指令更重要的,是理解其背后的逻辑思想和应用场景。最好的学习方法不是死记硬背,而是找一个小型的实物PLC或者一个靠谱的仿真软件(比如西门子的S7-PLCSIM,三菱的GX Simulator),把每一个指令都搭一个小程序,观察其运行状态,改变输入条件,看输出如何变化。当你真正理解了LD/OUT是如何构建电流通路的,SET/RST是如何锁存状态的,TON是如何累积时间的,你就能用这些简单的“词汇”,写出控制复杂机器的“篇章”。编程到最后,拼的不是记住了多少晦涩的指令,而是逻辑的严谨和对工艺的深刻理解。