跳到主要内容

数据类型

SCL 是强类型语言,每个变量和常量在使用前必须声明其数据类型。数据类型决定了变量占用内存的大小、取值范围以及可执行的运算。

Darra SCL 全面支持 IEC 61131-3 Third Edition 标准定义的数据类型体系,包括基本类型、复合类型和用户自定义类型。


基本数据类型

基本数据类型是 SCL 语言内置的最小数据单元,按用途分为位类型整数类型浮点类型时间/日期类型

位与逻辑类型

类型位宽范围IEC 标准默认值
BOOL1 bitTRUE / FALSEIEC 61131-3FALSE
BYTE8 bit16#00 ~ 16#FFIEC 61131-316#00
WORD16 bit16#0000 ~ 16#FFFFIEC 61131-316#0000
DWORD32 bit16#00000000 ~ 16#FFFFFFFFIEC 61131-316#00000000
LWORD64 bit16#0000000000000000 ~ 16#FFFFFFFFFFFFFFFFIEC 61131-3 Ed316#0000000000000000

BOOL 是 SCL 中最基础的类型,用于逻辑运算和位控制。BYTE / WORD / DWORD / LWORD 常用来存放位串数据或与硬件 IO 模块直接交互。

VAR
bStart : BOOL := FALSE;
bStop : BOOL;
xInputByte : BYTE;
wStatus : WORD := 16#8000;
dwConfig : DWORD := 16#00FF00FF;
lwCounter : LWORD;
END_VAR

有符号整数

类型位宽范围IEC 标准默认值
SINT8 bit-128 ~ 127IEC 61131-3 Ed30
INT16 bit-32,768 ~ 32,767IEC 61131-30
DINT32 bit-2,147,483,648 ~ 2,147,483,647IEC 61131-30
LINT64 bit-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807IEC 61131-3 Ed30

无符号整数

类型位宽范围IEC 标准默认值
USINT8 bit0 ~ 255IEC 61131-3 Ed30
UINT16 bit0 ~ 65,535IEC 61131-30
UDINT32 bit0 ~ 4,294,967,295IEC 61131-30
ULINT64 bit0 ~ 18,446,744,073,709,551,615IEC 61131-3 Ed30
VAR
iLoopCount : INT := 1000;
diPosition : DINT := -50000;
liBigCounter: LINT;
uiSpeed : UINT := 500;
udiTotal : UDINT := 0;
usiIndex : USINT := 1;
END_VAR

浮点类型

类型位宽有效位数范围IEC 标准默认值
REAL32 bit6 ~ 9 位±1.18E-38 ~ ±3.40E+38IEC 61131-30.0
LREAL64 bit15 ~ 18 位±2.23E-308 ~ ±1.80E+308IEC 61131-30.0

浮点运算在 SCL 中需要额外注意精度问题。REAL 适用于大多数过程控制场景(PID、滤波、比例运算),LREAL 用于需要高精度的位置计算、天文角度运算或长时间积分。

VAR
rTemperature : REAL := 25.5;
rSetpoint : REAL := 100.0;
lrPosition : LREAL := 123456.789012345;
rPi : REAL := 3.14159;
END_VAR

时间与日期类型

类型位宽范围说明默认值
TIME32 bitT#-24d20h31m23s647ms ~ T#24d20h31m23s647ms时间长度,有符号T#0s
DATE32 bitD#1990-01-01 ~ D#2168-12-31日历日期(日精度)D#0001-01-01
TOD32 bitTOD#00:00:00.000 ~ TOD#23:59:59.999一天内时刻(毫秒精度)TOD#00:00:00.000
DT64 bit日期 + 时刻组合完整时间戳DT#0001-01-01-00:00:00.000

TIME 在定时器功能块(TON / TOF / TP)中作为周期和延时参数使用。DATETOD 用于排产、日志和定时任务。

VAR
tCycleTime : TIME := T#100ms;
tTimeout : TIME := T#5s30ms;
dToday : DATE := D#2026-07-26;
tNow : TOD := TOD#14:30:00.500;
dtTimestamp : DT;
END_VAR

// TIME 字面量格式
// T#100ms — 100 毫秒
// T#2s500ms — 2 秒 500 毫秒
// T#1m30s — 1 分 30 秒
// T#1h15m20s300ms — 1 小时 15 分 20 秒 300 毫秒
// T#-5s — 负值时间(用于倒计时)

