PLC从入门到精通10-程序越写越乱改一处崩一片?3 招教你拆 FB/FC 写出让团队能协作的程序

程序写到第 5000 行,改一个变量的地址,全线崩盘。新人接手老项目,看 3 天代码找不到逻辑。设备出故障,A 段程序改了影响 B 段——这些都是"程序没结构"的典型症状。

F2 第 10 章调研中强调:专业工程师和业余程序员的差距不在编程技巧,而在程序结构。业余程序员写"一锅端"——所有逻辑堆在 OB1 主程序里。专业工程师写"模块化"——每个功能独立封装,互不干扰。

这篇文章我讲 3 招:FB(功能块)+ FC(函数)+ DB(数据块)——西门子 S7-1200/1500 的"程序架构三件套"。学会这 3 招,程序可读性提升 5 倍,调试时间减半,团队协作无障碍

目录

一、程序结构崩盘的 4 个征兆

征兆 1:变量命名混乱

征兆 2:双线圈问题反复出现

征兆 3:调试不敢动一段程序

征兆 4:新人看不懂

二、FB 功能块:把功能"封装"成积木

2.1 FB 是什么?

2.2 FB 的 3 大特征

2.3 一个 FB 的标准结构

2.4 FB 调用方式

三、FC 函数:可重用的代码片段

3.1 FC 是什么?

3.2 FC 的 3 大特征

3.3 FC 典型应用:模拟量线性转换

3.4 FB vs FC 怎么选

四、DB 数据块:参数和工艺数据管理

4.1 DB 是什么?

4.2 DB 的 3 种类型

4.3 Global DB 的典型用法:工艺参数

4.4 F2 推荐:参数分层管理

五、西门子三层架构:程序架构最佳实践

5.1 西门子三层架构图

5.2 三层架构的工程价值

5.3 OB1 主程序的最佳实践

5.4 三层架构 vs 一锅端

六、5 大典型 FB 拆分案例

案例 1:FB_电机启停(最常用)

案例 2:FB_阀门控制

案例 3:FB_PID 控制

案例 4:FB_顺序控制(SFC)

案例 5:FB_通信解析

八、FB/FC 重入(Reentrant)的工程陷阱

FB 重入 = 一个 FB 同时被多个调用者执行

九、PLC 编程规范 6 条(F2 第 11 章提炼)

1. 单一职责原则

2. 命名统一原则

3. 全局变量最小化

4. OB1 ≤ 100 行

5. 注释即文档

6. 版本号

十、工业程序质量评估的 5 个维度

十一、常量与配方管理:工艺参数 DB 的最佳实践

配方 DB 设计模式

配方切换流程

十二、多人协作 PLC 编程的工作流标准

Git 下的 PLC 项目版本管理

编码规范自动化

十三、程序优化的 5 个量化指标

十四、PLC 代码审查 7 问

十五、PLC 程序设计的"反模式"

十六、PLC 代码审查 7 问与反模式

代码审查 7 问

必须避免的 5 种反模式

十七、Git 与 PLC 项目的版本管理

十八、程序量化评估标准


一、程序结构崩盘的 4 个征兆

征兆 1:变量命名混乱

OB1 里用 M0.0M1.0M100.0,没有注释,半年后没人知道 M100.0 是啥。

征兆 2:双线圈问题反复出现

不同网络给同一变量赋值,前面的逻辑被默默失效。改了 A 处影响 B 处

征兆 3:调试不敢动一段程序

“这段代码谁都不敢动,怕改了设备崩”——程序已经脆弱到无法维护

征兆 4:新人看不懂

同事离职,新人接手,看 1 个月代码也理不清逻辑——因为没结构。

⚠️ 避坑警告程序没结构是技术债,越欠越多。第 1 个月写程序简单,第 6 个月调试地狱,第 12 个月重写整个项目。专业工程师从第一天就考虑结构


二、FB 功能块:把功能"封装"成积木

2.1 FB 是什么?

FB(Function Block,功能块)——把"一种功能"封装成独立模块。FB 有自己的内存(Instance DB),可以"记住"状态

打个比方:FB 像一个智能家电——你按下启动按钮(输入),它自己决定怎么工作(内部逻辑 + 状态),最后告诉你结果(输出)。同一个 FB 可以做"电机 1"、“电机 2”、“电机 3”——每个实例独立,状态不互相干扰

2.2 FB 的 3 大特征

