博图SCL语言中常用运算符使用详解及实战案例(下)
二、运算符优先级 (从高到低)
理解优先级对于避免逻辑错误至关重要。当不确定时,使用括号`()`是最安全、最清晰的做法。
1. `()` (括号)
2. `NOT`, `-` (取负) - 一元运算符
3. `**` (幂)
4. `*`, `/`, `MOD`
5. `+`, `-`(加减)
6. `=`, `<>`, `>`, `<`, `>=`, `<=` (比较运算符)
7. `AND`, `&` (逻辑与/按位与)
8. `XOR` (逻辑异或/按位异或)
9. `OR` (逻辑或/按位或)
10. `:=` (赋值)
三、实战案例
案例1:电机控制联锁 (逻辑与、比较运算符)
FUNCTION_BLOCK MotorControl
VAR_INPUT
StartButton: BOOL; // 启动按钮信号
StopButton: BOOL; // 停止按钮信号
OverTemp: BOOL; // 过热信号 (TRUE表示过热)
LubOilPressureOK: BOOL; // 润滑油压正常信号 (TRUE表示正常)
SpeedActual: REAL; // 实际转速
END_VAR
VAR_OUTPUT
MotorRunCmd: BOOL; // 电机运行命令输出
END_VAR
VAR
StartPermitted: BOOL; // 内部启动允许标志
END_VAR
// 启动允许条件:无过热、润滑油压正常、转速为零(防止带载启动)
StartPermitted := NOT OverTemp AND LubOilPressureOK AND (SpeedActual <= 0.1);
// 电机运行命令逻辑:启动允许时按下启动按钮则启动,或已运行时未按停止按钮则保持运行
MotorRunCmd := (StartPermitted AND StartButton AND NOT StopButton) OR (MotorRunCmd AND NOT StopButton);
// 安全联锁:一旦过热或油压丢失,立即停止电机 (覆盖启动命令)
IF OverTemp OR NOT LubOilPressureOK THEN
MotorRunCmd := FALSE;
END_IF;
END_FUNCTION_BLOCK
案例2:状态字解析 (位操作、比较运算符)
假设设备状态通过一个`WORD`(16位) `statusWord`返回,不同位代表不同状态(如0位=就绪,1位=运行,2位=故障,3位=警告...)。
FUNCTION ParseStatusWord : BOOL // 返回TRUE表示有严重故障需要停机
VAR_INPUT
statusWord: WORD; // 设备状态字
END_VAR
VAR_TEMP
isReady, isRunning, isFault, isWarning: BOOL;
END_VAR
// 使用位掩码提取状态位
isReady := (statusWord AND 16#0001) <> 0; // 检查第0位 (掩码 16#0001 = 2#0000_0000_0000_0001)
isRunning := (statusWord AND 16#0002) <> 0; // 检查第1位 (掩码 16#0002 = 2#0000_0000_0000_0010)
isFault := (statusWord AND 16#0004) <> 0; // 检查第2位 (掩码 16#0004 = 2#0000_0000_0000_0100)
isWarning := (statusWord AND 16#0008) <> 0; // 检查第3位 (掩码 16#0008 = 2#0000_0000_0000_1000)
// 更新HMI或进行逻辑判断
// 例如:如果有故障位(isFault)被置位,或者设备应该在运行但未运行(isRunning应为TRUE但实际FALSE),则返回TRUE表示严重故障
ParseStatusWord := isFault OR (isReady AND NOT isRunning AND ...); // ... 根据具体逻辑补充条件
// 或者使用移位和类型转换检查特定位 (另一种方法)
// isReady := BOOL((statusWord SHR 0) AND 1); // 右移0位后取最低位
END_FUNCTION
案例3:模拟量限幅与报警 (比较运算符、算术运算符)
FUNCTION ProcessTemperature : REAL
VAR_INPUT
rawTempInput: INT; // 来自AI模块的原始值 (例如 0-27648对应0.0-100.0℃)
scaleMin: REAL := 0.0; // 量程下限
scaleMax: REAL := 100.0; // 量程上限
alarmHigh: REAL := 85.0; // 高温报警阈值
alarmLow: REAL := 10.0; // 低温报警阈值
END_VAR
VAR_OUTPUT
scaledTemp: REAL; // 工程单位温度值
highAlarm: BOOL; // 高温报警输出
lowAlarm: BOOL; // 低温报警输出
END_VAR
CONST
rawMax: INT := 27648; // AI模块满量程原始值
END_CONST
// 1. 标度转换 (注意避免整数除法截断!)
scaledTemp := scaleMin + ( (scaleMax - scaleMin) * REAL(rawTempInput) / REAL(rawMax) );
// 2. 输出限幅 (可选,确保输出在工程范围内)
scaledTemp := LIMIT(MIN := scaleMin, MAX := scaleMax, IN := scaledTemp);
// 3. 报警判断
highAlarm := scaledTemp > alarmHigh;
lowAlarm := scaledTemp < alarmLow;
// 返回处理后的温度值
ProcessTemperature := scaledTemp;
END_FUNCTION
案例4:使用MOD实现循环计数/分时操作
// 在周期性任务中调用 (例如 OB30)
PROGRAM Main
VAR
cycleCounter: INT := 0; // 循环计数器
task1Active, task2Active, task3Active: BOOL;
END_VAR
// 每周期计数器加1
cycleCounter := cycleCounter + 1;
// 使用MOD 3 将计数分为3个阶段 (0,1,2)
CASE (cycleCounter MOD 3) OF
0: // 阶段0
task1Active := TRUE;
task2Active := FALSE;
task3Active := FALSE;
// 执行任务1...
1: // 阶段1
task1Active := FALSE;
task2Active := TRUE;
task3Active := FALSE;
// 执行任务2...
2: // 阶段2
task1Active := FALSE;
task2Active := FALSE;
task3Active := TRUE;
// 执行任务3...
END_CASE;
// 防止计数器过大溢出 (可选)
IF cycleCounter >= 30000 THEN
cycleCounter := 0;
END_IF;
END_PROGRAM
案例4:使用MOD实现循环计数/分时操作
```scl
// 在周期性任务中调用 (例如 OB30)
PROGRAM Main
VAR
cycleCounter: INT := 0; // 循环计数器
task1Active, task2Active, task3Active: BOOL;
END_VAR
// 每周期计数器加1
cycleCounter := cycleCounter + 1;
// 使用MOD 3 将计数分为3个阶段 (0,1,2)
CASE (cycleCounter MOD 3) OF
0: // 阶段0
task1Active := TRUE;
task2Active := FALSE;
task3Active := FALSE;
// 执行任务1...
1: // 阶段1
task1Active := FALSE;
task2Active := TRUE;
task3Active := FALSE;
// 执行任务2...
2: // 阶段2
task1Active := FALSE;
task2Active := FALSE;
task3Active := TRUE;
// 执行任务3...
END_CASE;
// 防止计数器过大溢出 (可选)
IF cycleCounter >= 30000 THEN
cycleCounter := 0;
END_IF;
END_PROGRAM
四、关键总结与最佳实践
1. 明确赋值`:=` vs 比较`=`:** 这是SCL新手最常见的错误。赋值用`:=`,比较是否相等用`=`。
2. 警惕整数除法`/`: `INT / INT` 或 `DINT / DINT` 结果会被**截断**为整数!要得到浮点结果,必须将至少一个操作数转换为`REAL`或`LREAL`(使用`REAL()`函数或在变量声明时使用浮点类型)。
3. 理解位操作`AND`, `OR`, `XOR`, `NOT`, `SHR`, `SHL`:这是处理状态字、标志位、通信协议数据的利器。务必清楚是按位操作(操作数是整数)还是逻辑操作(操作数是`BOOL`)。
4. 利用`MOD`:实现循环计数、奇偶判断、分时任务调度非常方便。
5. 善用括号`()`: 当表达式复杂或对优先级不确定时,使用括号强制指定计算顺序。这能显著提高代码可读性和避免逻辑错误。
6. 注意短路求值: `AND`和`OR`运算符在可能的情况下会短路求值(`AND`遇到`FALSE`则停止,`OR`遇到`TRUE`则停止)。利用这点可以将计算代价高的条件或可能出错的条件(如除以零检查)放在后面。`&`和`OR`(当用于`BOOL`)不短路。
7. 浮点数比较精度: 不要直接用`=`比较两个`REAL`或`LREAL`是否完全相等(由于浮点精度误差)。应检查它们的差值是否小于一个很小的容差`eps` (e.g., `ABS(a - b) < 1.0e-6`)。
熟练掌握SCL运算符是编写高效、可靠PLC程序的基础。结合具体的自动化控制任务多加练习,就能灵活运用这些运算符解决实际问题。