SCL 编程示例
本章通过 8 个完整的工业编程示例,展示 SCL 的典型应用场景。每个示例包含功能说明、变量表、完整代码和关键注释。
1. 电机启停控制(自锁/互锁)
功能:实现电机的启动自锁、停止按钮、互锁保护、过载报警。
变量表
FUNCTION_BLOCK MotorControl
VAR_INPUT
bStartBtn : BOOL; // 启动按钮(常开)
bStopBtn : BOOL; // 停止按钮(常闭)
bFaultOther : BOOL; // 互锁信号(来自其他电机)
bOverload : BOOL; // 过载信号
END_VAR
VAR_OUTPUT
xRun : BOOL; // 运行输出
xAlarm : BOOL; // 报警输出
END_VAR
VAR
bRunLatch : BOOL; // 自锁中间变量
END_VAR
代码
// 自锁逻辑:启动按钮上升沿置位,停止或故障时复位
IF bStartBtn AND NOT bFaultOther AND NOT bOverload THEN
bRunLatch := TRUE;
END_IF;
IF bStopBtn OR bFaultOther OR bOverload THEN
bRunLatch := FALSE;
END_IF;
// 输出
xRun := bRunLatch AND NOT bOverload;
xAlarm := bOverload;
// 互锁保护:当本电机运行时,占用互锁信号
IF xRun THEN
// 对外输出互锁信号(通过全局变量传递)
gbMotorBusy[idx] := TRUE;
ELSE
gbMotorBusy[idx] := FALSE;
END_IF;
2. 交通灯定时控制
功能:十字路口交通灯 — 红 → 绿 → 黄 → 红灯循环。
变量表
FUNCTION_BLOCK TrafficLight
VAR_INPUT
bEnable : BOOL; // 启用
bFlash : BOOL; // 闪烁模式(夜间)
END_VAR
VAR_OUTPUT
xRed : BOOL;
xYellow : BOOL;
xGreen : BOOL;
END_VAR
VAR
iState : INT := 0;
tTimer : TON;
aTimes : ARRAY[0..3] OF TIME := [T#30s, T#25s, T#5s, T#2s];
// 状态 0: 红灯 30s, 状态 1: 绿灯 25s, 状态 2: 黄灯 5s, 状态 3: 全红 2s
END_VAR
代码
IF NOT bEnable THEN
xRed := FALSE;
xYellow := FALSE;
xGreen := FALSE;
iState := 0;
tTimer(IN := FALSE);
RETURN;
END_IF;
IF bFlash THEN
// 夜间闪烁模式:黄灯 0.5s 闪烁
xRed := FALSE;
xGreen := FALSE;
xYellow := CLK_500ms;
RETURN;
END_IF;
// 定时器驱动状态切换
tTimer(IN := TRUE, PT := aTimes[iState]);
IF tTimer.Q THEN
iState := (iState + 1) MOD 4;
tTimer(IN := FALSE); // 复位定时器
END_IF;
// 状态解码
CASE iState OF
0: // 红灯
xRed := TRUE;
xYellow := FALSE;
xGreen := FALSE;
1: // 绿灯
xRed := FALSE;
xYellow := FALSE;
xGreen := TRUE;
2: // 黄灯
xRed := FALSE;
xYellow := TRUE;
xGreen := FALSE;
3: // 全红(安全间隔)
xRed := TRUE;
xYellow := FALSE;
xGreen := FALSE;
END_CASE;
3. 模拟量采集与滤波
功能:采集 4-20mA 模拟量输入,进行中值滤波和限幅处理。
变量表
FUNCTION_BLOCK AnalogFilter
VAR_INPUT
diRaw : DINT; // 原始 AD 值
rScaleMin : REAL; // 量程下限(工程单位)
rScaleMax : REAL; // 量程上限(工程单位)
END_VAR
VAR_OUTPUT
rValue : REAL; // 滤波后工程值
rRawValue : REAL; // 原始工程值
bValid : BOOL; // 数据有效标志
END_VAR
VAR
aiBuffer : ARRAY[1..5] OF DINT; // 滤波缓冲区
iIdx : INT := 1;
iTemp : ARRAY[1..5] OF DINT; // 排序用临时数组
i, j : INT;
iMin, iMax : DINT; // 限幅阈值
END_VAR
代码
// 1. 限幅检查:超出合理范围则丢弃
IF (diRaw < -32768) OR (diRaw > 32767) THEN
bValid := FALSE;
rValue := 0.0;
rRawValue := 0.0;
RETURN;
END_IF;
// 2. 写入环形缓冲区
aiBuffer[iIdx] := diRaw;
iIdx := iIdx + 1;
IF iIdx > 5 THEN
iIdx := 1;
END_IF;
// 3. 排序取中值
FOR i := 1 TO 5 DO
iTemp[i] := aiBuffer[i];
END_FOR;
FOR i := 1 TO 4 DO
FOR j := i + 1 TO 5 DO
IF iTemp[i] > iTemp[j] THEN
// 交换
iTemp[i] := iTemp[i] + iTemp[j];
iTemp[j] := iTemp[i] - iTemp[j];
iTemp[i] := iTemp[i] - iTemp[j];
END_IF;
END_FOR;
END_FOR;
// 4. 取中值(第三个元素)
rRawValue := DINT_TO_REAL(aiBuffer[iIdx - 1]); // 临时简化
rValue := DINT_TO_REAL(iTemp[3]); // 中值滤波结果
// 5. 转换为工程值
rValue := (rValue / 32767.0) * (rScaleMax - rScaleMin) + rScaleMin;
rRawValue := (DINT_TO_REAL(diRaw) / 32767.0) * (rScaleMax - rScaleMin) + rScaleMin;
bValid := TRUE;
4. PID 温度控制
功能:使用增量式 PID 算法控制加热器输出,维持温度在设定值。
变量表
FUNCTION_BLOCK PidTemperature
VAR_INPUT
rSetpoint : REAL; // 温度设定值(°C)
rFeedback : REAL; // 温度反馈值(°C)
rKp : REAL; // 比例增益
rTi : REAL; // 积分时间(秒)
rTd : REAL; // 微分时间(秒)
rCycle : REAL; // 采样周期(秒)
END_VAR
VAR_OUTPUT
rOutput : REAL; // 控制输出(0.0 ~ 100.0 %)
bActive : BOOL; // 调节器激活
END_VAR
VAR
rError : REAL; // 偏差
rPrevError : REAL; // 上次偏差
rIntegral : REAL; // 积分累加
rDerivative : REAL; // 微分项
rP, rI, rD : REAL; // 三项分量
rOutRaw : REAL; // 原始输出
rDeltaOut : REAL; // 增量输出
bFirstRun : BOOL := TRUE;
END_VAR
代码
// 计算偏差
rError := rSetpoint - rFeedback;
// 比例项
rP := rKp * rError;
// 积分项(带抗积分饱和)
IF rTi > 0.0 THEN
rIntegral := rIntegral + (rError * rCycle / rTi);
// 抗积分饱和:限制积分累加范围
IF rIntegral > 100.0 THEN
rIntegral := 100.0;
ELSIF rIntegral < -100.0 THEN
rIntegral := -100.0;
END_IF;
rI := rKp * rIntegral;
ELSE
rI := 0.0;
END_IF;
// 微分项
IF NOT bFirstRun AND (rTd > 0.0) THEN
rDerivative := (rError - rPrevError) / rCycle;
rD := rKp * rTd * rDerivative;
ELSE
rD := 0.0;
END_IF;
// 增量式输出
rDeltaOut := rP + rI + rD;
rOutRaw := rOutRaw + rDeltaOut;
// 输出限幅
IF rOutRaw > 100.0 THEN
rOutput := 100.0;
ELSIF rOutRaw < 0.0 THEN
rOutput := 0.0;
ELSE
rOutput := rOutRaw;
END_IF;
// 激活标志
bActive := ABS(rError) < 50.0;
// 保存状态
rPrevError := rError;
bFirstRun := FALSE;
5. 状态机(State Machine)实现
功能:实现一个设备的主状态机 — 空闲、启动、运行、暂停、错误、停止。
变量表
FUNCTION_BLOCK DeviceStateMachine
VAR_INPUT
bStart : BOOL;
bStop : BOOL;
bPause : BOOL;
bFault : BOOL;
bReset : BOOL;
bReady : BOOL; // 设备就绪(气源、电源正常)
END_VAR
VAR_OUTPUT
xRunning : BOOL;
xPaused : BOOL;
xFaulted : BOOL;
xIdle : BOOL;
eState : INT; // 当前状态码
END_VAR
VAR
iState : INT := 0;
// 0=IDLE, 10=STARTING, 20=RUNNING, 30=PAUSED, 40=STOPPING, 50=FAULT
tTimer : TON;
iStep : INT;
END_VAR
代码
CASE iState OF
0: // IDLE — 空闲
xRunning := FALSE;
xPaused := FALSE;
xFaulted := FALSE;
xIdle := TRUE;
eState := 0;
IF bStart AND bReady THEN
iState := 10;
iStep := 0;
END_IF;
10: // STARTING — 启动中
xIdle := FALSE;
eState := 10;
iStep := iStep + 1;
// 启动延迟或初始化序列
tTimer(IN := TRUE, PT := T#2s);
IF tTimer.Q THEN
iState := 20;
END_IF;
// 启动过程中检测故障
IF bFault THEN
iState := 50;
END_IF;
20: // RUNNING — 运行中
xRunning := TRUE;
eState := 20;
IF bPause THEN
iState := 30;
END_IF;
IF bStop THEN
iState := 40;
END_IF;
IF bFault THEN
iState := 50;
END_IF;
30: // PAUSED — 暂停
xRunning := FALSE;
xPaused := TRUE;
eState := 30;
IF NOT bPause THEN
iState := 20; // 恢复运行
END_IF;
IF bStop THEN
iState := 40;
END_IF;
40: // STOPPING — 停止中
xRunning := FALSE;
eState := 40;
iStep := iStep - 1;
IF iStep <= 0 THEN
iState := 0; // 回到空闲
END_IF;
50: // FAULT — 故障
xRunning := FALSE;
xFaulted := TRUE;
eState := 50;
IF bReset AND NOT bFault THEN
iState := 0; // 复位后回到空闲
END_IF;
ELSE
// 未知状态安全保护
xRunning := FALSE;
xFaulted := TRUE;
iState := 50;
END_CASE;
6. 数组排序
功能:实现冒泡排序算法,对 INT 数组进行升序排列。
变量表
FUNCTION SortArray : BOOL
VAR_IN_OUT
aiData : ARRAY[*] OF INT; // 待排序数组(可变长度)
END_VAR
VAR_INPUT
iLen : INT; // 数组有效长度
eOrder : INT; // 0=升序, 1=降序
END_VAR
VAR
i, j : INT;
iTemp : INT;
bSwapped : BOOL;
END_VAR
代码
// 参数校验
IF (iLen < 2) OR (iLen > 1000) THEN
SortArray := FALSE;
RETURN;
END_IF;
// 冒泡排序
FOR i := 1 TO iLen - 1 DO
bSwapped := FALSE;
FOR j := 1 TO iLen - i DO
IF eOrder = 0 THEN
// 升序
IF aiData[j] > aiData[j + 1] THEN
// 交换
iTemp := aiData[j];
aiData[j] := aiData[j + 1];
aiData[j + 1] := iTemp;
bSwapped := TRUE;
END_IF;
ELSE
// 降序
IF aiData[j] < aiData[j + 1] THEN
iTemp := aiData[j];
aiData[j] := aiData[j + 1];
aiData[j + 1] := iTemp;
bSwapped := TRUE;
END_IF;
END_IF;
END_FOR;
// 无交换则提前结束
IF NOT bSwapped THEN
EXIT;
END_IF;
END_FOR;
SortArray := TRUE;
7. 通讯数据处理
功能:解析 Modbus TCP 读到的数据帧,提取有效负载并校验。
变量表
FUNCTION_BLOCK ModbusDataParser
VAR_INPUT
abRaw : ARRAY[1..256] OF BYTE; // 原始数据缓冲区
iLen : INT; // 数据长度
END_VAR
VAR_OUTPUT
uiTransactionId : UINT;
uiProtocolId : UINT;
uiDataLen : UINT;
byUnitId : BYTE;
byFuncCode : BYTE;
abPayload : ARRAY[1..252] OF BYTE;
iPayloadLen : INT;
bValid : BOOL;
END_VAR
VAR
iCalcCrc : INT;
i : INT;
END_VAR
代码
// 初始化
bValid := FALSE;
iPayloadLen := 0;
// 最小帧长度检查
IF iLen < 12 THEN
RETURN;
END_IF;
// 解析 MBAP 头(Modbus Application Protocol)
uiTransactionId := BYTE_TO_UINT(abRaw[1]) * 256 + BYTE_TO_UINT(abRaw[2]);
uiProtocolId := BYTE_TO_UINT(abRaw[3]) * 256 + BYTE_TO_UINT(abRaw[4]);
uiDataLen := BYTE_TO_UINT(abRaw[5]) * 256 + BYTE_TO_UINT(abRaw[6]);
byUnitId := abRaw[7];
byFuncCode := abRaw[8];
// 协议标识校验
IF uiProtocolId <> 0 THEN
RETURN; // 非 Modbus 协议
END_IF;
// 提取有效负载(从第9字节开始)
FOR i := 9 TO iLen DO
abPayload[i - 8] := abRaw[i];
END_FOR;
iPayloadLen := iLen - 8;
// 成功
bValid := TRUE;
8. 报警处理逻辑
功能:报警检测、确认、恢复、历史记录。
变量表
FUNCTION_BLOCK AlarmManager
VAR_INPUT
bTriggers : ARRAY[1..16] OF BOOL; // 报警触发信号
bAckBtn : BOOL; // 报警确认按钮
bResetBtn : BOOL; // 报警复位按钮
END_VAR
VAR_OUTPUT
xActive : BOOL; // 有报警激活
xCritical : BOOL; // 有严重报警
xSound : BOOL; // 声音输出
END_VAR
VAR
iActive : INT; // 激活报警数量
iIdx : INT;
// 报警状态数组
abAlarmActive : ARRAY[1..16] OF BOOL;
abAlarmAcked : ARRAY[1..16] OF BOOL;
abAlarmHistory : ARRAY[1..16] OF BOOL; // 历史报警
tFlash : TON;
xFlash : BOOL;
// 报警优先级数组(1=最高)
aiPriority : ARRAY[1..16] OF INT := [
1, 2, 2, 3, 3, 3, 3, 4,
4, 4, 4, 4, 4, 4, 4, 4
];
END_VAR
代码
// 1. 扫描触发信号,更新报警激活状态
FOR iIdx := 1 TO 16 DO
// 上升沿检测:信号从 FALSE 变为 TRUE
IF bTriggers[iIdx] AND NOT abAlarmActive[iIdx] THEN
// 新报警触发
abAlarmActive[iIdx] := TRUE;
abAlarmAcked[iIdx] := FALSE;
abAlarmHistory[iIdx] := TRUE;
END_IF;
// 触发信号已消失
IF NOT bTriggers[iIdx] AND abAlarmActive[iIdx] THEN
// 如果已确认,则复位报警
IF abAlarmAcked[iIdx] THEN
abAlarmActive[iIdx] := FALSE;
END_IF;
END_IF;
END_FOR;
// 2. 确认按钮:确认所有激活报警
IF bAckBtn THEN
FOR iIdx := 1 TO 16 DO
IF abAlarmActive[iIdx] THEN
abAlarmAcked[iIdx] := TRUE;
END_IF;
END_FOR;
END_IF;
// 3. 复位按钮:清除所有已确认且无触发的报警
IF bResetBtn THEN
FOR iIdx := 1 TO 16 DO
IF abAlarmAcked[iIdx] AND NOT bTriggers[iIdx] THEN
abAlarmActive[iIdx] := FALSE;
END_IF;
END_FOR;
END_IF;
// 4. 统计激活报警数
iActive := 0;
xCritical := FALSE;
FOR iIdx := 1 TO 16 DO
IF abAlarmActive[iIdx] THEN
iActive := iActive + 1;
// 优先级 1 或 2 为严重报警
IF aiPriority[iIdx] <= 2 THEN
xCritical := TRUE;
END_IF;
END_IF;
END_FOR;
xActive := iActive > 0;
// 5. 声音输出:新报警时触发,闪烁 3 秒
tFlash(IN := xActive AND NOT xActive, PT := T#3s);
xSound := xActive AND NOT tFlash.Q;
总结
| 示例 | 核心知识点 | 适用场景 |
|---|---|---|
| 电机启停控制 | 自锁/互锁、IF 条件 | 基础逻辑控制 |
| 交通灯定时控制 | CASE 状态机、TON 定时器 | 顺序控制、时序逻辑 |
| 模拟量采集与滤波 | 环形缓冲区、冒泡排序、工程换算 | 信号处理 |
| PID 温度控制 | 增量式 PID、抗积分饱和、限幅 | 过程控制 |
| 状态机实现 | 主状态机、状态切换、安全保护 | 设备控制 |
| 数组排序 | 冒泡排序、FOR 循环、EXIT 提前退出 | 数据处理 |
| 通讯数据处理 | 字节数组解析、协议校验 | 通讯协议 |
| 报警处理逻辑 | 报警触发/确认/恢复、优先级管理 | 设备诊断 |