变量与数据类型
Darra PLC 变量系统遵循 IEC 61131-3 标准,提供类型安全的变量声明、多级作用域和丰富的数据类型。变量表由 PLCVariableTable 管理,支持符号名到内存地址的双向映射。
变量声明
声明语法
VAR // 局部变量
iCount : INT := 0;
rSpeed : REAL;
bReady : BOOL := FALSE;
END_VAR
VAR_INPUT // 输入参数 (功能块)
bEnable : BOOL;
END_VAR
VAR_OUTPUT // 输出参数 (功能块)
bDone : BOOL;
END_VAR
VAR_IN_OUT // 输入输出参数 (引用传递)
arBuffer : ARRAY[0..99] OF INT;
END_VAR
VAR_GLOBAL // 全局变量
giBatchCount : INT;
grTemperature : REAL;
END_VAR
VAR RETAIN // 掉电保持变量
riTotalParts : INT;
rrCalibOffset : REAL;
END_VAR
VAR CONSTANT // 常量
MAX_SPEED : REAL := 6000.0;
TIMEOUT : TIME := T#30s;
END_VAR
作用域
| 作用域 | 关键字 | 可见范围 | 生命周期 |
|---|---|---|---|
| 局部 | VAR | 当前 POU 内部 | 每次调用重新初始化 |
| 全局 | VAR_GLOBAL | 所有 POU | 程序运行期间 |
| 保持 | VAR RETAIN | 声明所在 POU | 跨停止/启动保持 |
| 常量 | VAR CONSTANT | 声明所在 POU | 编译时确定 |
Retain 变量
Retain 变量在 PLC 停止、重新启动后保持上次运行的值。适用于产量计数、累计运行时间、标定参数等。冷启动 (OB100) 时可选择是否重置 Retain 变量。
基本数据类型
位类型
| 类型 | 大小 | 范围 | 用途 |
|---|---|---|---|
BOOL | 1 位 | TRUE / FALSE | 开关量 |
BYTE | 8 位 | 0 ~ 255 | 位掩码 |
WORD | 16 位 | 0 ~ 65535 | 状态字 |
DWORD | 32 位 | 0 ~ 2^32-1 | 控制字 |
LWORD | 64 位 | 0 ~ 2^64-1 | 时间戳 |
整数类型
| 类型 | 大小 | 有符号 | 范围 |
|---|---|---|---|
SINT | 8 位 | 是 | -128 ~ 127 |
USINT | 8 位 | 否 | 0 ~ 255 |
INT | 16 位 | 是 | -32768 ~ 32767 |
UINT | 16 位 | 否 | 0 ~ 65535 |
DINT | 32 位 | 是 | ±2.1×10^9 |
UDINT | 32 位 | 否 | 0 ~ 4.2×10^9 |
LINT | 64 位 | 是 | ±9.2×10^18 |
ULINT | 64 位 | 否 | 0 ~ 1.8×10^19 |
浮点类型
| 类型 | 大小 | 精度 | 用途 |
|---|---|---|---|
REAL | 32 位 | ~7 位有效数字 | 一般模拟量 |
LREAL | 64 位 | ~15 位有效数字 | 高精度运算 (角度/位置) |
时间与字符串
| 类型 | 格式 | 示例 |
|---|---|---|
TIME | T#<值> | T#1s, T#500ms, T#2h30m |
DATE | D#<日期> | D#2026-04-07 |
TOD | TOD#<时间> | TOD#14:30:00 |
DT | DT#<日期时间> | DT#2026-04-07-14:30:00 |
STRING | 单引号 | 'Hello', 最长 255 字符 |
WSTRING | 双字节 | Unicode 字符串 |
数组
VAR
// 一维数组
aiSensors : ARRAY[0..7] OF INT;
// 多维数组
arGrid : ARRAY[0..3, 0..3] OF REAL;
// 初始化
aiPreset : ARRAY[0..4] OF INT := [10, 20, 30, 40, 50];
END_VAR
// 访问
aiSensors[3] := 100;
arGrid[1, 2] := 3.14;
结构体
TYPE ST_MotorStatus :
STRUCT
bOnline : BOOL;
bFault : BOOL;
iErrorCode : INT;
rActualSpeed : REAL;
rActualPos : LREAL;
tRunTime : TIME;
END_STRUCT;
END_TYPE
VAR
stMotor1 : ST_MotorStatus;
astMotors : ARRAY[0..5] OF ST_MotorStatus;
END_VAR
// 访问
stMotor1.rActualSpeed := 1500.0;
astMotors[2].bFault := TRUE;
枚举
TYPE E_MachineState :
(
IDLE := 0,
HOMING := 1,
RUNNING := 2,
PAUSED := 3,
STOPPING := 4,
ERROR := 99
);
END_TYPE
VAR
eState : E_MachineState := E_MachineState.IDLE;
END_VAR
IF eState = E_MachineState.RUNNING THEN
// ...
END_IF;
类型转换
// 显式类型转换函数
rValue := INT_TO_REAL(iCount);
iValue := REAL_TO_INT(rTemp); // 四舍五入
diValue := INT_TO_DINT(iSmall);
sValue := INT_TO_STRING(iCount);
tValue := DINT_TO_TIME(diMs); // 毫秒转 TIME
类型安全
Darra PLC 编译器强制类型检查。不同类型之间赋值必须使用显式转换函数,隐式转换将报编译错误。这避免了因精度丢失或溢出导致的运行时故障。
变量作用域完整列表
IEC 61131-3 完整作用域关键字:
FUNCTION_BLOCK FB_Example
VAR_INPUT // 输入参数, 按值传递 (简单类型) 或按引用 (复杂类型)
bStart : BOOL;
rSpeed : REAL;
END_VAR
VAR_OUTPUT // 输出参数
bDone : BOOL;
rActual : REAL;
END_VAR
VAR_IN_OUT // 输入输出, 总是按引用传递, 修改直接影响调用方
stContext : ST_Context;
END_VAR
VAR // FB 实例持有的内部状态, 实例间独立
tonDelay : TON;
iState : INT;
END_VAR
VAR_TEMP // 临时变量, 每次调用重新初始化 (栈分配)
iLocal : INT;
END_VAR
VAR_STAT // 静态变量, 同一 FB 所有实例共享 (类级共享)
iGlobalCounter : INT;
END_VAR
VAR_INST // 实例内存变量 (=VAR, 在 FUNCTION 里等价)
iRetry : INT;
END_VAR
VAR CONSTANT // 常量, 编译时确定, 不可修改
MAX_SPEED : REAL := 6000.0;
TIMEOUT : TIME := T#30s;
END_VAR
VAR RETAIN // 断电保持, 掉电后 PV 保留
riTotalParts : DINT;
END_VAR
VAR PERSISTENT // 持久化 (比 RETAIN 更强, 冷启动也不清零)
riCalibOffset : LREAL;
END_VAR
END_FUNCTION_BLOCK
作用域详细对比
| 关键字 | 作用域 | 生命周期 | 初始化时机 | 典型用途 |
|---|---|---|---|---|
VAR_INPUT | POU 内只读 | 调用期间 | 每次调用赋值 | 函数参数 |
VAR_OUTPUT | POU 内可写 | 跨调用保持 (FB) | 初次调用初始化 | 返回结果 |
VAR_IN_OUT | POU 内可写 | 引用调用方 | 永不复制 | 大数据结构传递 |
VAR | POU 内部 | FB 实例生命周期 | 实例创建时 | 内部状态 |
VAR_TEMP | POU 内部 | 单次调用 | 每次调用重置 | 局部临时 |
VAR_STAT | POU 内部 | 程序运行期 | 首次访问 | 类级计数 |
VAR_GLOBAL | 全项目 | 程序运行期 | OB100 初始化 | 全局共享 |
VAR CONSTANT | 声明处 | 编译时 | 编译期求值 | 魔数替代 |
VAR RETAIN | 声明处 | 断电保持 | 冷启动可选清零 | 产量累计 |
VAR PERSISTENT | 声明处 | 永久保持 | 仅手动清零 | 标定参数 |
VAR_IN_OUT 零拷贝
VAR_IN_OUT 用引用传参, 适合传递大数组或结构体。相比 VAR_INPUT (值传递) 零开销, 但修改会反映到调用方。需要注意所有权。
RETAIN 与 PERSISTENT
两者都是断电保持, 但清零策略不同:
| 特性 | VAR RETAIN | VAR PERSISTENT |
|---|---|---|
| 热重启保持 | ✓ | ✓ |
| 冷启动保持 | 可配置 (默认清零) | ✓ 始终保持 |
| 下载新程序保持 | ✗ | ✓ |
| 手动复位命令 | 可清零 | 仅专用命令 |
| 物理存储 | 非易失内存 (NVRAM) | 非易失内存 (NVRAM) + 备份 |
| 典型用途 | 产量 / 运行小时 | 标定系数 / 工具 TCP / 零点偏移 |
何时用哪个
- 产量计数, 累计运行时: 每次开机可清零 →
VAR RETAIN - 机械标定, 软限位, 螺距补偿: 下载新程序也不能丢 →
VAR PERSISTENT - HMI 临时数据: 不需要掉电保持 → 普通
VAR
初始值
所有变量都可以指定初始值:
VAR
// 简单类型初始化
bReady : BOOL := FALSE;
iCount : INT := 100;
rSpeed : REAL := 1500.0;
// 字符串初始化
sName : STRING[32] := 'Darra PLC';
// 数组初始化
aiPreset : ARRAY[0..4] OF INT := [10, 20, 30, 40, 50];
arZeros : ARRAY[0..99] OF REAL := [100(0.0)]; // 100 个 0.0
// 结构体初始化
stMotor : ST_Motor := (sName := 'M1', iId := 1, rMaxSpeed := 3000.0);
// 枚举初始化
eState : E_State := E_State.IDLE;
// 时间初始化
tDelay : TIME := T#500ms;
dToday : DATE := D#2026-04-18;
END_VAR
初始值执行时机
- 冷启动 (OB100): 所有变量重置为初始值
- 热重启: RETAIN / PERSISTENT 保持上次值, 其他重置
- 在线重新下载: 由用户选择"保留数据"或"清零"
地址映射 (AT 语句)
用 AT 显式绑定变量到内存地址:
VAR
// I 区 — 物理输入
bStartBtn AT %IX0.0 : BOOL; // DI 通道 0
bStopBtn AT %IX0.1 : BOOL; // DI 通道 1
wEncoder AT %IW10 : WORD; // AI 字 (encoder 值)
diPosition AT %ID20 : DINT; // AI 双字 (位置)
rTempSensor AT %IW100 : REAL; // AI 浮点 (4字节, 别名 %ID100)
// Q 区 — 物理输出
bMotor AT %QX0.0 : BOOL; // DO 通道 0
bValve AT %QX0.1 : BOOL; // DO 通道 1
wAnalogOut AT %QW10 : WORD; // AO 字
rSetPoint AT %QW200 : REAL; // AO 浮点
// M 区 — HMI 共享
bHMI_Start AT %MX0.0 : BOOL; // HMI 启动按钮
iRecipeNo AT %MW100 : INT; // HMI 配方选择
rSetTemp AT %MD200 : REAL; // HMI 温度设定
sMessage AT %MB300 : STRING[64]; // HMI 消息
// D 区 — 数据块
stBatch AT %D10.0 : ST_BatchRecord; // DB10 存批次记录
// S 区 — 系统变量 (只读)
tScanTime AT %SW0 : TIME; // 当前扫描时间
iCpuLoad AT %SW2 : INT; // CPU 负载 0-100%
END_VAR
地址语法
%<区域><大小><偏移>[.<位>]
区域: I, Q, M, D, S
大小: X (位), B (字节), W (字, 2B), D (双字, 4B), L (长字, 8B)
偏移: 字节偏移 (0 开始)
位: 0-7 (仅 X 格式需要)
示例:
%IX0.0 → I 区第 0 字节的 bit 0
%IW10 → I 区第 10~11 字节组成的字
%MD200 → M 区第 200~203 字节组成的双字
%D10.DBW0 → 数据块 DB10 的第 0 字节起的字
地址重叠
不同大小标识可能访问同一内存, 例如:
| 地址 | 物理位置 |
|---|---|
%MB0 | M 区字节 0 |
%MB1 | M 区字节 1 |
%MW0 | M 区字节 0~1 (= MB0 高 + MB1 低) |
%MD0 | M 区字节 0~3 |
%MX0.0 | M 区字节 0 的 bit 0 |
如果同时声明 iCount AT %MW0 和 bFlag AT %MX0.0, 两个变量占用同一块内存, 容易互相覆盖。编译器的 IOConflictChecker 会警告此类重叠。

