掌握函数和功能块,从"脚本编写者"升级为"软件架构师"
在前三课中,我们学习了ST语言的基础语法和流程控制,已经能够用代码表达复杂的逻辑。但如果我们把所有代码都写在一个主程序里,很快就会陷入"面条式代码"的困境。今天,我们将学习ST编程的模块化艺术——程序组织单元,这是从小工到大师的关键一步。
一、为什么需要模块化?从"盖棚子"到"建大楼"的思维升级
想象一下这两个场景:
盖个棚子:把所有木材、钉子堆在一起,需要什么就拿什么,虽然混乱但也能完成。
建摩天大楼:必须预先设计梁、柱、板等标准构件,然后按图纸组装。
如果你只写几十行代码,放在一个程序里确实可以。但面对成百上千行的工业控制程序时,模块化不是"可选项",而是"必选项"。
模块化编程的三大核心价值:
可重用性:一次编写,多次使用(如通用的PID算法、报警处理)
可维护性:bug定位和修复更容易,修改影响范围可控
可读性:清晰的架构让后续维护者快速理解系统
二、IEC 61131-3的模块化基石:程序组织单元
IEC 61131-3标准定义了四种主要的程序组织单元,它们就像建筑中的标准预制件:
1. 函数 - 纯计算的黑盒子
函数是最简单的POUs,它接收输入参数,进行计算,然后返回结果。关键特性:无记忆性,每次调用都独立。
定义示例:计算圆的面积
FUNCTION 计算圆面积 : REAL
VAR_INPUT
半径 : REAL;
END_VAR
计算圆面积 := 3.14159 * 半径 * 半径;
END_FUNCTION
调用示例:
面积1 := 计算圆面积(半径 := 10.0}); // 结果是314.159
面积2 := 计算圆面积(5.0); // 结果是78.53975
优势对比:
LD中:需要多个MUL指令框和中间变量
ST中:一行清晰的函数调用,意图明确
2. 功能块 - 有记忆的智能单元
功能块是工业控制的核心!它不仅包含逻辑,还保持内部状态,非常适合表示具有记忆功能的控制单元。
定义示例:电机控制功能块
FUNCTION_BLOCK 电机控制
VAR_INPUT
启动按钮 : BOOL;
停止按钮 : BOOL;
故障信号 : BOOL;
END_VAR
VAR_OUTPUT
运行状态 : BOOL;
就绪信号 : BOOL;
END_VAR
VAR
// 内部状态变量
启动延时 : TON;
启动次数 : INT;
END_VAR
// 启保停逻辑,带故障保护
运行状态 := (启动按钮 OR 运行状态) AND NOT(停止按钮) AND NOT(故障信号);
// 启动延时逻辑
启动延时(IN := 运行状态, PT := T#2S);
就绪信号 := 启动延时.Q;
// 运行次数统计
IF 启动按钮 AND NOT(运行状态) THEN
启动次数 := 启动次数 + 1;
END_IF
END_FUNCTION_BLOCK
使用示例(实例化):
// 声明功能块实例
VAR
主电机 : 电机控制;
辅电机 : 电机控制;
END_VAR
// 调用功能块
主电机(启动按钮 := 启动信号1,
停止按钮 := 停止信号1,
故障信号 := 故障信号1);
辅电机(启动按钮 := 启动信号2,
停止按钮 := 停止信号2,
故障信号 := 故障信号2);
// 使用输出
运行指示灯 := 主电机.运行状态;
就绪指示灯 := 主电机.就绪信号;
这才是真正的思维升级! 你现在不是在控制单个线圈,而是在管理一个"智能电机对象"。
三、从LD到ST:功能块思维的完美对应
如果你熟悉LD中的功能块用法,ST中的功能块概念其实一脉相承:
| LD中的概念 | ST中的对应 | 说明 |
|---|
| 功能块实例 | 功能块变量声明 | 都是先创建实例再使用 |
| 输入引脚 | VAR_INPUT变量 | 调用时传入参数 |
| 输出引脚 | VAR_OUTPUT变量 | 通过实例名.输出名访问 |
| 内部状态 | VAR变量 | 保持上次调用的状态 |
关键优势:在ST中编写功能块的逻辑比在LD中更简洁、表达能力更强!
四、实战案例:构建一个完整的设备控制系统
让我们用一个真实的案例来展示模块化的威力:一个简单的传送带系统,包含电机、传感器和报警管理。
1. 首先定义基础功能块
电机控制功能块(复用上面的例子)
报警管理功能块:
FUNCTION_BLOCK 报警管理
VAR_INPUT
故障信号 : BOOL;
确认按钮 : BOOL;
END_VAR
VAR_OUTPUT
报警状态 : BOOL;
报警未确认 : BOOL;
END_VAR
VAR
报警记忆 : BOOL;
END_VAR
// 报警触发和记忆
IF 故障信号 THEN
报警记忆 := TRUE;
END_IF
// 报警确认
IF 确认按钮 THEN
报警记忆 := FALSE;
END_IF
报警状态 := 报警记忆;
报警未确认 := 报警记忆 AND NOT(确认按钮);
END_FUNCTION_BLOCK
2. 在主程序中组装这些"智能积木"
PROGRAM 主传送带控制
VAR
// 声明功能块实例
传送带电机 : 电机控制;
电机报警 : 报警管理;
光电传感器1 AT %I0.0 : BOOL; // 直接映射到实际输入
光电传感器2 AT %I0.1 : BOOL;
// 其他变量
运行模式 : INT;
产品计数 : INT;
END_VAR
// 电机控制逻辑
传送带电机(启动按钮 := (运行模式 = 1) AND 光电传感器1,
停止按钮 := 急停信号,
故障信号 := 电机报警.报警状态);
// 报警管理逻辑
电机报警(故障信号 := (传送带电机.运行状态 AND NOT(光电传感器2) AND 运行模式 = 1),
确认按钮 := 报警确认按钮);
// 产品计数逻辑
IF 光电传感器1 AND NOT(光电传感器2) THEN
产品计数 := 产品计数 + 1;
END_IF
END_PROGRAM
五、模块化设计的最佳实践
单一职责原则:每个功能块只做好一件事
高内聚低耦合:功能块内部紧密相关,与外部交互尽量简单
合理的抽象层次:
底层:电机控制、阀门控制等设备级功能块
中层:传送带控制、温度控制等单元级功能块
高层:生产线控制、工艺过程控制等系统级功能块
完善的文档:为每个功能块编写使用说明,特别是输入输出参数的含义
六、从模块化到面向对象
IEC 61131-3第三版后还支持更高级的面向对象特性:
继承:可以基于现有功能块创建特化版本
接口:定义标准化的交互契约
多态:同一接口的不同实现
这些高级特性让模块化设计更加强大,我们会在后续高级课程中详细介绍。
总结与预告
通过本课的学习,你已经掌握了ST语言模块化编程的核心:
✅ 理解了模块化的必要性:从"写脚本"到"建系统"
✅ 掌握了函数的用法:无状态的纯计算单元
✅ 精通了功能块的应用:有记忆的智能控制单元
✅ 学会了系统组装方法:用功能块构建复杂系统
现在,你不再只是ST语法的使用者,而是工业软件的设计师!
课后实践:
回顾你当前的项目,找出重复出现的控制逻辑(如多个相同的电机、阀门),尝试将其封装成功能块,体验"一次编写,多处使用"的高效。
在下一课中,我们将探讨ST与LD的混合编程技巧,教你如何根据场景选择最佳工具,发挥协同作战的最大威力!
本文中的功能块设计模式基于IEC 61131-3标准,在实际项目中请根据具体设备特性和安全要求进行调整优化。