特征 1:有自己的内存

  • 每个 FB 调用都生成一个背景 DB(Instance DB),存储 FB 内部的局部变量
  • FB_A 调用 1 次 = FB_A_Instance_1(独立的内存)
  • FB_A 调用 2 次 = FB_A_Instance_2(另一个独立的内存)
  • 两个实例完全独立,互不影响

特征 2:可参数化

  • 输入(IN)、输出(OUT)、输入输出(IN_OUT)参数可配置
  • 同一个 FB,控制不同的电机只需传不同的参数(如启动按钮地址、停止按钮地址、热继电器地址)

特征 3:可重用

  • 写一个 FB_电机启停,在多个电机控制中复用
  • 改一处 → 所有调用都更新(节省开发时间)

2.3 一个 FB 的标准结构

FUNCTION_BLOCK "FB_电机启停"
VAR
    // 静态变量(断电保持)
    running : BOOL;       // 电机运行状态
    fault : BOOL;         // 故障状态
END_VAR

BEGIN
    // 启动逻辑:按下启动按钮 + 没故障 + 没停止 → SET running
    IF start_btn AND NOT fault AND NOT stop_btn THEN
        running := TRUE;
    END_IF;
    
    // 停止逻辑:按停止按钮 / 有故障 → RST running
    IF stop_btn OR fault THEN
        running := FALSE;
    END_IF;
    
    // 输出
    motor_run := running;
END_FUNCTION_BLOCK

关键点:FB 内部用局部变量(runningfault),不用全局 M 区。这就是"封装"——外部只能通过 IN/OUT 接口访问 FB。

2.4 FB 调用方式

// OB1 主程序
"FB_电机启停_DB"(
    start_btn := "start_1",         // 输入:启动按钮
    stop_btn := "stop_1",           // 输入:停止按钮
    fault := "fault_1",             // 输入:故障信号
    motor_run => "motor_1_run"      // 输出:电机运行
);
"FB_电机启停_DB2"(
    start_btn := "start_2",
    stop_btn := "stop_2",
    ...
);

结果:调用 1 次就控制 1 个电机,调用 2 次就控制 2 个电机——完全不写重复代码


三、FC 函数:可重用的代码片段

3.1 FC 是什么?

FC(Function,函数)——把"一段计算逻辑"封装成可重用模块。FC 没有自己的内存,每次调用都是同样的输入同样的输出。

打个比方:FC 像计算器——你输入 2+3,它返回 5。下次输入 5+7,它返回 12计算器不"记住"任何事

3.2 FC 的 3 大特征

特征 1:无内存

  • 每次调用 FC,临时变量(VAR_TEMP)用完即释放
  • FC 不能"记住"上次调用的结果

特征 2:必须有返回值

  • FC 的输出参数(Output、Return)是必须传的
  • 调用 FC 时必须处理返回值

特征 3:纯函数化

  • FC 内部不依赖任何"状态"——输入确定则输出确定
  • 测试 FC 比测试 FB 容易得多

3.3 FC 典型应用:模拟量线性转换

FUNCTION "FC_线性转换" : REAL
VAR_INPUT
    raw_value : INT;       // 原始值(0-27648 对应 4-20mA)
    raw_min : INT := 0;
    raw_max : INT := 27648;
    eng_min : REAL;       // 工程量最小值(如 0℃)
    eng_max : REAL;       // 工程量最大值(如 100℃)
END_VAR

BEGIN
    // 4-20mA 对应 0-27648,转 0-100℃
    "FC_线性转换" := (raw_value - raw_min) / (raw_max - raw_min) * (eng_max - eng_min) + eng_min;
END_FUNCTION

调用

tank_temp := "FC_线性转换"(
    raw_value := "AI_temp",
    eng_min := 0.0,
    eng_max := 100.0
);

好处:转换逻辑写一次,所有模拟量都能用。改转换算法只需改 FC 内部。

3.4 FB vs FC 怎么选

场景用 FB用 FC
电机控制(要"记住"运行状态)
模拟量转换(无状态)
PID 控制(要"记住"积分项)
通信数据解析(无状态)
顺序控制(要"记住"当前步)
报警判断(无状态)

💡 效率技巧口诀:要"记住"状态用 FB,不要状态用 FC


四、DB 数据块:参数和工艺数据管理

4.1 DB 是什么?

DB(Data Block,数据块)——存储数据的"容器"。FB 调用必须配 Instance DB;全局数据用 Global DB。

4.2 DB 的 3 种类型

Instance DB(背景数据块)

  • 每个 FB 调用自动生成一个
  • 存储 FB 内部的局部变量
  • FB 和它的 Instance DB 是"绑定的"

