3. PLC编程基础与IEC 61131-3

在 TwinCAT 3 中,PLC 程序不再由单一语言写成,而是由 IEC 61131-3 标准定义的多种编程语言共同构成。TwinCAT PLC Control 作为编程环境,允许在同一个 PLC 项目中创建不同语言的 POU(Program Organization Unit,程序组织单元),并让它们互相调用。理解“语言—变量—POU”三者的关系,是后续编写可维护程序的基础。

一、IEC 61131-3 的五种语言与 TwinCAT 的对应

IEC 61131-3 定义了两种文本语言和三种图形语言。TwinCAT PLC Control 对它们都有支持,但实际工程中使用频率差异明显:

  • ST(Structured Text,结构化文本):类 Pascal 的文本语言,适合数学运算、条件分支、循环和复杂算法,是当前 TwinCAT 项目中最常用的语言。
  • LD(Ladder Diagram,梯形图):以继电器逻辑为原型的图形语言,适合表达开关量联锁和电气逻辑,对电气背景的工程师最直观。
  • FBD(Function Block Diagram,功能块图):用方框和连线表示信号流,适合表达逻辑组合、模拟量处理和功能块连接。
  • IL(Instruction List,指令表):类似汇编的文本语言,IEC 61131-3 第三版已将其列为过时,TwinCAT 中仍可读旧代码,但不建议新项目使用。
  • SFC(Sequential Function Chart,顺序功能图):用于描述步序和状态转移,适合设备动作流程。

选择语言的原则不是“哪个高级用哪个”,而是看逻辑形态:连续量计算和算法用 ST;纯开关联锁用 LD 或 FBD;有明显步骤切换的流程用 SFC。同一个项目中混用是正常做法。

二、变量声明:名称、类型与作用域

每个 POU 都有自己的声明区,分为 VAR、VAR_INPUT、VAR_OUTPUT、VAR_IN_OUT、VAR_GLOBAL 等类别。声明的基本形式是“名称 : 类型 := 初值;”,例如:

VAR
    iStep      : INT := 0;        // 步序编号
    bStart     : BOOL;            // 启动按钮
    rSetpoint  : REAL := 50.0;    // 设定值,单位 ℃
    tDelay     : TIME := T#2S;    // 延时 2 秒
    aTemp      : ARRAY[1..8] OF REAL;  // 8 路温度
END_VAR

作用域决定变量能被谁访问。VAR_INPUT 是调用者传入的形参,VAR_OUTPUT 是返回给调用者的结果,VAR_IN_OUT 是引用传递(类似指针,可读写实参本身),VAR 是 POU 内部私有变量。全局变量在 GVL(Global Variable List)中声明,用 VAR_GLOBAL 定义,任何 POU 都能访问,但滥用全局变量会让程序耦合度升高,应优先用输入输出接口传递数据。

变量命名建议采用“类型前缀 + 语义”的方式,如 b 表示 BOOL、i 表示 INT、r 表示 REAL、t 表示 TIME、s 表示 STRING。这样在 ST 代码和 FBD 连线中都能快速识别变量类型,减少误用。

三、数据类型:标准类型与派生类型

IEC 61131-3 的标准数据类型包括 BOOL、BYTE、WORD、DWORD、SINT/INT/DINT、USINT/UINT/UDINT、REAL/LREAL、TIME、STRING 等。TwinCAT 还支持基于标准类型的派生类型(DUT,Data Unit Type),例如:

TYPE ST_Motor :
STRUCT
    bRun      : BOOL;
    bFault    : BOOL;
    rSpeed    : REAL;
    iCurrent  : INT;
END_STRUCT
END_TYPE

用 ST_Motor 声明一个变量,就能把电机的运行、故障、转速、电流打包在一起,代码中写 Motor1.bRun 即可访问。结构体让数据组织清晰,也便于在 HMI 中按结构绑定变量。数组和结构体可以嵌套,但要注意:数组下标默认从 1 开始(可在声明中指定 ARRAY[0..7]),结构体成员访问用点号。

四、POU:程序、功能块与函数

POU 是 IEC 61131-3 对可调用逻辑单元的统称,分为三类:

  • PROGRAM(程序):顶层执行单元,由任务周期调用,拥有自己的静态变量,不能被其他 POU 实例化调用。
  • FUNCTION_BLOCK(功能块,FB):可实例化的逻辑单元,每次调用需要声明一个实例,实例保存自己的状态。例如定时器 TON、计数器 CTU 都是功能块。
  • FUNCTION(函数):无内部状态的纯计算单元,相同输入必得相同输出,不能有静态变量,适合做数学运算和类型转换。

