在西门子S7-1200/1500 PLC编程中,FB(功能块)与FC(函数)表面相似——都能被多次调用、封装逻辑、复用代码。但二者在底层机制、数据存储方式和执行行为上存在根本性差异。这种差异不只影响程序可读性,更直接决定:变量能否持久保存、多实例是否相互干扰、调试时断点是否稳定、HMI能否实时监控数值、甚至系统重启后状态能否恢复。而承载这一差异的核心载体,正是背景数据块(Instance DB)。
一、先看现象:调用两次同一个块,结果为何不同?
假设你编写了一个计算电机运行时间的逻辑:
-
用FC实现:
- 创建FC1,内部声明一个静态变量
RunTime_s : INT; - 在主程序中连续调用两次
CALL FC1; - 第一次调用后
RunTime_s = 10; - 第二次调用时,
RunTime_s的值不是独立的——它可能被第一次调用覆盖,也可能因优化被清零,结果不可预测。
- 创建FC1,内部声明一个静态变量
-
用FB实现:
- 创建FB1,内部声明同名变量
RunTime_s : INT(位于“变量声明表”的STATIC区或IN_OUT接口); - 每次调用都显式关联一个独立的数据块(如
DB10对应第一次调用,DB20对应第二次); - 调用1修改
DB10.RunTime_s,调用2修改DB20.RunTime_s,两者完全隔离。
- 创建FB1,内部声明同名变量
这个“隔离性”,就是FB的立身之本;而实现隔离的唯一途径,就是每次调用都绑定专属的背景数据块。
二、本质区别:FB自带“身份证”,FC没有
| 特性 | FB(Function Block) | FC(Function) |
|---|---|---|
| 是否必须分配数据块 | 是 —— 每次调用必须指定一个Instance DB | 否 —— 无数据块概念,仅靠临时局部变量(TEMP)或全局变量支撑 |
| 变量生存期 | 永久存在于Instance DB中,PLC断电保持(若启用保持性) | 仅单次执行有效,下次调用时TEMP重置,IN/OUT参数不保留值 |
| 多实例支持 | 天然支持:每个Instance DB = 一个独立实例 | 不支持:所有调用共享同一组局部变量空间(除非手动用全局DB模拟) |
| 接口变量默认存储位置 | IN/OUT/IN_OUT/STATIC → 自动映射到Instance DB |
IN/OUT/IN_OUT → 仅传递值,不存储;TEMP → 堆栈临时区 |
| 是否可带“记忆” | 可:STATIC区变量在Instance DB中持久化 |
不可:无持久化机制,需额外用全局DB+地址计算模拟,极易出错 |
关键结论:
FB ≠ FC + 手动加DB;
FB = 封装逻辑 + 自动绑定专属数据容器 + 实例生命周期管理。
这个“自动绑定专属数据容器”,就是Instance DB的生成与关联机制。
三、Instance DB:不是“可选项”,而是FB的呼吸器官
当你在TIA Portal中双击FB图标,进入其“块接口”视图,会看到左侧有“接口”(Interface)区域,右侧有“变量声明表”(Variable Declaration Table)。此时:
- 若你为FB声明了
IN变量Start : Bool、OUT变量Running : Bool、STATIC变量Counter : INT; - 当你在OB1中插入一行调用:
FB1( Start := I0.0, Running => Q0.0 ); - TIA Portal不会允许你保存该调用,直到你为
FB1指定一个数据块——例如DB100。
为什么?因为编译器需要明确:Start 的输入值存到 DB100.Start,Running 的输出值从 DB100.Running 读取,Counter 的当前值始终保存在 DB100.Counter。没有这个DB,接口变量就失去了物理载体——它们只是空中楼阁。
Instance DB的结构由FB的接口自动生成并严格对应:
- 每个
IN变量 → DB中同名字段(类型、初始值一致); - 每个
OUT变量 → DB中同名字段(可读可写); - 每个
STATIC变量 → DB中同名字段(即使未出现在接口列表,也强制存在); IN_OUT变量 → DB中同名字段(双向绑定)。
因此,DB100 不是“辅助存储”,它是FB1在内存中的实体分身。删掉DB100,FB1调用立即报错;修改DB100中某个值,下次调用FB1时立刻生效。
四、实操验证:三步看清Instance DB如何工作
-
创建FB并声明变量:
- 新建FB2,接口中添加:
IN:Pulse : Bool
OUT:EdgeDetected : Bool
STATIC:LastState : Bool := FALSE - 在FB2内部编写上升沿检测逻辑:
EdgeDetected := Pulse AND NOT LastState; LastState := Pulse;
- 新建FB2,接口中添加:
-
生成Instance DB并调用:
- 在OB1中拖入FB2,系统弹出窗口要求选择或新建DB → 点击“新建”生成
DB1; - 此时打开
DB1,可见字段:Pulse : Bool、EdgeDetected : Bool、LastState : Bool; - 初始值:
LastState = FALSE(来自FB中声明的初始值)。
- 在OB1中拖入FB2,系统弹出窗口要求选择或新建DB → 点击“新建”生成
-
监控与修改:
- 在在线监控模式下,强制将
DB1.LastState设为TRUE; - 下次触发
Pulse时,EdgeDetected不再动作(因TRUE AND NOT TRUE = FALSE); - 若改用FC实现相同逻辑,
LastState无法在调用间保持,每次都是FALSE初值,永远只检测到第一次上升沿。
- 在在线监控模式下,强制将
这证明:Instance DB是FB维持状态的唯一合法内存空间;脱离它,FB退化为无记忆的FC。
五、常见误区与踩坑现场
-
❌ “我给FC配一个DB,不就和FB一样了吗?”
错。FC调用时,DB字段不会自动映射到接口。你必须手动用DB1.Pulse赋值给FC的输入形参,再手动把FC输出写回DB1.EdgeDetected。这不仅冗余,且LastState仍无处安放——FC无STATIC区,你只能把它也塞进DB,再在FC开头读、结尾写,相当于用5行代码模拟FB的1行调用,且极易遗漏读/写顺序导致逻辑错乱。 -
❌ “FB的Instance DB太大,我想共用一个DB给多个FB实例。”
绝对禁止。一个DB只能绑定一个FB调用。若强行让FB1和FB2都用DB1,编译器会报错:“DB1已作为FB1的实例数据块使用”。这是硬性约束,防止内存越界和字段覆盖。 -
❌ “我把所有变量都放在
TEMP里,省得建DB。”
TEMP变量在每次调用结束即释放,且大小受限(S7-1200约2KB/块)。更重要的是:TEMP不能初始化,不能保持,不能被HMI读取,不能在线修改——它只适合存放中间计算结果,如TempSum := IN1 + IN2。 -
❌ “我在FB里没声明
STATIC,所以不需要Instance DB。”
错。只要FB有IN/OUT/IN_OUT接口,就必须Instance DB。STATIC只是锦上添花;接口变量才是DB存在的刚性理由。
六、高级应用:Instance DB如何赋能工程实践
-
批量实例化设备模块
产线有12台输送机,每台需独立启停、故障计数、运行时长统计。- 正确做法:编写1个FB(
ConveyorCtrl),生成12个Instance DB(DB101~DB112); - OB1中循环调用:
ConveyorCtrl[0](Start:=M100.0, FaultCount=>DB101.FaultCnt); ConveyorCtrl[1](Start:=M100.1, FaultCount=>DB102.FaultCnt); // ... - HMI只需绑定
DB101.FaultCnt至画面控件,无需任何脚本。
- 正确做法:编写1个FB(
-
调试时精准定位问题实例
当某台电机失控,监控DB205.Running为TRUE但DB205.Start为FALSE,立刻判断是FB内部逻辑异常(如锁存未释放);若用FC,则所有电机共享同一套临时变量,无法区分具体是哪一台出错。 -
实现PLC断电保持
在DB101属性中勾选“保持性”,则DB101.RunTime_s在断电后仍保留。FC无法做到——TEMP变量断电即失,全局DB虽可设保持,但需手动管理地址与初始化,风险极高。 -
与SCL高级语法协同
使用FOR循环调用FB时,Instance DB名可动态生成:FOR i := 0 TO 7 DO ValveCtrl[i](OpenCmd := OpenCmdArray[i], DBNumber := 200 + i); END_FOR;此处
ValveCtrl为FB,DBNumber参数用于指定第i个实例对应的DB编号(需提前建好DB200~DB207),实现配置化部署。
七、一句话总结核心逻辑
FB的本质,是以Instance DB为锚点,将代码逻辑与数据实体强绑定;
FC的本质,是纯代码片段,执行完即焚,不留痕迹;
Instance DB不是“附加功能”,而是FB从语法定义上就携带的不可分割的组成部分——就像汽车必须有轮子才能叫汽车,FB没有Instance DB,就不具备作为FB的资格。

暂无评论,快来抢沙发吧!