SCL 语法完全教程
SCL(Structured Control Language,结构化控制语言)是 Darra PLC 的文本编程语言,基于 IEC 61131-3 标准中的结构化文本(ST)语法。语法类似 Pascal / Ada,适合复杂算法、数据处理、状态机等高级控制逻辑。
目录
- SCL 基础
- 数据类型
- 变量声明
- 运算符
- 控制流语句
- 函数 (FUNCTION)
- 功能块 (FUNCTION_BLOCK)
- 程序 (PROGRAM)
- 标准函数库
- 标准系统函数
- 数组与字符串操作
- 指针与引用
- 面向对象编程
- 预处理器与编译指令
1. SCL 基础
1.1 什么是 SCL?
SCL 是 Darra PLC 的文本编程语言,对应于 IEC 61131-3 标准的 ST(结构化文本)。与梯形图(LAD)的图形化不同,SCL 用代码写控制逻辑,类似 C 语言但是用 Pascal 风格的关键字。
SCL 的定位:
- 复杂算法 — PID 变体、滤波、运动规划
- 数据处理 — 数组排序、统计、协议解析
- 数学运算 — 浮点矩阵、三角变换、坐标映射
- 状态机 — 多状态工艺步、安全连锁
- 配方管理 — 配方参数计算与切换
1.2 Hello World
PROGRAM Main
VAR
sMessage : STRING[50];
END_VAR
sMessage := 'Hello, Darra PLC!';
// 在调试窗口输出
// 实际 PLC 中可写到一个 DB 变量供 HMI 读取
END_PROGRAM
试一试:在 Darra IDE 中新建一个 SCL 程序,输入上面的代码,编译后在线监视变量
sMessage的值。
1.3 程序组织单元(POU)
SCL 有三种程序组织单元:
| POU 类型 | 关键字 | 有记忆? | 用途 |
|---|---|---|---|
| 程序 | PROGRAM | 否(每次扫描结束输出清零) | 顶层入口,任务分配 |
| 函数 | FUNCTION | 否(纯组合逻辑) | 数学运算、转换、无状态算法 |
| 功能块 | FUNCTION_BLOCK | 是(内部状态保持) | 电机控制、PID、计数器 |
1.4 注释
// 单行注释 — 两个斜杠
/*
多行注释
块注释
*/
(*
另一种多行注释
IEC 61131-3 标准注释风格
*)
1.5 大小写敏感
SCL 不区分大小写,以下写法都合法:
motor_run := TRUE; // 小写
MOTOR_RUN := TRUE; // 大写
Motor_Run := TRUE; // 混合
但推荐使用约定:
- 变量名:
camelCase(首字母小写驼峰) - 常量:
UPPER_SNAKE(全大写 + 下划线) - 类型名:
PascalCase(首字母大写) - FB 实例名:
fbLowerCamel(小写驼峰 + fb 前缀)
1.6 语句结束符
每条语句以 分号 ; 结束:
xOut := xIn1 AND xIn2;
iCount := iCount + 1;
1.7 文件结构
一个典型的 SCL 文件:
// 文件: MotorControl.scl
// 功能: 电机启停控制功能块
FUNCTION_BLOCK MotorControl
VAR_INPUT
bStart : BOOL; // 启动按钮
bStop : BOOL; // 停止按钮
END_VAR
VAR_OUTPUT
xRun : BOOL; // 运行状态
END_VAR
VAR
fbTimer : TON; // 内部定时器实例
END_VAR
// 自锁逻辑
IF bStart AND NOT bStop THEN
xRun := TRUE;
END_IF;
IF bStop THEN
xRun := FALSE;
END_IF;
END_FUNCTION_BLOCK
2. 数据类型
SCL 是强类型语言,所有变量必须先声明类型再使用。
2.1 基本数据类型
位与逻辑类型
| 类型 | 位宽 | 范围 | 默认值 | 说明 |
|---|---|---|---|---|
BOOL | 1 bit | TRUE / FALSE | FALSE | 布尔量、逻辑运算 |
BYTE | 8 bit | 16#00 ~ 16#FF | 16#00 | 位串、硬件 IO |
WORD | 16 bit | 16#0000 ~ 16#FFFF | 16#0000 | 位串、状态字 |
DWORD | 32 bit | 16#00000000 ~ 16#FFFFFFFF | 16#00000000 | 双字位串 |
LWORD | 64 bit | 16#0000000000000000 ~ 16#FFFFFFFFFFFFFFFF | 16#0000000000000000 | 长字位串 |
示例:
VAR
bStart : BOOL := FALSE;
bStop : BOOL;
xInputByte : BYTE;
wStatus : WORD := 16#8000;
dwConfig : DWORD := 16#00FF00FF;
lwCounter : LWORD;
END_VAR
有符号整数
| 类型 | 位宽 | 范围 | 默认值 |
|---|---|---|---|
SINT | 8 bit | -128 ~ 127 | 0 |
INT | 16 bit | -32,768 ~ 32,767 | 0 |
DINT | 32 bit | -2,147,483,648 ~ 2,147,483,647 | 0 |
LINT | 64 bit | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | 0 |
示例:
VAR
iLoopCount : INT := 1000;
diPosition : DINT := -50000;
liBigCounter: LINT;
END_VAR
无符号整数
| 类型 | 位宽 | 范围 | 默认值 |
|---|---|---|---|
USINT | 8 bit | 0 ~ 255 | 0 |
UINT | 16 bit | 0 ~ 65,535 | 0 |
UDINT | 32 bit | 0 ~ 4,294,967,295 | 0 |
ULINT | 64 bit | 0 ~ 18,446,744,073,709,551,615 | 0 |
示例:
VAR
uiSpeed : UINT := 500;
udiTotal : UDINT := 0;
usiIndex : USINT := 1;
END_VAR
浮点类型
| 类型 | 位宽 | 有效位数 | 范围 | 默认值 |
|---|---|---|---|---|
REAL | 32 bit | 6 ~ 9 位 | ±1.18E-38 ~ ±3.40E+38 | 0.0 |
LREAL | 64 bit | 15 ~ 18 位 | ±2.23E-308 ~ ±1.80E+308 | 0.0 |
示例:
VAR
rTemp : REAL := 25.5;
rPi : REAL := 3.14159;
lrAngle: LREAL := 3.14159265358979;
END_VAR
精度陷阱:
REAL只有 6-9 位有效数字,累加 100 万次后误差可达几百。需要高精度累积时用LREAL。
时间与日期类型
| 类型 | 位宽 | 说明 | 示例 |
|---|---|---|---|
TIME | 32 bit | 毫秒精度时间 | T#500ms、T#2.5s |
DATE | 32 bit | 日期 | D#2026-04-07 |
TOD | 32 bit | 时刻(Time of Day) | TOD#14:30:00 |
DT | 64 bit | 日期和时间 | DT#2026-04-07-14:30:00 |
TIME 字面量语法:
tDelay1 := T#100ms; // 100 毫秒
tDelay2 := T#2.5s; // 2.5 秒
tDelay3 := T#1m30s; // 1 分 30 秒
tDelay4 := T#1h15m30s; // 1 小时 15 分 30 秒
tDelay5 := T#24h; // 24 小时
日期示例:
VAR
tDelay : TIME := T#500ms;
dToday : DATE := D#2026-04-07;
todNow : TOD := TOD#14:30:00;
dtStamp : DT := DT#2026-04-07-14:30:00;
dtEvent : DT := DT#2026-12-31-23:59:59;
END_VAR
字符串类型
| 类型 | 说明 | 最大长度 |
|---|---|---|
STRING | 单字节 ASCII 字符串 | 255 字符 |
STRING[N] | 指定长度的单字节字符串 | N 字符 |
WSTRING | 双字节 Unicode 字符串 | 255 字符 |
WSTRING[N] | 指定长度的双字节字符串 | N 字符 |
示例:
VAR
sName : STRING[32] := 'Darra PLC';
sMessage : STRING[255];
wsUnicode: WSTRING[50] := "中文Unicode";
END_VAR
2.2 字面量语法
数字字面量:
iDecimal := 12345; // 十进制
iBinary := 2#10101010; // 二进制(前缀 2#)
iOctal := 8#77; // 八进制(前缀 8#)
iHex := 16#FF; // 十六进制(前缀 16#)
浮点字面量:
r1 := 3.14159; // 小数
r2 := 1.5e6; // 科学计数法(1.5 × 10^6)
r3 := 1.5E-3; // 科学计数法(1.5 × 10^-3)
布尔字面量:
b1 := TRUE;
b2 := FALSE;
2.3 类型转换
SCL 是强类型语言,不同类型之间不能自动转换,必须使用显式转换函数。
隐式转换(允许的有限情况):
// 小范围 → 大范围(隐式允许)
iSmall : INT := 100;
diBig : DINT := iSmall; // 合法:INT 自动提升到 DINT
// 整数 → 浮点(隐式允许)
iVal : INT := 10;
rVal : REAL := iVal; // 合法:INT 自动转为 REAL
显式转换(必须使用转换函数):
// 大范围 → 小范围(必须显式)
diBig : DINT := 100000;
iSmall : INT := INT_TO_INT(diBig); // 合法,但可能溢出
// 浮点 → 整数(必须显式)
rVal : REAL := 25.8;
iVal : INT := REAL_TO_INT(rVal); // 结果为 25(截断,非四舍五入)
// 位串 → 整数(必须显式)
wWord : WORD := 16#00FF;
iVal : INT := WORD_TO_INT(wWord);
// 字符串 → 数值
sText : STRING[10] := '123';
iVal : INT := STRING_TO_INT(sText);
常见错误:忘记显式转换会编译报错。例如
DINT := INT + DINT需要先做INT_TO_DINT(INT)。
2.4 数组
一维数组:
VAR
aiValues : ARRAY[0..9] OF INT; // 10 个元素
arTemp : ARRAY[1..100] OF REAL; // 100 个元素,下标从 1 开始
abFlags : ARRAY[0..31] OF BOOL; // 32 个布尔
END_VAR
多维数组:
VAR
arMatrix : ARRAY[0..2, 0..2] OF REAL; // 3×3 矩阵
aiGrid : ARRAY[0..3, 0..4, 0..5] OF INT; // 4×5×6 三维
END_VAR
数组初始化:
VAR
// 全部元素赋同一值
aiAllOnes : ARRAY[0..4] OF INT := [5(1)]; // [1, 1, 1, 1, 1]
// 逐个赋值
aiValues : ARRAY[0..4] OF INT := [10, 20, 30, 40, 50];
// 部分赋值,其余为默认值
aiPartial : ARRAY[0..4] OF INT := [10, 20]; // [10, 20, 0, 0, 0]
// 二维数组初始化
arMatrix : ARRAY[0..1, 0..2] OF INT := [1, 2, 3, 4, 5, 6];
END_VAR
数组访问:
// 读写元素
aiValues[0] := 100;
iX := aiValues[iIndex];
// 多维访问
arMatrix[1, 2] := 3.14;
rVal := arMatrix[row, col];
常见错误:数组下标越界!
ARRAY[0..9]只有 10 个元素,访问[10]会运行时错误。Darra PLC 支持运行时边界检查(可配置关闭以提升性能)。
2.5 结构体
定义结构体类型:
TYPE MotorData :
STRUCT
bEnable : BOOL;
rSpeed : REAL;
diPosition : DINT;
sName : STRING[32];
END_STRUCT;
END_TYPE
嵌套结构体:
TYPE AxisConfig :
STRUCT
bEnabled : BOOL;
rMaxSpeed : REAL;
rAcceleration: REAL;
rDeceleration: REAL;
END_STRUCT;
END_TYPE
TYPE MachineAxis :
STRUCT
stConfig : AxisConfig; // 嵌套结构体
diActualPos : DINT;
diTargetPos : DINT;
bHome : BOOL;
END_STRUCT;
END_TYPE
结构体变量声明与访问:
VAR
stMotor1 : MotorData;
stAxisX : MachineAxis;
END_VAR
// 访问成员
stMotor1.bEnable := TRUE;
stMotor1.rSpeed := 1500.0;
stMotor1.sName := '主轴电机';
rSpeed := stAxisX.stConfig.rMaxSpeed; // 嵌套访问
2.6 枚举类型
TYPE MachineState :
(
IDLE := 0, // 空闲
RUNNING := 1, // 运行
PAUSED := 2, // 暂停
ERROR := 3, // 故障
EMERGENCY := 4 // 急停
);
END_TYPE
使用枚举:
VAR
eState : MachineState := MachineState.IDLE;
END_VAR
CASE eState OF
MachineState.IDLE:
// 空闲逻辑
MachineState.RUNNING:
// 运行逻辑
MachineState.ERROR:
// 故障处理
END_CASE;
提示:枚举值在代码中用
类型名.值访问,比直接写数字 0、1、2 更可读。
2.7 引用类型 (REF_TO)
VAR
refValue : REF_TO INT; // 指向 INT 的引用
refMotor : REF_TO MotorData; // 指向结构体的引用
END_VAR
(详见第 12 章「指针与引用」)
2.8 ANY 类型
ANY 是 IEC 61131-3 第三版引入的泛型占位类型,用于标准库函数:
// 标准库函数 MOVE 可接受 ANY 类型
MOVE(src := anyValue, dest := anyDest);
注意:
ANY不能用于用户自定义函数(目前 Darra IDE 不支持用户泛型)。
3. 变量声明
3.1 变量声明区
SCL 用 VAR...END_VAR 块声明变量,不同的声明区有不同的作用域和访问权限:
| 声明区 | 关键字 | 作用域 | 说明 |
|---|---|---|---|
| 输入 | VAR_INPUT | 外部传入 | 只读,调用方传入 |
| 输出 | VAR_OUTPUT | 外部读取 | 只写,函数执行后返回给调用方 |
| 输入输出 | VAR_IN_OUT | 双向传递 | 引用传递,可读写 |
| 内部变量 | VAR | 内部使用 | 仅本 POU 内可见 |
| 临时变量 | VAR_TEMP | 内部临时 | 每次调用重新分配,不保持 |
| 静态变量 | VAR_STATIC | FB 内部 | 保持上一次调用的值(仅 FB) |
| 常量 | VAR CONSTANT | 全局/内部 | 编译时常量,不可修改 |
| 全局变量 | VAR_GLOBAL | 全局可见 | 所有 POU 共享 |
3.2 变量声明语法
FUNCTION_BLOCK Example
VAR_INPUT
bEnable : BOOL; // 输入:只读,从外部传入
iSetpoint : INT := 100; // 带默认值(调用方可不传)
END_VAR
VAR_OUTPUT
xRunning : BOOL; // 输出:只写,外部读取
rActual : REAL;
END_VAR
VAR_IN_OUT
stData : SomeStruct; // 输入输出:引用传递,可读写
END_VAR
VAR
iCounter : INT := 0; // 内部变量,保持值
fbTimer : TON; // 内部功能块实例
END_VAR
VAR_TEMP
iTemp : INT; // 临时变量,每次调用重新分配
END_VAR
VAR CONSTANT
c_MAX_SPEED : INT := 1500; // 常量
END_VAR
3.3 RETAIN 与 PERSISTENT
RETAIN(保持性变量):PLC 断电后值保留(靠电池或超级电容保持 RAM)。
VAR RETAIN
diProductionCount : DINT := 0; // 断电后保留
rLastTemperature : REAL;
END_VAR
PERSISTENT(持久化变量):PLC 断电后值保留在非易失存储器。
VAR PERSISTENT
sSerialNumber : STRING[20]; // 持久化,不掉电丢失
diTotalHours : DINT;
END_VAR
NON_RETAIN(显式非保持):即使控制器默认开启 RETAIN,也强制不保持。
VAR RETAIN
diCounter : DINT; // 保持
END_VAR
VAR NON_RETAIN
diTempValue : DINT; // 不保持
END_VAR
3.4 AT 声明(地址分配)
将变量映射到绝对地址(用于硬件 IO 或共享内存):
VAR
xInput AT %I0.0 : BOOL; // 数字量输入 0.0
xOutput AT %Q0.0 : BOOL; // 数字量输出 0.0
wConfig AT %MW100 : WORD; // 保持寄存器 MW100
diCounter AT %MD200 : DINT; // 双字 MD200
aiBuffer AT %MB100 : ARRAY[0..9] OF BYTE; // 字节数组
END_VAR
地址格式:
| 前缀 | 含义 | 示例 |
|---|---|---|
%I | 输入 (Input) | %I1.0、%IW64 |
%Q | 输出 (Output) | %Q0.0、%QW32 |
%M | 内存 (Memory) | %M0.0、%MW100 |
%DB | 数据块 (Data Block) | %DB1.DBW0 |
3.5 默认值
所有变量声明时可以不赋值,此时使用类型的默认值:
| 类型 | 默认值 |
|---|---|
BOOL | FALSE |
| 整数类型 | 0 |
| 浮点类型 | 0.0 |
| 字符串 | ''(空字符串) |
| 时间/日期 | T#0s |
| 数组 | 全部元素为默认值 |
| 结构体 | 全部成员为默认值 |
显式指定默认值:
VAR
iCounter : INT := 100;
rGain : REAL := 1.0;
bEnable : BOOL := TRUE;
sName : STRING[20] := 'Default';
END_VAR
4. 运算符
4.1 算术运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
+ | 加法 | rSum := a + b; |
- | 减法 | rDiff := a - b; |
* | 乘法 | rProd := a * b; |
/ | 除法 | rQuot := a / b; |
MOD | 取模(余数) | iRem := 10 MOD 3; // 结果为 1 |
** | 幂运算 | rPow := 2.0 ** 3.0; // 结果为 8.0 |
示例:
iSum := 10 + 20; // 30
iDiff := 50 - 23; // 27
iProd := 6 * 7; // 42
rQuot := 100.0 / 3.0; // 33.3333
iRem := 17 MOD 5; // 2(余数)
// 注意:整数除法会截断
iResult := 10 / 3; // 结果为 3,不是 3.333
rResult := 10.0 / 3.0; // 结果为 3.3333
常见错误:整数除法会截断!
10 / 3结果是3,要得到浮点结果需先转换成REAL。
4.2 比较运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
= | 等于 | IF a = b THEN |
<> | 不等于 | IF a <> b THEN |
< | 小于 | IF a < b THEN |
> | 大于 | IF a > b THEN |
<= | 小于等于 | IF a <= b THEN |
>= | 大于等于 | IF a >= b THEN |
示例:
IF iCount = 100 THEN
xDone := TRUE;
END_IF;
IF rTemp > 85.0 THEN
xAlarm := TRUE;
END_IF;
4.3 逻辑运算符
| 运算符 | 别名 | 说明 |
|---|---|---|
AND | & | 逻辑与(两个都为 TRUE 才为 TRUE) |
OR | 逻辑或(一个为 TRUE 即为 TRUE) | |
XOR | 逻辑异或(不同时 TRUE) | |
NOT | 逻辑非(取反) |
示例:
// 与
xOut := xA AND xB; // A 和 B 都为 TRUE 时输出 TRUE
xOut := xA & xB; // 同上,& 是 AND 的别名
// 或
xOut := xA OR xB; // A 或 B 任一为 TRUE 输出 TRUE
// 异或
xOut := xA XOR xB; // A 和 B 不同时输出 TRUE
// 非
xOut := NOT xA; // 输出 A 的反相
// 组合
xOut := (xA AND xB) OR (NOT xC AND xD);
4.4 位运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
AND | 按位与 | wResult := wA AND wB; |
OR | 按位或 | wResult := wA OR wB; |
XOR | 按位异或 | wResult := wA XOR wB; |
NOT | 按位取反 | wResult := NOT wA; |
SHL | 左移 | wResult := wValue SHL 3; |
SHR | 右移 | wResult := wValue SHR 2; |
ROL | 循环左移 | wResult := wValue ROL 4; |
ROR | 循环右移 | wResult := wValue ROR 4; |
示例:
wA := 16#00FF;
wB := 16#0F0F;
wAnd := wA AND wB; // 16#000F
wOr := wA OR wB; // 16#0FFF
wXor := wA XOR wB; // 16#0FF0
wNot := NOT wA; // 16#FF00
wShifted := wA SHL 4; // 16#0FF0(左移 4 位)
wRotated := wA ROL 4; // 16#0FF0(循环左移 4 位)
4.5 赋值运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
:= | 普通赋值 | xOut := xIn; |
S= | 置位(Set) | xOut S= xIn;(xIn 触发时 xOut 置 TRUE 并保持) |
R= | 复位(Reset) | xOut R= xIn;(xIn 触发时 xOut 置 FALSE 并保持) |
S= 和 R= 的典型用法:
// S= 置位:当 bSet 为 TRUE 时,xFlag 置为 TRUE 并保持
xFlag S= bSet;
// R= 复位:当 bReset 为 TRUE 时,xFlag 置为 FALSE 并保持
xFlag R= bReset;
// 等价于:
IF bSet THEN
xFlag := TRUE;
END_IF;
IF bReset THEN
xFlag := FALSE;
END_IF;
4.6 运算符优先级
从高到低:
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 1(最高) | () | 括号 |
| 2 | NOT | 逻辑非 |
| 3 | ** | 幂运算 |
| 4 | *、/、MOD | 乘、除、取模 |
| 5 | +、- | 加、减 |
| 6 | <、>、<=、>= | 比较 |
| 7 | =、<> | 等于、不等于 |
| 8 | AND、& | 逻辑与 |
| 9 | XOR | 逻辑异或 |
| 10(最低) | OR | 逻辑或 |
示例:
// 不带括号:
rResult := 2 + 3 * 4; // 14(乘法优先)
xOut := xA OR xB AND xC; // xA OR (xB AND xC)(AND 优先于 OR)
// 推荐加括号,清晰:
rResult := 2 + (3 * 4);
xOut := xA OR (xB AND xC);
5. 控制流语句
5.1 IF 语句
语法:
IF 条件1 THEN
// 条件1 为 TRUE 时执行
ELSIF 条件2 THEN
// 条件1 为 FALSE 且条件2 为 TRUE 时执行
ELSE
// 所有条件都不满足时执行
END_IF;
示例:
IF rTemp > 100.0 THEN
xAlarm := TRUE;
xAutoMode := FALSE;
ELSIF rTemp > 80.0 THEN
xWarning := TRUE;
ELSE
xNormal := TRUE;
END_IF;
// 嵌套 IF
IF bEnable THEN
IF iCounter >= 100 THEN
xDone := TRUE;
END_IF;
END_IF;
5.2 CASE 语句
语法:
CASE 表达式 OF
值1:
// 当表达式 = 值1 时执行
值2, 值3:
// 当表达式 = 值2 或 值3 时执行
值4..值10:
// 当表达式在 4 到 10 之间时执行
ELSE
// 其他情况
END_CASE;
示例:
CASE iMode OF
0:
xIdle := TRUE;
1:
xRun := TRUE;
rSpeed := 1000.0;
2, 3:
xPause := TRUE;
rSpeed := 0.0;
4..10:
xError := TRUE;
iErrorCode := iMode;
ELSE
xError := TRUE;
iErrorCode := 99;
END_CASE;
提示:CASE 比长串 IF-ELSIF 更清晰,尤其适合状态机。
5.3 FOR 循环
语法:
FOR 循环变量 := 起始值 TO 终止值 BY 步长 DO
// 循环体
END_FOR;
示例:
// 从 0 到 9 循环
FOR i := 0 TO 9 DO
aiArray[i] := i * 10;
END_FOR;
// 从 1 到 100,步长 2(奇数值)
FOR i := 1 TO 100 BY 2 DO
aiOdd[i] := i;
END_FOR;
// 递减
FOR i := 10 DOWNTO 0 DO
aiReverse[i] := i;
END_FOR;
// 数组求和
iSum := 0;
FOR i := 0 TO 99 DO
iSum := iSum + aiValues[i];
END_FOR;
5.4 WHILE 循环
语法:
WHILE 条件 DO
// 循环体(条件为 TRUE 时反复执行)
END_WHILE;
示例:
i := 0;
WHILE aiValues[i] <> 0 AND i < 100 DO
i := i + 1;
END_WHILE;
// 找到第一个值为 0 的元素
// 注意:确保条件最终会变为 FALSE,否则死循环!
5.5 REPEAT 循环
语法:
REPEAT
// 循环体(至少执行一次)
UNTIL 条件
END_REPEAT;
示例:
i := 0;
REPEAT
i := i + 1;
aiValues[i] := i * 2;
UNTIL i >= 10
END_REPEAT;
// 至少执行一次,条件为 TRUE 时退出
WHILE vs REPEAT:
WHILE先判断后执行(可能一次都不执行),REPEAT先执行后判断(至少执行一次)。
5.6 EXIT、CONTINUE、RETURN
EXIT — 立即退出循环:
FOR i := 0 TO 99 DO
IF aiValues[i] = 0 THEN
EXIT; // 找到目标值,立即退出循环
END_IF;
iCount := iCount + 1;
END_FOR;
CONTINUE — 跳过本次循环剩余部分:
FOR i := 0 TO 99 DO
IF aiValues[i] < 0 THEN
CONTINUE; // 跳过负数,不处理
END_IF;
iSum := iSum + aiValues[i];
END_FOR;
RETURN — 立即退出当前 POU:
FUNCTION SafeDivide : REAL
VAR_INPUT
a : REAL;
b : REAL;
END_VAR
IF b = 0.0 THEN
SafeDivide := 0.0;
RETURN; // 除数为 0,直接返回
END_IF;
SafeDivide := a / b;
END_FUNCTION
5.7 循环嵌套与作用域
FOR i := 0 TO 9 DO
FOR j := 0 TO 9 DO
arMatrix[i, j] := INT_TO_REAL(i * 10 + j);
END_FOR;
END_FOR;
注意:循环变量不能嵌套声明同名变量。不要在循环内修改循环变量的值(除非很确定你在做什么)。
6. 函数 (FUNCTION)
6.1 函数声明
语法:
FUNCTION 函数名 : 返回数据类型
VAR_INPUT
// 输入参数(只读)
END_VAR
VAR_OUTPUT
// 输出参数(只写)
END_VAR
VAR_IN_OUT
// 输入输出参数(引用传递,可读写)
END_VAR
VAR
// 临时变量(每次调用重新分配)
END_VAR
// 函数体
函数名 := 返回值; // 必须给函数名赋值!
END_FUNCTION
6.2 最简单的函数
FUNCTION Add : INT
VAR_INPUT
a : INT;
b : INT;
END_VAR
Add := a + b;
END_FUNCTION
调用:
iResult := Add(10, 20); // 位置参数
iResult := Add(a := 10, b := 20); // 命名参数(推荐)
6.3 多参数函数
FUNCTION CalculateMotorTorque : REAL
VAR_INPUT
rCurrent : REAL; // 电流 (A)
rFlux : REAL; // 磁通 (Wb)
rResistance : REAL; // 电阻 (Ohm)
END_VAR
VAR
rTemp : REAL;
END_VAR
rTemp := rCurrent * rFlux;
CalculateMotorTorque := rTemp - (rCurrent * rCurrent * rResistance);
END_FUNCTION
调用:
rTorque := CalculateMotorTorque(
rCurrent := 5.0,
rFlux := 0.8,
rResistance := 0.5
);
6.4 多输出函数
SCL 函数只有一个返回值,但可以通过 VAR_OUTPUT 返回多个值:
FUNCTION MinMax : VOID // 返回类型为 VOID(无返回值)
VAR_INPUT
value1 : REAL;
value2 : REAL;
END_VAR
VAR_OUTPUT
minVal : REAL;
maxVal : REAL;
END_VAR
IF value1 < value2 THEN
minVal := value1;
maxVal := value2;
ELSE
minVal := value2;
maxVal := value1;
END_IF;
END_FUNCTION
调用:
MinMax(
value1 := 10.5,
value2 := 20.3,
minVal => rMin, // => 箭头表示输出参数映射
maxVal => rMax
);
6.5 VAR_IN_OUT 参数
VAR_IN_OUT 参数按引用传递,函数内部修改会影响外部变量:
FUNCTION Swap : VOID
VAR_IN_OUT
a : INT;
b : INT;
END_VAR
VAR
temp : INT;
END_VAR
temp := a;
a := b;
b := temp;
END_FUNCTION
调用:
Swap(a := iX, b := iY); // iX 和 iY 的值会被交换
6.6 函数调用语法
位置参数(按顺序):
rResult := MyFunc(1.0, 2.0, 3.0);
命名参数(推荐):
rResult := MyFunc(a := 1.0, b := 2.0, c := 3.0);
链式调用:
rResult := Func1(Func2(a := 10, b := 20), c := 30);
6.7 递归
SCL 支持递归(函数调用自身),但需要限制递归深度:
FUNCTION Factorial : DINT
VAR_INPUT
n : DINT;
END_VAR
IF n <= 1 THEN
Factorial := 1;
ELSE
Factorial := n * Factorial(n - 1); // 递归调用
END_IF;
END_FUNCTION
警告:PLC 运行时的堆栈深度有限(通常 256 层),递归太深会导致堆栈溢出。工业控制中不推荐深度递归,建议用循环替代。
7. 功能块 (FUNCTION_BLOCK)
7.1 功能块声明
功能块(FB)与函数(FC)最大的区别:FB 有内部状态,保持上一次调用的值。
语法:
FUNCTION_BLOCK 功能块名
VAR_INPUT
// 输入参数
END_VAR
VAR_OUTPUT
// 输出参数
END_VAR
VAR_IN_OUT
// 输入输出参数
END_VAR
VAR
// 内部变量(保持状态)
END_VAR
// 功能块体
END_FUNCTION_BLOCK
7.2 最简单的 FB — 计数器
FUNCTION_BLOCK Counter
VAR_INPUT
bCount : BOOL; // 计数触发
bReset : BOOL; // 复位
END_VAR
VAR_OUTPUT
iValue : INT; // 当前计数值
END_VAR
IF bReset THEN
iValue := 0;
ELSIF bCount THEN
iValue := iValue + 1; // 状态保持,每次调用累加
END_IF;
END_FUNCTION_BLOCK
7.3 FB 实例化
FB 必须先声明实例才能使用:
PROGRAM Main
VAR
fbCounter1 : Counter; // 声明 FB 实例
fbCounter2 : Counter;
xTrig : BOOL;
iVal1 : INT;
iVal2 : INT;
END_VAR
// 调用(调用方法已在 FB 中定义,这里直接写输入输出)
fbCounter1(bCount := xTrig);
iVal1 := fbCounter1.iValue; // 读取输出
fbCounter2(bCount := xTrig, bReset := TRUE);
iVal2 := fbCounter2.iValue;
END_PROGRAM
7.4 带定时器的 FB
FUNCTION_BLOCK PulseGenerator
VAR_INPUT
bEnable : BOOL;
tOnTime : TIME := T#500ms; // 导通时间
tOffTime : TIME := T#500ms; // 断开时间
END_VAR
VAR_OUTPUT
xPulse : BOOL; // 脉冲输出
END_VAR
VAR
fbOnTimer : TON; // 导通定时器
fbOffTimer : TON; // 断开定时器
bOnPhase : BOOL; // 当前相位
END_VAR
IF NOT bEnable THEN
xPulse := FALSE;
bOnPhase := TRUE;
fbOnTimer(IN := FALSE);
fbOffTimer(IN := FALSE);
RETURN;
END_IF;
IF bOnPhase THEN
fbOnTimer(IN := TRUE, PT := tOnTime);
xPulse := TRUE;
IF fbOnTimer.Q THEN
bOnPhase := FALSE;
fbOnTimer(IN := FALSE);
END_IF;
ELSE
fbOffTimer(IN := TRUE, PT := tOffTime);
xPulse := FALSE;
IF fbOffTimer.Q THEN
bOnPhase := TRUE;
fbOffTimer(IN := FALSE);
END_IF;
END_IF;
END_FUNCTION_BLOCK
7.5 FB 的 INIT 和 EXIT 方法
FB 在第一次调用时执行 __INIT,最后一次调用后执行 __EXIT:
FUNCTION_BLOCK MyDevice
VAR
iInitCount : INT;
END_VAR
// 主逻辑
iInitCount := iInitCount + 1;
END_FUNCTION_BLOCK
// 附加 INIT 方法(在第一次调用前自动执行)
METHOD __INIT
iInitCount := 0;
// 初始化硬件、分配资源等
END_METHOD
// 附加 EXIT 方法(在 FB 销毁时自动执行)
METHOD __EXIT
// 释放资源、关闭硬件等
END_METHOD
7.6 THIS 引用
在 FB 方法内部,THIS 指向当前 FB 实例:
FUNCTION_BLOCK Motor
VAR
rSpeed : REAL;
END_VAR
// 方法中访问自身
METHOD SetSpeed
VAR_INPUT
rNewSpeed : REAL;
END_VAR
THIS.rSpeed := rNewSpeed; // THIS 指向当前 FB 实例
END_METHOD
7.7 FB 继承 (EXTENDS)
FUNCTION_BLOCK BaseMotor
VAR
rSpeed : REAL;
bRun : BOOL;
END_VAR
// 基础电机逻辑
END_FUNCTION_BLOCK
// 继承 BaseMotor
FUNCTION_BLOCK AdvancedMotor EXTENDS BaseMotor
VAR
rAcceleration : REAL;
rDeceleration : REAL;
END_VAR
// 继承来自 BaseMotor 的 rSpeed 和 bRun
// 可以增加新的功能
IF bRun THEN
rSpeed := rSpeed + rAcceleration * 0.1;
END_IF;
END_FUNCTION_BLOCK
7.8 接口实现 (IMPLEMENTS)
INTERFACE IMotionControl
METHOD MoveTo : BOOL
VAR_INPUT
diTargetPos : DINT;
rSpeed : REAL;
END_END_METHOD
END_INTERFACE
// 实现接口
FUNCTION_BLOCK ServoMotor IMPLEMENTS IMotionControl
VAR
diPosition : DINT;
END_VAR
// 必须实现 IMotionControl 的所有方法
METHOD MoveTo : BOOL
VAR_INPUT
diTargetPos : DINT;
rSpeed : REAL;
END_VAR
diPosition := diTargetPos;
MoveTo := TRUE;
END_METHOD
END_FUNCTION_BLOCK
8. 程序 (PROGRAM)
8.1 PROGRAM 声明
PROGRAM 是 PLC 的顶层入口,每个程序可以分配给一个任务(OB)按周期执行。
PROGRAM Main
VAR
iCounter : INT;
xOutput AT %Q0.0 : BOOL;
END_VAR
iCounter := iCounter + 1;
IF iCounter >= 1000 THEN
xOutput := TRUE;
END_IF;
END_PROGRAM
8.2 任务分配
在 IDE 中配置 PROGRAM 的执行周期:
// 快速任务(1ms 周期)
PROGRAM FastTask
// 高速 IO 处理、EtherCAT 通讯
END_PROGRAM
// 主任务(10ms 周期)
PROGRAM MainTask
// 控制逻辑、运动规划
END_PROGRAM
// 慢速任务(100ms 周期)
PROGRAM SlowTask
// 数据记录、HMI 通讯
END_PROGRAM
8.3 多程序协作
PROGRAM Main
VAR
diCycleCount : DINT;
END_VAR
diCycleCount := diCycleCount + 1;
// 调用子程序
ProcessInputs();
ExecuteControl();
UpdateOutputs();
END_PROGRAM
FUNCTION ProcessInputs : VOID
// 读取输入
END_FUNCTION
FUNCTION ExecuteControl : VOID
// 控制逻辑
END_FUNCTION
FUNCTION UpdateOutputs : VOID
// 更新输出
END_FUNCTION
9. 标准函数库
9.1 数学函数
| 函数 | 说明 | 示例 |
|---|---|---|
ABS(x) | 绝对值 | ABS(-10) → 10 |
SQRT(x) | 平方根 | SQRT(16.0) → 4.0 |
LN(x) | 自然对数 | LN(2.71828) → 1.0 |
LOG(x) | 常用对数(以 10 为底) | LOG(100.0) → 2.0 |
EXP(x) | 指数函数 | EXP(1.0) → 2.71828 |
SIN(x) | 正弦(弧度) | SIN(0.0) → 0.0 |
COS(x) | 余弦(弧度) | COS(0.0) → 1.0 |
TAN(x) | 正切(弧度) | TAN(0.7854) → 1.0 |
ASIN(x) | 反正弦 | ASIN(1.0) → 1.5708 |
ACOS(x) | 反余弦 | ACOS(0.0) → 1.5708 |
ATAN(x) | 反正切 | ATAN(1.0) → 0.7854 |
EXPT(x, y) | 幂函数 | EXPT(2.0, 3.0) → 8.0 |
示例:
// 三角函数(注意:弧度制)
rRad := 45.0 * 3.14159 / 180.0; // 角度转弧度
rSin := SIN(rRad); // sin(45°) ≈ 0.7071
rCos := COS(rRad); // cos(45°) ≈ 0.7071
// 反三角函数
rAngle := ASIN(0.7071); // 0.7854 rad ≈ 45°
rDeg := rAngle * 180.0 / 3.14159; // 弧度转角度
// 平方根
rHypot := SQRT(a*a + b*b); // 勾股定理
9.2 统计函数
| 函数 | 说明 | 示例 |
|---|---|---|
MIN(x, y) | 取最小值 | MIN(10, 20) → 10 |
MAX(x, y) | 取最大值 | MAX(10, 20) → 20 |
SUM(arr) | 数组求和 | |
MEAN(arr) | 求平均值 | |
STDDEV(arr) | 求标准差 |
示例:
rLow := MIN(25.0, 18.5); // 18.5
rHigh := MAX(25.0, 18.5); // 25.0
// 限幅
rSpeed := MAX(0.0, MIN(rSpeed, 1500.0)); // 限制在 0-1500 之间
9.3 选择函数
| 函数 | 说明 | 示例 |
|---|---|---|
SEL(g, a, b) | 二选一:g=0 选 a,g=1 选 b | SEL(xIn, v0, v1) |
MUX(k, v0, v1, v2, ...) | 多路选择 | MUX(iSel, a, b, c, d) |
LIMIT(lo, x, hi) | 限幅:lo ≤ x ≤ hi | LIMIT(0, rVal, 100) |
示例:
// SEL:二选一
rOut := SEL(xMode, rNormalSpeed, rFastSpeed);
// 等价于:IF xMode THEN rOut := rFastSpeed; ELSE rOut := rNormalSpeed; END_IF;
// MUX:多路选择
rOut := MUX(iChannel, rCh0, rCh1, rCh2, rCh3);
// 等价于:CASE iChannel OF 0: rOut := rCh0; 1: rOut := rCh1; ... END_CASE;
// LIMIT:限幅
rOut := LIMIT(0.0, rInput, 100.0);
// 等价于:rOut := MAX(0.0, MIN(rInput, 100.0));
9.4 类型转换函数
| 函数 | 说明 |
|---|---|
BOOL_TO_INT(x) | BOOL 转 INT |
INT_TO_REAL(x) | INT 转 REAL |
REAL_TO_INT(x) | REAL 转 INT(截断) |
REAL_TO_DINT(x) | REAL 转 DINT |
DINT_TO_REAL(x) | DINT 转 REAL |
WORD_TO_INT(x) | WORD 转 INT |
INT_TO_WORD(x) | INT 转 WORD |
STRING_TO_INT(s) | 字符串转 INT |
INT_TO_STRING(x) | INT 转字符串 |
TIME_TO_DINT(t) | TIME 转 DINT(毫秒) |
DINT_TO_TIME(d) | DINT 转 TIME |
示例:
iVal := REAL_TO_INT(25.8); // 25(截断,非四舍五入)
iVal := REAL_TO_INT(25.8 + 0.5); // 26(四舍五入技巧)
rVal := INT_TO_REAL(100); // 100.0
sText := INT_TO_STRING(12345); // '12345'
iMillis := TIME_TO_DINT(T#2.5s); // 2500
9.5 字符串函数
| 函数 | 说明 | 示例 |
|---|---|---|
LEN(s) | 字符串长度 | LEN('Hello') → 5 |
LEFT(s, n) | 取左边 n 个字符 | LEFT('Hello', 2) → 'He' |
RIGHT(s, n) | 取右边 n 个字符 | RIGHT('Hello', 3) → 'llo' |
MID(s, p, n) | 从位置 p 取 n 个字符 | MID('Hello', 2, 2) → 'll' |
CONCAT(s1, s2) | 拼接字符串 | CONCAT('A', 'B') → 'AB' |
INSERT(s1, s2, p) | 在位置 p 插入 s2 | INSERT('AB', '12', 1) → 'A12B' |
DELETE(s, p, n) | 删除从 p 开始的 n 个字符 | DELETE('Hello', 2, 2) → 'Hlo' |
REPLACE(s1, s2, p, n) | 替换从 p 开始的 n 个字符为 s2 | REPLACE('Hello', 'XYZ', 2, 2) → 'HXYZo' |
FIND(s1, s2) | 查找 s2 在 s1 中的位置 | FIND('Hello', 'll') → 3 |
示例:
sFull := CONCAT('Darra', ' PLC'); // 'Darra PLC'
sLeft := LEFT('Darra PLC', 5); // 'Darra'
iPos := FIND('192.168.1.100', '.'); // 4(第一个点位置)
sIP := DELETE('192.168.1.100', 4, 1); // '192168.1.100'
9.6 日期时间函数
| 函数 | 说明 |
|---|---|
ADD_TIME(t1, t2) | 时间相加 |
SUB_TIME(t1, t2) | 时间相减 |
TIME_TO_DINT(t) | TIME 转 DINT(毫秒) |
DINT_TO_TIME(d) | DINT 转 TIME |
示例:
tNew := ADD_TIME(T#10s, T#5s); // T#15s
tDiff := SUB_TIME(T#30s, T#10s); // T#20s
iMs := TIME_TO_DINT(T#1.5s); // 1500
tTime := DINT_TO_TIME(2000); // T#2s
9.7 位操作函数
| 函数 | 说明 |
|---|---|
SET_BIT(x, n) | 将第 n 位置 1 |
GET_BIT(x, n) | 读取第 n 位的值 |
TOGGLE_BIT(x, n) | 翻转第 n 位 |
示例:
wData := 16#0000;
wData := SET_BIT(wData, 3); // 16#0008(第 3 位置 1)
bBit := GET_BIT(wData, 3); // TRUE
wData := TOGGLE_BIT(wData, 3); // 16#0000(第 3 位翻转回 0)
10. 标准系统函数
10.1 系统时间函数
| 函数 | 说明 | 返回类型 |
|---|---|---|
SYS_TIME() | 获取系统时间(毫秒) | UDINT |
SYS_DATE() | 获取当前日期 | DT |
SYS_TICK() | 获取系统滴答计数(1μs 精度) | ULINT |
示例:
// 运行时间
diStartTime := SYS_TIME();
// ... 执行某些操作 ...
diElapsed := SYS_TIME() - diStartTime; // 耗时(毫秒)
// 高精度计时
liStartTick := SYS_TICK();
// ... 执行操作 ...
liElapsedTick := SYS_TICK() - liStartTick; // 耗时(微秒)
10.2 系统控制函数
| 函数 | 说明 | 使用注意 |
|---|---|---|
SYS_RESET() | 软件复位 PLC | 会重启运行时,谨慎使用 |
SYS_DIAG(id) | 获取诊断信息 | 返回诊断字符串 |
示例:
// 紧急情况复位
IF bEmergencyReset THEN
SYS_RESET();
END_IF;
// 获取诊断信息
sInfo := SYS_DIAG(1); // 系统版本
sInfo := SYS_DIAG(2); // 运行时间
sInfo := SYS_DIAG(3); // 内存使用
11. 数组与字符串操作
11.1 数组初始化
VAR
// 全部元素相同值
aiOnes : ARRAY[0..4] OF INT := [5(1)];
// 逐个列举
aiValues : ARRAY[0..4] OF INT := [10, 20, 30, 40, 50];
// 混合初始值
aiMixed : ARRAY[0..4] OF INT := [10, 3(20), 30];
// 结果:[10, 20, 20, 20, 30]
// 二维数组
arMatrix : ARRAY[0..1, 0..2] OF INT := [1, 2, 3, 4, 5, 6];
// 相当于:
// [0,0]=1, [0,1]=2, [0,2]=3
// [1,0]=4, [1,1]=5, [1,2]=6
END_VAR
11.2 数组切片
Darra SCL 支持数组切片操作:
// 切片:从原数组取一段
aiSlice := aiSource[2..5]; // 取索引 2 到 5 的四个元素
// 切片赋值
aiDest[0..3] := aiSource[10..13];
11.3 数组比较
// 数组不能直接用 = 比较,必须逐元素比较
FUNCTION ArrayEqual : BOOL
VAR_INPUT
arr1 : ARRAY[0..9] OF INT;
arr2 : ARRAY[0..9] OF INT;
END_VAR
VAR
i : INT;
END_VAR
ArrayEqual := TRUE;
FOR i := 0 TO 9 DO
IF arr1[i] <> arr2[i] THEN
ArrayEqual := FALSE;
RETURN;
END_IF;
END_FOR;
END_FUNCTION
11.4 字符串操作技巧
字符串拼接:
sFull := CONCAT('Temp: ', CONCAT(INT_TO_STRING(iTemp), '°C'));
// 或使用 + 运算符(如果支持)
sFull := 'Temp: ' + INT_TO_STRING(iTemp) + '°C';
格式化字符串:
sMsg := CONCAT('轴 ', INT_TO_STRING(iAxis),
' 速度 ', INT_TO_STRING(iSpeed),
' RPM');
字符串遍历:
FOR i := 1 TO LEN(sText) DO
cChar := MID(sText, i, 1); // 取第 i 个字符
// 处理字符
END_FOR;
12. 指针与引用
12.1 REF_TO(引用)
语法:
VAR
refInt : REF_TO INT; // 指向 INT 的引用
refMotor : REF_TO MotorData; // 指向结构体的引用
END_VAR
赋值:
VAR
iValue : INT;
refValue : REF_TO INT;
END_VAR
refValue := REF(iValue); // REF 运算符取引用
// 现在 refValue 指向 iValue
END_VAR
解引用:
// 使用 ^ 运算符解引用
iValue := refValue^; // 读取 refValue 指向的值
refValue^ := 100; // 写入 refValue 指向的值
NULL 检查:
IF refValue <> NULL THEN
iValue := refValue^;
ELSE
// 引用为空,做错误处理
END_IF;
12.2 引用作为参数
FUNCTION ProcessData : VOID
VAR_IN_OUT
refData : REF_TO INT; // 引用类型的 IN_OUT 参数
END_VAR
IF refData <> NULL THEN
refData^ := refData^ * 2;
END_IF;
END_FUNCTION
12.3 REF vs POINTER
Darra SCL 支持 REF_TO(引用,推荐)和 POINTER(指针,兼容旧代码):
| 特性 | REF_TO | POINTER |
|---|---|---|
| 语法 | REF_TO | POINTER TO |
| 取地址 | REF(var) | ADR(var) |
| 解引用 | ref^ | ptr^ |
| NULL 检查 | ref <> NULL | ptr <> NULL |
| 安全性 | 推荐 | 兼容旧代码 |
POINTER 示例:
VAR
pValue : POINTER TO INT;
iValue : INT;
END_VAR
pValue := ADR(iValue); // ADR 取地址
pValue^ := 100; // 解引用赋值
END_VAR
13. 面向对象编程
13.1 方法 (METHOD)
语法:
FUNCTION_BLOCK MyClass
VAR
iValue : INT;
END_VAR
// 主逻辑
iValue := iValue + 1;
END_FUNCTION_BLOCK
// 附加方法
METHOD GetValue : INT
GetValue := THIS.iValue;
END_METHOD
METHOD SetValue : VOID
VAR_INPUT
iNewVal : INT;
END_VAR
THIS.iValue := iNewVal;
END_METHOD
调用:
fbMyClass.SetValue(100);
iVal := fbMyClass.GetValue();
13.2 属性 (PROPERTY)
语法:
FUNCTION_BLOCK Motor
VAR
rSpeed : REAL;
rMaxSpeed : REAL := 1500.0;
END_VAR
// 属性定义
PROPERTY Speed : REAL
GET
Speed := THIS.rSpeed;
END_GET
SET
// 带限幅的设置
IF rNewValue <= THIS.rMaxSpeed THEN
THIS.rSpeed := rNewValue;
ELSE
THIS.rSpeed := THIS.rMaxSpeed;
END_IF;
END_SET
END_PROPERTY
PROPERTY IsRunning : BOOL
GET
IsRunning := THIS.rSpeed > 0.0;
END_GET
// 只读属性:没有 SET 块
END_PROPERTY
调用:
fbMotor.Speed := 1000.0; // SET 调用
rCurrentSpeed := fbMotor.Speed; // GET 调用
bRun := fbMotor.IsRunning; // 只读属性
13.3 接口 (INTERFACE)
定义接口:
INTERFACE ISwitchable
METHOD TurnOn : VOID
METHOD TurnOff : VOID
METHOD IsOn : BOOL
END_INTERFACE
实现接口:
FUNCTION_BLOCK Lamp IMPLEMENTS ISwitchable
VAR
bOn : BOOL;
END_VAR
METHOD TurnOn : VOID
THIS.bOn := TRUE;
END_METHOD
METHOD TurnOff : VOID
THIS.bOn := FALSE;
END_METHOD
METHOD IsOn : BOOL
IsOn := THIS.bOn;
END_METHOD
END_FUNCTION_BLOCK
多态使用:
FUNCTION_BLOCK Motor IMPLEMENTS ISwitchable
VAR
bRunning : BOOL;
END_VAR
METHOD TurnOn : VOID
THIS.bRunning := TRUE;
END_METHOD
METHOD TurnOff : VOID
THIS.bRunning := FALSE;
END_METHOD
METHOD IsOn : BOOL
IsOn := THIS.bRunning;
END_METHOD
END_FUNCTION_BLOCK
13.4 继承 (EXTENDS)
FUNCTION_BLOCK Animal
VAR
sName : STRING[20];
END_VAR
// 基类逻辑
END_FUNCTION_BLOCK
FUNCTION_BLOCK Dog EXTENDS Animal
VAR
iLegs : INT := 4;
END_VAR
// 继承自 Animal 的 sName
// 子类扩展
END_FUNCTION_BLOCK
14. 预处理器与编译指令
14.1 条件编译
{IF DEFINED(DEBUG)}
// 调试模式下的代码
SYS_DIAG('Debug: counter = ', iCounter);
{ELSE}
// 发布模式下的代码
{END_IF}
14.2 区域折叠
{REGION '初始化'}
// 初始化代码
iCounter := 0;
bReady := FALSE;
{END_REGION}
{REGION '主循环'}
// 主控制逻辑
iCounter := iCounter + 1;
{END_REGION}
14.3 编译提示与错误
{WARNING '此函数尚未完整实现,请检查'}
// 编译时输出警告
{ERROR '内存不足,请减少数组大小'}
// 编译时输出错误,阻止编译
14.4 页面分隔
// 在打印时插入分页
{PAGE}
// 设置打印标题
{TITLE '电机控制模块'}
附录:常见错误速查表
| 错误 | 原因 | 改正 |
|---|---|---|
忘记 END_IF | IF 块不完整 | 每一层 IF 都有对应的 END_IF |
忘记 END_FOR | FOR 循环没闭合 | 在循环体后加 END_FOR |
| 数组越界 | 访问 ARRAY[0..9] 的 [10] | 检查边界条件 |
| 类型不匹配 | INT := REAL 没有显式转换 | 使用 REAL_TO_INT() |
| 整数除法截断 | 10 / 3 得到 3 | 转成 REAL 再除 |
| 忘记给函数名赋值 | 函数没有返回值 | 函数体内写 函数名 := 值; |
| 死循环 | WHILE TRUE DO 无退出条件 | 确保有退出条件或 EXIT |
| 递归太深 | 堆栈溢出 | 用循环替代递归 |
| 分号漏了 | 语句结束没写 ; | 每条语句末尾加 ; |
本教程由 Darra 文档团队维护,持续更新。 最后更新:2026-07-26