第一次接触PLC编程时,看到FB和FC这两个缩写词,我完全摸不着头脑。直到参与了一个传送带物料分拣项目后,才真正理解了它们的价值。想象一下,你正在搭建一个智能分拣系统:传送带上的传感器检测物料,PLC控制气缸将不同物料推入对应料槽。这个过程中,类似"读取传感器信号"、"控制气缸动作"这样的操作会反复出现。如果每次都重写相同代码,不仅效率低下,后期维护更是噩梦。
FB(功能块)和FC(功能)就是解决这类问题的利器。简单来说,它们就像乐高积木中的标准件。FB更像是带记忆功能的智能积木,每次使用都能记住自己的状态;FC则像普通积木,每次使用都是全新的。在实际项目中,我用FB处理需要记忆状态的操作(比如电机启停计数),用FC实现纯逻辑运算(比如信号比较转换)。
FB最特别之处在于它自带"记忆芯片"——背景数据块(DB)。在分拣系统项目中,我设计了一个电机控制FB,它的结构是这样的:
pascal复制FUNCTION_BLOCK MotorControl
VAR_INPUT
Start : BOOL; // 启动信号
Stop : BOOL; // 停止信号
Speed : INT; // 转速设定
END_VAR
VAR_OUTPUT
Running : BOOL; // 运行状态
CurrentSpeed : INT; // 实际转速
END_VAR
VAR_STATIC
RunHours : REAL; // 累计运行小时
StartCount : UINT; // 启动次数统计
END_VAR
当这个FB被调用时,所有静态变量(STAT)都会保存在专属的DB中。比如系统中有3台电机,我就创建3个不同的DB(如DB1、DB2、DB3)分别存储它们的工作数据。这样即使PLC断电重启,上次记录的运行小时数也不会丢失。
在分拣系统中,FB特别适合这些场景:
有个实际案例:系统要求统计每个气缸的动作次数。如果不用FB,就需要手动创建大量全局变量。而使用FB后,只需在静态变量区添加ActionCount:INT,每次动作时ActionCount:=ActionCount+1即可。三台气缸就对应三个DB,数据自动隔离存储。
FC更像是数学中的函数,比如y=f(x)。在分拣项目中,我常用FC处理这些任务:
这是一个信号处理的FC示例:
pascal复制FUNCTION FilterSignal : REAL
VAR_INPUT
RawValue : REAL; // 原始信号
FilterFactor : REAL; // 滤波系数(0-1)
PrevValue : REAL; // 上次滤波值
END_VAR
VAR_TEMP
Result : REAL; // 临时计算结果
END_VAR
Result := PrevValue * FilterFactor + RawValue * (1 - FilterFactor);
FilterSignal := Result;
注意FC没有静态变量区,所有中间结果要么通过输出参数返回,要么存入全局DB。临时变量(TEMP)只在本次调用有效,下次调用时会被清零。
通过合理设计接口,FC可以实现高度复用。比如在分拣系统中,我创建了一个通用比较器FC:
pascal复制FUNCTION Compare : BOOL
VAR_INPUT
Value1 : REAL;
Value2 : REAL;
Mode : INT; // 1:> 2:= 3:< 4:>= 5:<= 6:<>
END_VAR
CASE Mode OF
1: Compare := Value1 > Value2;
2: Compare := Value1 = Value2;
//...其他比较模式
END_CASE;
这个FC被用在系统各处:物料重量比较、光电信号判断、速度监控等。每次调用只需传入不同参数,就像瑞士军刀一样灵活。
在分拣系统开发中,我总结出选择FB或FC的决策矩阵:
| 考量因素 | FB优势场景 | FC优势场景 |
|---|---|---|
| 数据持久性需求 | 需要保存状态数据(如计数器) | 仅需临时计算 |
| 调用频率 | 高频调用(如周期扫描任务) | 低频调用(如初始化操作) |
| 参数复杂度 | 参数多且需要独立配置 | 参数简单或固定 |
| 内存占用 | 可接受额外DB开销 | 要求最小化内存占用 |
| 调试便利性 | 数据可视化方便(有专属DB) | 逻辑流程清晰(无状态干扰) |
优秀的设计往往是FB和FC的有机结合。比如在分拣系统中:
具体到代码层面,这种组合非常常见:
pascal复制// 在电机控制FB中调用滤波FC
CurrentSpeed := FilterSignal(
RawValue := AnalogInput,
FilterFactor := 0.8,
PrevValue := LastSpeed);
根据多次项目经验,我总结出功能块开发的五个步骤:
在分拣系统调试中,这些方法特别有效:
比如在信号处理FC中,我增加了安全保护:
pascal复制IF FilterFactor < 0 THEN
FilterFactor := 0;
ELSIF FilterFactor > 1 THEN
FilterFactor := 1;
END_IF;
当系统中需要多个相同类型的设备时,FB的多重背景功能就大显身手。在分拣系统二期扩展中,我这样管理新增的5个气缸:
pascal复制VAR
Cylinder1 : FB_Cylinder(DB1);
Cylinder2 : FB_Cylinder(DB2);
//...其他气缸
END_VAR
每个气缸实例都有自己的DB,但共享同一套控制逻辑。当需要修改气缸控制算法时,只需更新FB代码,所有实例自动同步。
随着项目迭代,功能块也需要升级。我的经验是:
例如在电机控制FB升级时:
pascal复制FUNCTION_BLOCK MotorControl_V2
(* 2023-08-20 更新:
1. 增加软启动功能
2. 优化过载检测算法 *)
在培训新人时,这些问题是最高频的:
问题1:FB和FC调用后数据异常
问题2:功能块执行效率低
问题3:功能块无法复用
在分拣系统调试中,曾遇到FB偶尔数据丢失的问题。最终发现是多个OB块调用了同一个FB实例,导致数据冲突。解决方案是为每个调用创建独立实例,并规范调用关系。