3、IEC 61131-3标准基础:标准中的程序组织单元(POU)、FB与FC的区别、变量类型与作用域

IEC 61131-3是PLC编程领域的国际通用标准,它定义了五种编程语言(LD、FBD、ST、IL、SFC)以及一套统一的软件模型。对于从事结构化编程的工程师而言,该标准中最重要的概念莫过于程序组织单元(POU)、功能块(FB)与功能(FC)的区分,以及变量类型与作用域的规则。这三者构成了模块化封装与复用的语法与逻辑基础。如果无法透彻理解POU的边界、FB的实例化特性以及变量的可见范围,后续的封装设计将不可避免地出现数据耦合混乱、资源浪费或调试困难等问题。本章将围绕这三个核心主题展开,并结合工程案例说明如何在实际项目中应用这些规则。

IEC 61131-3 软件模型与POU关系图 程序组织单元 POU PROGRAM(程序) 主循环调度入口 可调用FB/FC 全局变量访问 示例: MAIN, Task_1 FB(功能块) 带静态记忆 需要实例化 输出依赖内部状态 示例: FB_MotorCtrl FC(功能) 无静态记忆 无需实例化 输出仅由输入决定 示例: FC_Scale, FC_Avg 变量类型与作用域 全局变量(VAR_GLOBAL) • 所有POU可见 • 跨任务共享数据 • 需谨慎使用 • 生命周期: 全程 示例: g_bEmergencyStop 局部变量(VAR) • 仅当前POU可见 • 每次调用重新初始化 • 用于临时计算 • 生命周期: 单次扫描 示例: nTemp, fResult 静态变量(VAR RETAIN/STAT) • 保持值跨扫描周期 • RETAIN: 断电保持 • 仅限FB内部使用 • 生命周期: 运行期 示例: bState, nCount FB与FC是POU的子类型,PROGRAM是顶层调度单元;变量作用域决定数据可见性与生命周期

上图展示了IEC 61131-3软件模型的核心关系。在标准中,POU是用户程序的最小独立编译单元,它包含三种类型:程序(PROGRAM)、功能块(FB)和功能(FC)。程序是任务调度的顶层入口,功能块和功能则是可复用的逻辑模块。理解这三者之间的调用关系以及变量作用域的边界,是进行结构化封装的前提。下面我们将逐一深入剖析。

3.1 程序组织单元(POU)的体系结构与职责划分

核心原理:POU作为软件封装的基本单位

在IEC 61131-3标准中,POU被定义为“用户程序中的最小独立软件单元”。每个POU都有明确的接口定义(输入、输出参数)和内部实现。从工程角度看,POU类似于面向对象编程中的“类”或“函数”,但其执行模型与PLC的循环扫描机制紧密绑定。一个完整的PLC应用程序通常由一个或多个PROGRAM组成,每个PROGRAM内部可以调用多个FB和FC实例。POU的划分直接决定了代码的可维护性、可测试性和复用性。

标准规定,POU必须具有以下特征:唯一的名称、明确的接口(变量声明区)、可执行的代码体(语句或图形元素)。这种强制性的结构划分,使得不同工程师编写的代码能够遵循统一的规范,从而降低集成时的冲突风险。

判断方法:如何合理划分POU边界

在实际项目中,划分POU的边界通常遵循以下原则:

  • 单一职责原则:一个POU只完成一个特定的功能,例如“电机速度控制”或“温度PID调节”。避免将不相关的逻辑强行塞入同一个POU。
  • 复用潜力评估:如果一段逻辑需要在多个设备或工艺段中重复使用,应将其封装为FB或FC,而不是复制粘贴代码。
  • 任务周期匹配:不同执行周期(如10ms、100ms)的逻辑应放在不同的PROGRAM中,由不同的任务调用,避免相互阻塞。
  • 数据耦合度控制:POU之间的数据交互应尽量通过显式参数传递,减少对全局变量的依赖。

下表总结了三种POU的典型应用场景与选择依据:

POU类型 是否需要实例化 内部状态保持 典型应用场景 示例
PROGRAM 否(由任务直接调用) 是(全局生命周期) 主循环、任务调度、设备组逻辑 MainProgram, ConveyorTask
FB 是(必须创建实例) 是(静态变量保持) 需要记忆状态的复杂控制,如电机启停、阀门控制 FB_Motor, FB_Valve
FC 否(直接调用) 否(无记忆) 纯计算、转换、比较逻辑 FC_Scale, FC_CheckLimit

案例拆解:从设备控制到POU划分