功能块与函数的区别是初学者最容易混淆的点:功能块有“记忆”,函数没有。例如一个计算平均值的逻辑,如果每次调用都从零开始累加,就应该用功能块;如果只是把两个输入相加返回,用函数即可。

下面这张图展示了 TwinCAT PLC Control 中一个典型项目的组织关系:任务调用 PROGRAM,PROGRAM 内部实例化并调用功能块和函数,变量通过声明区在 POU 之间传递。

TwinCAT PLC Control 项目组织与 POU 调用关系 PLC 任务(Task) 周期调用,如 10 ms PROGRAM MAIN 顶层程序,含 VAR / VAR_INPUT / VAR_OUTPUT FUNCTION_BLOCK FB_MotorControl VAR_INPUT: bStart, bStop VAR_OUTPUT: bRun, bFault VAR: tOnDelay : TON 有状态,需实例化调用 FUNCTION F_ScaleTemp VAR_INPUT: rRaw : REAL VAR_OUTPUT: rTemp : REAL 无状态,直接调用 rTemp := rRaw * 0.1 - 50; GVL 全局变量表 bSystemReady : BOOL rLineSpeed : REAL iAlarmCode : INT VAR_GLOBAL,全局可访问 实例化调用 直接调用 读写全局变量 PROGRAM 由任务周期调用;FUNCTION_BLOCK 需声明实例后调用并保持状态;FUNCTION 无状态直接调用;GVL 提供跨 POU 的全局数据通道。

五、一个可运行的 ST 示例

下面用 ST 写一个电机启停控制功能块,包含输入输出声明、定时器实例和基本逻辑,可直接在 TwinCAT PLC Control 中新建 FB 并粘贴:

FUNCTION_BLOCK FB_MotorControl
VAR_INPUT
    bStart   : BOOL;      // 启动按钮,常开
    bStop    : BOOL;      // 停止按钮,常闭
    tOnDelay : TIME := T#1S;
END_VAR
VAR_OUTPUT
    bRun     : BOOL;
    bFault   : BOOL;
END_VAR
VAR
    tDelay   : TON;       // 定时器实例
    bInternal: BOOL;
END_VAR

// 启动延时
tDelay(IN := bStart AND NOT bStop, PT := tOnDelay);

// 运行输出
IF tDelay.Q THEN
    bInternal := TRUE;
END_IF

IF bStop THEN
    bInternal := FALSE;
END_IF

bRun := bInternal;
bFault := bStart AND bStop;   // 同时按下视为异常

END_FUNCTION_BLOCK

在 MAIN 程序中实例化并调用:

PROGRAM MAIN
VAR
    fbMotor1 : FB_MotorControl;
    bStart1  : BOOL;
    bStop1   : BOOL;
    bRun1    : BOOL;
END_VAR

fbMotor1(bStart := bStart1, bStop := bStop1, tOnDelay := T#2S);
bRun1 := fbMotor1.bRun;

END_PROGRAM

这段代码体现了几个关键点:功能块必须声明实例 fbMotor1;调用时用“实例名(参数 := 值)”的方式传参;输出通过实例名加点号访问;定时器 TON 作为功能块实例嵌套在 FB 内部,每次调用自动更新。运行后,bStart1 为 TRUE 且 bStop1 为 FALSE 时,经过 2 秒 bRun1 变为 TRUE;bStop1 为 TRUE 时立即复位。

在 TwinCAT PLC Control 中,按 F5 编译可检查变量声明和类型是否匹配;若提示“未声明的标识符”,优先检查变量是否在正确的 VAR 区声明,以及大小写是否一致(ST 不区分大小写,但拼写必须一致)。

六、语言选择与组织建议

实际项目中,建议按以下方式组织:用 ST 编写算法和数据处理,用 FBD 或 LD 编写联锁和开关逻辑,用 SFC 描述设备步序。每个功能块只做一件事,输入输出接口清晰,内部变量私有。全局变量只保留系统级状态(如急停、模式、报警码),设备级数据通过功能块接口传递。这样在调试时,可以单独调用某个功能块观察其输入输出,而不必在庞大的主程序中逐行排查。

变量声明和 POU 划分是 PLC 程序结构的骨架。骨架清晰,后续增加功能、修改逻辑和排查故障都会明显更省力。