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 之间传递。
五、一个可运行的 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 时立即复位。
六、语言选择与组织建议
实际项目中,建议按以下方式组织:用 ST 编写算法和数据处理,用 FBD 或 LD 编写联锁和开关逻辑,用 SFC 描述设备步序。每个功能块只做一件事,输入输出接口清晰,内部变量私有。全局变量只保留系统级状态(如急停、模式、报警码),设备级数据通过功能块接口传递。这样在调试时,可以单独调用某个功能块观察其输入输出,而不必在庞大的主程序中逐行排查。
变量声明和 POU 划分是 PLC 程序结构的骨架。骨架清晰,后续增加功能、修改逻辑和排查故障都会明显更省力。