跳到主要内容

OB 组织块调度

组织块 (Organization Block, OB) 是 PLC 程序的执行入口。Darra PLC 支持 12 种 OB 类型,由 OB 调度器按优先级进行抢占式调度。

OB 类型总览

OB名称触发方式优先级典型用途
OB1主循环每个扫描周期1 (最低)主程序逻辑
OB10异步事件事件触发8通讯回调、报警处理
OB20延时中断一次性延时触发10延时后执行操作
OB30周期中断固定时间间隔12闭环控制 (PID)
OB35快速周期中断短间隔 (≥1ms)14高速计数、位置环
OB40硬件中断IO 边沿触发16紧急响应
OB80时间错误扫描超时20过载诊断
OB82IO 访问错误IO 读写失败18IO 故障处理
OB100启动PLC 冷启动初始化
OB101热重启PLC 热重启恢复状态
OB121编程错误运行时异常22除零/溢出处理
OB122IO 错误模块故障22模块离线处理

OB1 — 主循环

OB1 是 PLC 的主程序,每个扫描周期执行一次:

PROGRAM OB1
VAR
fbMotor : FB_MotorControl;
fbConveyor : FB_ConveyorControl;
END_VAR

// 每个扫描周期执行
fbMotor(bStart := %IX0.0, bStop := %IX0.1);
fbConveyor(bEnable := fbMotor.bRunning);

%QX0.0 := fbMotor.bRunning;
%QX0.1 := fbConveyor.bActive;

END_PROGRAM
扫描周期

OB1 的执行周期即 PLC 的扫描周期。默认 50ms,可配置范围 1ms ~ 10000ms。实际周期 = IO 输入读取 + OB1 执行 + IO 输出写入 + 高优先级 OB 插入时间。

OB10 — 异步事件

由特定事件触发执行,适合处理非周期性任务:

PROGRAM OB10
VAR
iEventId : INT;
END_VAR

// 事件分发
CASE iEventId OF
1: // 通讯连接事件
HandleConnection();
2: // 报警触发事件
HandleAlarm();
3: // 配方切换事件
LoadRecipe(iRecipeNo);
END_CASE;

END_PROGRAM

OB30 — 周期中断

以固定时间间隔执行,不受 OB1 扫描周期影响。适合需要精确时间基准的控制算法:

PROGRAM OB30   // 配置: 10ms 周期
VAR
fbPID_Temp : FB_PID;
rActTemp : REAL;
rSetTemp : REAL;
rOutput : REAL;
END_VAR

// 每 10ms 精确执行
rActTemp := INT_TO_REAL(%IW100) * 0.1; // 温度传感器
rSetTemp := %MD200; // HMI 设定值

fbPID_Temp(
rSetpoint := rSetTemp,
rFeedback := rActTemp,
rKp := 2.0,
rKi := 0.5,
rKd := 0.1
);

rOutput := fbPID_Temp.rOutput;
%QW200 := REAL_TO_INT(rOutput * 100.0); // 输出到执行器

END_PROGRAM
周期中断配置

OB30 的执行间隔在项目配置中设定。多个 OB30 实例可配置不同间隔(如 OB30=10ms、OB31=20ms)。实时模式下间隔精度可达微秒级。

OB35 — 快速周期中断

高优先级的快速中断,适合运动控制和高速计数:

PROGRAM OB35   // 配置: 1ms 周期
VAR
diEncoder : DINT;
diLastEncoder : DINT;
rVelocity : REAL;
END_VAR

// 每 1ms 执行 — 高速位置环
diEncoder := %ID0; // 编码器反馈
rVelocity := DINT_TO_REAL(diEncoder - diLastEncoder) / 0.001; // 速度估算
diLastEncoder := diEncoder;

END_PROGRAM

OB40 — 硬件中断

由物理 IO 信号的边沿触发,用于紧急事件响应:

PROGRAM OB40
VAR
iChannel : INT; // 触发通道号
END_VAR

// 硬件中断: 急停按钮
IF iChannel = 0 THEN
// 立即停止所有运动
MC_Stop_All();
bEmergencyStop := TRUE;
END_IF;

END_PROGRAM

OB80 — 时间错误

