☰
IEC 61131-3标准详解:PLC编程语言与工程实践指南
2026/10/23 3:37:34 网站建设 项目流程

干工控这些年,被问得最多的入门问题里,IEC 61131标准肯定排前三。很多刚接手PLC项目的人,拿到项目资料第一眼看到的不是梯形图就是上百个变量表,再翻几页又看到FBD、ST、SFC,整个人是懵的。这篇文章我就把IEC 61131这个标准拆开讲一遍,重点放在PLC编程最常打交道的第3部分,也就是编程语言的定义上,顺带把硬件要求、通信接口和工程实践的边界也画清楚。不管你是刚入行的电气工程师,还是想在设备维护岗位上把程序真正看懂读透,这篇都值得花十分钟读完。

1. 为什么PLC工程师必须懂IEC 61131标准

1.1 它解决的真实痛点

在IEC 61131标准出现之前,PLC领域几乎是一家厂商一套规矩。你学会了A厂的梯形图,换到B厂的项目里,指令表写法、变量定义方式、程序组织逻辑全都不一样,基本等于从头学一遍。更麻烦的是同一个项目里,电气工程师画的图、上位机工程师看的变量、维护人员抄的地址,各说各话,中间全是无效沟通。

IEC 61131出现以后,相当于给整个PLC行业立了一套通用语法和通用数据结构。硬件上,电源、CPU、输入输出通道怎么划分有统一说法;软件上,编程语言、变量类型、程序组织方式有了统一框架。你做项目时写在纸上的逻辑,换一个支持该标准的平台,只要不是严重依赖厂商库函数,基本都能平移过去。这才是真正的工程资产。

我自己的体会是,这个标准最有价值的地方不在于条文本身,而在于它强迫整个行业把“程序的写法”“数据的存法”“任务的调度法”分开考虑。过去很多编程是想到哪写到哪,变量地址满天飞;有了标准框架以后,程序组织、数据模型、执行模型都有了章法,调试和交接的效率能提升不少。

1.2 标准不是“条文”,而是行业共识

很多人一听到“国际标准”四个字就头疼,觉得那是评委和认证机构才需要看的东西。实际上IEC 61131更像一份行业通用语言说明书,它由多个部分组成,实际工程里最常打交道的就这几块:

标准部分主要内容工程上的意义
IEC 61131-1通用信息定义PLC的组成、术语体系
IEC 61131-2设备要求与测试明确电气、机械、EMC指标
IEC 61131-3编程语言五种编程语言、数据类型、程序组织
IEC 61131-4用户指南教你怎么选型、怎么用
IEC 61131-5通信与现场总线和上位机通信的约定
IEC 61131-6功能安全涉及安全相关系统的开发

其中第3部分是编程人员的“主战场”。它定义了五种编程语言:梯形图LD、功能块图FBD、结构化文本ST、指令表IL、顺序功能图SFC。很多人以为只要会用梯形图就算会PLC,这恰恰是误区。标准给了你五种语言,你要做的是在正确场景用正确工具。

这里补充一个很实际的概念:标准还定义了程序组织单元,也就是POU。你写的每个功能块、每个函数、每个主程序,在标准里的角色都不一样。后面我会专门讲这部分,因为它直接关系到程序能不能复用、能不能维护。

1.3 标准对硬件层的要求

IEC 61131-2很多人不关注,但做项目时踩坑最多的反而是这里。它规定了PLC设备在电气、机械、环境方面的要求,比如电源波动范围、抗干扰等级、工作温度范围。出口项目和现场环境恶劣的项目,如果选型时不看这个部分,设备在现场老是复位、通讯老掉线,查到最后往往是硬件抗性不达标。

所以我的建议是,读IEC 61131不要只盯着语言。你至少要清楚手头这个控制器属于什么防护等级、什么供电类别,以及它的数字量输入是高电平有效还是低电平有效。这些细节在标准里都有出处,只是大多数编程教程不会跟你讲。

2. 五种编程语言:选择逻辑与实际场景

2.1 LD梯形图和FBD功能块图:电气工程师的“母语”

LD梯形图是PLC最经典的语言,它长得就像一张继电器控制电路图。左侧是母线,中间是触点和线圈,信号从左往右流。你只要理解常开、常闭、线圈吸合这三件事,就能看懂八成以上的逻辑。正因为它和继电器电路同构,工厂里的电工老师傅也能参与排查问题,这是LD至今没有被淘汰的根本原因。

FBD功能块图则是把程序看成一个个方块,方块代表功能,线代表数据流转。你做一个PID控制、做一路模拟量处理,用LD写计算会非常别扭,用FBD搭就顺得多。我个人在实际项目里通常把LD用于开关逻辑互锁,把FBD用于模拟量链路的搭建,比如把传感器信号经过滤波、量程转换再送给PID。