假设我们需要为一台包装机编写控制程序,该设备包含三个伺服轴、两个气缸和一个温度传感器。如果将所有逻辑写在一个PROGRAM中,代码量将超过2000行,且难以调试。合理的划分方案是:

  1. 创建三个FB实例:FB_ServoAxis(用于三个轴,实例化为Axis1、Axis2、Axis3)、FB_Cylinder(用于两个气缸,实例化为CylA、CylB)、FB_TempControl(用于温度控制)。
  2. 创建一个PROGRAM(PackagingTask),负责在10ms周期内调用上述FB实例,并处理轴与气缸的联锁逻辑。
  3. 创建一个FC(FC_CalculateSpeed),用于根据产品规格计算目标速度,该FC无内部状态,仅做数学运算。

这种划分使得每个FB的代码量控制在200行以内,且可以独立测试。例如,单独给FB_ServoAxis输入模拟位置反馈,验证其状态机转换是否正确。

经验提醒:在划分POU时,不要过度设计。如果一个FB内部逻辑过于复杂(超过500行),应考虑拆分为多个子FB。反之,如果FC被频繁调用且需要记忆上次结果,则说明应该将其改为FB。

3.2 FB与FC的本质区别:状态记忆与实例化

核心原理:FB的“记忆”与FC的“无状态”

FB与FC最根本的区别在于是否拥有静态内存。FB在每次调用结束后,其内部静态变量(VAR)的值会被保留,直到下一次调用。这使得FB能够实现状态机、累计计数、延时等需要“记住过去”的功能。而FC在调用结束后,其所有局部变量(VAR)都会被释放,下一次调用时重新初始化,因此FC的输出完全由当前输入决定,不存在历史依赖。

这一区别直接影响了实例化要求:FB必须被实例化后才能使用,每个实例拥有独立的静态变量副本。例如,定义了一个FB_Motor,可以创建MotorA和MotorB两个实例,它们各自维护自己的运行状态和计时器,互不干扰。而FC没有实例概念,直接通过名称调用,所有调用者共享同一段代码,但数据不共享。

判断方法:何时选择FB,何时选择FC

工程师在选择时,可以依据以下判断路径:

  • 是否存在跨扫描周期的状态保持需求? 如果答案是“是”,则必须使用FB。例如,电机启动/停止的互锁逻辑、报警延时确认。
  • 是否需要多个独立副本? 如果同一逻辑需要应用于多个对象(如多台电机),且每个对象的状态需要独立跟踪,则使用FB并创建多个实例。
  • 是否仅做纯函数式转换? 如果逻辑是“输入A + 输入B = 输出C”,且不依赖任何历史值,则使用FC。例如,工程量转换、平均值计算、限幅处理。
  • 是否需要在FC中调用FB? 标准允许FC内部调用FB,但此时FB的实例必须作为FC的静态变量或全局变量声明。这种用法较少,通常不推荐,因为它破坏了FC的无状态特性。

案例拆解:FB与FC混合使用的工程实例

以一条灌装生产线的“灌装量控制”为例。我们需要根据灌装机的实时流量信号,计算累计灌装量,并在达到目标值时停止灌装。该功能需要记忆累计值,因此必须使用FB。我们定义FB_FillingControl,其内部包含静态变量rAccumulatedVolume(累计体积)和bFillingDone(完成标志)。每次扫描周期,FB读取瞬时流量rFlowRate,乘以扫描周期tCycleTime,累加到累计值中,并与目标值比较。

同时,我们需要一个FC来计算流量传感器的线性化输出。传感器输出4-20mA信号,对应0-100 L/min。我们定义FC_Linearize,输入为原始电流值rCurrent,输出为实际流量rFlow。该FC内部仅做一次线性映射计算,无状态保持。

在PROGRAM中,我们调用FC_Linearize将电流转换为流量,然后将流量值传递给FB_FillingControl的实例Filling1。这样,FC负责“计算”,FB负责“控制与记忆”,职责清晰。

风险提示:初学者常犯的错误是将FB当作FC使用,即不创建实例直接调用FB。这在大多数PLC编程环境中会报错或导致数据混乱。另外,在FC中试图使用静态变量保持数据也是非标准的,应改用FB。

3.3 变量类型与作用域:数据可见性与生命周期管理

核心原理:变量的“可见范围”与“生存周期”

IEC 61131-3标准定义了多种变量类型,其核心维度有两个:作用域(Scope)和生命周期(Lifetime)。作用域决定了变量在哪些POU中可以被访问,生命周期决定了变量从创建到销毁的时间跨度。正确管理变量作用域是避免数据竞争、意外修改和内存泄漏的关键。