字符与字符串类型

类型存储方式最大长度默认值说明
CHAR单字节1''ASCII 单字符
STRING[N]字节数组N(缺省 80)''ASCII 字符串,长度可变
WCHAR双字节1''Unicode 宽字符(UTF-16)
WSTRING[N]双字节数组N(缺省 80)''Unicode 字符串(UTF-16)

STRING 是工业自动化中最常用的文本类型,用于配方名、报警文本、产品批号和通讯报文。WSTRING 适用于国际化场景(中文、日文、西里尔字母等)。

VAR
cLetter : CHAR := 'A';
sProductName : STRING[32] := 'Conveyor-01';
sMessage : STRING := '默认长度 80'; // 不指定长度则默认 80
wsChinese : WSTRING[16] := "达拉控制";
wcSymbol : WCHAR := "Ω";
END_VAR

STRING 内部存储格式

STRING[N] 实际占用 N + 1 个字节:第一个字节存放当前有效长度(0 ~ N),后续 N 个字节存放字符数据。WSTRING[N] 类似,占用 (N + 1) * 2 个字节。

// STRING[10] "Hello" 的内存布局
// [0] = 5 (当前长度)
// [1] = 'H' [2] = 'e'
// [3] = 'l' [4] = 'l'
// [5] = 'o' [6] ~ [10] = 未定义

数组类型

声明语法

数组使用 ARRAY[ 下限..上限 ] OF 类型 声明,下标必须是整数常量表达式:

VAR
// 一维数组
aiValues : ARRAY[0..9] OF INT;
arData : ARRAY[1..100] OF REAL;

// 二维数组
aMatrix : ARRAY[0..3, 0..5] OF BOOL;
aGrid : ARRAY[1..10, 1..10] OF DINT;

// 三维数组
aCube : ARRAY[0..1, 0..2, 0..3] OF BYTE;

// 数组元素可以是 STRUCT
aMotors : ARRAY[1..6] OF AxisData;
END_VAR

下标范围

IEC 61131-3 允许下标从任意整数开始,不强制从 0 或 1。但建议遵循以下约定:

行业常用下标起点原因
过程控制1符合"第一个元件"的工程习惯
运动控制0 或 1取决于轴编号规则
通用0与 C 语言数组下标一致

数组初始化

VAR
// 全初始化
aiAllZero : ARRAY[0..4] OF INT := [0, 0, 0, 0, 0];

// 部分初始化(未指定元素保持默认值)
aiPartial : ARRAY[0..4] OF INT := [100, 200];

// 指定下标初始化(IEC 61131-3 Ed3)
aiExplicit : ARRAY[0..4] OF INT := [0 := 10, 2 := 30, 4 := 50];

// 二维数组初始化
aMatrixInit : ARRAY[0..1, 0..2] OF INT := [
[1, 2, 3],
[4, 5, 6]
];
END_VAR

数组访问

// 一维
aiValues[0] := 42;
arData[iIndex + 1] := rSensorValue;

// 多维
aMatrix[2, 3] := TRUE;
aGrid[row, col] := diEncoderPos;

// 下标范围检查(运行时可配置)
// Darra PLC 默认开启数组越界检查
// 越界时触发运行时错误,停止当前块执行

结构体类型

声明

STRUCT ... END_STRUCT 用于定义复合数据结构,将多个不同类型的变量组合为一个整体:

TYPE MotorData :
STRUCT
bRunning : BOOL;
rCurrent : REAL;
rSpeed : REAL;
rTemperature : REAL;
diPosition : DINT;
sName : STRING[32];
tRunTime : TIME;
END_STRUCT;
END_TYPE

嵌套结构

结构体可以嵌套,形成层次化的数据模型:

TYPE EncoderData :
STRUCT
diCounts : DINT;
rResolution : REAL; // 脉冲/圈
bFault : BOOL;
END_STRUCT;
END_TYPE

TYPE DriveData :
STRUCT
stMotor : MotorData;
stEncoder : EncoderData;
rTorque : REAL;
bBrakeEngaged : BOOL;
tCycleTime : TIME;
END_STRUCT;
END_TYPE

变量声明与访问

VAR
myMotor : MotorData;
driveA : DriveData;
aDrives : ARRAY[1..4] OF DriveData;
END_VAR