这里提醒一句,LD和FBD在很多平台上可以混合使用。同一个程序里,安全互锁用LD写,运算链路用FBD写,完全没有问题。没必要抱着一种语言用到底。

2.2 ST结构化文本:复杂算法的首选

ST的语法长得像Pascal和C之间的混合体。它没有触点线圈的图形拖拽,而是直接用语句写逻辑:

IF bStart AND NOT bFault THEN bRun := TRUE; ELSE bRun := FALSE; END_IF;

这套语言对写过高级语言的人来说几乎没有学习成本。做配方管理、做数据处理、做运动控制里的轨迹计算,用ST写比用LD拖几百个触点高效得多。我认识很多工程师,平时界面全用梯形图,一遇到复杂数学运算就偷偷插入一个ST程序段,这正是ST的定位。

ST的另一个优势是它可以很好地跟其他语言嵌套调用。比如你可以在LD里调用一个用ST写的功能块。语言本身不是壁垒,程序组织才是。

2.3 IL指令表和SFC顺序功能图:两端的取舍

IL指令表是最接近汇编的语言,一条指令一个操作码,适合资源极度受限的老平台。现在的工程环境里,新的硬件平台很多已经不再强调IL,因为它可读性太差,后期维护成本高。我建议新手知道它存在就行,不必花太多精力。

SFC顺序功能图则是完全不同的思路。它用步、转换条件、动作来描述一个过程的执行顺序。你把设备的工序画成一个个步骤,步骤之间写上“转换条件”,条件满足了就跳到下一步。SFC非常适合描述工艺流程、批量控制、设备状态机,但前提是逻辑本身是顺序型、阶段型的。如果逻辑是大量互相纠缠的条件判断,硬套SFC反而会很难受。

需要特别提醒的是,SFC里的每一步都有可能长期停留,现场调试时经常出现“设备卡在某一步不动了”的情况。所以用SFC写流程,一开始就要设计超时和手动跳步逻辑,否则排产的时候很痛苦。

2.4 我在项目里的选型组合策略

讲了这么多语言,落到实际项目里到底怎么选?我给一个我常用的参考组合:

场景首选语言说明
电机启停、阀控、互锁保护LD电气人员容易读懂,审计直观
模拟量采集、标定、滤波FBD数据流清晰,可挂标准库
配方管理、复杂计算、数组处理ST表达能力强,代码量小
多工序批次控制SFC阶段划分天然清晰
老设备维护IL尽量少用,仅做维护阅读

这套组合不是标准规定的,而是工程实践里验证过的高效组合。语言本身只是载体,真正值钱的是你的逻辑拆分能力。

3. PLC运行的底层逻辑:扫描、任务与变量

3.1 扫描周期:程序执行的三部曲

很多入门教材讲PLC扫描周期只会说一句“PLC循环执行程序”,然后就没有然后了。实际你写程序、查bug,必须把扫描机制刻在脑子里。

标准模型下,PLC的一个扫描周期通常包含三个阶段:读取输入、执行程序、更新输出。输入信号在扫描开始时被一次性读入映像区,程序执行过程中读到的都是这个映像,而不是实时物理信号。程序执行完毕后再统一把结果写到输出模块。这样设计是为了保证逻辑一致性,避免程序执行到一半时输入变化导致判断错乱。

但这也带来一个经典坑:你在程序中间把某个输出置位了,同一个扫描周期后面又因为其他条件把它复位,那么最终输出的结果只取决于最后一次写入。这个现象在LD双重线圈里特别常见,后面我会单独讲。

扫描时间直接决定系统的响应能力。一个设备要求急停信号能在几十毫秒内触发,而你的程序扫描周期跑到了100毫秒,那这个要求就实现不了。所以写程序时要养成估算扫描周期长度的习惯,尤其是大型设备或者项目里用了大量浮点运算的时候。

3.2 变量与数据类型:看似简单,坑最多

IEC 61131-3定义了一套标准数据类型,其中日常编程八成时间都在用下面几种:

  • BOOL:布尔量,真或假,对应数字量输入输出。
  • INT / DINT:整数和双整数,做计数、地址索引、状态码。
  • REAL / LREAL:浮点数,做运算、标定、PID参数。
  • TIME:时间,做定时比较。
  • STRING:字符串,做人机界面显示、报警文本封装。
  • BYTE / WORD / DWORD:位串,做通信协议数据打包时非常有用。

这里最容易被忽视的是有符号数和无符号数的区别。INT是有符号的,范围是-32768到32767。如果你读取一台设备的原始计数寄存器,它返回的是无符号数,直接赋给INT,一不小心就变成负值,报警判断全部错乱。