标准将变量分为以下几类:

  • 全局变量(VAR_GLOBAL):在全局变量表中声明,所有POU均可访问。生命周期贯穿整个程序运行期。常用于设备级共享数据,如急停状态、生产模式。
  • 局部变量(VAR):在POU内部声明,仅该POU可见。对于FC,局部变量在每次调用时初始化;对于FB,局部变量在实例创建时初始化,但每次调用后不保留(除非声明为VAR)。
  • 静态变量(VAR STAT):仅能在FB内部声明。它在FB实例的整个生命周期内保持值不变,即使FB调用结束也不丢失。这是FB实现状态记忆的基础。
  • 保持变量(VAR RETAIN):可以是全局或局部,其值在PLC断电后通过非易失性存储器保留。常用于保存工艺参数、累计产量等关键数据。
  • 常量(VAR CONSTANT):声明后不可修改,用于定义固定参数,如PI值、设备编号。

操作流程:变量声明的规范步骤

在实际编程中,建议按照以下流程进行变量规划:

  1. 识别数据归属:首先判断该数据是设备级共享(如急停信号)还是模块内部状态(如电机当前转速)。设备级共享数据放入全局变量表,模块内部数据放入FB或FC的局部声明区。
  2. 确定生命周期需求:如果数据需要在断电后保持(如配方号),则声明为RETAIN;如果仅在单次扫描内有效(如临时计算结果),则使用普通VAR。
  3. 命名规范:建议采用匈牙利命名法或前缀法。例如,全局变量以g_开头(g_bEmergencyStop),FB内部静态变量以st_开头(st_rAccumulated),常量以C_开头(C_MAX_SPEED)。
  4. 限制全局变量数量:全局变量越多,POU之间的耦合度越高,调试难度越大。理想情况下,全局变量应控制在10个以内,且仅用于真正的设备级状态。

案例拆解:变量作用域导致的典型问题与修复

某工程师在编写一个多工位转盘控制程序时,将工位位置计数器nPosition声明为全局变量。结果发现,当两个工位同时运行时,计数器被相互覆盖,导致定位错误。分析原因:nPosition本应是每个工位FB的独立状态,却因全局声明而共享了同一块内存。

修复方案:将nPosition移入FB_Station内部,声明为VAR(静态变量)。这样,每个工位实例(Station1、Station2)拥有独立的计数器。修改后,程序运行正常。这个案例说明,变量作用域的错误选择会直接导致逻辑混乱。

另一个常见问题是RETAIN变量的滥用。某设备使用VAR RETAIN保存了所有FB的内部状态,导致PLC每次上电后,设备从上次停止的位置继续运行,而操作员期望从初始状态开始。解决方法是仅将需要断电保持的工艺参数(如目标温度)设为RETAIN,而将运行状态(如当前步骤)设为普通VAR,并在初始化时清零。

最佳实践:在FB封装时,对外接口(VAR_INPUT、VAR_OUTPUT)应尽量使用基础数据类型(BOOL、INT、REAL),避免直接暴露内部静态变量。这样可以隐藏实现细节,提高模块的独立性。同时,对于需要跨POU传递的复杂数据,可以使用用户自定义结构体(STRUCT)作为接口参数。

3.4 常见误区与总结复盘

误区一:混淆FB与FC的使用场景

很多工程师习惯将所有逻辑都写成FC,因为FC调用简单。但当需要记忆状态时,FC无法实现,只能借助全局变量或外部静态变量,这破坏了封装性。正确的做法是:有状态用FB,无状态用FC。

误区二:过度使用全局变量

全局变量虽然方便,但会导致“意大利面条式”的数据流。例如,一个FB内部直接修改了全局变量g_nSpeed,而另一个FB也读取该变量,这会造成隐式耦合。建议通过输入输出参数显式传递数据。

误区三:忽视变量初始化

FB的静态变量在首次调用时默认初始化为零或空值,但某些PLC系统允许自定义初始值。如果未正确设置初始值,可能导致设备启动时处于错误状态。例如,一个控制阀门开度的FB,其内部静态变量st_rOpenPercent应初始化为0(全关),而不是默认的0.0(可能被解释为全开)。

误区四:在FC中使用静态变量

标准规定FC内部不能声明VAR STAT。如果确实需要保持数据,应改用FB。某些PLC厂商的扩展允许在FC中使用静态变量,但这不符合IEC标准,且移植性差。

总结复盘:本章核心要点

维度 关键结论 工程建议
POU划分 PROGRAM负责调度,FB负责有状态控制,FC负责无状态计算 每个POU代码量控制在300行以内,便于测试
FB与FC选择 需要记忆状态或独立副本时选FB,纯函数转换选FC 优先考虑复用性,避免重复代码
变量作用域 全局变量用于设备级共享,局部变量用于POU内部,静态变量用于FB状态保持 全局变量数量最小化,命名加前缀
生命周期管理 RETAIN用于断电保持,普通VAR用于临时数据 谨慎使用RETAIN,避免意外恢复运行