1. PLC编程中的"套路"哲学
刚入行那会儿,我也曾沉迷于钻研各种高级PLC指令和复杂算法,直到有次在产线调试时,被一位老师傅的程序震撼到了——他用最基础的逻辑指令搭建的流水线控制程序,运行效率比我用高级功能块写的版本快30%,维护起来更是像看说明书一样简单。那次经历让我深刻理解了在工业自动化领域,好的编程套路比炫技重要得多。
所谓"套路",本质上是经过大量现场验证的标准化编程范式。就像老木匠不用看图纸也能打出严丝合缝的榫卯,成熟的PLC工程师都有一套自己的"编程拳法"。在西门子博途(TIA Portal)平台下,这种套路主要体现在三个方面:程序结构设计模式、功能块封装规范和异常处理机制。
重要提示:好的编程套路必须具备三个特征——可维护性(三个月后自己还能看懂)、可扩展性(产线改造时不用推倒重来)、容错性(不会因为一个传感器故障导致全线停机)。
2. 博途平台下的三种黄金写法
2.1 模块化流水线控制法
这是我最推荐新手掌握的"万金油"写法,特别适合装配线、包装机这类有明确工艺顺序的设备。核心思想是把整个流程拆分为若干个状态(Step),通过状态跳转条件实现控制。
以典型的瓶装线为例,我们通常会定义这些状态机变量:
ST复制TYPE E_ConveyorState :
(
STOPPED := 0,
LOADING := 1,
FILLING := 2,
CAPPING := 3,
LABELING := 4,
UNLOADING := 5,
FAULT := 16#FF
);
END_TYPE
具体实现时,推荐使用SCL语言编写状态机:
SCL复制CASE #iState OF
E_ConveyorState.STOPPED:
IF #bStart THEN
#iState := E_ConveyorState.LOADING;
END_IF;
E_ConveyorState.LOADING:
#ActivateLoader(TRUE);
IF #bBottleInPosition THEN
#iState := E_ConveyorState.FILLING;
END_IF;
// 其他状态处理...
E_ConveyorState.FAULT:
#EmergencyStop(TRUE);
IF #bReset THEN
#iState := E_ConveyorState.STOPPED;
END_IF;
END_CASE;
实操技巧:
- 每个状态必须设置超时监控,防止卡死
- 状态跳转条件建议用上升沿触发,避免重复执行
- 故障状态要保留现场数据,方便后期诊断
2.2 功能块模板化封装
博途的FB功能块就像乐高积木,封装得好可以大幅提升代码复用率。我总结了一套"5+3"封装原则:
5个必选参数:
ST复制FUNCTION_BLOCK FB_ConveyorControl
VAR_INPUT
// 控制信号
bEnable : BOOL; // 使能信号
bStart : BOOL; // 启动触发
bStop : BOOL; // 停止触发
bReset : BOOL; // 复位触发
iManualSpeed : INT; // 手动模式速度
END_VAR
3个标准输出:
ST复制VAR_OUTPUT
bRunning : BOOL; // 运行状态
bFault : BOOL; // 故障状态
iErrorCode : WORD; // 错误代码
END_VAR
内部实现时,建议采用这种结构:
- 输入信号有效性检查
- 运行状态机(参考2.1节)
- 安全互锁逻辑
- 故障诊断处理
避坑指南:功能块内部绝对不要直接使用全局变量,所有依赖的外部信号都应通过接口传入。我曾经接手过一个项目,因为前工程师在FB里直接读取了DB块数据,导致程序移植时排查了整整两天故障。
2.3 标准化异常处理框架
工业现场最怕的就是故障时"一瘫全瘫"。好的异常处理应该像洋葱一样分层:
第一层:设备级保护
LAD复制// 电机过流保护示例
[电机过流信号]----[延时200ms]----(切断接触器)
|
+----[记录故障代码]
第二层:工艺级互锁
SCL复制// 灌装阀与传送带互锁
IF NOT #bConveyorRunning THEN
#FillValve := FALSE;
#iErrorCode := 16#1001;
END_IF;
第三层:系统级恢复
建议创建一个专用的故障恢复FB,包含:
- 故障分类(可恢复/不可恢复)
- 自动恢复策略(最大尝试次数3次)
- 恢复后状态初始化
3. 让程序既有面子又有里子的技巧
3.1 注释的艺术
好注释不是解释"what",而是说明"why":
SCL复制// 错误示例:打开阀门
#FillValve := TRUE;
// 正确示例:当液位低于设定值且无故障时启动灌装
IF (#rLevel < #rSetpoint) AND NOT #bFault THEN
#FillValve := TRUE; // 使用气动阀需保持最小500ms脉冲
END_IF;
3.2 变量命名规范
推荐使用匈牙利命名法的变体:
- b开头:BOOL型(bRunning)
- i开头:INT型(iErrorCode)
- r开头:REAL型(rSetpoint)
- t开头:TIME型(tDelay)
- s开头:STRING型(sRecipeName)
对于全局变量,建议加前缀:
- g_:全局变量(g_bSystemReady)
- m_:模块级变量(m_bConveyorEnabled)
3.3 仿真调试技巧
博途的PLCSIM Advanced有个隐藏功能——在仿真时可以修改变量强制状态。我常用的调试组合拳:
- 在OB35中插入调试代码
- 使用Watch Table批量监控关键变量
- 利用Trace功能捕捉时序问题
ST复制// 调试代码示例
IF #bDebugMode THEN
#rTestValue := SIN(#tRunTime / 1000.0);
END_IF;
4. 常见问题排坑实录
4.1 扫描周期导致的幽灵故障
现象:偶尔出现莫名其妙的互锁触发
排查:在OB1开始处添加:
SCL复制#tCycleStart := T#0MS;
#tCycleStart := TIME();
在结束处计算周期时间,如果超过20ms就要优化逻辑
4.2 功能块实例数据丢失
现象:设备重启后FB内部状态异常
解决:对于需要保持的数据,必须显式声明为Retain:
ST复制VAR RETAIN
iProductionCount : INT;
END_VAR
4.3 网络通信阻塞
典型表现:HMI响应延迟
优化方案:
- 将周期通信改为事件触发
- 使用优化的DB块访问方式
SCL复制// 避免这样写
#hmiTag := "DB1".StaticVar;
// 应该这样
"DB1".Optimized_Access := TRUE;
#hmiTag := "DB1".OptimizedVar;
5. 程序架构设计进阶
5.1 面向对象思维在PLC中的应用
虽然博途不支持完整的OOP,但我们可以模拟类继承:
- 基础功能块(如FB_BaseMotor)
- 派生功能块(如FB_Pump继承FB_BaseMotor)
- 通过"接口"参数实现多态
ST复制// 在基础FB中定义虚方法
METHOD VirtualMethod : BOOL
VAR_INPUT
iParam : INT;
END_VAR
// 在派生FB中重写
METHOD VirtualMethod : BOOL
VAR_INPUT
iParam : INT;
END_VAR
VirtualMethod := iParam > 0;
5.2 配方管理系统设计
推荐采用这种结构:
code复制DB_Recipe
├── Header (包含配方版本、校验和等)
├── ParameterSet[1..10] (参数数组)
└── AuditTrail (修改记录)
关键技巧:
- 使用UDT统一数据类型
- 添加CRC校验防止数据损坏
- 变更时自动备份旧版本
5.3 安全编程规范
必须遵守的黄金法则:
- 急停必须硬线连接,不可仅靠PLC程序
- 安全相关变量命名加前缀"S_"
- 安全逻辑必须独立于普通控制逻辑
ST复制// 安全速度监控示例
IF #S_bEnable THEN
#S_rActualSpeed := #rEncoderSpeed;
IF #S_rActualSpeed > #S_rMaxSpeed THEN
#S_bSafeTorqueOff := TRUE;
END_IF;
END_IF;
6. 性能优化实战技巧
6.1 扫描周期压缩大法
我优化过的一个典型案例:将3.5ms的扫描周期压缩到1.2ms
优化前:
ST复制// OB1中顺序执行
Process_A();
Process_B();
Process_C();
优化后:
ST复制// 分时处理
CASE #iTimeSlot OF
0: Process_A_Part1();
1: Process_B_Part1();
2: Process_C();
3: Process_A_Part2();
//...
END_CASE;
#iTimeSlot := (#iTimeSlot + 1) MOD 10;
6.2 数据块访问优化
实测对比:
| 访问方式 | 执行时间 |
|---|---|
| 标准DB访问 | 12μs |
| 优化DB访问 | 3μs |
| 绝对地址访问 | 1μs |
推荐做法:
- 频繁访问的数据放在优化DB中
- 使用"AT"关键字创建变量别名
ST复制VAR
rValue : REAL;
iRawValue AT rValue : DWORD;
END_VAR
6.3 中断服务程序使用准则
OB35等周期中断的注意事项:
- 执行时间必须远小于中断周期
- 避免在中断中调用其他FB
- 关键变量使用"VOLATILE"声明
ST复制VAR VOLATILE
bEmergencyStop : BOOL;
END_VAR
7. 版本控制与团队协作
7.1 博途项目Git管理方案
虽然TIA Portal原生不支持Git,但可以这样操作:
- 项目保存为"另存为类型化项目"
- 忽略这些文件:
- *.ap15
- *.awl
- *.hmi
- 关键数据定期导出为XML:
Powershell复制TIAExport.exe -f "Project.ap15" -t "C:\Export"
7.2 差异比较技巧
使用TIA Portal自带的比较工具时:
- 先比较硬件配置
- 再比较程序块
- 最后比较PLC变量表
专业技巧:对于大型项目,可以按功能区域分块比较,比如先比较"灌装区",再比较"包装区"。
7.3 代码审查清单
我们团队使用的Checklist:
- [ ] 所有FB都有错误处理接口
- [ ] 关键操作都有操作确认
- [ ] 数值输入有范围限制
- [ ] 定时器都有预设值检查
- [ ] 数组访问都有边界保护
8. 从老司机那里偷师的20个技巧
- 在DB块中预留10%的备用变量
- 使用"注释行"功能创建程序流程图
- 重要参数设置写保护密码
- 定期执行"项目清理"功能
- 在线修改前先创建还原点
- HMI按钮添加操作延时防止误触
- 使用WString存储UTF-8文本
- 关键设备添加"运行小时"统计
- 模拟量输入添加软件滤波器
- 使用ProDiag功能实现预测性维护
- 创建测试用例DB用于仿真
- 使用SCL的"EXIT"语句优化程序流
- 在OB82中处理模块诊断事件
- 使用TRACE功能捕捉偶发故障
- 重要功能添加"维护模式"开关
- 使用"REFERENCE"优化大数组传递
- 定时器编号按功能区域划分
- 创建标准报警文本模板
- 使用"VARIANT"实现通用功能块
- 在OB1结尾添加看门狗复位
这些年在产线调试中最大的体会是:好的PLC程序就像优秀的交响乐,既要每个乐器(功能块)各司其职,又要指挥(主程序)统筹得当。最近在做一个饮料灌装项目时,我把状态机模式和模块化设计结合起来,不仅提前两周完成调试,还收到了客户"这是见过最易维护的程序"的评价。