还有BOOL型变量的赋值习惯。很多人习惯用“=1”表示真,但在ST里直接写变量名就是真值,不必比较。用错了不仅啰嗦,还容易在可读性上误导别人。标准里允许你写 bRun := TRUE,这比 bRun := 1 要清晰得多。

3.3 任务配置与扫描时间计算

IEC 61131体系中“任务”这个概念决定了程序段多久执行一次。你可以配置为循环执行、按时间间隔执行、由事件触发执行。高速IO处理用事件触发,常规逻辑用循环执行,界面刷新用低频时间间隔,这就是任务调度的基本思路。

实际项目里我做扫描时间估算时会用一个比较笨但有效的办法:先按程序规模估一个基准,比如3000条指令的工程大概在2到5毫秒量级,再把通信块、模拟量滤波块的时间加上,最后留出20%到30%的余量。如果你发现扫描周期已经高到影响响应,不要急着换CPU,先查程序里是不是有大量REAL运算写在循环里、有没有不必要的定时器刷屏。

提示:扫描时间不是越小越好。扫描太快,输入滤波和通讯块反而可能来不及稳定处理;关键是让系统在最坏情况下仍然满足工艺响应要求。

4. 编程前的必修课:从IEC 61131-3出发的工程实践

4.1 程序组织单元:FUNCTION、FUNCTION_BLOCK、PROGRAM怎么选

标准里程序组织单元的分类是很多工程师没有真正吃透的部分,但它是决定程序结构好坏的核心。

类型特点典型用途
FUNCTION无内部状态,输入相同则输出相同量程转换、数学计算、位拼接
FUNCTION_BLOCK有内部状态,调用结果依赖历史定时器、计数器、PID、电机控制封装
PROGRAM主程序入口,绑定任务放在扫描任务里运行

FUNCTION就像一把计算器,同样输入永远是同样输出,不做记忆。FUNCTION_BLOCK就是一个带记忆的小设备,你给它一个启动信号,它会“记得”自己已经启动了,下次调用时能维持输出。这个区别极其重要,很多人把一个需要保持状态的逻辑写在FUNCTION里,结果一调用就丢状态,查半天也不知道为什么。

PROGRAM则是最上层的容器,它负责把各个功能块串起来,并且被分配到具体任务里执行。整个应用的结构应该是:PROGRAM调用FUNCTION_BLOCK,FUNCTION_BLOCK内部调用FUNCTION,而不是把所有代码堆在一个巨型PROGRAM里。

4.2 一个真实案例:电机启停控制程序

我用一个最简单的电机启停控制,把前面这些概念串起来。假设现场有一个电机,通过数字量输出启动接触器,启动按钮、停止按钮和热继电器常闭触点分别是三个数字量输入。

先定义一个功能块:

FUNCTION_BLOCK FB_Motor VAR_INPUT bStartCmd : BOOL; (* 启动按钮 *) bStopCmd : BOOL; (* 停止按钮 *) bFault : BOOL; (* 热继电器故障信号 *) END_VAR VAR_OUTPUT bRun : BOOL; (* 接触器输出 *) END_VAR VAR bRunInternal : BOOL; (* 内部保持位 *) END_VAR

控制逻辑在ST里写就是:

(* 故障信号优先:有故障立即断开 *) IF bFault THEN bRunInternal := FALSE; ELSEIF bStopCmd THEN bRunInternal := FALSE; ELSEIF bStartCmd THEN bRunInternal := TRUE; END_IF; bRun := bRunInternal;

眼熟的人会发现,这本质上就是自保持回路:启动信号接通后线圈自己保持,直到停止或故障信号断开它。

如果要用LD表达同样逻辑,就是一个并联自保持回路加一个故障常闭触点串联。实际项目里我会把这类逻辑用LD写,方便电工师傅看;把它封装成功能块的格式引用,主程序就剩下一行调用:

fbMotor1(bStartCmd := btnStart, bStopCmd := btnStop, bFault := relayFault);

这里你感受到POU的价值了。封装好以后,主程序代码量锐减,现场改逻辑只需要改功能块内部,主程序不用动。

4.3 变量命名与布局规范

程序写得好不好,三个月后回头看最清楚。变量命名是我最强调的规范之一。IEC 61131标准没有强制规定命名规则,但行业里有不少成熟的约定。

我的做法是给变量加前缀:

前缀含义示例
b布尔量bStartCmd, bFault
by / w / dw字节、字、双字wStatusWord
i / di整数、双整数iCount, diTotal
r实数rPressure
t时间tDelay
s字符串sAlarmText

