在结构化文本(ST)编程中,功能块调用是电气自动化项目最基础、最频繁的操作之一。它直接决定逻辑可读性、调试效率与后期维护成本。下面以 MyFB(IN := Signal, Q => Output); 这一典型语句为线索,逐层拆解其语法结构、执行机制、常见错误及工程实践要点。全文不依赖图形,所有概念均通过文字精准定义和实例说明。
一、先明确:什么是功能块(Function Block)?
功能块不是函数,也不是普通变量。它是带记忆的可重用逻辑封装单元,内部拥有自己的输入(IN)、输出(OUT)和静态变量(如内部计时器值、状态标志)。每次调用都独立保存上次执行后的内部状态——这是它与无状态函数(FUNCTION)的本质区别。
例如,一个自保持启停功能块 TON(延时接通定时器),调用两次 TON1 和 TON2,它们各自的 ET(已耗时间)和 Q(输出状态)互不影响。
功能块必须先声明实例名,再调用。不能像函数那样直接写 MyFB(...);必须有 MyFB : MyFBType; 这样的变量声明前置。
二、语句分解:MyFB(IN := Signal, Q => Output);
这条语句共含四个关键成分,按执行顺序和语义层级排列如下:
-
MyFB
→ 这是已声明的功能块实例名,类型为MyFBType(由用户自定义或系统库提供)。它不是类型名,而是内存中一个具体对象的“代号”。
✅ 正确声明示例(在 VAR 块中):MyFB : MyFBType;❌ 错误写法:
MyFBType(IN := Signal, Q => Output);—— 类型名不能直接调用。 -
IN := Signal
→ 输入参数赋值,使用:=(赋值运算符)。IN是功能块MyFBType内部定义的输入变量名;Signal是当前程序上下文中存在的布尔型(BOOL)、整型(INT)或结构体变量。:=表示“把Signal的当前值复制给MyFB.IN”,发生在功能块执行前。- 支持任意兼容类型:若
IN是REAL,则Signal可为REAL、INT(自动转换),但不可为STRING(类型不匹配报错)。
-
Q => Output
→ 输出参数映射,使用=>(输出关联运算符)。Q是功能块内部的输出变量名;Output是调用者侧的变量名(必须已声明)。=>不是赋值,而是建立“地址绑定”:MyFB.Q的值在功能块执行完毕后,自动写入Output所指向的内存地址。Output必须是可写变量(VAR、VAR_IN_OUT),不能是常量(如TRUE)或表达式(如A + B)。- 若
Output是数组元素(如Flags[3])或结构体成员(如Motor.Status.Running),只要地址有效,完全合法。
-
分号
;
→ ST 语句结束符,不可省略。缺位将导致编译器报“syntax error near ‘Q’”。
三、完整可运行示例(含声明+调用)
以下是一个最小但真实的 PLC 程序片段(符合 IEC 61131-3 标准),可在 Codesys、TIA Portal、Unity Pro 等主流平台直接验证:
// 在全局或组织块(OB)的 VAR 块中声明
VAR
StartButton : BOOL := FALSE;
MotorOn : BOOL := FALSE;
MyFB : SR; // 使用标准置位复位功能块(SR 型)
END_VAR
// 在主程序(如 MAIN 或 Cyclic OB)中调用
MyFB(S := StartButton, R := NOT StartButton, Q => MotorOn);
说明:
SR是 IEC 标准功能块,含输入S(置位)、R(复位)、输出Q。- 此处
S := StartButton将按钮信号传入;R := NOT StartButton实现按下即启、松开即停(非自锁);Q => MotorOn把输出结果存入MotorOn变量。 - 注意:
NOT StartButton是表达式,允许出现在:=右侧;但=>右侧只接受左值(l-value),即能被写入的变量。
四、必须避开的 5 类典型错误
| 错误类型 | 错误写法 | 原因 | 修正方式 |
|---|---|---|---|
| 未声明实例 | MyFB(IN := Signal);(无 VAR MyFB : MyFBType;) |
编译器找不到 MyFB 对象 |
在 VAR 区补全声明 |
| 混淆赋值与映射 | MyFB(IN => Signal); |
=> 用于输出,IN 是输入,方向反了 |
改为 IN := Signal |
| 输出目标非法 | MyFB(Q => TRUE); |
TRUE 是常量,无法写入 |
改为 Q => OutputVar(变量名) |
| 类型强转缺失 | MyFB(IN := 123);(IN 是 REAL,123 是 INT) |
部分平台严格检查,需显式转换 | 改为 IN := REAL#123 或 IN := 123.0 |
| 重复调用同一实例 | MyFB(...); 多次出现且无间隔 |
功能块状态被连续覆盖,逻辑紊乱 | 每个实例名仅调用一次/周期;需多路逻辑时声明多个实例(如 MyFB1, MyFB2) |
五、高级技巧:如何处理复杂参数?
1. 结构体输入/输出
若 MyFBType 定义输入为结构体:
TYPE MyFBType : FUNCTION_BLOCK
VAR_INPUT
Config : CONFIG_STRUCT;
END_VAR
VAR_OUTPUT
Result : RESULT_STRUCT;
END_VAR
END_TYPE
调用时可整体传入:
MyFB(Config := (Kp := 1.5, Ti := 10.0), Result => MyResult);
括号内为结构体字面量,字段名必须与 CONFIG_STRUCT 定义一致。
2. 数组输入
当 IN 是数组(如 ARRAY[0..2] OF INT),可传入同维数组变量:
MyArray : ARRAY[0..2] OF INT := [1, 2, 3];
MyFB(IN := MyArray);
3. 引用传递(VAR_IN_OUT)
部分功能块设计 VAR_IN_OUT 参数(如数据交换类 FB),此时用 := 和 => 均可,但语义不同:
Data := LocalBuf:把LocalBuf值拷入功能块内部副本;Data => LocalBuf:让功能块直接操作LocalBuf内存(零拷贝,高效)。
选择取决于是否需要功能块修改原始数据。
六、执行时序与扫描周期关系
PLC 程序按循环扫描执行:读输入 → 执行程序 → 写输出。
功能块调用 MyFB(...); 的执行时机在此循环的“执行程序”阶段,且严格按代码书写顺序执行。
例如:
MyFB1(IN := A, Q => X);
MyFB2(IN := X, Q => Y); // 此处 X 已是 MyFB1 的最新输出
→ MyFB2 读到的是 MyFB1 本周期计算出的 X,而非上周期旧值。这是 ST 的确定性优势。
注意:若两功能块并行逻辑无依赖,顺序无关;但存在数据链时(如滤波后比较),顺序即逻辑。
七、调试核心:如何确认调用真正生效?
- 在线监控:在编程软件中,将光标悬停于
MyFB上,查看其内部变量(如IN,Q,内部Timer.ET)实时值; - 强制赋值测试:手动将
Signal强制为TRUE,观察Output是否按预期变为TRUE; - 断点单步:在支持调试的平台(如 Codesys)中,在该行设断点,观察调用前后各变量变化;
- 日志输出:添加辅助语句
IF MyFB.Q THEN Log('MyFB active'); END_IF;辅助追踪。
八、命名与工程规范建议
- 实例名
MyFB应具业务含义:ConveyorStartFB,TempCtrlPID,避免泛称; - 输入/输出参数名(
IN,Q)是功能块类型定义的一部分,不得在调用时更改;若需语义提示,可用注释:MyFB(IN := ConveyorSensor, Q => ConveyorRunning); // IN=光电开关,Q=运行标志 - 同一功能块多次使用时,实例名后缀编号(
ValveCtrl_1,ValveCtrl_2),禁止用ValveCtrl1(易与类型名混淆); - 所有功能块类型定义应集中存放于专用 POU(Program Organization Unit),如
FBS_LIBRARY,便于复用与版本管理。
九、与其他语言调用方式对比(加深理解)
| 语言 | 调用语法 | 关键差异 |
|---|---|---|
| ST(本文) | MyFB(IN := A, Q => B); |
显式区分输入赋值 := 与输出映射 =>;支持结构体字面量 |
| LD(梯形图) | 触点驱动功能块符号,连线表示 IN/Q |
图形化,但复杂参数(如结构体)难以表达 |
| FBD(功能块图) | 方框引脚连线 | 直观,但大规模网络易混乱,不易版本比对 |
| IL(指令表) | CAL MyFB IN:=A Q=>B |
接近汇编,已基本淘汰 |
ST 因其文本可比性、Git 友好、参数显式化,已成为现代自动化项目的首选编程语言。
十、最后验证清单(写完每条调用后快速自查)
- ✅
MyFB是否已在VAR区声明?类型是否匹配? - ✅ 所有
:=右侧变量是否已声明且类型兼容? - ✅ 所有
=>右侧是否为可写变量(非常量、非表达式)? - ✅ 分号
;是否存在? - ✅ 若含结构体/数组,字段名与维度是否与类型定义完全一致?
- ✅ 在线下载后,是否通过强制与监控确认逻辑行为符合预期?
通过此清单,99% 的功能块调用问题可在编码阶段拦截。

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