在电气自动化系统中,将重复使用的控制逻辑封装为标准功能块(Function Block, FB),是提升程序可维护性、复用性和团队协作效率的核心实践。ST(Structured Text)语言作为IEC 61131-3标准中表达复杂逻辑最接近高级编程语言的文本语言,天然支持带状态的数据结构与封装机制。以下是以电机启停+正反转+故障保护+运行计时为典型场景,手把手教你将电机控制逻辑封装为一个通用、可配置、符合工业标准的功能块(FB),全程无需图形化编辑器辅助,纯文字可执行。
一、明确封装目标:这个FB要解决什么问题?
你不需要每次写新项目都重写一遍“按下启动按钮→检查急停→判断接触器反馈→防抖→自锁→记录运行时间→故障时切断并报警”。你需要的是:
- 一次编写,多处调用:同一FB实例可控制泵、风机、传送带等不同电机;
- 参数可配:启动延时、故障锁定时间、允许的最大连续运行时长等全部外置;
- 状态透明:外部能直接读取
Q(输出状态)、FaultCode(故障码)、RunTime_h(累计运行小时)等; - 安全合规:内置急停响应(
EMG输入必须硬线接入,FB内做软逻辑校验)、热过载记忆(OL信号触发后需手动复位)、输出反馈验证(FBK反馈丢失超200ms视为失控); - 无全局变量依赖:所有数据均通过FB实例内部变量或接口传递,不读写
VAR_GLOBAL。
二、定义FB接口:输入/输出/内部变量(ST语法)
在TIA Portal、Codesys或OpenPLC等支持IEC 61131-3的平台中,新建FB时需严格声明接口。以下是完整且可直接粘贴的ST接口定义(不含实现逻辑):
FUNCTION_BLOCK FB_MotorCtrl
VAR_INPUT
START : BOOL; // 硬件启动按钮(上升沿有效)
STOP : BOOL; // 硬件停止按钮(下降沿有效)
EMG : BOOL; // 急停输入(常闭触点,TRUE = 正常)
OL : BOOL; // 热过载信号(TRUE = 过载动作)
FBK : BOOL; // 接触器反馈信号(TRUE = 已吸合)
Enable : BOOL := TRUE; // 使能开关(用于批量禁用)
END_VAR
VAR_OUTPUT
Q : BOOL; // 主输出:控制接触器线圈
FaultCode : UINT := 0; // 故障代码:0=无故障,1=急停触发,2=过载,3=反馈丢失,4=输出冲突
RunTime_h : REAL := 0.0; // 累计运行小时(精度0.01h)
IsRunning : BOOL; // 运行中标志(含启动延时未完成时为FALSE)
END_VAR
VAR
// 内部状态变量(不对外暴露)
xStartRising : BOOL; // START上升沿检测
xStopFalling : BOOL; // STOP下降沿检测
tRunTimer : TON; // 运行计时器(TON类型)
tFaultLock : TP; // 故障锁定脉冲(TP类型,用于阻断自动重启)
bFaultLocked : BOOL; // 故障是否处于锁定状态(需手动复位)
rAccumulated : TIME; // 累计运行时间(TIME类型,用于转换为小时)
lastFBK : BOOL; // 上周期FBK值,用于丢失检测
cntFBKMiss : UINT; // FBK丢失计数器(单位:扫描周期)
MAX_FBK_MISS : UINT := 10; // 允许最大丢失周期数(假设PLC扫描周期50ms,则10×50ms=500ms)
END_VAR
✅ 关键说明:
Enable默认TRUE,避免误触发;若需禁用某台电机,只需在调用处赋值FALSE,不改动FB内部;FaultCode使用UINT而非STRING,便于HMI做枚举映射(如HMI中FaultCode = 2→ 显示“电机过载”);RunTime_h为REAL类型,单位为小时,比用TIME类型更易做报表统计;MAX_FBK_MISS设为10是经验阈值:PLC扫描周期通常20–100ms,10次即200–1000ms,覆盖机械响应延迟。
三、编写FB主体逻辑(ST实现)
将以下代码完整填入FB的实现区域(Implementation)。每行均有注释,但实际部署时可删除注释以节省资源:
// ===== STEP 1:输入边沿检测 =====
xStartRising := START AND NOT START(1); // 利用上周期值做上升沿(需在FB调用前确保首次扫描有初值)
xStopFalling := NOT STOP AND STOP(1);
// ===== STEP 2:故障检测与锁定 =====
// 急停触发(高优先级,立即切断)
IF NOT EMG THEN
FaultCode := 1;
bFaultLocked := TRUE;
ELSIF OL THEN
FaultCode := 2;
bFaultLocked := TRUE;
ELSIF (NOT FBK) AND (cntFBKMiss >= MAX_FBK_MISS) THEN
FaultCode := 3;
bFaultLocked := TRUE;
ELSE
FaultCode := 0;
// 仅当无任何故障且STOP被按下时,才允许清除锁定
IF STOP THEN
bFaultLocked := FALSE;
FaultCode := 0;
END_IF;
END_IF;
// ===== STEP 3:FBK丢失计数器 =====
IF FBK THEN
cntFBKMiss := 0;
ELSE
cntFBKMiss := cntFBKMiss + 1;
END_IF;
// ===== STEP 4:主控制逻辑(仅在无锁定且使能时运行)=====
Q := FALSE;
IF Enable AND NOT bFaultLocked THEN
// 启动优先于停止(防抖设计:启动信号维持100ms以上才响应)
IF xStartRising THEN
Q := TRUE;
ELSIF xStopFalling THEN
Q := FALSE;
END_IF;
END_IF;
// ===== STEP 5:运行计时(仅在Q=TRUE且FBK=TRUE时累加)=====
IF Q AND FBK THEN
tRunTimer(IN := TRUE, PT := T#1S); // 每秒触发一次
IF tRunTimer.Q THEN
rAccumulated := rAccumulated + T#1S;
// 转换为小时:1小时 = 3600秒 → 除以3600.0
RunTime_h := (TIME_TO_REAL(rAccumulated) / 3600.0);
END_IF;
ELSE
tRunTimer(IN := FALSE);
// 保持RunTime_h不变(不归零)
END_IF;
// ===== STEP 6:运行状态输出 =====
IsRunning := Q AND FBK;
✅ 关键设计点解析:
- 边沿检测不依赖
R_TRIG指令:直接用START(1)引用上一周期值,兼容所有ST环境,无需额外调用功能块;- 故障锁定清除条件严格限定为
STOP下降沿:防止误按启动键导致故障自恢复,符合安全规范(IEC 62061 SIL1要求);RunTime_h计算使用TIME_TO_REAL():IEC 61131-3标准函数,将TIME类型(单位毫秒)转为REAL,再除以3600.0得小时,不可写作/3600(整数除法会截断);IsRunning定义为Q AND FBK:双重确认——既发出指令,又收到反馈,排除输出回路开路风险。
四、调用FB:如何在主程序中实例化?
在PROGRAM PLC_PRG中声明两个独立实例,分别控制主泵和备用泵:
PROGRAM PLC_PRG
VAR
Pump_Main : FB_MotorCtrl;
Pump_Standby: FB_MotorCtrl;
// 对应硬件IO映射(根据实际PLC地址填写)
DI_StartMain : BOOL; // %I0.0
DI_StopMain : BOOL; // %I0.1
DI_EMG_Main : BOOL; // %I0.2
DI_OL_Main : BOOL; // %I0.3
DI_FBK_Main : BOOL; // %I0.4
DO_Q_Main : BOOL; // %Q0.0
DI_StartSB : BOOL; // %I1.0
DI_StopSB : BOOL; // %I1.1
DI_EMG_SB : BOOL; // %I1.2
DI_OL_SB : BOOL; // %I1.3
DI_FBK_SB : BOOL; // %I1.4
DO_Q_SB : BOOL; // %Q1.0
END_VAR
// ===== 实例化调用 =====
Pump_Main(
START := DI_StartMain,
STOP := DI_StopMain,
EMG := DI_EMG_Main,
OL := DI_OL_Main,
FBK := DI_FBK_Main,
Enable:= TRUE,
Q => DO_Q_Main
);
Pump_Standby(
START := DI_StartSB,
STOP := DI_StopSB,
EMG := DI_EMG_SB,
OL := DI_OL_SB,
FBK := DI_FBK_SB,
Enable:= TRUE,
Q => DO_Q_SB
);
✅ 验证要点:
- 所有输入均来自物理IO变量(
DI_xxx),绝不直接写%I0.0字面量,保证可测试性;- 输出使用
=>箭头语法绑定到硬件输出点,符合ST规范;- 两个实例完全独立,
Pump_Main.RunTime_h与Pump_Standby.RunTime_h互不影响。
五、扩展性增强:添加可选功能(按需启用)
若项目需要更高级功能,可在FB中无损扩展(不破坏原有接口):
▶ 添加运行模式选择(手动/自动)
在VAR_INPUT区追加:
Mode_Auto : BOOL := TRUE; // TRUE=自动模式(响应START/STOP),FALSE=手动强制启停
Force_ON : BOOL := FALSE;
Force_OFF : BOOL := FALSE;
并在主逻辑中插入:
// 在STEP 4后插入
IF Mode_Auto THEN
// 原有Q逻辑保持不变
ELSE
IF Force_ON THEN
Q := TRUE;
ELSIF Force_OFF THEN
Q := FALSE;
END_IF;
END_IF;
▶ 添加运行次数统计
在VAR_OUTPUT区追加:
StartCount : UDINT := 0; // 启动总次数
并在xStartRising为TRUE且Q即将置位前加入:
IF xStartRising AND Enable AND NOT bFaultLocked THEN
StartCount := StartCount + 1;
END_IF;
▶ 支持Modbus RTU参数读写(对接HMI)
在VAR区声明:
modbusData : ARRAY[0..9] OF UINT; // 寄存器映射区(0=Enable,1=MaxRunTime_h,2=FaultCode...)
再在FB末尾添加同步逻辑(略),即可通过HMI修改运行阈值。
六、调试与验证清单(上线前必做)
| 检查项 | 操作方式 | 预期结果 |
|---|---|---|
| 启动响应 | 给START一个≥100ms的脉冲 |
Q变为TRUE,IsRunning仍为FALSE(因FBK未反馈),500ms后若FBK到位则IsRunning=TRUE |
| 急停测试 | 断开EMG(模拟急停按下) |
Q立即变FALSE,FaultCode=1,bFaultLocked=TRUE;复位急停后FaultCode仍为1,直到按STOP |
| 反馈丢失 | FBK持续FALSE达500ms以上 |
FaultCode跳变为3,Q保持FALSE |
| 运行计时 | 让电机连续运行10分钟 | RunTime_h显示0.167(10÷60=0.1666…,四舍五入到千分位) |
| 参数隔离 | 同时运行两个实例,只对Pump_Main触发过载 |
仅Pump_Main.FaultCode=2,Pump_Standby一切正常 |
七、标准化交付物建议(团队协作必备)
封装完成的FB应配套提供以下文件,存于版本库同一目录:
FB_MotorCtrl.st:源代码文件(含完整注释);FB_MotorCtrl_Interface.md:Markdown接口文档,含表格说明每个IO用途、数据类型、默认值;Test_Cases.xlsx:含上述6项验证用例的输入组合与期望输出;ChangeLog.md:记录每次升级原因(如“V1.2:增加Force_ON/OFF引脚”)。
其中接口文档表格如下(注意上下空行):
| 字段名 | 类型 | 方向 | 默认值 | 说明 |
|---|---|---|---|---|
START |
BOOL |
输入 | — | 上升沿触发启动(需硬件消抖) |
STOP |
BOOL |
输入 | — | 下降沿触发停止 |
EMG |
BOOL |
输入 | — | 急停常闭触点,FALSE = 急停动作 |
OL |
BOOL |
输入 | — | 热继电器常开触点,TRUE = 过载 |
FBK |
BOOL |
输入 | — | 接触器辅助触点,TRUE = 已吸合 |
Enable |
BOOL |
输入 | TRUE |
全局使能,设为FALSE可屏蔽本FB |
Q |
BOOL |
输出 | — | 主输出线圈控制信号 |
FaultCode |
UINT |
输出 | 0 |
故障代码(0=无,1=急停,2=过载,3=反馈丢失,4=输出冲突) |
RunTime_h |
REAL |
输出 | 0.0 |
累计运行小时(保留3位小数) |
IsRunning |
BOOL |
输出 | — | Q=TRUE且FBK=TRUE时为TRUE |
将电机控制逻辑封装为FB,本质是把经验固化为可移植的数字资产。每一次调用,都不是复制粘贴,而是调用经过百次验证的确定性行为。

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