// 结构体成员访问
myMotor.bRunning := TRUE;
myMotor.rSpeed := 1500.0;
myMotor.sName := '主轴电机';
myMotor.diPosition := diEncoderFeedback;

// 嵌套访问
driveA.stMotor.rTemperature := 65.2;
driveA.stEncoder.bFault := FALSE;

// 数组中的结构体
aDrives[1].stMotor.rCurrent := 12.5;
aDrives[2].bBrakeEngaged := TRUE;

结构体初始化

VAR
// 按成员顺序初始化
stInitMotor : MotorData := [
bRunning := FALSE,
rCurrent := 0.0,
rSpeed := 0.0,
rTemperature := 25.0,
diPosition := 0,
sName := '未命名',
tRunTime := T#0s
];

// 部分初始化(未指定字段保持默认)
stPartial : MotorData := [
rTemperature := 25.0,
sName := 'Default'
];
END_VAR

枚举类型

声明

枚举将一组有意义的命名常量组织为独立的数据类型,提高代码可读性:

TYPE AxisState :
(
AXIS_IDLE := 0,
AXIS_HOMING := 1,
AXIS_RUNNING := 2,
AXIS_ERROR := 3,
AXIS_STOPPED := 4
);
END_TYPE

TYPE MachineMode :
(
MODE_MANUAL := 1,
MODE_SEMI_AUTO := 2,
MODE_AUTO := 3
);
END_TYPE

变量声明与赋值

VAR
eAxisState : AxisState;
eMode : MachineMode;
END_VAR

// 赋值
eAxisState := AxisState.AXIS_IDLE;
eMode := MachineMode.MODE_AUTO;

// CASE 语句配合枚举
CASE eAxisState OF
AxisState.AXIS_IDLE:
bReady := TRUE;
AxisState.AXIS_HOMING:
bHomingActive := TRUE;
AxisState.AXIS_RUNNING:
bMotionEnabled := TRUE;
AxisState.AXIS_ERROR:
bFault := TRUE;
ResetAxis();
AxisState.AXIS_STOPPED:
bStopped := TRUE;
END_CASE;

类型安全

Darra SCL 的枚举是类型安全的:

  • 不同枚举类型之间不能隐式比较或赋值
  • 枚举值可通过显式转换读取底层整数值
  • 编译器检查 CASE 语句是否覆盖全部枚举分支
// 编译错误:不同类型不能比较
// IF eAxisState = eMode THEN // ✗ 禁止

// 正确做法:显式转换
IF USINT(eAxisState) = USINT(eMode) THEN
// 需要显式类型转换
END_IF;

用户自定义类型(UDT)

TYPE ... END_TYPE 块用于定义全局可用的自定义类型,包括结构体、枚举和类型别名:

// 类型别名
TYPE MyReal : REAL; END_TYPE
TYPE CounterRange : DINT; END_TYPE

// 结构体 UDT
TYPE ConveyorData :
STRUCT
bRunning : BOOL;
rSpeed : REAL;
diDistance : DINT;
sProductID : STRING[16];
tProcessTime : TIME;
END_STRUCT;
END_TYPE

// 枚举 UDT
TYPE ProductType :
(
PRODUCT_A := 10,
PRODUCT_B := 20,
PRODUCT_C := 30
);
END_TYPE

// 数组 UDT
TYPE AxisArray : ARRAY[1..8] OF REAL; END_TYPE

UDT 一旦定义,可以在项目的所有 POU(PROGRAM、FUNCTION、FUNCTION_BLOCK)中复用:

FUNCTION_BLOCK FB_ConveyorControl
VAR_INPUT
stConfig : ConveyorData;
eProduct : ProductType;
END_VAR
VAR_OUTPUT
rSpeedCmd : REAL;
END_VAR
VAR
aTemperatures : AxisArray;
END_VAR

// 使用 UDT
rSpeedCmd := stConfig.rSpeed;
aTemperatures[1] := 45.2;

END_FUNCTION_BLOCK

类型转换

SCL 是强类型语言,不同数据类型之间的运算必须经过显式或隐式转换。

隐式转换

以下情况编译器会自动插入转换代码,无需显式写转换函数:

源类型目标类型条件风险
SINTINT / DINT / LINT更宽有符号安全无损
USINTUINT / UDINT / ULINT更宽无符号安全无损
INTDINT / LINT更宽有符号安全无损
UINTUDINT / ULINT更宽无符号安全无损
USINT / UINTINT / DINT / LINT同宽或更宽有符号安全无损
INTREAL浮点安全可能精度损失(INTREAL 无损,DINTREAL 大值有损)
DINTLREAL64 位浮点安全无损
REALLREAL更宽浮点安全无损
BYTEWORD / DWORD更宽位串安全高位填 0
WORDDWORD更宽位串安全高位填 0
VAR
siSmall : SINT := 100;
iMedium : INT;
diLarge : DINT;
rFloat : REAL;
lrDouble : LREAL;
END_VAR

// 以下为隐式转换(自动执行)
iMedium := siSmall; // SINT → INT, 安全
diLarge := iMedium; // INT → DINT, 安全
rFloat := diLarge; // DINT → REAL, 大值可能精度损失
lrDouble := rFloat; // REAL → LREAL, 安全

显式转换

所有不在隐式转换表中的情况必须使用显式转换函数。转换函数命名格式为 <目标类型>_TO_<源类型>

转换函数源类型目标类型说明
INT_TO_REALINT / DINT / SINTREAL有符号整数 → 浮点
DINT_TO_REALDINTREAL32 位有符号整数 → 浮点
REAL_TO_INTREALINT浮点 → 整数(截断舍入)
REAL_TO_DINTREALDINT浮点 → 32 位整数
REAL_TO_LREALREALLREAL单精度 → 双精度
LREAL_TO_REALLREALREAL双精度 → 单精度(可能精度损失)
DINT_TO_TIMEDINTTIME毫秒数 → 时间
TIME_TO_DINTTIMEDINT时间 → 毫秒数
DINT_TO_TODDINTTOD午夜后的毫秒数 → 时刻
TOD_TO_DINTTODDINT时刻 → 午夜后的毫秒数
DATE_TO_DINTDATEDINT日期 → 天数偏移
DINT_TO_DATEDINTDATE天数偏移 → 日期
WORD_TO_BYTEWORDBYTE位串截断(低 8 位)
DWORD_TO_WORDDWORDWORD位串截断(低 16 位)
INT_TO_BOOLINTBOOL非零为 TRUE
BOOL_TO_INTBOOLINTTRUE → 1, FALSE → 0
VAR
rValue : REAL := 123.456;
iInteger : INT;
diBigInt : DINT;
tDuration : TIME;
dwBits : DWORD := 16#AABBCCDD;
wLowWord : WORD;
END_VAR

// 显式转换
iInteger := REAL_TO_INT(rValue); // 123(截断)
diBigInt := REAL_TO_DINT(rValue); // 123(截断)
tDuration := DINT_TO_TIME(5000); // T#5s
wLowWord := DWORD_TO_WORD(dwBits); // 16#CCDD

浮点取整方式

REAL_TO_INT / REAL_TO_DINT / LREAL_TO_DINT 等浮点→整数的转换使用**截断(truncation toward zero)**方式,即直接丢弃小数部分:

输入转换结果
REAL_TO_INT(3.99)3
REAL_TO_INT(-3.99)-3
REAL_TO_INT(0.001)0

如需四舍五入,使用标准函数 ROUND

iRounded := REAL_TO_INT(ROUND(3.99));   // 4
iRounded := REAL_TO_INT(ROUND(-3.99)); // -4
iRounded := REAL_TO_INT(ROUND(0.001)); // 0

REF_TO 和 POINTER TO 类型

REF_TO(引用)

REF_TO 是 IEC 61131-3 Third Edition 引入的安全引用类型,提供指针能力但具有编译期类型检查和空引用保护:

TYPE
// 引用基本类型
RefInt : REF_TO INT;

// 引用结构体
RefMotor : REF_TO MotorData;
END_TYPE

FUNCTION_BLOCK FB_ReferenceExample
VAR_INPUT
refValue : REF_TO INT;
END_VAR
VAR_OUTPUT
iResult : INT;
END_VAR
VAR
refMotor : REF_TO MotorData;
myMotor : MotorData;
END_VAR

// 引用赋值
refValue^ := 42; // 通过引用写入值
iResult := refValue^; // 通过引用读取值

// 引用结构体
refMotor := REF(myMotor); // 获取引用
refMotor^.rSpeed := 1500.0; // 通过引用修改成员

END_FUNCTION_BLOCK