Global DB(全局数据块)

  • 手工创建,存储全局数据
  • 类似 C 语言的全局变量
  • 所有程序块都能访问

System DB(系统数据块)

  • PLC 自动生成,存储系统配置
  • 用户不能修改

4.3 Global DB 的典型用法:工艺参数

DATA_BLOCK "DB_工艺参数"
VAR
    tank_temp_setpoint : REAL := 80.0;   // 罐温目标值 80℃
    tank_temp_hyst : REAL := 2.0;        // 滞回 2℃
    pump_pressure_max : REAL := 5.0;     // 泵最大压力 5bar
    cycle_time : TIME := T#30s;          // 循环时间 30 秒
END_VAR

好处

  • 所有工艺参数集中在一处,改一处就改全部
  • HMI 上显示参数一目了然
  • 不同产品配方切换只需切换 DB

4.4 F2 推荐:参数分层管理

第 1 层:硬件配置(CPU、模块)→ TIA Portal 设备组态 第 2 层:IO 标签(I0.0、Q0.0)→ PLC 变量表 第 3 层:工艺参数(温度、压力、流量)→ Global DB 第 4 层:功能模块(电机、PID)→ FB + Instance DB 第 5 层:主程序(OB1 调用所有 FB)→ 简洁

💡 效率技巧这种分层结构让程序可读性提升 5 倍。F2 第 11 章专门讲 PLC 编程规范,分层管理是核心思想。


五、西门子三层架构:程序架构最佳实践

5.1 西门子三层架构图

        ┌─────────────────────────┐
        │  OB1 主程序(顶层)       │
        │  - 调用 FB                │
        │  - 全局逻辑                │
        └─────────────┬───────────┘
                      ↓
        ┌─────────────────────────┐
        │  FB 功能块(中层)         │
        │  - FB_电机启停             │
        │  - FB_PID 控制            │
        │  - FB_通信解析             │
        │  - FB_顺序控制             │
        └─────────────┬───────────┘
                      ↓
        ┌─────────────────────────┐
        │  FC 函数 + DB 数据(底层)│
        │  - FC_线性转换             │
        │  - FC_数值滤波             │
        │  - DB_工艺参数             │
        │  - DB_配方管理             │
        └─────────────────────────┘

5.2 三层架构的工程价值

价值 1:可读性

  • OB1 只调用 FB,不写具体逻辑
  • 看 OB1 就能知道系统有几个功能模块
  • 进入 FB 看具体实现

价值 2:可维护性

  • 改一个电机控制只需改 FB_电机启停
  • 不影响其他电机
  • 改工艺参数只需改 DB_工艺参数

价值 3:可重用性

  • 写一个 FB_电机启停,所有电机都用
  • 项目之间复用(不同项目可能有相同的电机控制)

价值 4:可测试性

  • FB 可以单独测试(仿真器模拟输入)
  • 不依赖整个系统

价值 5:团队协作

  • 多人开发:A 写 FB_电机,B 写 FB_PID,互不干扰
  • 模块负责人清晰

5.3 OB1 主程序的最佳实践

// OB1 主程序(精简版)
// 注释:所有控制逻辑由 FB 封装,OB1 只做调度

"FB_系统初始化"();                    // 系统初始化
"FB_安全监控"();                      // 安全监控
"FB_电机1控制"();                     // 电机 1
"FB_电机2控制"();                     // 电机 2
"FB_PID 温度控制"();                  // 温度控制
"FB_通信解析"();                      // 通信
"FB_HMI 更新"();                      // HMI 更新
"FB_报警处理"();                      // 报警

OB1 简洁到极致——只有 FB 调用,一目了然。

5.4 三层架构 vs 一锅端

对比项一锅端三层架构
程序行数5000+OB1: 50-100, FB: 各 100-500
调试时间8 小时1-2 小时
团队协作困难顺畅
5 年后维护几乎无法维护仍可维护
新人上手1 个月1 周

⚠️ 避坑警告"一锅端"是业余程序员的标志。如果你发现 OB1 超过 1000 行,说明程序需要重构了。F2 第 11 章明确:OB1 超过 500 行 = 架构有问题。


六、5 大典型 FB 拆分案例

案例 1:FB_电机启停(最常用)

输入:start_btn、stop_btn、fault、reset 输出:motor_run、motor_fault 应用:所有电机控制(传送带、风机、泵、压缩机)

案例 2:FB_阀门控制

输入:open_btn、close_btn、open_limit、close_limit、fault 输出:valve_open、valve_close 应用:所有气动/电动阀门

