3. IEC 61131-3编程模型:软件模型、程序组织单元(POU)、变量与数据类型、任务与调度

IEC 61131-3 是 IEC 61131 系列标准中最具革命性、也是工程应用价值最高的部分。它统一了可编程逻辑控制器(PLC)的编程语言和编程模型,使得不同厂商的 PLC 在程序结构、变量声明和任务调度上具备了逻辑上的一致性。理解这一章的核心,不在于死记硬背语法,而在于建立一套“软件架构思维”——即如何将一个复杂的工业控制需求,通过标准化的软件模型拆解为可管理、可复用、可调度的程序组织单元,并确保其在实时操作系统环境下稳定运行。本章将围绕软件模型、POU、变量与数据类型、任务与调度四大支柱展开深度解读。

IEC 61131-3 编程模型总览 配置 (Configuration) 资源1 (Resource) —— 资源2 (Resource) —— 资源n (Resource) 定义整个控制系统的硬件映射、全局变量、访问路径与任务调度策略 资源 (Resource) 任务 (Task) 1 → 程序 (Program) A 任务 (Task) 2 → 程序 (Program) B 资源相当于一个独立的CPU/处理单元 资源 (Resource) 任务 (Task) 3 → 程序 (Program) C 任务 (Task) 4 → 程序 (Program) D 支持多任务抢占式调度 程序 (Program) 由 FB + FB + 全局变量组成 主循环逻辑入口 功能块 (FB) 带静态变量/实例化 如:PID、定时器、计数器 函数 (Function) 无状态,输入→输出 如:ABS、SIN、LIMIT 变量与数据类型 BOOL, INT, REAL, STRING, TIME, ARRAY, STRUCT VAR / VAR_INPUT / VAR_OUTPUT / VAR_GLOBAL / VAR_TEMP 任务与调度 周期执行 / 事件触发 / 优先级抢占 看门狗、时间片、抖动控制

上图清晰地展示了 IEC 61131-3 软件模型的层级关系:最上层是配置,它定义了整个控制系统的硬件拓扑和全局资源;中间层是资源,对应具体的物理处理单元(如 CPU 核);资源内部承载着任务,任务负责调度程序的执行;而程序、功能块和函数共同构成了程序组织单元(POU)。最底层是贯穿所有层级的变量与数据类型体系。下面我们将逐一深入剖析。

3.1 软件模型:从配置到资源的层级解构

核心原理:分层抽象与硬件解耦

IEC 61131-3 的软件模型并非简单的代码堆放,而是一种严谨的工程分层架构。其核心思想是将“软件逻辑”与“物理硬件”进行解耦。一个配置(Configuration)代表一个完整的控制系统,它包含了所有硬件资源、全局变量和通信路径。在大型分布式控制系统中,一个配置可能对应一条生产线;在小型设备中,一个配置就是一个单机控制器。配置内部可以包含多个资源(Resource),每个资源通常映射到一个独立的处理单元(如一个 CPU 核心或一个协处理器)。这种设计使得程序可以在不同资源间移植,而无需修改逻辑代码。

判断方法:如何划分配置与资源

在实际工程中,划分配置与资源的依据是物理隔离性与故障域。如果两个控制任务共享同一个 CPU 和电源域,它们应属于同一配置下的不同任务;如果它们运行在物理隔离的控制器中(例如通过以太网通信的两个 PLC),则应划分为两个配置。资源划分则更关注实时性要求:高速运动控制应独占一个资源,而普通逻辑控制可共享另一个资源。

层级典型映射作用域示例
配置整条产线/单台设备全局变量、全局访问路径汽车焊装线控制系统
资源CPU 核/独立处理器任务调度、程序实例运动控制核、逻辑控制核
任务周期/事件调度器程序执行触发10ms 周期任务、中断任务
程序应用主逻辑POU 组合主控程序、安全程序
经验提醒:在 CODESYS 或 TwinCAT 中新建项目时,默认会生成一个配置和一个资源。初学者常忽略多资源设计,但在多核 PLC 或软 PLC(如 TwinCAT on Windows)中,合理分配资源可显著提升系统实时性。

案例拆解:双资源运动控制系统的软件模型设计

