4. FC 函数与 FB 功能块的核心区别
在 TIA Portal 中,FC(Function,函数)和 FB(Function Block,功能块)是两种最常用的可复用程序单元。很多初学者会把它们当作“差不多的东西”,直到在电机启停控制里反复踩坑,才真正理解二者的分水岭:FC 没有静态存储,FB 带背景数据块(Instance DB)。这一节我们就把这个区别讲透,并用同一个电机启停任务分别用 FC 和 FB 实现,观察它们的行为差异。
一、本质区别:存储能力决定一切
FC 是一段“无记忆”的代码。它只有输入参数(Input)、输出参数(Output)、输入输出参数(InOut)和临时变量(Temp)。每次调用 FC,Temp 变量都会重新分配、重新初始化,调用结束后这些临时数据就消失了。FC 本身不占用任何背景数据块,因此它无法保存“上一次调用时的状态”。
FB 则是一段“有记忆”的代码。除了 Input、Output、InOut、Temp 之外,FB 还拥有静态变量(Static)。Static 变量存放在一个专门的数据块里,这个数据块就是背景数据块。每次调用 FB 时,系统会把背景数据块里的静态变量值“带进来”,执行完再把新值“写回去”。因此 FB 天然具备保持状态的能力,比如记住电机当前是运行还是停止、记住故障是否已确认。
二、参数传递方式对比
FC 的参数传递是“一次性”的。假设你要用 FC 实现电机启停,就必须把“电机运行状态”这个需要保持的变量,通过 InOut 参数从外部传进来,FC 内部修改后再传出去。也就是说,状态存储的责任被推给了调用者。
FB 的参数传递是“带上下文”的。你只需要在 FB 接口区定义好 Input(如启动按钮、停止按钮、故障信号)和 Static(如 RunState、FaultLatched),调用时系统自动关联一个背景数据块。同一个 FB 可以被多次调用,每次调用分配不同的背景数据块,彼此状态互不干扰——这正是 FB 适合做“多台电机控制”的原因。
下面这张图直观展示了 FC 与 FB 在电机启停任务中的结构差异:FC 的状态必须外挂到全局 DB,而 FB 的状态封装在自己的背景 DB 中。
三、电机启停任务:FC 实现方式
假设控制要求:按下启动按钮 I0.0,电机 Q0.0 运行;按下停止按钮 I0.1,电机停止;电机过载信号 I0.2 为 1 时,电机立即停止并保持故障状态,直到按下复位按钮 I0.3。
用 FC 实现时,由于 FC 没有 Static 变量,我们必须把“运行状态”和“故障锁存”这两个需要保持的量,通过 InOut 参数交给外部全局 DB。示例接口定义如下:
FC_MotorCtrl
Input:
Start : Bool; // I0.0 启动按钮
Stop : Bool; // I0.1 停止按钮
Overload : Bool; // I0.2 过载信号
Reset : Bool; // I0.3 复位按钮
InOut:
RunState : Bool; // 运行状态,需外部保持
FaultLatch : Bool; // 故障锁存,需外部保持
Output:
Motor : Bool; // Q0.0 电机输出
Temp:
TmpStart : Bool; // 临时中间变量
在 FC 内部,逻辑大致是:先处理故障锁存——如果 Overload 为 1,则 FaultLatch 置位;如果 Reset 为 1,则 FaultLatch 复位。然后处理启停——在 FaultLatch 为 0 的前提下,Start 置位 RunState,Stop 复位 RunState。最后 Motor = RunState AND NOT FaultLatch。
关键点在于:RunState 和 FaultLatch 这两个变量并不属于 FC,它们必须定义在一个全局 DB(例如 DB_MotorState)中,调用 FC 时把 DB_MotorState.RunState 和 DB_MotorState.FaultLatch 接到 InOut 引脚上。如果忘记接,或者多个电机共用同一个全局 DB,就会出现状态互相干扰的问题。
四、电机启停任务:FB 实现方式
用 FB 实现同样的任务,接口定义会发生变化:把 RunState 和 FaultLatch 从 InOut 移到 Static 区。FB 的接口如下:
FB_MotorCtrl
Input:
Start : Bool; // I0.0
Stop : Bool; // I0.1
Overload : Bool; // I0.2
Reset : Bool; // I0.3
Output:
Motor : Bool; // Q0.0
Static:
RunState : Bool; // 运行状态,自动保持
FaultLatch : Bool; // 故障锁存,自动保持
Temp:
TmpStart : Bool;
FB 内部逻辑与 FC 几乎一样,区别在于 RunState 和 FaultLatch 直接使用 Static 变量,不需要外部传入。调用 FB 时,TIA Portal 会自动弹出一个“背景数据块”的命名对话框,例如生成 FB_MotorCtrl_DB。这个背景 DB 就是 FB 的“记忆体”,里面存放着 RunState 和 FaultLatch 的当前值。
如果现场有三台电机,只需要把 FB_MotorCtrl 调用三次,分别分配三个背景 DB:FB_MotorCtrl_DB_1、FB_MotorCtrl_DB_2、FB_MotorCtrl_DB_3。每台电机的运行状态和故障锁存都独立保存在各自的背景 DB 中,互不影响。而如果用 FC,就必须手动创建三个全局 DB,并确保每次调用都接对 InOut 参数,工作量和出错概率都明显更高。
五、观察差异:一个容易踩的坑
在 FC 实现中,如果调用时把 RunState 接到了 M0.0,把 FaultLatch 接到了 M0.1,那么这两个 M 位就承担了状态存储的职责。此时如果程序其他地方也用了 M0.0,就会发生冲突。更隐蔽的问题是:FC 的 Temp 变量不能用来保存状态。有些初学者会把 RunState 写成 Temp 变量,结果发现电机一启动就停不下来,或者按了停止又自动启动——因为 Temp 变量在每次扫描周期结束时就被清掉了,FC 根本没有“记住”任何东西。
在 FB 实现中,Static 变量由背景 DB 托管,不会与 M 区或其他 DB 冲突。你可以在在线监控中直接打开背景 DB,看到 RunState 和 FaultLatch 的实时值,调试非常直观。这也是 FB 在工程实践中更受欢迎的原因:状态封装好、复用性强、调试方便。
六、小结
FC 与 FB 的核心区别可以归结为一句话:FC 无静态存储,状态必须外挂;FB 带背景数据块,状态自动封装。在电机启停这个典型任务中,FC 需要把 RunState 和 FaultLatch 通过 InOut 参数交给全局 DB,调用者负责状态管理;FB 则把这两个变量放进 Static 区,由背景 DB 自动保存,调用者只需连接输入输出信号。理解了这个差异,就能在后续的结构化编程中做出合理选择,避免“用 FC 存状态”这类常见错误。