POINTER TO(指针)

POINTER TO 是传统指针类型,与 REF_TO 相比缺少空值保护,但兼容 IEC 61131-3 早期版本:

TYPE
pInt : POINTER TO INT;
pMotor : POINTER TO MotorData;
END_TYPE

FUNCTION_BLOCK FB_PointerExample
VAR_INPUT
pData : POINTER TO INT;
iLength : INT;
END_VAR
VAR_OUTPUT
iSum : INT;
END_VAR
VAR
pCurrent : POINTER TO INT;
iIdx : INT;
END_VAR

pCurrent := pData;
iSum := 0;

FOR iIdx := 0 TO iLength - 1 DO
iSum := iSum + pCurrent^; // 解引用
pCurrent := pCurrent + SIZEOF(INT); // 地址偏移
END_FOR;

END_FUNCTION_BLOCK

REF_TO vs POINTER TO

特性REF_TOPOINTER TO
空引用检测运行时检测,解引用空引用触发错误无自动检测
类型安全编译期类型检查编译期类型检查
算术运算不允许指针算术支持地址偏移
与 ADR 配合使用 REF() 获取引用使用 ADR() 获取地址
兼容标准IEC 61131-3 Ed3+IEC 61131-3 Ed1+
推荐场景新项目、安全关键代码内存操作、兼容旧代码

ADR 和 REF 运算符

VAR
iValue : INT := 100;
pPointer : POINTER TO INT;
refInt : REF_TO INT;
diAddr : DINT;
END_VAR

// ADR — 获取变量地址(返回 DINT 或指针)
pPointer := ADR(iValue);
pPointer^ := 200;

// REF — 获取引用
refInt := REF(iValue);
refInt^ := 300;

// SIZEOF — 获取变量或类型占用字节数
diAddr := SIZEOF(iValue); // 2
diAddr := SIZEOF(MotorData); // 结构体总字节数

空引用安全模式

Darra SCL 默认启用 REF_TO 空引用运行时检测:

FUNCTION_BLOCK FB_SafeRefAccess
VAR_INPUT
refData : REF_TO MotorData;
END_VAR
VAR_OUTPUT
bValid : BOOL;
rSpeed : REAL;
END_VAR

// 解引用前检查(Darra 扩展)
IF refData = NULL THEN
bValid := FALSE;
rSpeed := 0.0;
ELSE
bValid := TRUE;
rSpeed := refData^.rSpeed;
END_IF;

END_FUNCTION_BLOCK

数据类型选用指南

按应用场景

场景推荐类型原因
位逻辑(启停、连锁)BOOL最小存储,逻辑运算效率最高
计数器(产品计数)UINT / UDINT无符号,无需负数处理
编码器位置DINT / LINT有符号,支持正反方向
模拟量采集INT / REAL匹配 AD 转换位宽,或标定后浮点
PID 计算REAL浮点精度足够,占用小
精密定位LREAL高精度坐标计算
定时器参数TIME直接用于 TON/TOF 功能块
报警文本STRING[128]长度可控,内存固定
国际化文本WSTRING[64]支持中文等非 ASCII 字符

按内存占用

// ❌ 低效:使用 DINT 存储开关量
VAR
diSwitch1 : DINT; // 32 bit 只用了 1 bit
diSwitch2 : DINT;
diSwitch3 : DINT; // 浪费 96 bit
END_VAR

// ✅ 高效:使用 BOOL 或位打包
VAR
bSwitch1 : BOOL;
bSwitch2 : BOOL;
bSwitch3 : BOOL; // 3 bit
END_VAR

// ✅ 更紧凑:使用 BYTE 位域(与 IO 模块交互)
VAR
xInputByte : BYTE; // 8 个数字量输入
END_VAR
// 访问: xInputByte.0 ~ xInputByte.7

默认值规则

SCL 变量在声明时若未显式初始化,则使用该类型的默认值:

  • 整数类型(INT / DINT / SINT 等):0
  • 浮点类型(REAL / LREAL):0.0
  • 位串类型(BYTE / WORD / DWORD):16#00 / 16#0000 / 16#00000000
  • 布尔类型(BOOL):FALSE
  • 时间类型(TIME / DATE / TOD / DT):T#0s / D#0001-01-01 / TOD#00:00:00.000
  • 字符串(STRING / WSTRING):空字符串 ''

相关文档