SCL 语言概述
SCL(Structured Control Language,结构化控制语言)是 Darra PLC 的文本编程语言,基于 IEC 61131-3 标准中定义的**结构化文本(ST)**语法。SCL 语法接近 Pascal / Ada,面向工业控制领域的高级算法、数据处理和复杂逻辑实现。
SCL 与梯形图(LAD)、连续功能图(CFC)共同构成 Darra IDE 三大 PLC 逻辑编辑器。
SCL 的应用场景
SCL 适用于需要编写复杂控制逻辑的工业自动化任务:
| 场景 | 说明 | 典型行业 |
|---|---|---|
| 复杂算法 | PID 变体、滤波器、运动轨迹规划 | 冶金、化工 |
| 数据处理 | 数组排序、统计计算、协议解析 | 水处理、环境监测 |
| 数学运算 | 浮点矩阵、三角变换、坐标映射 | 机器人、CNC |
| 状态机 | 多状态工艺步、安全连锁 | 包装、注塑 |
| 配方管理 | 配方参数计算与切换 | 食品饮料、制药 |
| 通讯协议 | 串口/以太网数据帧打包解析 | 楼宇、能源 |
| 批量控制 | ISA-88 批次顺序控制 | 化工、制药 |
| 运动控制 | 电子凸轮、插补前计算 | 电子制造 |
SCL vs LAD vs CFC
三种编程语言各有侧重,实际项目中常混合使用:
| 特性 | SCL | LAD(梯形图) | CFC(连续功能图) |
|---|---|---|---|
| 编程范式 | 文本(过程式) | 图形(继电器电路) | 图形(数据流连线) |
| 语法基础 | Pascal/Ada | 触点 + 线圈 | 功能块 + 连线 |
| 入门难度 | 中等 | 较低 | 中等 |
| 复杂算法 | 非常适合 | 不适合 | 中等 |
| 位逻辑 | 可行 | 非常适合 | 可行 |
| 数学运算 | 非常适合 | 困难 | 适合 |
| 状态机 | 非常适合 | 困难 | 中等 |
| 代码复用 | FUNCTION/FB | 子程序 + FB 实例 | FB 实例 |
| 调试效率 | 断点 + 监视 | 高亮线 + 线圈值 | 连线值实时显示 |
| 版本控制 | 文本 diff 友好 | XML diff 可行 | XML diff 可行 |
| 执行效率 | 编译优化佳 | 中等 | 中等 |
| 典型占比 | ~40% | ~40% | ~20% |
选型建议
- 控制逻辑以位运算为主(启停、连锁、互锁):优先 LAD
- 以数据流和连续处理为主(信号调理、滤波、比例运算):优先 CFC
- 以算法、状态机、数学处理为主:优先 SCL
- 需要版本管理和团队协作:SCL 的纯文本格式在 Git diff 上最清晰
- 新功能原型开发:SCL 快速迭代,稳定后再考虑是否图形化
基本程序结构
SCL 程序由若干**程序组织单元(POU)**组成,每种 POU 对应不同的用途:
PROGRAM(程序)
PROGRAM 是 PLC 执行的顶层入口,不保存实例状态,每次扫描周期执行完后输出值清零(除非使用 RETAIN)。
PROGRAM Main
VAR
iCounter : INT;
bStart : BOOL;
END_VAR
// 读取输入
bStart := %IX0.0;
// 控制逻辑
IF bStart THEN
iCounter := iCounter + 1;
END_IF;
// 写入输出
%QX0.0 := iCounter > 100;
END_PROGRAM
FUNCTION(函数)
FUNCTION 没有持久化状态,相同输入始终返回相同输出,内部变量不保持。
FUNCTION LimitFilter : REAL
VAR_INPUT
rValue : REAL;
rMin : REAL;
rMax : REAL;
END_VAR
IF rValue > rMax THEN
LimitFilter := rMax;
ELSIF rValue < rMin THEN
LimitFilter := rMin;
ELSE
LimitFilter := rValue;
END_IF;
END_FUNCTION
FUNCTION_BLOCK(功能块)
FUNCTION_BLOCK 有持久化状态,实例数据在两次调用之间保持。这是 IEC 61131-3 中最常用的 POU 类型。
FUNCTION_BLOCK FB_Integrator
VAR_INPUT
rInput : REAL;
bReset : BOOL;
END_VAR
VAR_OUTPUT
rOutput : REAL;
END_VAR
VAR
rIntegral : REAL;
rTs : TIME := T#10ms; // 采样周期
END_VAR
IF bReset THEN
rIntegral := 0.0;
ELSE
rIntegral := rIntegral + rInput * TIME_TO_REAL(rTs) / 1000.0;
END_IF;
rOutput := rIntegral;
END_FUNCTION_BLOCK
TYPE(数据类型声明)
用户自定义数据类型在 TYPE ... END_TYPE 块中声明:
// 枚举
TYPE AxisState : (
AXIS_IDLE := 0,
AXIS_HOMING := 1,
AXIS_RUNNING := 2,
AXIS_ERROR := 3
);
END_TYPE
// 结构体
TYPE ConveyorData :
STRUCT
bRunning : BOOL;
rSpeed : REAL;
diDistance : DINT;
sProduct : STRING[32];
END_STRUCT;
END_TYPE
SCL 编辑器功能
Darra IDE 的 SCL 编辑器提供工业级代码编辑体验:
语法高亮
- 关键字(
IF、FOR、CASE、VAR等)以粗体蓝色显示 - 数据类型(
INT、REAL、BOOL、STRING)以青色显示 - 字符串字面量以绿色显示
- 数字常量以紫色显示
- 注释以灰色斜体显示
- 错误下划线红色波浪线
代码折叠
支持折叠以下结构:
VAR ... END_VAR变量声明块FUNCTION / FUNCTION_BLOCK / PROGRAM ... END_FUNCTION / END_FUNCTION_BLOCK / END_PROGRAMIF ... END_IF条件分支CASE ... END_CASE分支FOR / WHILE / REPEAT ... END_FOR / END_WHILE / END_REPEAT循环TYPE ... END_TYPE类型声明WITH ... END_WITH区域(厂家扩展)
智能补全
输入前缀时自动弹出候选列表:
- 变量名(当前 POU + 全局变量)
- 功能块实例与成员
- 数据类型关键字
- 标准函数(
SQRT、SIN、ABS、LEN等) - 字符串与转换函数
- 内置 OB/FB 名称
参数提示
输入函数或 FB 名称后紧跟 ( 时,弹出参数列表提示:
- 参数名称与数据类型
- 输入/输出方向(VAR_INPUT / VAR_OUTPUT / VAR_IN_OUT)
- 当前参数高亮定位
- 已有默认值的参数灰显
重构支持
- 重命名符号:变量、FB 实例、POU 名称的跨文件重命名
- 提取变量:选中表达式 → 提取为中间变量
- 提取函数:选中代码块 → 提取为独立 FUNCTION
- 跳转定义:
F12跳转到变量声明或 POU 定义 - 查找所有引用:
Shift + F12列出所有引用位置 - 格式化文档:
Ctrl + K, Ctrl + D统一缩进与对齐
组织块(Organization Blocks)
组织块(OB)是 PLC 程序与操作系统之间的接口。每个 OB 有固定的优先级和触发条件,由 PLC 运行时自动调用。
主循环 OB
| OB 编号 | 名称 | 触发条件 | 优先级 | 说明 |
|---|---|---|---|---|
| OB1 | 主循环 | 循环执行 | 1(最低) | 主程序周期扫描,默认所有项目必须至少有一个 OB1 |
OB1 是 PLC 程序的核心,PLC 运行时在每个扫描周期中自动调用一次 OB1。扫描周期由系统配置决定(典型值 1ms ~ 100ms)。
启动 OB
| OB 编号 | 名称 | 触发条件 | 优先级 | 说明 |
|---|---|---|---|---|
| OB100 | 暖启动 | CPU 上电 | 最高 | 执行一次,用于初始化全局变量和硬件 |
OB100 在 PLC 从 STOP 切换到 RUN 时执行一次。常用于:
- 变量初始值设定
- 硬件模块配置校验
- 通讯连接建立
- 故障恢复后的状态恢复
时间中断 OB
| OB 编号 | 触发频率 | 优先级 | 典型用途 |
|---|---|---|---|
| OB30 ~ OB38 | 周期性(10ms ~ 60s 可配置) | 2 ~ 10 | PID 调节、高速计数、位置采样 |
每个循环中断 OB 可独立配置间隔时间。高频率 OB(如 OB30 配 10ms)用于对时间精度要求高的任务。
日钟中断 OB
| OB 编号 | 触发条件 | 优先级 | 典型用途 |
|---|---|---|---|
| OB20 ~ OB23 | 指定时刻(每日/每周/每月) | 3 ~ 6 | 日报统计、设备巡检、定时切换 |
错误 OB
| OB 编号 | 触发条件 | 优先级 | 默认响应 |
|---|---|---|---|
| OB80 | 扫描周期超时 | 26 | 记录错误,继续运行 |
| OB81 | 电源故障 | 26 | 记录错误,继续运行 |
| OB82 | 诊断中断 | 26 | 记录诊断消息 |
| OB83 | 插拔中断 | 26 | 记录模块变更 |
| OB84 | CPU 硬件故障 | 26 | 记录错误 |
| OB85 | 程序执行错误 | 26 | 记录错误,跳过故障块 |
| OB86 | 机架/站故障 | 26 | 记录分布式 IO 故障 |
| OB87 | 通讯错误 | 26 | 记录通讯失败 |
| OB88 | 中断堆栈溢出 | 26 | 记录溢出 |
| OB89 | 编程错误 | 26 | 记录语法/逻辑错误 |
| OB90 | 后台循环 | 26(最低) | 空闲时执行低优先级任务 |
| OB91 | 多处理器通讯 | 26 | 多 CPU 同步 |
| OB92 | IO 冗余故障 | 26 | 记录冗余切换 |
| OB93 | 温度/环境告警 | 26 | 记录环境异常 |
| OB94 | 存储区损坏 | 26 | 记录存储错误 |
| OB95 | 用户自定义 | 26 | 厂家保留 |
| OB96 | 用户自定义 | 26 | 厂家保留 |
| OB97 | 用户自定义 | 26 | 厂家保留 |
| OB98 | 用户自定义 | 26 | 厂家保留 |
| OB99 | 用户自定义 | 26 | 厂家保留 |
| OB121 | 编程错误 | 27 | 记录错误,停止当前块 |
| OB122 | IO 访问错误 | 27 | 记录 IO 错误 |
即使未编写对应的错误 OB,PLC 运行时也不会因此停机——系统会使用默认处理方式(记录诊断并继续)。编写错误 OB 可以让工程师在故障发生时执行自定义恢复逻辑,例如切换冗余通道或触发安全停机序列。
程序执行模型
循环扫描
PLC 程序执行遵循"输入采样 → 程序执行 → 输出刷新"的循环扫描模型:
┌─────────────────────────────────────────────────────┐
│ 一个扫描周期 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 读取输入 │ │ 执行程序 │ │ 刷新输出 │ │
│ │ (采样) │───▶│ (OB 调度) │───▶│ (写映像) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 过程映像 OB 链 过程映像 │
│ 输入区 执行 输出区 │
└─────────────────────────────────────────────────────┘
- 读取输入:PLC 将物理输入模块的状态批量复制到过程映像输入区(PII)
- 执行程序:按优先级顺序执行 OB1 及所有已触发的中断 OB
- 刷新输出:将**过程映像输出区(PIQ)**批量写入物理输出模块
这个模型保证了扫描周期内输入输出的一致性——程序执行期间输入值不会变化。
OB 优先级调度
OB 按优先级执行,优先级数值越低表示优先级越高:
// Darra PLC 扫描周期示例
// 周期设置: 10ms
// 中断配置: OB30 (5ms), OB35 (50ms)
PROGRAM Main // 优先级 1 (最低)
// 10ms 执行一次
// 可被所有中断 OB 打断
// 执行过程中若 5ms 定时到:
// 1. OB1 暂停
// 2. OB30 执行 (优先级 2)
// 3. OB30 执行完毕后, OB1 继续
END_PROGRAM
调度规则:
- 优先级高的 OB 可以打断优先级低的 OB
- 同优先级 OB 按触发时间顺序执行
- 错误 OB(OB80 ~ OB99)优先级高于普通 OB
- OB90(后台循环)仅在无其他 OB 等待时执行
- OB1 执行时间超过配置的扫描周期时触发 OB80
任务看门狗
每个循环中断 OB 和 OB1 均有独立看门狗:
| 参数 | 默认值 | 说明 |
|---|---|---|
| 扫描周期监视 | 周期的 2 倍 | OB1 执行超时触发 OB80 |
| 循环中断监视 | 配置周期的 1.5 倍 | OB30 ~ OB38 超时触发 OB80 |
| 最大循环嵌套 | 16 层 | 中断嵌套深度限制 |
| 最大 OB 调用栈 | 32 层 | POU 调用深度限制 |
// 看门狗复位(厂家扩展)
FUNCTION_BLOCK FB_WatchdogSafe
VAR_INPUT
bTrigger : BOOL;
tTimeout : TIME;
END_VAR
VAR_OUTPUT
bAlarm : BOOL;
END_VAR
VAR
tonWatch : TON;
END_VAR
tonWatch(IN := bTrigger, PT := tTimeout);
// 看门狗触发 → 安全输出
IF tonWatch.Q THEN
bAlarm := TRUE;
// 此处执行安全动作:输出清零 / 急停 / 报警
END_IF;
END_FUNCTION_BLOCK
执行模式
| 模式 | 说明 | 触发方式 |
|---|---|---|
| RUN | 正常循环执行 | 启动后默认 |
| STOP | 停止程序执行,输出保持或清零 | 编程设备命令 / 故障 |
| STARTUP | 暖启动(OB100 执行) | 上电 / STOP→RUN |
| HOLD | 冻结所有输出(调试用) | 调试命令 |
与 IEC 61131-3 标准的兼容性
Darra SCL 实现覆盖 IEC 61131-3 Third Edition 的全部必备要素:
| 标准要素 | 支持状态 | 说明 |
|---|---|---|
| 基本数据类型 | 全部支持 | BOOL / INT / DINT / REAL / LREAL / TIME / DATE / STRING 等 |
| 复合数据类型 | 全部支持 | ARRAY / STRUCT / STRING / WSTRING |
| 枚举 | 支持 | 支持显式赋值与范围检查 |
| 引用 | 支持 | REF_TO / POINTER TO |
| 标准函数 | 全部支持 | SQRT / SIN / COS / ABS / LEN / FIND 等 |
| 标准功能块 | 全部支持 | TON / TOF / TP / SR / RS / CTU / CTD / CTRL |
| 组织块 | 支持全部标准 OB | OB1 / OB100 / OB30-38 / OB20-23 / OB80-99 |
| 多语言互调 | 支持 | SCL 可调用 LAD / CFC 编写的 FB |
| RETAIN | 支持 | 保持型变量在掉电后恢复 |
| CONSTANT | 支持 | 编译期常量,不可运行时修改 |
| VAR_IN_OUT | 支持 | 引用传递参数 |