变量表 (PLCVariableTable)
变量表是 PLC 程序中所有变量的集中注册中心:
| 功能 | 说明 |
|---|---|
| 符号解析 | 变量名 ↔ 内存地址双向查找 |
| 类型检查 | 编译时和运行时类型验证 |
| 地址分配 | 自动为变量分配 M/V 区地址 |
| 监视支持 | 在线调试时读取/强制变量值 |
| 导入导出 | CSV/Excel 批量编辑变量 |
| 交叉引用 | 定位每个变量的所有读写位置 |
变量表界面提供以下列:

| 列名 | 说明 |
|---|---|
| 名称 | 变量符号名 |
| 类型 | BOOL / INT / REAL / 自定义 |
| 地址 | %Mxxx 或自动分配 |
| 初始值 | 启动时的初始化值 |
| 属性 | RETAIN / PERSISTENT / CONSTANT |
| 注释 | 中文描述 |
| 范围 | 最小值 / 最大值 (用于校验) |
| 单位 | 工程单位 (℃ / mm / rpm) |
命名约定 (匈牙利前缀)
推荐使用匈牙利前缀提高代码可读性:
| 前缀 | 类型 | 示例 |
|---|---|---|
b | BOOL | bStart, bReady |
by | BYTE | byStatus |
si | SINT | siTemp |
i | INT | iCount |
di | DINT | diPosition |
li | LINT | liTimestamp |
ui | UINT | uiSpeed |
udi | UDINT | udiEncoder |
w | WORD | wStatus |
dw | DWORD | dwControl |
r | REAL | rTemp |
lr | LREAL | lrAngle |
s | STRING | sName |
t | TIME | tDelay |
a / ar | ARRAY | aiData, arMatrix |
st | STRUCT | stMotor |
e | ENUM | eState |
fb | FB 实例 | fbPID |
p | 指针 | pBuffer |
全局变量加 g 前缀 (giCounter), RETAIN 加 r 前缀 (riTotal)。
排错表
| 问题 | 原因 | 解决 |
|---|---|---|
| 变量未声明错误 | 忘了在 VAR 区声明 | 补充声明 |
| 地址冲突警告 | %MW0 与 %MB0 重叠 | 重新分配地址 |
| 初始值未生效 | 热重启未冷启动 | 切冷启动或手动复位 |
| RETAIN 丢失 | NVRAM 电池耗尽 | 检查硬件, 换备份电池 |
| VAR_IN_OUT 改到源 | 传参按引用的副作用 | 改用 VAR_INPUT 按值传递 |
| 枚举值比较错误 | 忘加类型前缀 | 用 E_State.IDLE 而非 IDLE |