某包装机械需要同时控制 6 轴伺服运动(周期 1ms)和温度/压力模拟量采集(周期 50ms)。若将所有逻辑放在同一资源中,1ms 任务的高频中断会频繁打断模拟量处理,导致采样抖动。正确做法是:在同一个配置下创建两个资源——Resource_Motion 和 Resource_IO。前者运行 1ms 周期任务,后者运行 50ms 周期任务。两个资源通过配置级全局变量(如共享的 REAL 数组)交换数据。实施后,运动轴跟随误差从 ±0.05mm 降至 ±0.02mm,模拟量采样波动从 ±3% 降至 ±0.5%。代价是需要额外规划资源间通信延迟(约 0.1ms),但收益远大于成本。

风险提示:跨资源访问全局变量时,必须考虑数据一致性。建议使用“生产者-消费者”模式,并配合系统级同步机制(如 TwinCAT 的 Distributed Clock),避免读到半更新的数据。

3.2 程序组织单元(POU):程序、功能块与函数

核心原理:三种 POU 的本质区别

POU 是 IEC 61131-3 中最基本的代码封装单元,分为三类:函数(Function)、功能块(Function Block, FB)和程序(Program)。函数是无状态、无静态变量的纯逻辑单元,相同的输入必然产生相同的输出,例如 ABS()、LIMIT()。功能块则是有“记忆”的,它内部可以声明静态变量(VAR),每次调用后状态会保留,例如定时器 TON、计数器 CTU。程序是最高层级的 POU,它相当于一个“容器”,内部可以调用多个功能块和函数,并直接与任务绑定。程序之间不能互相调用,但可以通过全局变量或通信接口交互。

操作流程:POU 的声明与实例化

在结构化文本(ST)中,函数和功能块的声明方式不同。函数直接通过函数名调用,而功能块必须先声明实例(Instance),再通过实例调用。例如:

// 函数调用(无状态)
result := LIMIT(0, 100, input_value);

// 功能块实例化与调用(有状态)
VAR
    myTimer : TON;  // 声明 TON 功能块实例