案例 3:FB_PID 控制

输入:setpoint、process_value、kp、ki、kd 输出:output 应用:温度、压力、流量、液位控制

案例 4:FB_顺序控制(SFC)

输入:start、stop、fault、step_done[] 输出:current_step、step_command[] 应用:生产线顺序动作

案例 5:FB_通信解析

输入:raw_data[] 输出:parsed_value[] 应用:Modbus、PROFINET、EtherCAT 通信

💡 效率技巧F2 推荐的 FB 设计原则:单一职责 + 接口清晰 + 内部状态独立。每个 FB 只做一件事,外部通过 IN/OUT 通信,内部状态不外泄。


八、FB/FC 重入(Reentrant)的工程陷阱

FB 重入 = 一个 FB 同时被多个调用者执行

S7-1200 默认 FB 不可重入。如果 OB1 和 OB35(循环中断 100ms)同时调用同一个 FB → CPU 报错或数据错乱

解决:西门子 FB 属性的"块可重入"勾选。三菱用多个 Instance DB 实现。

不可重入的风险

  • PID 积分项在 OB1 和中断里同时累加 → 积分翻倍
  • 计数器同时被两个调用者 +1 → 丢计数
  • HMI 读写值到一半被中断打断 → 读到中间值

九、PLC 编程规范 6 条(F2 第 11 章提炼)

1. 单一职责原则

每个 FB 只做一件事。"FB_电机控制"和"FB_电机状态诊断"分开。

2. 命名统一原则

  • 动宾结构:valve_openmotor_starttemp_read
  • BOOL 加 is/has 前缀:is_runninghas_fault
  • 常数大写下划线:MAX_TEMPMIN_PRESSURE

3. 全局变量最小化

全局变量 ≤ 10 个(IO 除外)。FB 间通过 IN/OUT 参数通信。

4. OB1 ≤ 100 行

超过 100 行 → 重构拆 FB。F2 第 11 章说 OB1 超过 500 行 = 架构有问题。

5. 注释即文档

每个 Network 第一行写注释(“电机 1 启动逻辑”)。每个变量声明写注释(“罐温设定值,℃”)。

6. 版本号

每个 FB 内部注释版本号 + 修改日期 + 修改人。// V1.2 2026-08-11 张三:增加超时保护


十、工业程序质量评估的 5 个维度

维度优秀程序糟糕程序
可读性看程序 = 看文档,30 分钟理解看 3 天也看不懂
可维护性改一处只影响一个 FB改一处崩全局
可测试性每个 FB 可独立测试必须全套联动才能测
鲁棒性异常输入 / 断电 / 通信中断都有处理任何异常 = 失控
可扩展性加一个电机 = 复制 FB + 改 IO加一个电机要改 100 行程序

F2 第 10 章的结论:专业工程师和业余程序员的差距不在编程技巧,在程序结构——业余的写一锅端,专业的写模块化。


十一、常量与配方管理:工艺参数 DB 的最佳实践

配方 DB 设计模式

DATA_BLOCK "DB_配方_饼干"
VAR
    recipe_name : STRING[20] := '巧克力饼干';
    oven_temp : REAL := 180.0;     // 烤炉温度 ℃
    belt_speed : REAL := 2.5;      // 传送带速度 m/s
    baking_time : TIME := T#12m;   // 烘烤时间
    cooling_time : TIME := T#3m;   // 冷却时间
END_VAR

配方切换流程

  1. HMI 选配方号 → PLC 从配方数组加载目标配方 → 复写到"运行参数"DB
  2. 运行参数 DB 是实时用的参数(可在线修改),配方 DB 是只读库(不可随意改)
  3. 两层分离:运行参数 ≠ 配方库 ≠ 离线备份

F2 推荐:配方数量 > 50 个时用上位机数据库(SQL)管理,PLC 只保留当前运行配方。

十二、多人协作 PLC 编程的工作流标准

Git 下的 PLC 项目版本管理

西门子 TIA Portal → 导出 .awl/.scl 源码 → Git 管理 → PR/MR 审核 → 合并 → 导入 TIA 编译。

三菱 GX Works3 → 导出 ST 文本 → Git 管理。

团队分支策略

  • main:生产版本(最终下载到 PLC)
  • develop:开发版本
  • feature/FB_xxx:某功能开发分支

合并前必须由另一人 Review——两双眼睛比一双眼睛找到的 bug 多 3 倍

编码规范自动化

