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 靠参数“现算现用”,FB 靠背景数据块“记住过去”。参数传递方式上,FC 必须把需要保持的量通过 InOut 参数交给外部存储(如全局 DB 或 M 区);FB 则可以直接把状态放在自己的 Static 变量里,由背景数据块自动托管。

二、参数传递方式对比

FC 的参数传递是“一次性”的。假设你要用 FC 实现电机启停,就必须把“电机运行状态”这个需要保持的变量,通过 InOut 参数从外部传进来,FC 内部修改后再传出去。也就是说,状态存储的责任被推给了调用者。

FB 的参数传递是“带上下文”的。你只需要在 FB 接口区定义好 Input(如启动按钮、停止按钮、故障信号)和 Static(如 RunState、FaultLatched),调用时系统自动关联一个背景数据块。同一个 FB 可以被多次调用,每次调用分配不同的背景数据块,彼此状态互不干扰——这正是 FB 适合做“多台电机控制”的原因。

下面这张图直观展示了 FC 与 FB 在电机启停任务中的结构差异:FC 的状态必须外挂到全局 DB,而 FB 的状态封装在自己的背景 DB 中。

FC 函数:无静态存储 启动/停止按钮 I0.0 / I0.1 FC_MotorCtrl Input: Start, Stop InOut: RunState 全局 DB MotorState (外部存储) 电机输出 Q0.0 读写状态 取回状态 状态必须外挂到全局 DB FC 自身不保存 RunState 多次调用需手动管理不同 DB FB 功能块:带背景数据块 启动/停止按钮 I0.0 / I0.1 FB_MotorCtrl Input: Start, Stop Static: RunState 背景 DB FB_MotorCtrl_DB (自动关联) 电机输出 Q0.0 自动读写 自动取回 状态封装在背景 DB 中 FB 自动保存 RunState 多次调用各配一个背景 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 无静态存储,状态必须外挂;FB 带背景数据块,状态自动封装。在电机启停这个典型任务中,FC 需要把 RunState 和 FaultLatch 通过 InOut 参数交给全局 DB,调用者负责状态管理;FB 则把这两个变量放进 Static 区,由背景 DB 自动保存,调用者只需连接输入输出信号。理解了这个差异,就能在后续的结构化编程中做出合理选择,避免“用 FC 存状态”这类常见错误。