输入输出参数上,我会再标一下方向,比如Input变量后缀Cmd、Fbk,输出变量后缀Run、Fault。这套命名虽然简单,但大程序里能让你一眼识别数据类型和信号方向,排查问题效率高很多。

另外一个习惯是坚持用符号名而不是绝对地址。过去老式PLC程序里全是I0.0、Q0.1这种,维护的人得拿着一张地址表到处查。符号名体系下,你看到bStartCmd就知道是启动按钮,根本不用看地址分配。尤其审计的时候,符号名程序比地址程序友好太多了。

5. 调试与排查实录:我踩过的典型坑

5.1 类型不匹配与隐式转换的坑

我在一个设备项目里遇到过报警频繁误触发的问题。查了一个下午,最后发现是一路压力传感器的原始数值赋给了INT型变量,量程压力达到20兆帕的时候原始计数超过了32767,INT直接变成负值,高报警判断当场失效。

这是非常典型的IEC 61131编程错误。传感器原始值很多是无符号整数,你用有符号INT存,数值一大就出问题。现在的平台普遍提供在线监控,你把变量值列出来一看,负值出现的位置一目了然。排查的思路就是:先看变量类型,再看量程边界,最后看转换逻辑。

现在多数编程环境对直接类型不匹配会报错,但有些平台会做隐式转换。隐式转换一旦发生,你看着程序没错,运行结果却莫名其妙。我的经验是,只要涉及多字节数据或者模数转换,一律显式调用转换函数,绝不指望平台自动处理。

5.2 双重输出与线圈重复“战争”

LD里同一个线圈如果在程序里出现两次,也就是所谓双重输出,后果很隐蔽。假设你先写了一段联锁逻辑,把电机输出置为运行,后面另一个无关逻辑又把同一个线圈复位了,那前面的联锁等于白做。扫描周期模型决定了最终结果只认最后一次写入。

排查这种问题有两种办法。第一种,打开编程软件的交叉索引功能,搜索这个变量,看它到底出现在哪些位置。第二种,从设计上杜绝,一个输出点只允许在一个功能块内部被赋值,其他地方全部使用中间变量。我现在写程序,接触器输出永远只出现在一个地方,所有逻辑都构造在中间变量上。这件事虽然增加了一点代码量,但查问题的时间省了几倍。

5.3 SFC卡状态的处理与超时机制

SFC写流程时,卡状态是最常见的故障。设备停下来以后,状态停留在某一步,没有任何动作告诉你它为什么停。

根本原因是SFC的每一步都在等待转换条件。等待本身不是问题,问题是没有“等不到”时的应对方案。我在SFC里给每一个需要长期等待的状态都加了超时计时器:进入步骤时启动T_ON,转换条件满足时复位它;如果T_ON达到上限,就跳到故障处理步。

另一个实用小技巧是给每一步加上“手动跳步”入口。调试的时候,现场可能会要求临时跳过某一步,你要是没有预留这个接口,就得改程序重新下载,非常被动。用SFC做流程,一定把调试期需要的跳步逻辑当成标准功能做进去。

5.4 现场常见问题速查表

下面这张表是我在实际项目里反复遇到并且验证过解决方法的清单,分享出来供你排查时参考:

故障现象常见原因排查思路解决办法
输出点有时动有时不动程序内多处写同一个输出交叉索引查写入点统一输出点,逻辑走中间变量
计数数值突然变负数有符号/无符号类型不匹配看监控值和数据类型显式转换或改用无符号类型
扫描周期突然变长功能块内部写了等待循环查看任务周期统计删除循环等待,改用定时触发
SFC流程卡住不动转换条件缺失且无超时检查当前激活步和条件值加转换超时和故障跳步
模拟量显示乱跳信号线干扰或滤波参数不对用示波器或软件滤波测试加输入滤波并做软件均值
停机后重启状态丢失变量没有掉电保持属性查看变量保持属性配置关键变量设置为掉电保持
通信数据对不上位序或字节序不一致对比双方协议文档统一字节序并做位拼接

最后再分享一个小技巧

写完程序不要急着下载,先冷眼检查一遍变量的保持属性。很多新手把设备产量、累计运行时间这种明显需要断电保持的数据,定义成了普通变量,结果设备一断电,数据清零。这个坑我在现场被客户问过好几次,每次都得给人家解释半天,后来我在变量表里把所有需要保持的变量单独做了一块区域,一眼就能查清楚。

IEC 61131标准给我最大的感受是,它不是一个要背下来的考试大纲,而是一个帮你把糊涂工程变清晰的框架。你越早按它的语言分类、程序组织、数据模型去思考,越少走我以前走过的弯路。先把标准里五种语言用熟、把POU概念吃透,再回头看那些复杂的工程,你会发现一切都顺了。

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

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

立即咨询