用西门子 Openness 写一个 Python 脚本批量检查:变量命名符合规范?注释存在?双线圈?F2 第 11 章总结:编码规范写在文件里有人看但没人执行,自动化检查才真正落地

十三、程序优化的 5 个量化指标

指标及格优秀
OB1 行数> 500< 200< 100
全局变量数> 100< 50< 10
每 FB 行数> 200< 100< 50
函数参数数> 10< 8< 5
循环嵌套> 3 层≤ 2 层≤ 1 层

F2 第 10 章结论:程序不是"能跑就行"——能跑只是第 1 层,能改 = 第 2 层,团队能协作 = 第 3 层,5 年后还能维护 = 第 4 层。

十四、PLC 代码审查 7 问

  1. 每个 FB 只做一件事?(单一职责)
  2. 全局变量 ≤ 10 个?(最小化全局状态)
  3. OB1 ≤ 100 行?
  4. 每个变量有注释?
  5. 有无双线圈?(编译警告零容忍)
  6. 有无 SET/OUT 混用?
  7. 安全回路独立于 PLC 程序?

7 个全 Yes → 优秀程序。有 1 个 No → 改完再上线。

十五、PLC 程序设计的"反模式"

  • “上帝 FB”:一个 FB 1000 行包揽所有逻辑 → 拆成 10 个 FB
  • “马蜂窝 IF”:IF 嵌套 5 层 → CASE/状态机替代
  • “散弹式变量”:到处读写同一个全局变量 → 封装成 FB 接口
  • “硬编码”:限值/延时直接写在梯形图里 → 提到 DB 常量化
  • “注释反着写”// motor_stop := TRUE// 停止电机 有用 10 倍——注释说"为什么",不说"做了什么"

十六、PLC 代码审查 7 问与反模式

代码审查 7 问

  1. 每个 FB 只做一件事? 2. 全局变量 ≤ 10? 3. OB1 ≤ 100 行? 4. 每个变量有注释? 5. 无编译警告(双线圈)? 6. 无 SET/OUT 混用? 7. 安全回路独立于 PLC?

7 全 Yes → 优秀程序。有 1 个 No → 改完再上线。

必须避免的 5 种反模式

  • “上帝 FB”:一个 FB 1000 行包揽所有逻辑 → 拆成 10 个
  • “马蜂窝 IF”:IF 嵌套 5 层 → CASE/状态机替代
  • “散弹式变量”:到处读写同一个全局变量 → 封装成 FB 接口
  • “硬编码”:限值/延时直接写梯形图 → 提到 DB 常量化
  • “废话注释”// 停止电机 → 应写 // 故障信号触发后延时 2s 停止以防管道水锤

十七、Git 与 PLC 项目的版本管理

西门子 TIA Portal → 导出 .awl/.scl 源码 → Git → PR 审核 → 合并 → 导入编译。三菱 → 导出 ST 文本 → Git。

分支策略:main = 生产版本、develop = 开发版本、feature/FB_xxx = 某功能。

合并前必须另一人 Review——两双眼睛发现的 bug 比一双多 3 倍


十八、程序量化评估标准

指标及格优秀
OB1 行数> 500< 200< 100
全局变量> 100< 50< 10
每 FB 行数> 200< 100< 50
函数参数> 10< 8< 5
循环嵌套> 3≤ 2≤ 1

F2 结论:能跑 = 第 1 层,能改 = 第 2 层,团队能协作 = 第 3 层,5 年后还能维护 = 第 4 层。大部分程序停在第 1 层。

扩展阅读 + 系列预告

本篇核心要点回顾

  1. 程序崩盘 4 征兆:变量混乱、双线圈反复、不敢改段、新人看不懂
  2. FB 三特征:有内存、可参数化、可重用
  3. FC 三特征:无内存、必须有返回值、纯函数化
  4. FB vs FC:要"记住"状态用 FB,不要状态用 FC
  5. 三层架构:OB1 调用 FB,FB 调用 FC 和 DB

思考题

  1. 你的 OB1 主程序有多少行?有没有超过 500 行?是否需要重构?
  2. 你的项目里有几个 FB/FC?它们是按"功能"拆分的还是按"组件"拆分的?

下一篇预告: 第 11 篇《4-20mA 信号飘成心电图?3 类模拟量干扰 + 4 招硬核解决方案》——模拟量干扰源、屏蔽层单端接地、信号隔离器应用。


标签:#PLC编程 #FB功能块 #FC函数 #数据块 #西门子 #梯形图 #自动化架构

SEO 关键词:PLC程序结构、FB功能块、FC函数、数据块、西门子编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每日干货分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值