运算符与表达式
SCL 提供了丰富的运算符用于构建表达式,涵盖算术、比较、逻辑、位运算和赋值操作。本章详细介绍每种运算符的语法、优先级规则和类型转换约定。
算术运算符
| 运算符 | 名称 | 说明 | 示例 | 结果 |
|---|---|---|---|---|
+ | 加法 | 数值相加 | 5 + 3 | 8 |
- | 减法 | 数值相减 | 10 - 4 | 6 |
* | 乘法 | 数值相乘 | 6 * 7 | 42 |
/ | 除法 | 数值相除,结果为 REAL | 10 / 3 | 3.333333 |
MOD | 取模 | 整数除法余数 | 10 MOD 3 | 1 |
** | 幂运算 | 指数运算 | 2 ** 3 | 8.0 |
除法注意: 整数参与除法时,运算结果提升为 REAL 类型。如需整数截断,使用 TRUNC 或 DIV 指令。
// 算术运算示例
VAR
iA, iB, iSum : INT;
rResult : REAL;
diRemainder : DINT;
END_VAR
iSum := iA + iB; // 加法
rResult := 100.0 / 3.0; // 除法 → 33.33333
diRemainder := 17 MOD 5; // 取模 → 2
rResult := 2.0 ** 10.0; // 幂运算 → 1024.0
比较运算符
所有比较运算符返回 BOOL 类型。
| 运算符 | 名称 | 说明 | 示例 |
|---|---|---|---|
= | 等于 | 两值相等为 TRUE | iVal = 100 |
<> | 不等于 | 两值不等为 TRUE | iVal <> 0 |
< | 小于 | 左值小于右值为 TRUE | iTemp < 85 |
> | 大于 | 左值大于右值为 TRUE | rSpeed > 500.0 |
<= | 小于等于 | 左值小于等于右值为 TRUE | iCount <= 1000 |
>= | 大于等于 | 左值大于等于右值为 TRUE | diPosition >= 0 |
// 比较运算示例
IF iPressure > 1000 THEN
bOverload := TRUE;
END_IF;
IF diPosition = 0 AND bHomeSensor THEN
bHomed := TRUE;
END_IF;
逻辑运算符
逻辑运算符用于 BOOL 表达式的组合判断,也可用于位置位运算。
| 运算符 | 等效写法 | 说明 | 真值表 (A, B) |
|---|---|---|---|
AND | & | 逻辑与 | 仅 TRUE & TRUE → TRUE |
OR | — | 逻辑或 | 任一 TRUE → TRUE |
XOR | — | 异或 | 两值不同 → TRUE |
NOT | — | 逻辑非 (单目) | NOT TRUE → FALSE |
// 逻辑运算示例
IF bEnable AND bSafetyGate THEN
bRun := TRUE;
END_IF;
IF bStart OR bManualOverride THEN
bCycleRequest := TRUE;
END_IF;
IF NOT bFault THEN
bReady := TRUE;
END_IF;
// AND 与 & 等效
IF bEnable & bSafetyGate THEN
bRun := TRUE;
END_IF;
位运算符
位运算符作用于 BYTE、WORD、DWORD、LWORD 等位串类型,按位执行逻辑运算或移位操作。
按位逻辑
| 运算符 | 说明 | 示例 | 结果 |
|---|---|---|---|
AND | 按位与 | 16#0F AND 16#F0 | 16#00 |
OR | 按位或 | 16#0F OR 16#F0 | 16#FF |
XOR | 按位异或 | 16#0F XOR 16#F0 | 16#FF |
NOT | 按位取反 (单目) | NOT 16#0F | 16#F0 (以 BYTE 为例) |
移位与循环
| 运算符 | 名称 | 方向 | 说明 |
|---|---|---|---|
SHL | 左移 | 左 | 低位补 0,高位丢弃 |
SHR | 右移 | 右 | 高位补 0,低位丢弃 |
ROL | 循环左移 | 左 | 移出的高位回填到低位 |
ROR | 循环右移 | 右 | 移出的低位回填到高位 |
移位运算符的第二个操作数为移位位数(UINT 或 INT 常量)。
// 位运算示例
VAR
wStatus : WORD;
wControl : WORD;
wResult : WORD;
dwFlags : DWORD;
dwMasked : DWORD;
END_VAR
// 按位设置标志位
wControl := 16#0000;
wControl := wControl OR 16#0001; // 置位 Bit 0
wControl := wControl OR 16#0080; // 置位 Bit 7
// 按位清除标志位
wStatus := wStatus AND NOT 16#0004; // 清除 Bit 2
// 位掩码检测
IF (wStatus AND 16#0002) <> 0 THEN
// Bit 1 为 TRUE
END_IF;
// 左移 3 位
wResult := wStatus SHL 3;
// 右移 2 位
wResult := wStatus SHR 2;
// 循环左移 4 位
wResult := ROL(wStatus, 4);
// 循环右移 4 位
wResult := ROR(wStatus, 4);
// DWORD 掩码操作
dwMasked := dwFlags AND 16#00FF0000;
dwFlags := dwFlags OR 16#00010000;
赋值运算符
| 运算符 | 名称 | 说明 |
|---|---|---|
:= | 赋值 | 将右值赋给左变量 |
赋值是 SCL 中最基本的操作,等号右边可以是字面量、变量、函数调用或任意复杂表达式。
// 赋值示例
iCounter := 0; // 字面量赋值
rSetpoint := rMasterSpeed * 0.75; // 表达式赋值
bReady := iTemp < 85 AND iLevel > 0; // 比较表达式赋值
sName := 'Motor_1'; // 字符串赋值
tTimeout := T#500ms; // 时间段赋值
arrData[5] := 100; // 数组元素赋值
stMotor.rSpeed := 1500.0; // 结构体成员赋值
运算符优先级表
从最高优先级到最低优先级,同一行内的运算符优先级相同。
| 优先级 | 类别 | 运算符 | 结合性 |
|---|---|---|---|
| 1 (最高) | 括号 | (表达式) | 左到右 |
| 2 | 幂运算 | ** | 右到左 |
| 3 | 单目 | NOT、+ (正号)、- (负号) | 右到左 |
| 4 | 乘除 | *、/、MOD | 左到右 |
| 5 | 加减 | +、- | 左到右 |
| 6 | 移位 | SHL、SHR、ROL、ROR | 左到右 |
| 7 | 比较 | <、>、<=、>= | 左到右 |
| 8 | 等于/不等 | =、<> | 左到右 |
| 9 | 按位与 | AND | 左到右 |
| 10 | 按位异或 | XOR | 左到右 |
| 11 | 按位或 | OR | 左到右 |
| 12 (最低) | 赋值 | := | 右到左 |
经验法则:
- 括号优先:不确定优先级时,用括号明确表达式的计算顺序。
- 算术高于比较,比较高于逻辑:
a + b > c AND d等价于((a + b) > c) AND d。 - 逻辑与 (
AND) 优先级高于逻辑或 (OR):a OR b AND c等价于a OR (b AND c)。
表达式求值顺序
表达式的求值遵循以下规则:
- 按优先级从高到低:先算优先级高的运算符,再算优先级低的运算符。
- 同优先级从左到右:优先级相同时,从左到右依次计算(幂运算
**为右到左)。 - 短路求值:
AND和OR采用短路求值——左操作数足以确定结果时,右操作数不再计算。
// 短路求值示例
// 如果 iIndex < 0,右侧 arrData[iIndex] 不会计算,避免越界
IF iIndex >= 0 AND iIndex < 100 AND arrData[iIndex] > 0 THEN
bValid := TRUE;
END_IF;
// 如果 bFault,右侧的复杂函数调用不会执行
IF NOT bFault OR ResetSystem() THEN
bRunning := TRUE;
END_IF;
表达式中的类型转换
隐式提升规则
SCL 在不损失精度和范围的前提下,会自动进行类型提升:
| 源类型 | 可隐式提升到 |
|---|---|
SINT | INT、DINT、LINT、REAL、LREAL |
INT | DINT、LINT、REAL、LREAL |
DINT | LINT、REAL、LREAL |
LINT | LREAL |
USINT | UINT、UDINT、ULINT、INT、DINT、LINT、REAL、LREAL |
UINT | UDINT、ULINT、DINT、LINT、REAL、LREAL |
UDINT | ULINT、LINT、REAL、LREAL |
ULINT | LREAL |
REAL | LREAL |
规则:
- 有符号和无符号混合表达式中,有符号类型优先提升到足够容纳两者的更大类型。
REAL和LREAL不会自动提升为整数类型——整数到浮点可提升,反之必须显式转换。TIME、DATE、TOD、DT在内部以整数存储,但与其他类型混合运算时需显式转换。
显式类型转换
当隐式转换不可用时,使用类型转换函数:
VAR
iVal : INT;
rVal : REAL;
diVal : DINT;
byArr : ARRAY[0..3] OF BYTE;
END_VAR
iVal := INT_TO_INT(100); // 同一类型,无操作
rVal := INT_TO_REAL(iVal); // INT → REAL
iVal := REAL_TO_INT(rVal); // REAL → INT(截断小数)
diVal := REAL_TO_DINT(rVal); // REAL → DINT
byArr := DWORD_TO_BYTE_ARRAY(16#12345678); // DWORD → 字节数组
集合运算 — IN 运算符
IN 运算符用于判断值是否属于给定的集合范围,返回 BOOL 类型。
// IN 运算符语法
// <值> IN [<元素1>, <元素2>, ...]
// <值> IN <下限>..<上限>
// 示例
IF iState IN [1, 2, 3] THEN
// iState 为 1、2 或 3
bValidState := TRUE;
END_IF;
IF iSpeed IN 1000..3000 THEN
// iSpeed 在 1000 到 3000 之间(含边界)
bInRange := TRUE;
END_IF;
// 组合使用
IF iErrorCode IN [10, 20, 30..50] THEN
// iErrorCode 为 10、20 或在 30..50 范围内
bRecoverable := TRUE;
END_IF;
// 等价于显式比较
IF iErrorCode = 10 OR iErrorCode = 20
OR (iErrorCode >= 30 AND iErrorCode <= 50) THEN
bRecoverable := TRUE;
END_IF;
综合示例
示例 1: 电机速度控制
FUNCTION_BLOCK FB_MotorSpeedControl
VAR_INPUT
bEnable : BOOL;
bFault : BOOL;
rSpeedRef : REAL; // 速度给定 (rpm)
iMode : INT; // 0=停止, 1=低速, 2=中速, 3=高速
END_VAR
VAR_OUTPUT
rSpeedOut : REAL;
bRunning : BOOL;
wStatus : WORD;
END_VAR
VAR
rRampRate : REAL := 10.0;
rMaxSpeed : REAL := 3000.0;
END_VAR
// 模式与速度映射 + 安全逻辑
IF bEnable AND NOT bFault THEN
// 使用 IN 运算符判断模式
IF iMode IN [1, 2, 3] THEN
// 根据模式设定目标速度
CASE iMode OF
1: rSpeedRef := 500.0; // 低速
2: rSpeedRef := 1500.0; // 中速
3: rSpeedRef := 3000.0; // 高速
END_CASE;
// 限幅处理
IF rSpeedRef > rMaxSpeed THEN
rSpeedRef := rMaxSpeed;
END_IF;
// 加减速斜坡
IF rSpeedOut < rSpeedRef THEN
rSpeedOut := rSpeedOut + rRampRate;
ELSIF rSpeedOut > rSpeedRef THEN
rSpeedOut := rSpeedOut - rRampRate;
END_IF;
// 状态字:运行中
wStatus := wStatus OR 16#0001;
bRunning := TRUE;
ELSE
// 无效模式,停止
rSpeedOut := 0.0;
bRunning := FALSE;
wStatus := 16#0000;
END_IF;
ELSE
// 未使能或有故障,安全停止
rSpeedOut := 0.0;
bRunning := FALSE;
wStatus := wStatus AND NOT 16#0001;
wStatus := wStatus OR 16#0008; // 故障状态位
END_IF;
END_FUNCTION_BLOCK
示例 2: 数据校验与位域操作
FUNCTION F_CheckTemperature : BOOL
VAR_INPUT
rCurrentTemp : REAL;
rSetpoint : REAL;
rHysteresis : REAL;
dwAlarmFlags : DWORD;
END_VAR
VAR
rDelta : REAL;
bOverTemp : BOOL;
bUnderTemp : BOOL;
bAlarmActive : BOOL;
END_VAR
// 计算偏差
rDelta := rCurrentTemp - rSetpoint;
IF rDelta < 0.0 THEN
rDelta := -rDelta; // 绝对值
END_IF;
// 判断是否超出滞环范围
bOverTemp := rCurrentTemp > (rSetpoint + rHysteresis);
bUnderTemp := rCurrentTemp < (rSetpoint - rHysteresis);
// 检查报警标志——使用按位与
// 位 0 = 超温报警, 位 1 = 欠温报警, 位 2 = 传感器故障
bAlarmActive := (dwAlarmFlags AND 16#00000007) <> 0;
// 组合判断
IF (bOverTemp OR bUnderTemp) AND NOT bAlarmActive THEN
// 温度超出范围且无硬件报警,产生警告
F_CheckTemperature := TRUE;
ELSE
F_CheckTemperature := FALSE;
END_IF;
// 使用 IN 运算符检查温度是否在合理工作范围
IF rCurrentTemp IN -40.0..85.0 THEN
// 温度在传感器正常工作范围内
// 继续执行后续逻辑
END_IF;
END_FUNCTION
示例 3: 复杂组合表达式
// 电机启动条件——综合使用算术、比较、逻辑、位运算
bStartAllowed :=
(NOT bEmergencyStop) // 急停未触发
AND (diActualPosition >= 0) // 位置有效
AND (rDriveCurrent <= (rRatedCurrent * 1.1)) // 电流在额定 110% 以内
AND (wDriveStatus AND 16#0020) <> 0 // 驱动器就绪位
AND (iMode IN [1, 2, 3]) // 运行模式有效
AND (NOT (dwFaultLog AND 16#0000FF00) <> 0); // 无严重故障
// 位置偏差计算
diPositionError := diTargetPosition - diActualPosition;
IF ABS(diPositionError) > 1000 THEN
bPositionError := TRUE;
END_IF;
// 速度前馈补偿
rFeedForward := (rTargetSpeed * 0.85) + (rAcceleration * 0.15);
rFinalOutput := (rPIDOutput * 0.7) + (rFeedForward * 0.3);