标准功能块
Darra PLC 提供完整的 IEC 61131-3 标准功能块库,包括定时器、计数器、触发器和数学函数。这些功能块由 IecFunctionBlocks.cs 和 IEC61131Functions.cs 实现。
定时器
TON — 接通延时
输入持续为 TRUE 达到预设时间后,输出变为 TRUE。
VAR
tonDelay : TON;
END_VAR
tonDelay(IN := bInput, PT := T#3s);
// tonDelay.Q — 延时后输出 (BOOL)
// tonDelay.ET — 已经过时间 (TIME)
| 输入 | 类型 | 说明 |
|---|---|---|
IN | BOOL | 使能输入 |
PT | TIME | 预设时间 |
| 输出 | 类型 | 说明 |
|---|---|---|
Q | BOOL | IN 为 TRUE 且 ET >= PT 时为 TRUE |
ET | TIME | 经过时间 (IN=FALSE 时复位为 0) |
TOF — 断开延时
输入变为 FALSE 后,输出延时一段时间才变为 FALSE。
VAR
tofMotor : TOF;
END_VAR
tofMotor(IN := bMotorRun, PT := T#5s);
bCoolantOn := tofMotor.Q; // 电机停止后冷却液继续运行 5 秒
TP — 脉冲定时器
输入上升沿触发一个固定宽度的脉冲。
VAR
tpPulse : TP;
END_VAR
tpPulse(IN := bTrigger, PT := T#200ms);
bBuzzer := tpPulse.Q; // 蜂鸣 200ms
计数器
CTU — 递增计数器
VAR
ctuParts : CTU;
END_VAR
ctuParts(CU := bPartSensor, R := bReset, PV := 100);
// ctuParts.Q — CV >= PV 时为 TRUE (BOOL)
// ctuParts.CV — 当前计数值 (INT)
bBatchDone := ctuParts.Q;
iPartCount := ctuParts.CV;
CTD — 递减计数器
VAR
ctdRemain : CTD;
END_VAR
ctdRemain(CD := bPartSensor, LD := bLoadPreset, PV := 50);
// ctdRemain.Q — CV <= 0 时为 TRUE
// ctdRemain.CV — 当前计数值 (从 PV 递减)
bMaterialEmpty := ctdRemain.Q;
CTUD — 双向计数器
VAR
ctudStock : CTUD;
END_VAR
ctudStock(CU := bItemIn, CD := bItemOut, R := bReset, LD := bLoad, PV := 200);
// ctudStock.QU — CV >= PV (上溢出)
// ctudStock.QD — CV <= 0 (下溢出)
// ctudStock.CV — 当前计数值
触发器
SR — 置位优先触发器
VAR
srLatch : SR;
END_VAR
srLatch(SET1 := bSet, RESET := bReset);
bOutput := srLatch.Q1;
// SET1 和 RESET 同时为 TRUE 时,Q1 = TRUE (置位优先)
RS — 复位优先触发器
VAR
rsLatch : RS;
END_VAR
rsLatch(SET := bSet, RESET1 := bReset);
bOutput := rsLatch.Q1;
// SET 和 RESET1 同时为 TRUE 时,Q1 = FALSE (复位优先)
R_TRIG — 上升沿检测
VAR
rtSensor : R_TRIG;
END_VAR
rtSensor(CLK := bSensorInput);
IF rtSensor.Q THEN
iCount := iCount + 1; // 仅在上升沿计数一次
END_IF;
F_TRIG — 下降沿检测
VAR
ftButton : F_TRIG;
END_VAR
ftButton(CLK := bButton);
IF ftButton.Q THEN
// 按钮释放时执行
bToggle := NOT bToggle;
END_IF;
数学函数
基本数学
| 函数 | 说明 | 示例 |
|---|---|---|
ABS(x) | 绝对值 | ABS(-5.3) → 5.3 |
SQRT(x) | 平方根 | SQRT(16.0) → 4.0 |
LN(x) | 自然对数 | LN(2.718) → 1.0 |
LOG(x) | 常用对数 | LOG(100.0) → 2.0 |
EXP(x) | 指数 (e^x) | EXP(1.0) → 2.718 |
EXPT(x,y) | 幂运算 (x^y) | EXPT(2.0, 10.0) → 1024.0 |
三角函数
| 函数 | 说明 | 输入单位 |
|---|---|---|
SIN(x) | 正弦 | 弧度 |
COS(x) | 余弦 | 弧度 |
TAN(x) | 正切 | 弧度 |
ASIN(x) | 反正弦 | 弧度 |
ACOS(x) | 反余弦 | 弧度 |
ATAN(x) | 反正切 | 弧度 |
ATAN2(y,x) | 四象限反正切 | 弧度 |
选择与限制
// LIMIT — 限幅
rClamped := LIMIT(MN := 0.0, IN := rValue, MX := 100.0);
// SEL — 二选一 (G=FALSE 选 IN0, G=TRUE 选 IN1)
rResult := SEL(G := bSelect, IN0 := rValueA, IN1 := rValueB);
// MUX — 多选一 (按 K 索引选择)
rResult := MUX(K := iIndex, IN0 := 1.0, IN1 := 2.0, IN2 := 3.0);
// MIN / MAX
rMin := MIN(rA, rB, rC);
rMax := MAX(rA, rB, rC);
位操作
| 函数 | 说明 | 示例 |
|---|---|---|
SHL(x, n) | 左移 n 位 | SHL(1, 3) → 8 |
SHR(x, n) | 右移 n 位 | SHR(16, 2) → 4 |
ROL(x, n) | 循环左移 | |
ROR(x, n) | 循环右移 |
字符串函数
| 函数 | 说明 |
|---|---|
LEN(s) | 字符串长度 |
LEFT(s, n) | 左取 n 字符 |
RIGHT(s, n) | 右取 n 字符 |
MID(s, n, p) | 从 p 位置取 n 字符 |
CONCAT(s1, s2) | 拼接 |
FIND(s1, s2) | 查找子串位置 |
INSERT(s1, s2, p) | 插入 |
DELETE(s, n, p) | 删除 |
REPLACE(s1, s2, n, p) | 替换 |
数据移动
// MOVE — 赋值 (常用于 CFC/LAD)
MOVE(IN := rSource, OUT => rDest);
// 批量操作在 ST 中通常直接赋值
arDest := arSource; // 数组复制
stDest := stSource; // 结构体复制
性能提示
标准函数内部已高度优化, 执行效率远高于等价的 ST 手写循环。优先使用标准函数库而非自行实现。
面向对象 (OOP) 扩展
IEC 61131-3 Third Edition 引入面向对象特性, Darra PLC 完整支持。
继承 (EXTENDS)
FUNCTION_BLOCK 可继承另一个 FB, 复用代码并扩展:
// 基类: 通用设备
FUNCTION_BLOCK FB_Device
VAR_INPUT
bEnable : BOOL;
END_VAR
VAR_OUTPUT
bReady : BOOL;
iErrorCode : INT;
END_VAR
VAR
sDeviceName : STRING[32];
END_VAR
METHOD PUBLIC Initialize
sDeviceName := 'Device';
iErrorCode := 0;
END_METHOD
METHOD PUBLIC Diagnose : STRING[64]
Diagnose := CONCAT(sDeviceName, ': OK');
END_METHOD
END_FUNCTION_BLOCK
// 子类: 电机设备
FUNCTION_BLOCK FB_Motor EXTENDS FB_Device // 继承
VAR_INPUT
rSetSpeed : REAL;
END_VAR
VAR_OUTPUT
rActualSpeed : REAL;
END_VAR
VAR
rRampRate : REAL := 10.0;
END_VAR
// 覆盖基类方法
METHOD PUBLIC Initialize
SUPER^.Initialize(); // 调用父类
sDeviceName := 'Motor';
END_METHOD
METHOD PUBLIC SetRampRate
VAR_INPUT
rRate : REAL;
END_VAR
rRampRate := LIMIT(0.1, rRate, 100.0);
END_METHOD
END_FUNCTION_BLOCK
接口 (INTERFACE / IMPLEMENTS)
定义统一契约, 多个 FB 可实现同一接口, 支持多态:
// 定义接口
INTERFACE I_Controllable
METHOD Start : BOOL
END_METHOD
METHOD Stop : BOOL
END_METHOD
METHOD GetStatus : E_Status
END_METHOD
END_INTERFACE
// 实现接口
FUNCTION_BLOCK FB_Pump IMPLEMENTS I_Controllable
VAR
eStatus : E_Status;
END_VAR
METHOD Start : BOOL
eStatus := E_Status.RUNNING;
Start := TRUE;
END_METHOD
METHOD Stop : BOOL
eStatus := E_Status.STOPPED;
Stop := TRUE;
END_METHOD
METHOD GetStatus : E_Status
GetStatus := eStatus;
END_METHOD
END_FUNCTION_BLOCK
// 多态调用
VAR
pController : REF_TO I_Controllable;
fbPump : FB_Pump;
fbMotor : FB_Motor_Controllable;
END_VAR
// 运行时切换实例
IF bUsePump THEN
pController := REF(fbPump);
ELSE
pController := REF(fbMotor);
END_IF;
// 统一调用接口
pController^.Start(); // 调用具体实现
方法 (METHOD)
方法是 FB 上定义的函数, 带访问控制和返回值:
FUNCTION_BLOCK FB_Counter
VAR
iValue : INT;
END_VAR
// 公共方法
METHOD PUBLIC Increment : INT
iValue := iValue + 1;
Increment := iValue;
END_METHOD
// 公共方法带参数
METHOD PUBLIC AddValue : INT
VAR_INPUT
iDelta : INT;
END_VAR
iValue := iValue + iDelta;
AddValue := iValue;
END_METHOD
// 私有方法 (只能在 FB 内部调用)
METHOD PRIVATE Validate : BOOL
Validate := iValue >= 0 AND iValue <= 10000;
END_METHOD
// 只读属性
METHOD PUBLIC GET Value : INT
Value := iValue;
END_METHOD
// 读写属性
PROPERTY PUBLIC Counter : INT
GET
Counter := iValue;
END_GET
SET
IF Counter >= 0 THEN
iValue := Counter;
END_IF;
END_SET
END_PROPERTY
END_FUNCTION_BLOCK
// 调用
VAR
fbCnt : FB_Counter;
iNow : INT;
END_VAR
iNow := fbCnt.Increment();
iNow := fbCnt.AddValue(5);
iNow := fbCnt.Value; // 调 GET
fbCnt.Counter := 100; // 调 SET
访问控制
| 修饰符 | 可见性 |
|---|---|
PUBLIC | 外部 + 子类 (默认) |
PRIVATE | 仅本 FB 内部 |
PROTECTED | 本 FB + 子类 |
INTERNAL | 同项目 |
FINAL / ABSTRACT
// 抽象 FB, 必须被继承
FUNCTION_BLOCK ABSTRACT FB_ControlBase
METHOD ABSTRACT Execute // 必须被子类实现
END_METHOD
END_FUNCTION_BLOCK
// 终态 FB, 不能被继承
FUNCTION_BLOCK FINAL FB_SingletonLogger
// ...
END_FUNCTION_BLOCK
自定义功能块完整示例
综合应用继承、接口、方法:
// 通用斜坡生成器基类
FUNCTION_BLOCK FB_RampBase IMPLEMENTS I_Controllable
VAR_INPUT
bEnable : BOOL;
rTarget : REAL;
rRate : REAL := 10.0;
END_VAR
VAR_OUTPUT
rOutput : REAL;
bAtTarget : BOOL;
END_VAR
VAR
rLastOutput : REAL;
tonDelay : TON;
END_VAR
METHOD PUBLIC Start : BOOL
bEnable := TRUE;
Start := TRUE;
END_METHOD
METHOD PUBLIC Stop : BOOL
bEnable := FALSE;
Stop := TRUE;
END_METHOD
METHOD PUBLIC GetStatus : E_Status
IF bEnable THEN
GetStatus := E_Status.RUNNING;
ELSE
GetStatus := E_Status.STOPPED;
END_IF;
END_METHOD
// 主执行逻辑 (子类可覆盖)
METHOD PUBLIC Execute
IF bEnable THEN
IF rLastOutput < rTarget THEN
rLastOutput := MIN(rLastOutput + rRate, rTarget);
ELSE
rLastOutput := MAX(rLastOutput - rRate, rTarget);
END_IF;
END_IF;
rOutput := rLastOutput;
bAtTarget := ABS(rOutput - rTarget) < 0.01;
END_METHOD
END_FUNCTION_BLOCK
// S 形斜坡子类
FUNCTION_BLOCK FB_SCurveRamp EXTENDS FB_RampBase
VAR_INPUT
rJerk : REAL := 5.0;
END_VAR
VAR
rAccel : REAL;
END_VAR
// 覆盖 Execute 实现 S 形
METHOD PUBLIC Execute
IF bEnable THEN
// S 形加加速度逻辑
IF rLastOutput < rTarget THEN
rAccel := MIN(rAccel + rJerk, rRate);
rLastOutput := rLastOutput + rAccel * 0.001;
ELSE
rAccel := MAX(rAccel - rJerk, -rRate);
rLastOutput := rLastOutput + rAccel * 0.001;
END_IF;
END_IF;
rOutput := rLastOutput;
bAtTarget := ABS(rOutput - rTarget) < 0.01;
END_METHOD
END_FUNCTION_BLOCK
排错表
| 问题 | 原因 | 解决 |
|---|---|---|
| 方法调用找不到 | 方法是 PRIVATE | 改为 PUBLIC |
| SUPER^ 调用报错 | 基类无此方法 | 确认方法存在 |
| 接口实现不全 | 忘了实现某个方法 | 补实现 |
| FB 实例不共享数据 | 每实例独立 VAR | 用 VAR_STAT 共享 |
| 抽象 FB 可实例化 | 未标记 ABSTRACT | 加 ABSTRACT 关键字 |
| 多态失效 | 用具体类型而非接口 | 改用 REF_TO I_XXX |
最佳实践
- 单一职责: 每个 FB 只做一件事
- 优先组合: 组合 (has-a) 优于继承 (is-a)
- 接口小而精: 接口只暴露公共协议, 不要包含实现细节
- GET/SET 属性: 对外暴露数据优先用 PROPERTY 而非直接 VAR_OUTPUT
- 初始化放 Initialize 方法: OB100 统一调用所有
fbXxx.Initialize() - 诊断属性: 每个 FB 暴露
sDiagMsg : STRING便于在线监视 - 避免深层继承: 继承层级 ≤ 3 层