简介:面向工业自动化工程师与PLC编程学习者的标准化编程方法资料,聚焦S7-1200与S7-1500平台,针对程序严谨性差、可复制性低、技术提升难等实际痛点,系统梳理从程序架构规划到变量命名的落地路径。内容涵盖OB、FB、FC与I/O、M、DB、UDT的组织方式,现场设备命名规则与驼峰、帕斯卡命名规范,S88设备对象分解与状态模型,以及中央空调系统实例中的八步法流程,帮助读者建立可复用的编程框架。资源包为1个PDF文件,约1.98MB,便于随身查阅与团队内部分享。已有347人学习,适合希望摆脱面向过程编程、向模块化与标准化进阶的初、中级工程师参考,也可作为团队统一编程规范的培训素材。
1. 从“每次从零开始”说起:PLC标准化编程到底要解决什么
接手一个已经运行三年的产线程序,打开一看,变量叫M100.0、Temp1、a1,功能块里塞了八百行梯形图,注释只有一句“调试用”。想改一个电机启停逻辑,得先把整段程序从头读到尾,生怕动了一根线把别的设备带翻车。这种场景在工控圈太常见了,血泪经验就是:程序能跑,但没人敢碰。
PLC标准化编程方法与设计要解决的正是这个问题。它面向 S7-1200、S7-1500 这类主流控制器,把程序架构、变量命名、功能块模型、设备对象分解这几件事定成规矩,让程序具备严谨性、可复制性和可维护性。适合谁?正在做自动化方向毕设或课程设计的学生,需要一套能讲清楚“为什么这么分层”的完整方案;也适合刚带项目、想把团队代码统一起来的工程师,拿这套结构直接改就能用。核心不是写多复杂的逻辑,而是让下一个人接手时不用骂人。
2. 标准化现状与理论基础:三个层次和一套对象模型
2.1 工程师编程方法的三个层次
把身边做 PLC 的人排一排,基本能分成三档。第一层次是没有程序规划,面向过程编程,想到哪写到哪,IO 点直接串逻辑,程序能跑但故障率高,改一处牵全身。第二层次是自己攒了一些功能块,电机、变频器、伺服各做一个 FB,复用性有了,但块与块之间接口不统一,变量命名各写各的,换个人接手照样懵。第三层次才进入标准化:先定程序架构,再定变量命名规则,最后按设备对象做功能块,整个项目像搭积木一样有章法。
这三个层次不是能力高低的问题,是方法问题。很多工程师卡在第二层,因为做功能块已经能解决重复劳动,但没意识到接口和命名才是协作的瓶颈。标准化的切入点就在第三层:把“每个控制单元,在每种控制模式下、每种状态下、在什么条件下、做什么”这句话落成可执行的程序结构。这句话是自动化编程的经典总结,听着像废话,但真按它去拆设备,逻辑会清晰很多。
2.2 程序结构的四个基本要素
S7-1200/1500 的程序结构绕不开四样东西:组织块 OB、功能块 FB、功能 FC,以及变量体系(I、Q、M、DB、UDT)。OB 是程序入口,负责调度;FB 带背景数据块,适合有状态保持的逻辑,比如电机控制;FC 不带记忆,适合纯计算或一次性映射;DB 存数据,UDT 定义结构化数据类型。
标准化的做法是把职责分开:OB 只做调用和调度,不写具体逻辑;FB 封装设备行为,一个设备类型一个 FB;FC 处理 IO 映射、报警汇总这类无状态任务;UDT 把一台电机的所有信号打包成一个类型,变量表里一眼能看出归属。编程语言上,SCL 适合写复杂运算和状态机,LAD 适合电气人员维护的联锁逻辑,GRAPH 适合步序明显的流程。选型原则通常是:维护人员看得懂优先用 LAD,逻辑复杂到梯形图铺不开就用 SCL 封装成“黑匣子”,对外只暴露接口。
2.3 S88 标准下的设备对象分解
S88 是批量控制领域的设备分解标准,放到通用 PLC 编程里同样好用。它把设备从大到小分成几层:Process Cell(产线/工艺单元)、Unit(设备单元)、Equipment Module(设备模块)、Control Module(控制模块)。以中央空调系统为例,整个冷冻站是一个 Unit,冷却塔、冷冻泵、主机各是 Equipment Module,每台泵的启停控制就是一个 Control Module。
这样分的好处是每一层只关心自己的状态,上层调下层不关心内部怎么实现。控制模块对应一个 FB,设备模块对应一组 FB 的协调,单元层写模式切换和联锁。状态模型跟着设备走:停止、启动中、运行、停止中、故障,每个状态有明确的进入条件和退出条件。把状态图画清楚,程序里的 IF 判断就有了依据,不会出现“这个信号到底该在哪个状态判断”的扯皮。
2.4 变量命名规则:驼峰与帕斯卡的分工
命名是标准化里最容易被跳过、又最影响可读性的一环。现场设备命名可以按“设备类型 + 扩展 + 含义”来组织,比如 MCM 表示命令运行(Motor Command),MIM 表示电机运行反馈(Motor Indication Motor running),MIS 表示隔离开关反馈,MID 表示热保护故障反馈。这套缩写看着别扭,但团队用熟之后,看变量名就知道信号性质。
标识符层面分两套规则。驼峰命名用于变量、PLC 变量、数据类型和结构,首字母小写,后续单词首字母大写,比如motorAuto,不允许用下划线分隔。帕斯卡命名用于程序块、数据块、工艺对象、库、变量表,首字母大写,每个单词首字母大写,比如MotorControl(FB1),为了结构化允许少量下划线。两套规则的分界线是“这个东西是数据还是容器”:数据用驼峰,容器用帕斯卡。定好之后写进项目规范,新加变量先对照再敲键盘。
3. 总体架构设计:八步法把标准化拆成可执行动作
3.1 程序架构的八个组成部分
标准化不是一句口号,得拆成能落地的步骤。八步法的第一步就是程序架构设计,把整个项目切成八类子程序:IO 点映射子程序、初始化子程序、功能块调用子程序、单元子程序、报警子程序、数据交换子程序、功能块子程序、其他子程序。每一类对应一个或一组 OB/FC,职责边界写清楚。
IO 点映射单独成块,是因为现场接线一变,只需要改这一处,不用满程序找点。初始化子程序处理上电默认值和首循环标志。功能块调用子程序集中放 FB 的实例调用,方便看谁被调了几次。单元子程序按设备单元组织,一个单元一个 FC 或 FB。报警子程序汇总所有故障位,做延时和确认逻辑。数据交换子程序处理与上位机、变频器、仪表的通讯。功能块子程序存放自研 FB 的源码。其他子程序兜底放临时逻辑,但要求定期清理。
这个架构的价值在于“改哪里”有答案。IO 点变了改映射块,设备逻辑变了改功能块,联锁变了改单元块,互不干扰。
3.2 变量表与数据类型的设计
第二步创建 PLC 变量表,第三步创建 PLC 数据类型(UDT)。变量表按设备或功能分区,不要一张表铺到底。UDT 的设计直接决定程序的可读性,以电机为例:
// UDT: typeMotor —— 一台电机的标准信号包 TYPE "typeMotor" VERSION : 0.1 STRUCT cmdRun : Bool; // 运行命令 cmdStop : Bool; // 停止命令 cmdAuto : Bool; // 自动模式命令 fbRunning : Bool; // 运行反馈 fbFault : Bool; // 综合故障反馈 fbIsolator : Bool; // 隔离开关反馈 fbThermo : Bool; // 热保护故障反馈 stStatus : Int; // 状态字,对应状态模型 tRunDelay : Time; // 启动延时设定 END_STRUCT; END_TYPE这个 UDT 把一台电机的命令、反馈、状态、参数收在一个结构里。变量表里声明Motor1 : "typeMotor",程序里写Motor1.cmdRun,比M100.0清楚一个量级。参数stStatus用 Int 而不是 Bool 数组,是为了跟状态模型对齐,一个整数代表一个状态,判断时用 CASE 比一堆 IF 干净。tRunDelay放在 UDT 里,意味着每台电机可以独立设延时,不用在 FB 里写死。
UDT 的粒度要控制:太粗(一个 UDT 包整个单元)会导致复用困难,太细(一个信号一个 UDT)等于没包。通常一个设备类型一个 UDT,电机、阀门、变频器、仪表各一个,够用。
3.3 功能块与状态模型的程序模型
第四步创建电机功能块,第五步创建 S88 状态功能块。电机 FB 的接口用 UDT 做 InOut,内部按状态机写:
// FB: MotorControl —— 电机标准控制块(SCL 片段) CASE #statMotor OF 0: // 停止 #typMotor.stStatus := 0; #typMotor.cmdRun := FALSE; IF #typMotor.cmdAuto AND #typMotor.cmdRunReq THEN #statMotor := 10; END_IF; 10: // 启动中 #typMotor.stStatus := 10; #outRun := TRUE; IF #typMotor.fbRunning THEN #statMotor := 20; ELSIF #tonStart.Q THEN #statMotor := 90; // 启动超时转故障 END_IF; 20: // 运行 #typMotor.stStatus := 20; IF NOT #typMotor.cmdAuto OR #typMotor.cmdStop THEN #statMotor := 30; ELSIF #typMotor.fbFault THEN #statMotor := 90; END_IF; 30: // 停止中 #typMotor.stStatus := 30; #outRun := FALSE; IF NOT #typMotor.fbRunning THEN #statMotor := 0; END_IF; 90: // 故障 #typMotor.stStatus := 90; #outRun := FALSE; #outFault := TRUE; IF #ackFault THEN #statMotor := 0; END_IF; END_CASE;这段代码的关键不在语法,在结构。状态字stStatus对外暴露,上位机或报警块直接读,不用再解析一堆布尔量。启动超时用#tonStart.Q判断,定时器实例在 FB 静态变量里,每台电机独立计时。故障状态统一进 90,复位走ackFault,不跟启停命令混在一起。cmdRunReq是单元层给的请求,cmdAuto是模式选择,两个条件同时满足才启动,这样手动模式和自动模式不会打架。
S88 状态功能块是把这个模式抽象出来,让阀门、变频器、仪表都能套同一套状态迁移。做法是把状态判断和具体输出分开:状态块只管“现在该在哪个状态”,输出块根据状态决定“哪个线圈得电”。这样换设备类型时,状态逻辑不用重写。
3.4 调用层与 IO 映射的落地
第六步调用电机功能块,第七步编写设备单元控制程序,第八步编写 IO 点映射程序。调用层放在功能块调用子程序里,一个电机一个背景 DB,命名跟设备位号对应:
// OB1 或 FC 中的调用片段 "MotorControl_DB_冷却泵1"(typMotor := "冷却泵1", cmdRunReq := "Unit_冷冻站".reqPump1, ackFault := "HMI".ackAll); "MotorControl_DB_冷却泵2"(typMotor := "冷却泵2", cmdRunReq := "Unit_冷冻站".reqPump2, ackFault := "HMI".ackAll);单元层Unit_冷冻站是一个 FB 或 FC,内部根据模式(手动/自动/联锁)决定reqPump1什么时候为真。IO 映射块单独一个 FC,把物理输入I0.0赋给"冷却泵1".fbRunning,物理输出由"冷却泵1".outRun驱动Q0.0。这样电机 FB 里看不到任何绝对地址,换模块、改点位只动映射块。
IO 映射的写法要成对出现,输入映射和输出映射分两段,中间加注释分隔。映射块里不做逻辑判断,只做赋值,保证“看映射块就知道点在哪”。这一步是很多项目的翻车点:有人在映射块里顺手加了个滤波或取反,后面排查时死活找不到信号为什么反了。
4. 避坑与常见问题:五个最容易翻车的地方
4.1 功能块接口用绝对地址,复用变复制
现象:电机 FB 里直接写I0.0、Q0.0,换一台电机就把 FB 复制一份改地址,最后项目里十几个几乎一样的 FB。原因是没有把 IO 映射和逻辑分开,FB 依赖具体点位。解决:FB 接口只用 UDT 和形参,绝对地址全部收进 IO 映射 FC,调用时传结构变量。改完之后一个 FB 管所有同类设备,改逻辑只改一处。
4.2 状态字和布尔量双轨并行,判断打架
现象:程序里既有stStatus = 20的判断,又有fbRunning AND cmdAuto的判断,两个地方结论不一致,设备该停不停。原因是状态模型没贯彻,有人图省事直接读反馈。解决:约定状态判断只读stStatus,布尔量只用于 HMI 显示和报警,联锁条件统一走状态字。状态迁移的入口收在 FB 内部,外部只给命令。
4.3 命名规则定了不执行,半年后自己都不认识
现象:规范文档写了驼峰和帕斯卡,实际项目里Motor1、motor_1、M1混着用,变量表像垃圾场。原因是缺少检查环节,赶工期时随手敲。解决:变量表按设备分区,新建变量前先搜一遍有没有同类;FB 和 DB 命名在调用前统一过一遍;项目交付前做一次命名审查,把不符合规则的列出来批量改。通常的做法是让第二个人做交叉检查,自己看自己的命名永远觉得没问题。
4.4 IO 映射块里夹逻辑,排查时黑匣子
现象:某个输入信号在映射块里被取了反,或者加了 200ms 滤波,现场调试时按图纸查线全对,程序就是不动作。原因是映射块被当成逻辑块用。解决:映射块只做一对一赋值,需要滤波、取反、延时的,放到功能块或单独的预处理 FC 里,并在变量注释里写清楚。映射块保持“所见即所得”,是排查的第一站。
4.5 功能块背景 DB 命名随意,交叉引用找不到人
现象:调用了几十个 FB 实例,背景 DB 叫DB1、DB2、DB3,交叉引用里一片数字,想知道冷却泵1用的是哪个 DB 得一个个点开看。原因是调用时没改默认名。解决:背景 DB 命名跟设备位号绑定,比如MotorControl_DB_冷却泵1,调用时手动改。多实例调用时在 FB 静态变量里声明实例名,同样按位号命名。这一步花不了几分钟,省的是以后每次排查的半小时。
5. 验收与答辩:怎么证明这套标准化真的成立
标准化方案的验收,不是看程序能不能跑,是看“换个人能不能接”。我一般会准备一份验收清单,按下面几项逐条过:
| 验收项 | 检查方法 | 合格标准 |
|---|---|---|
| 程序架构 | 打开项目树,看 OB/FC 分组 | 八类子程序齐全,职责不交叉 |
| 变量命名 | 抽查 20 个变量和 5 个块名 | 驼峰/帕斯卡规则一致,无绝对地址混入 FB |
| UDT 设计 | 看设备类型是否都有对应 UDT | 电机、阀门等主要设备全覆盖,粒度合理 |
| 状态模型 | 挑一台设备走一遍状态迁移 | 状态字唯一,迁移条件可追溯 |
| IO 映射 | 改一个点位,看影响范围 | 只动映射块,功能块无需修改 |
| 复用性 | 新增一台同类设备 | 只加调用和映射,不复制 FB |
答辩时最容易被问的是“你这套东西跟直接写梯形图比,好在哪”。别讲概念,拿一个具体场景对比:新增一台冷却泵,传统写法复制一段逻辑改地址,大概二十分钟,还容易漏改;标准化写法在变量表加一个"冷却泵3" : "typeMotor",调用块加一行,映射块加两行,五分钟,且不会漏。再拿改逻辑举例:启动延时从 5 秒改 8 秒,传统写法满程序找定时器,标准化写法改 UDT 参数或 FB 默认值,一处生效。
还有一个技巧:答辩前把“翻车点”主动讲出来。比如说明为什么 IO 映射块不放滤波,为什么状态字用 Int 不用 Bool 数组,这些取舍讲清楚,比堆功能更能体现设计思考。评委通常更在意你有没有想过边界情况,而不是功能多花哨。
从那以后我每次交付 PLC 项目,都会在最后留半天做一次“陌生人测试”:让没参与编程的同事按图纸找一个信号、改一个参数、加一台设备,卡在哪就补哪。这套习惯比任何文档都管用。希望帮到你。
本文还有配套的精品资源,点击获取