4. 产线控制程序结构设计
PLCnext Control 的程序结构设计,核心不是“把梯形图写出来”,而是先决定用哪些语言、把代码切成哪些程序组织单元(POU)、这些 POU 由哪些任务在什么周期下调用、变量放在什么作用域里。这四件事一旦定错,后期改一条产线节拍往往要动半个项目。本节以一条典型包装产线为例,说明如何把结构设计落到可执行的工程文件上。
一、语言选择:按“控制对象”分工,而不是按个人习惯
PLCnext Engineer 遵循 IEC 61131-3,支持 LD(梯形图)、FBD(功能块图)、ST(结构化文本)、SFC(顺序功能图)、IL(指令表)以及 C++/C# 等高级语言。实际项目中推荐按对象性质分工:
- LD/FBD:用于安全联锁、急停链、气缸到位、输送线启停等布尔逻辑密集、需要电气人员现场读图的回路。
- ST:用于节拍计算、配方换算、报警优先级排序、通信数据打包等含算术、循环、字符串处理的逻辑。
- SFC:用于“上料—定位—压装—检测—下料”这类有明确步序和转移条件的工位流程。
- C++/C#:用于视觉结果解析、复杂算法、与 MES 的 JSON 交互等标准 IEC 语言不擅长的部分,通过 PLCnext 的 SDK 编译为功能块供 IEC 程序调用。
示例:某压装工位用 SFC 描述 6 个步序,每步的转移条件用 ST 写成布尔表达式,而急停与光栅信号仍用 LD 单独成段,便于安全回路审查。这种“混合语言”是 PLCnext 的常态,不必强求全项目统一。
二、程序组织单元:把“设备”切成可复用的 POU
POU 分为功能(FUN)、功能块(FB)和程序(PRG)。结构设计的关键是:FB 对应可复用设备,PRG 对应产线层级。以包装线为例:
- FB_Cylinder:封装单作用/双作用气缸的伸出、缩回、到位检测、超时报警,输入为
Cmd_Extend、Sensor_Ext、Sensor_Ret、Timeout,输出为Done、Fault。 - FB_Axis:封装伺服轴的使能、回零、绝对定位、状态字解析。
- FB_Station:调用若干 FB_Cylinder 与 FB_Axis,组成一个工位。
- PRG_Main:按工位顺序调用 FB_Station 实例,并处理线体级联锁。
每个 FB 实例必须显式命名,例如 fbCyl1_Clamp、fbCyl2_Eject,禁止使用匿名实例,否则在线监控时无法定位到具体气缸。FB 内部只使用 VAR_INPUT、VAR_OUTPUT、VAR_IN_OUT 和 VAR,不直接访问全局变量,保证可移植。
三、任务调度:周期任务与事件任务分开
PLCnext 的任务由资源(Resource)管理,常见配置如下:
| 任务 | 周期/触发 | 绑定 POU | 用途 |
|---|---|---|---|
| Task_Fast | 2 ms | PRG_Safety | 急停、光栅、安全门 |
| Task_Main | 10 ms | PRG_Main | 工位逻辑、气缸、轴控 |
| Task_Slow | 100 ms | PRG_HMI | HMI 数据刷新、报警汇总 |
| Task_Event | 事件触发 | PRG_Trace | 故障录波、MES 上传 |
调度设计要点:快任务只放安全与高速联锁,不放通信和字符串处理;慢任务与快任务之间通过全局变量或 VAR_GLOBAL 交换数据时,必须注意数据一致性——例如 100 ms 任务读取 10 ms 任务写的结构体,应使用双缓冲或标志位,避免读到半更新数据。任务优先级由系统按周期自动排序,周期越短优先级越高,因此不要在 2 ms 任务里调用耗时 FB。
四、变量作用域:局部优先,全局只做“接口”
PLCnext 的变量作用域分为:POU 局部变量、程序局部变量、全局变量(VAR_GLOBAL)、以及带 RETAIN/PERSISTENT 的保持变量。推荐原则:
- 设备内部状态一律用 FB 局部变量,外部只能通过输入输出引脚访问。
- 跨工位共享的“线体状态”“当前配方号”“报警字”放在全局变量区,并集中在一个
GVL_Line中声明,禁止散落。 - 需要断电保持的计数器、配方索引使用
VAR_GLOBAL RETAIN;需要跨固件升级保留的用PERSISTENT。 - HMI 与 MES 只读写全局变量或专用接口 FB,不直接访问 FB 内部。
五、模块化编程规范:命名、注释与版本
规范不是形式,而是让三个月后的自己能看懂。建议约定:
- FB 命名:
FB_设备类型,如FB_Cylinder;实例命名:fbCyl_工位_功能,如fbCyl_St2_Clamp。 - 变量命名:输入
i_、输出o_、局部l_、全局g_、保持r_。 - 每个 FB 头部写清功能、作者、版本、修改日期和调用示例。
- 版本用
V1.0形式,接口变更必须升版本并记录在 FB 注释中。
下面给出一个可直接参考的 FB 接口示例(ST 语言):
FUNCTION_BLOCK FB_Cylinder
VAR_INPUT
i_Cmd_Extend : BOOL; // 伸出命令
i_Cmd_Retract: BOOL; // 缩回命令
i_Sensor_Ext : BOOL; // 伸出到位
i_Sensor_Ret : BOOL; // 缩回到位
i_Timeout : TIME := T#3S;
END_VAR
VAR_OUTPUT
o_Done : BOOL;
o_Fault : BOOL;
END_VAR
VAR
l_Timer : TON;
l_State : INT;
END_VAR
// 状态机:0=Idle,1=Extending,2=Extended,3=Retracting,4=Fault
该 FB 被 FB_Station 调用时,只需连接输入输出,不关心内部状态机。若某气缸需要 5 s 超时,只改实例参数 i_Timeout := T#5S,不改 FB 本体。
六、结构设计检查闭环
完成上述设计后,用三个问题自检:第一,每个 FB 是否只依赖自己的输入输出,能否直接复制到另一个项目?第二,快任务里是否出现了通信、字符串或大循环?第三,全局变量表是否只保留了真正跨模块的接口?若答案是否定的,先调整结构再写逻辑。结构对了,后续加一个工位只是新增一个 FB 实例和一段调用,而不是重写整条产线。
把这张关系图落到工程里,就是:任务决定“什么时候跑”,POU 决定“跑什么”,变量作用域决定“数据给谁看”。三者对齐后,产线程序才具备可复用、可调试、可扩展的结构基础。