SCL 变量声明
SCL 的变量声明遵循 IEC 61131-3 标准,通过声明块关键字控制变量的作用域、生命周期、持久性和可见性。正确的变量声明是编写可靠、可维护 PLC 程序的基础。
声明块总览
| 声明块 | 作用域 | 生命周期 | 持久性 | 可见性 | 典型用途 |
|---|---|---|---|---|---|
VAR | POU 内部 | FB 实例生命周期 | 不保持 | POU 内读写 | 内部状态变量 |
VAR_INPUT | POU 只读 | 每次调用赋值 | — | 外部传入 | 功能块输入参数 |
VAR_OUTPUT | POU 可写 | 跨调用保持 (FB) | 不保持 | 外部读取 | 功能块输出结果 |
VAR_IN_OUT | POU 可写 | 引用调用方 | 跟随调用方 | 引用传递 | 大数据结构引用 |
VAR_TEMP | POU 内部 | 单次调用 | 不保持 | POU 内读写 | 临时计算变量 |
VAR_STATIC | POU 内部 | 程序运行期 | 保持 | POU 内读写 | 跨调用计数 |
VAR_CONSTANT | 全局可见 | 编译期确定 | — | 全项目只读 | 常量定义 |
VAR_GLOBAL | 全局共享 | 程序运行期 | 可配置 | 全项目读写 | 全局数据交换 |
VAR_ACCESS | 外部接口 | 程序运行期 | 不保持 | OPC UA / 外部 | 对外暴露变量 |
VAR_CONFIG | 实例配置 | 编译期确定 | — | 实例化时赋值 | 参数化配置 |
VAR — 内部变量
VAR 块声明 POU 内部的状态变量。对于功能块,变量值在两次调用之间保持;对于函数,不跨调用保持。
FUNCTION_BLOCK FB_MotorControl
VAR
tonRamp : TON; // 内部定时器
rtStart : R_TRIG; // 边沿检测
iState : INT := 0; // 状态机状态
rRampSpeed : REAL := 0.0; // 速度斜坡值
END_VAR
VAR_INPUT — 输入参数
声明由调用方传入的只读参数。简单类型按值传递,复杂类型按引用传递。
FUNCTION_BLOCK FB_PID
VAR_INPUT
bEnable : BOOL;
rSetpoint : REAL;
rFeedback : REAL;
rKp : REAL := 1.0;
stConfig : ST_PIDConfig; // 结构体按引用传递
END_VAR
// 调用语法
fbPID(bEnable := TRUE, rSetpoint := 1500.0, rFeedback := rActSpeed);
VAR_OUTPUT — 输出参数
声明由 POU 返回给调用方的输出结果。FB 实例的输出值在两次调用之间保持。
FUNCTION_BLOCK FB_PID
VAR_OUTPUT
bInRange : BOOL; // 到达设定范围
rOutput : REAL; // PID 输出值
rError : REAL; // 当前误差
END_VAR
VAR_IN_OUT — 输入输出参数 (引用传递)
始终按引用传递,零拷贝开销。POU 内对该参数的修改直接反映到调用方的原始变量。
FUNCTION_BLOCK FB_BufferProcess
VAR_IN_OUT
stBuffer : ST_BufferData; // 引用外部结构体
arSamples : ARRAY[0..1023] OF INT; // 引用外部数组
END_VAR
VAR_IN_OUT 传递的是引用而非副本。希望保护原始数据不被修改时,应使用 VAR_INPUT(复杂类型仍按引用传递,但编译器阻止写入)。
VAR_TEMP — 临时变量
仅当前调用周期内有效,每次调用重新初始化(栈分配)。使用前必须显式赋值,否则为不确定值。
FUNCTION_BLOCK FB_MatrixCalc
VAR_TEMP
i, j : INT; // 循环索引
rSum : REAL; // 中间累加
bFound : BOOL; // 查找标记
END_VAR
VAR_STATIC — 静态变量
跨调用保持但不持久化(掉电丢失)的变量,所有 FB 实例共享。
FUNCTION_BLOCK FB_ComponentManager
VAR_STATIC
siInstanceCount : SINT := 0; // 所有实例共享计数
END_VAR
VAR_CONSTANT — 常量
编译期确定、不可修改的命名常量,运行时零开销。
VAR CONSTANT
MAX_MOTOR_SPEED : REAL := 6000.0;
OVER_TEMP_THRESHOLD : REAL := 85.0;
WATCHDOG_TIMEOUT : TIME := T#500ms;
PI : REAL := 3.14159265;
DEG_TO_RAD : REAL := 0.01745329;
END_VAR
常量全大写、下划线分隔。不要用魔法数字散落在代码中,统一在 POU 顶部 VAR CONSTANT 集中管理。
VAR_GLOBAL — 全局变量
全项目可见的全局变量,在全局变量表或全局 POU 中定义。建议加 g 前缀。
VAR_GLOBAL
giBatchCount : DINT := 0;
gbMachineReady : BOOL := FALSE;
gbEmergencyStop : BOOL := TRUE;
grTargetTemp : REAL := 120.0;
gstProduction : ST_ProductionData;
END_VAR
在 POU 中通过 VAR_EXTERNAL 引用全局变量:
FUNCTION_BLOCK FB_Station1
VAR_EXTERNAL
gbMachineReady : BOOL;
gbEmergencyStop : BOOL;
giBatchCount : DINT;
END_VAR
VAR_ACCESS — 访问变量
对外暴露的变量,用于 OPC UA、DarraLink 远程访问或 HMI 通讯。
VAR_ACCESS
Machine_Status : BOOL := gbMachineReady;
Batch_Count : DINT := giBatchCount;
Temperature_Actual : REAL := grActualTemp;
END_VAR
VAR_CONFIG — 配置变量
在实例化时覆盖 FB 内部 VAR 的初始值,为不同实例配置不同参数。
VAR
fbConveyor1 : FB_Conveyor;
fbConveyor2 : FB_Conveyor;
END_VAR
VAR_CONFIG
fbConveyor1.iMaxSpeed := 3000;
fbConveyor2.iMaxSpeed := 1500;
fbConveyor1.tAccelTime := T#500ms;
fbConveyor2.tAccelTime := T#1s;
END_VAR
命名约定 (匈牙利前缀)
| 前缀 | 数据类型 | 示例 |
|---|---|---|
b | BOOL | bStart, bReady |
by | BYTE | byStatus |
w | WORD | wControl |
dw | DWORD | dwErrorCode |
si | SINT | siLevel |
i | INT | iCount, iIndex |
di | DINT | diPosition |
li | LINT | liUptime |
ui | UINT | uiVoltage |
udi | UDINT | udiTotalPulses |
r | REAL | rTemperature |
lr | LREAL | lrAngle |
s | STRING | sName |
ws | WSTRING | wsDisplayText |
t | TIME | tDelay |
a / ar | ARRAY | aiBuffer, arMatrix |
st | STRUCT | stMotorData |
e | 枚举 | eState, eMode |
fb | FB 实例 | fbPID, fbAxis |
p | POINTER TO | pBuffer |
rf | REF_TO | rfData |
前缀组合规则
| 修饰 | 前缀 | 示例 |
|---|---|---|
| 全局变量 | g + 类型前缀 | giBatchCount, grTemperature |
| RETAIN 变量 | r + 类型前缀 | riTotalParts, rrCalibOffset |
| 常量 | 全大写 + 下划线 | MAX_SPEED, TIMEOUT_MS |
VAR
iIndex : INT; // 局部
rTemperature : REAL;
bFault : BOOL;
fbPID : FB_PIDController; // FB 实例
END_VAR
VAR RETAIN
riTotalCount : DINT; // RETAIN 前缀 r
rrCalibOffset : LREAL;
END_VAR
初始值
默认初始化
| 数据类型 | 默认初始值 |
|---|---|
BOOL | FALSE |
BYTE, WORD, DWORD, LWORD | 0 |
SINT, INT, DINT, LINT | 0 |
USINT, UINT, UDINT, ULINT | 0 |
REAL, LREAL | 0.0 |
TIME | T#0ms |
STRING | '' (空字符串) |
ARRAY | 各元素按类型默认值 |
STRUCT | 各成员按类型默认值 |
| 枚举 | 枚举的第一个值 |
REF_TO, POINTER TO | 0 (空指针) |
显式初始值
VAR
bReady : BOOL := TRUE;
iCounter : INT := 100;
rTemperature : REAL := 25.0;
lrAngle : LREAL := 3.14159265358979;
tCycleTime : TIME := T#100ms;
sDeviceName : STRING[32] := 'Darra PLC';
// 数组初始化
aiPreset : ARRAY[0..4] OF INT := [10, 20, 30, 40, 50];
arZeros : ARRAY[0..99] OF REAL := [100(0.0)];
// 结构体初始化
stMotor : ST_MotorData := (
sName := 'M1', iAxisCount := 6, rMaxSpeed := 3000.0
);
eState : E_MachineState := IDLE;
END_VAR
- 冷启动 (OB100):所有变量重置为声明时的初始值
- 暖启动/热重启:
VAR RETAIN和VAR PERSISTENT保持上次值,其余重置 - 在线下载:由用户选择"保留数据"或"使用新初始值"
RETAIN 与 NON_RETAIN
VAR RETAIN — 掉电保持
VAR RETAIN 变量在 PLC 断电、停止、重启后保持最后的值。
VAR RETAIN
riTotalParts : DINT := 0; // 总产量
riGoodParts : DINT := 0; // 合格品
riRejectParts : DINT := 0; // 不合格品
rrCalibOffset : LREAL := 0.0; // 标定偏移
tTotalRunTime : TIME := T#0ms; // 累计运行时间
END_VAR
VAR RETAIN 中的 NON_RETAIN
在 VAR RETAIN 块中,可用 NON_RETAIN 标记个别变量不保持。
VAR RETAIN
riTotalCount : DINT; // 保持
VAR NON_RETAIN
iTempCounter : INT; // 不保持(即使在 RETAIN 块中)
rScratchPad : REAL;
END_VAR
rrCalibOffset : LREAL; // 保持
END_VAR
RETAIN 与 PERSISTENT 对比
| 特性 | VAR RETAIN | VAR PERSISTENT |
|---|---|---|
| 掉电保持 | ✓ | ✓ |
| 热重启保持 | ✓ | ✓ |
| 冷启动保持 | 可配置(默认清零) | ✓ 始终保持 |
| 程序下载后保持 | ✗ | ✓ |
| 物理存储 | NVRAM / UPS 缓存 | NVRAM + 冗余备份 |
| 典型用途 | 产量计数、运行时间 | 标定系数、零点偏移 |
// 每次冷启动可清零 → RETAIN
VAR RETAIN
riShiftCount : DINT; // 班次产量
END_VAR
// 下载新程序也不能丢 → PERSISTENT
VAR PERSISTENT
rrToolCalib : LREAL; // 工具标定
rrZeroOffset : LREAL; // 零点偏移
END_VAR
AT 声明 — 直接地址映射
AT 关键字将变量直接绑定到指定的内存地址,用于访问物理 I/O、系统变量或 HMI 共享内存。
地址语法
%<区域><大小><偏移>[.<位>]
| 组成部分 | 说明 | 值范围 |
|---|---|---|
% | 地址标识前缀 | 必选 |
| 区域 | I=物理输入, Q=物理输出, M=内存, D=数据块, S=系统 | I / Q / M / D / S |
| 大小 | X=位, B=字节, W=字(2B), D=双字(4B), L=长字(8B) | X / B / W / D / L |
| 偏移 | 字节起始偏移 | 0 ~ 65535 |
| 位 | 仅 X 格式需要 | 0 ~ 7 |
%IX0.0 // I 区 第0字节 bit0
%IW10 // I 区 第10~11字节 (WORD)
%ID20 // I 区 第20~23字节 (DINT)
%QX0.0 // Q 区 第0字节 bit0
%QW10 // Q 区 第10~11字节
%MD200 // M 区 第200~203字节
%D10.DBW0 // DB10 第0~1字节
物理 I/O 映射
VAR
// ===== 数字量输入 =====
bEmergencyStop AT %IX0.0 : BOOL; // 急停按钮
bStartBtn AT %IX0.1 : BOOL; // 启动按钮
bStopBtn AT %IX0.2 : BOOL; // 停止按钮
bLimitFwd AT %IX1.0 : BOOL; // 正限位
bLimitRev AT %IX1.1 : BOOL; // 负限位
// ===== 数字量输出 =====
bMotorRun AT %QX0.0 : BOOL; // 电机运行
bAlarmLamp AT %QX1.0 : BOOL; // 报警灯
// ===== 模拟量输入 =====
wEncoderPos AT %IW10 : WORD; // 编码器原始值
rTemperature AT %ID20 : REAL; // 温度传感器
rPressure AT %ID24 : REAL; // 压力传感器
diPosition AT %ID30 : DINT; // 绝对位置
// ===== 模拟量输出 =====
wAnalogOut AT %QW10 : WORD; // 模拟量输出原始值
rSpeedSetpoint AT %QD20 : REAL; // 速度设定
END_VAR
HMI 共享内存 (M 区)
VAR
bHMI_Start AT %MX0.0 : BOOL; // HMI 启动指令
bHMI_Stop AT %MX0.1 : BOOL; // HMI 停止指令
iRecipeSelect AT %MW10 : INT; // HMI 配方选择
rSetTemp AT %MD20 : REAL; // HMI 温度设定
rSetSpeed AT %MD24 : REAL; // HMI 速度设定
sOperatorMsg AT %MB100 : STRING[32]; // HMI 操作员消息
END_VAR
地址重叠警告
不同大小标识可能访问同一物理内存,编译器会检查并警告。
| 声明 | 物理范围 |
|---|---|
bFlag AT %MX0.0 | M 区字节 0 的 bit 0 |
byByte AT %MB0 | M 区字节 0 |
wWord AT %MW0 | M 区字节 0~1 |
dwDWord AT %MD0 | M 区字节 0~3 |
// 危险:wStatus 与 bError 共享同一内存
VAR
wStatus AT %MW0 : WORD; // 占 M0~M1
bError AT %MX0.0 : BOOL; // 与 wStatus 低位重叠!
END_VAR
// 安全做法:统一 WORD 访问,再位提取
bError := (wStatus.0 = 1);
VAR_EXTERNAL — 外部变量访问
在 POU 中引用全局变量时使用 VAR_EXTERNAL 声明,明确表达依赖关系。
FUNCTION_BLOCK FB_ProductionMonitor
VAR_EXTERNAL
gbMachineReady : BOOL;
gbAutoMode : BOOL;
giBatchCount : DINT;
grTargetTemp : REAL;
stProduction : ST_ProductionData;
END_VAR
| 方式 | 优点 | 缺点 |
|---|---|---|
VAR_EXTERNAL 声明 | 显式依赖,便于代码审查和测试 | 需额外声明代码 |
| 直接使用全局名 | 代码更简洁 | 隐藏依赖关系,重构困难 |
变量可见性与访问控制
可见性层级
全局 (VAR_GLOBAL / VAR_ACCESS)
└── 项目内所有 POU 可见
└── 外部接口 (OPC UA / HMI) 通过 VAR_ACCESS 选择性暴露
POU 内部 (VAR / VAR_TEMP / VAR_STATIC)
└── 仅声明所在的 POU 内部可见
└── FB 实例变量在实例间隔离
接口 (VAR_INPUT / VAR_OUTPUT / VAR_IN_OUT)
└── 调用方通过形参赋值访问
└── VAR_INPUT 只读,VAR_OUTPUT 只写,VAR_IN_OUT 读写
封装原则
FUNCTION_BLOCK FB_AxisControl
// ===== 公开接口 =====
VAR_INPUT
bEnable : BOOL;
rTargetPos : LREAL;
rTargetSpeed : REAL;
END_VAR
VAR_OUTPUT
bHomed : BOOL;
bInPosition : BOOL;
rActualPos : LREAL;
bFault : BOOL;
iErrorCode : INT;
END_VAR
// ===== 内部实现 (对外不可见) =====
VAR
fbPositionPID : FB_PIDController;
fbSpeedPID : FB_PIDController;
eState : E_AxisState;
rRampSpeed : REAL;
END_VAR
访问控制策略
| 策略 | 实现方式 | 示例场景 |
|---|---|---|
| 封装内部状态 | 敏感变量放 VAR,不暴露到 VAR_OUTPUT | PID 内部误差累积值 |
| 只读暴露 | 通过 VAR_OUTPUT 暴露读接口 | 当前位置、速度 |
| 参数化配置 | VAR_INPUT + 默认值 | PID 参数、限值 |
| 全局共享 | VAR_GLOBAL + VAR_EXTERNAL | 生产数据、报警状态 |
| 外部接口 | VAR_ACCESS | OPC UA 变量 |
| 掉电保护 | VAR RETAIN / VAR PERSISTENT | 标定参数、累计值 |
最佳实践
声明块顺序
固定顺序:VAR_INPUT → VAR_OUTPUT → VAR_IN_OUT → VAR → VAR_TEMP → VAR_STATIC → VAR_CONSTANT → VAR RETAIN
FUNCTION_BLOCK FB_BestPractice
VAR_INPUT ... END_VAR
VAR_OUTPUT ... END_VAR
VAR_IN_OUT ... END_VAR
VAR ... END_VAR
VAR_TEMP ... END_VAR
VAR_STATIC ... END_VAR
VAR CONSTANT ... END_VAR
VAR RETAIN ... END_VAR
VAR PERSISTENT ... END_VAR
END_FUNCTION_BLOCK
命名规范
// ✅ 良好命名
bEmergencyStop : BOOL; // 急停按钮状态
iCurrentSpeed : INT; // 当前速度 (rpm)
rTemperatureActual : REAL; // 实际温度 (°C)
stMotorStatus : ST_MotorStatus;
// ❌ 不良命名
bFlag1 : BOOL; // 含义不明
iData : INT; // 太泛
rX : REAL; // 无上下文
常见错误
| 问题 | 根因 | 解决方案 |
|---|---|---|
| 变量未声明 | 忘记在 VAR 块声明 | 编译器报错,按提示补充 |
| 地址冲突 | %MW0 与 %MB0 重叠 | 重新规划地址分配 |
| RETAIN 丢失 | NVRAM 电池耗尽 | 更换电池;重要数据加 PERSISTENT |
| VAR_IN_OUT 误改源 | 引用传递的副作用 | 不需改源数据时改用 VAR_INPUT |
| 初始值不生效 | 热重启而非冷启动 | 切到冷启动 (OB100) 或手动复位 |
| 临时变量随机值 | VAR_TEMP 未初始化 | 使用前显式赋值 |
| 枚举值比较错误 | 忘加类型前缀 | 用 E_State.RUNNING 而非 RUNNING |
推荐声明模板
FUNCTION_BLOCK FB_ConveyorControl
// --- 输入参数 ---
VAR_INPUT
bStart : BOOL; // 启动指令 (上升沿有效)
bStop : BOOL; // 停止指令 (上升沿有效)
rSetSpeed : REAL := 1000.0; // 目标速度 (rpm)
END_VAR
// --- 输出参数 ---
VAR_OUTPUT
bRunning : BOOL; // 运行中
rActualSpeed : REAL; // 实际速度 (rpm)
bFault : BOOL; // 故障
iErrorCode : INT; // 错误码
END_VAR
// --- 内部状态 ---
VAR
tonRamp : TON; // 加速斜坡定时
rtStart : R_TRIG; // 启动上升沿
eState : INT := 0; // 状态机
rInternalSpeed : REAL := 0.0; // 速度斜坡值
END_VAR
// --- 临时变量 ---
VAR_TEMP
iLoop : INT; // 循环索引
bAllClear : BOOL; // 安全条件汇总
END_VAR
// --- 常量 ---
VAR CONSTANT
ACCEL_RATE : REAL := 50.0; // 加速度 (rpm/cycle)
MAX_SPEED : REAL := 3000.0; // 最高速度 (rpm)
END_VAR
// --- 掉电保持 ---
VAR RETAIN
riRunHours : DINT := 0; // 累计运行小时数
riStartCount : DINT := 0; // 累计启动次数
END_VAR
// 程序主体
bAllClear := NOT bFault;
// ... 控制逻辑
END_FUNCTION_BLOCK