END_VAR
myTimer(IN := start_signal, PT := T#5s);
IF myTimer.Q THEN
    // 定时时间到,执行动作
END_IF;

程序(Program)作为顶层 POU,通常在任务中直接关联。一个程序可以包含多个功能块实例,形成“程序-功能块-函数”的调用树。

案例拆解:功能块复用在多工位控制中的应用

某装配线有 8 个工位,每个工位都需要一个“夹紧-检测-松开”的时序控制。若每个工位单独编写逻辑,代码量巨大且难以维护。工程师设计了一个通用功能块 FB_ClampSequence,内部封装了状态机(IDLE → CLAMPING → CHECKING → RELEASING),输入为启动信号和检测结果,输出为夹紧/松开指令。在主程序中,声明 8 个实例:Station1 : FB_ClampSequence; 至 Station8 : FB_ClampSequence;。每个实例独立运行,互不干扰。最终代码量减少 60%,且新增工位时只需复制一行声明。复盘发现,功能块内部必须避免使用全局变量,否则多实例会互相干扰——这是初学者的常见误区。

总结复盘:POU 的设计原则是“高内聚、低耦合”。函数用于纯算法,功能块用于带状态的设备控制,程序用于组织业务逻辑。合理划分 POU 边界,是提升代码可维护性的关键。

3.3 变量与数据类型:强类型与存储限定

核心原理:强类型语言的安全保障

IEC 61131-3 是强类型语言,所有变量必须显式声明数据类型。标准定义了基础数据类型(BOOL、BYTE、INT、REAL、STRING、TIME 等)和衍生数据类型(ARRAY、STRUCT、枚举)。强类型机制在编译期即可发现类型不匹配错误,避免运行时数据损坏。此外,变量还分为不同的存储限定符:VAR(内部变量)、VAR_INPUT(输入参数)、VAR_OUTPUT(输出参数)、VAR_IN_OUT(输入输出参数)、VAR_GLOBAL(全局变量)、VAR_TEMP(临时变量)。其中 VAR_TEMP 在每次调用时重新分配内存,不保持状态,常用于中间计算。

判断方法:选择合适的数据类型

选择数据类型时需考虑数值范围、精度和内存占用。例如,计数器的值若可能超过 32767,应使用 DINT 而非 INT;模拟量转换结果应使用 REAL 以保留小数。对于时间相关变量,使用 TIME 类型(如 T#2s)比直接使用 INT 更直观且不易出错。下表列出了常用数据类型的典型应用场景:

数据类型位宽典型应用注意事项
BOOL1 bit开关量、状态标志不可用于算术运算
INT16 bit计数器、小范围整数注意溢出风险
REAL32 bit模拟量、PID 参数避免直接比较相等
STRING可变报警文本、配方名称长度需预先定义
ARRAY可变批量数据、配方表索引从 0 或 1 开始(可配置)
STRUCT可变组合数据(如电机参数)可嵌套定义

案例拆解:全局变量滥用导致的隐性故障

某项目使用全局变量 g_bEmergencyStop 作为急停标志,在多个 POU 中读写。某次调试中,维护人员在功能块 A 中误将其赋值为 FALSE,导致急停信号被覆盖,安全回路失效。排查过程耗时 3 小时。复盘结论:全局变量应仅用于跨程序通信,且必须遵循“单一写入者”原则。改进方案:将急停信号封装为功能块 FB_SafetyMonitor,通过输出引脚提供给其他 POU 只读访问。此案例警示我们,强类型语言虽能防止类型错误,但无法防止逻辑滥用,设计规范至关重要。

风险提示:在 ST 语言中,VAR_TEMP 变量在功能块中不保持状态,若误将其用于跨周期累加,会导致数据丢失。务必区分“需要保持的变量”和“临时计算变量”。

3.4 任务与调度:实时性的核心保障

核心原理:任务是程序执行的驱动引擎

在 IEC 61131-3 中,程序本身不会自动执行,必须由任务(Task)触发。任务定义了程序的执行条件:周期执行(如每 10ms 执行一次)、事件触发(如上升沿触发)或系统触发(如开机执行)。任务还拥有优先级属性,高优先级任务可以抢占低优先级任务。调度器根据优先级和触发条件决定哪个程序获得 CPU 时间。理解任务模型是掌握实时控制的关键——错误的优先级设置会导致控制周期抖动,甚至系统崩溃。

操作流程:任务配置与优先级分配

配置任务时,需遵循以下步骤:

  1. 识别实时性需求:运动控制通常需要 1-2ms 周期,温度控制 50-100ms,人机交互 100ms 以上。
  2. 分配优先级:周期越短、越重要的任务优先级越高。例如,1ms 运动任务优先级设为 0(最高),10ms 逻辑任务设为 1,100ms 通信任务设为 2。
  3. 设置看门狗:为每个任务设置最大执行时间,超时则触发报警或停机。
  4. 避免任务过载:确保任务的平均执行时间不超过其周期的 70%,留出余量应对突发负载。
// 任务配置示例(CODESYS 风格)
TASK MotionTask (INTERVAL := T#1ms, PRIORITY := 0, WATCHDOG := T#5ms);
TASK LogicTask (INTERVAL := T#10ms, PRIORITY := 1, WATCHDOG := T#20ms);
PROGRAM MotionProgram (MotionTask);
PROGRAM LogicProgram (LogicTask);

案例拆解:任务优先级反转导致的运动抖动

某设备在高速运行时,伺服电机出现周期性抖动(约每 100ms 一次)。通过示波器观察,发现位置偏差在抖动时刻明显增大。分析任务配置发现:运动任务(1ms)优先级为 0,但通信任务(50ms)优先级也为 0。当通信任务执行时(耗时约 8ms),运动任务被阻塞,导致控制周期拉长至 9ms,引发抖动。解决方案:将通信任务优先级降为 2,并增加通信任务的看门狗。修改后抖动消失,位置偏差恢复至 ±0.01mm。此案例说明,优先级分配必须基于任务的实时性敏感度,而非功能重要性。

总结复盘:任务调度是 IEC 61131-3 中最具“实时系统”特征的部分。建议在项目初期就绘制任务时序图,明确每个任务的周期、优先级、最大执行时间和资源占用。使用系统自带的实时监控工具(如 TwinCAT 的 Task Monitor)定期检查任务执行时间,是预防调度问题的有效手段。

综上所述,IEC 61131-3 的编程模型通过软件模型的分层解耦、POU 的模块化封装、强类型变量体系以及灵活的任务调度机制,为工业控制软件提供了严谨的工程化框架。掌握这四大支柱,不仅能够编写出结构清晰、易于维护的 PLC 程序,更能从系统层面保障控制系统的实时性与可靠性。在实际项目中,建议工程师在编码前先完成软件架构设计,明确配置/资源划分、POU 接口定义和任务优先级矩阵,这将极大减少后期调试成本。