当 OB1 扫描周期超过配置的最大时间时触发:

PROGRAM OB80
VAR
tActCycle : TIME; // 实际扫描时间
tMaxCycle : TIME; // 配置的最大周期
END_VAR

// 记录超时事件
iOverrunCount := iOverrunCount + 1;

// 连续超时 → 降级处理
IF iOverrunCount > 10 THEN
bReducedMode := TRUE; // 进入降级模式
END_IF;

END_PROGRAM

OB100 — 启动 (冷启动)

PLC 冷启动时执行一次,用于初始化:

PROGRAM OB100

// 初始化全局变量
giMachineState := 0;
grSetTemperature := 25.0;

// 清零计数器
giPartCount := 0;
giErrorCount := 0;

// 初始化通讯
InitModbusConnection();
InitEtherCATMaster();

END_PROGRAM

OB121 / OB122 — 错误处理

PROGRAM OB121   // 编程错误
VAR
iErrorCode : INT;
iBlockId : INT;
END_VAR

// 处理运行时错误 (除零、数组越界等)
CASE iErrorCode OF
1: // 除零错误
LogError('除零错误', iBlockId);
2: // 数组越界
LogError('数组越界', iBlockId);
3: // 数值溢出
LogError('数值溢出', iBlockId);
END_CASE;

END_PROGRAM

优先级调度机制

OB 调度器采用基于优先级的抢占式调度:

优先级: OB121/122 > OB80 > OB40 > OB35 > OB30 > OB10 > OB1

执行示例:
┌─OB1────────┐ ┌─OB1(继续)──┐
│ │←OB30→│ │
│ ┌OB40┐ │ │ │
│ │ │ │ │ │
──┴────┴────┴──┴─────┴────────────┴──→ 时间
  • 高优先级 OB 可以抢占正在执行的低优先级 OB
  • 被抢占的 OB 在高优先级 OB 完成后恢复执行
  • 同一优先级的 OB 按触发时间顺序排队
周期裕量

OB30/OB35 的执行时间必须远小于其配置周期,否则会导致 OB80 时间错误。建议保持至少 50% 的周期裕量。使用性能面板监控各 OB 的实际执行时间。

配置方法

在项目配置中设定各 OB 的参数:

参数说明默认值范围
OB1.CycleTime主循环周期50ms1ms ~ 10s
OB30.Interval周期中断间隔10ms1ms ~ 1s
OB35.Interval快速中断间隔1ms125μs ~ 100ms
OB1.MaxCycleTime最大允许扫描时间200ms> CycleTime
OB1.WatchdogTime看门狗超时500ms> MaxCycleTime

OB20 — 延时中断 (Time-Delayed)

SRT_DINT 指令启动一个一次性延时触发器, 到达时间后调用 OB20:

// 用户代码触发延时
SRT_DINT(OB_NR := 20, DTIME := T#5s, SIGN := 16#ABCD);
// 5 秒后系统调用 OB20

PROGRAM OB20
VAR_INPUT
iSign : WORD; // SRT_DINT 传入的标识
END_VAR

// 处理 5 秒后要做的动作
CASE iSign OF
16#ABCD:
CloseCoolantValve();
16#DEF0:
TriggerAlarm();
END_CASE;

END_PROGRAM

OB82 — IO 访问错误

当 IO 模块离线、读/写失败时触发:

PROGRAM OB82
VAR_INPUT
iFaultingModule : INT; // 故障模块编号
iErrorCode : INT; // 错误类型
END_VAR

// 记录 IO 故障
LogError('IO_Fault',
'Module=', INT_TO_STRING(iFaultingModule),
'Code=', INT_TO_STRING(iErrorCode));

// 触发报警
bAlarm_IO := TRUE;

// 如果是安全关键 IO, 停 PLC
IF iFaultingModule IN [1, 2, 3] THEN // 安全 IO 列表
StopPLC();
END_IF;

END_PROGRAM

任务扫描统计 (Task Profiler)

Darra PLC 实时统计每个 OB 的执行时间, 通过 S 区和 TaskProfiler 对象访问:

统计指标

指标说明
MinCycleTime历史最短扫描时间
AvgCycleTimeEWMA 指数加权平均 (alpha=1/8, 短期波动敏感)
MaxCycleTime历史最长扫描时间
CurrCycleTime最近一次执行耗时
Jitter周期抖动 (Max - Min 或 标准差)
OverrunCount超过看门狗的次数
TotalRuns累计执行次数
TotalTime累计执行时间
CpuPercent占 CPU 百分比 (= TotalTime / WallClock)

访问示例

VAR
profiler : TaskProfiler;
stStats : ST_TaskStats;
END_VAR

// 读取 OB1 统计
stStats := profiler.GetStats(OB_ID := 1);

// 输出到 HMI
%MD500 := TIME_TO_REAL(stStats.MaxCycleTime); // HMI 显示
%MD504 := TIME_TO_REAL(stStats.AvgCycleTime);
%MW508 := INT_TO_INT(stStats.OverrunCount);

// 抖动过大报警
IF TIME_TO_MS(stStats.Jitter) > 5 THEN
LogWarn('OB1 jitter too high: ', TIME_TO_STRING(stStats.Jitter));
END_IF;

诊断仪表板

在 IDE 的 诊断 → 任务统计 面板可视化查看所有 OB 的性能数据:

┌────────┬─────────┬─────────┬─────────┬─────────┬──────────┐
│ OB │ Min │ Avg │ Max │ Jitter │ Overruns │
├────────┼─────────┼─────────┼─────────┼─────────┼──────────┤
│ OB1 │ 2.1ms │ 8.5ms │ 45ms │ 3ms │ 0 │
│ OB30 │ 850μs │ 1.2ms │ 2.8ms │ 0.5ms │ 0 │
│ OB35 │ 120μs │ 180μs │ 420μs │ 80μs │ 2 │
└────────┴─────────┴─────────┴─────────┴─────────┴──────────┘

周期抖动 (Jitter)

抖动是 PLC 实时性的核心指标, 反映周期性任务执行时刻的偏差:

理想:  ┃       ┃       ┃       ┃       ┃
↑ ↑ ↑ ↑ ↑
0ms 10ms 20ms 30ms 40ms

实际: ┃ ┃ ┃ ┃ ┃
↑ ↑ ↑ ↑ ↑
0ms 9.2ms 20.8ms 28.9ms 41.1ms
←jitter→

抖动来源:

  • 高优先级 OB 抢占
  • 系统中断 (NDIS / USB / SMI)
  • CPU 频率波动 (SpeedStep / Turbo Boost)
  • Windows 内核调度 (用户态运行时)

DarraRT PLC 通过以下手段把抖动控制在微秒级:

  1. 核心隔离: DarraRT.sys 锁定 CPU 核, 禁止 Windows 调度
  2. SMI 检测: 识别 BIOS 干扰并告警
  3. TSC 高精度时基: 微秒级对齐
  4. DPC/ISR 屏蔽: 禁止 APIC 中断干扰

典型指标 (1ms OB35):

硬件抖动
消费级 Intel i5 (无 RT 内核)50~200μs
DarraRT.sys + 核心隔离< 10μs
DarraRT.sys + Intel Xeon + RTSS< 3μs

排错表

问题可能原因解决
OB1 扫描时间忽高忽低被 OB30/35 抢占Max - Min, 若大则减少高优先级负载
OB80 频繁触发MaxCycleTime 太小或负载过重加大 MaxCycleTime 或优化 OB1 代码
OB35 抖动 > 100μs核心未隔离在内核驱动中启用 isolate
OB121 触发运行时异常 (除零/越界)加 guard, 检查数据
OB100 每次都进热启动条件不满足检查 NVRAM 电池和 RETAIN 区 CRC

最佳实践

  • OB1 代码简洁: 耗时任务移到 OB30/35 周期中断
  • OB35 < 1ms: 快速中断的代码必须极简
  • 留 50% 周期裕量: MaxCycleTime ≥ 2 × CycleTime
  • 看门狗: WatchdogTime ≥ 2 × MaxCycleTime
  • OB100 初始化全部全局变量: 包括 fbXxx 的内部状态
  • 所有错误 OB 都要写日志: OB80/82/121/122 不能只是跑一下就完事
  • 在线监视 Profiler: 部署后 24 小时观察抖动趋势

相关文档