IO 系统概述
IO 系统连接 PLC 程序和现场硬件,支持 EtherCAT、Modbus、CANopen 等 100 多种工业协议。不论哪种协议,PLC 程序里写变量的方式完全一样。
IEC 61131-3 内存区域
Darra 遵循 IEC 61131-3 标准,用 4 个前缀区分用途:
| 区域 | 前缀 | 说明 | 访问权限 |
|---|---|---|---|
| I (Input) | %I | 物理输入 | 只读 |
| Q (Output) | %Q | 物理输出 | 读写 |
| M (Marker) | %M | 中间变量 | 读写 |
| V (Variable) | %V | 全局变量 | 读写 |
数据宽度
| 后缀 | 宽度 | 地址示例 | 说明 |
|---|---|---|---|
| X (Bit) | 1 bit | %IX0.0 | 单个 DI 点 |
| B (Byte) | 8 bit | %IB0 | 字节 |
| W (Word) | 16 bit | %IW100 | 字(模拟量) |
| D (DWord) | 32 bit | %ID200 | 双字(浮点) |
扫描周期
PLC 每个扫描周期分三个阶段:
输入在周期开始时一次性读完,程序执行期间输入不会变。所有输出在周期末尾一次性写出。这样 PLC 程序始终看到一致的 IO 快照。
核心功能
| 功能 | 说明 |
|---|---|
| 点位注册 | 统一管理所有 IO 点位及其地址 |
| 读写缓存 | 减少对硬件的重复访问 |
| 边沿检测 | 自动识别 DI 上升沿/下降沿 |
| 消抖处理 | 可配置消抖时间(默认 10ms) |
| 强制值 | 调试时锁定 IO 为指定值 |
| 变更通知 | 值变化时触发回调 |
支持的协议
| 类别 | 协议 |
|---|---|
| 实时以太网 | EtherCAT、EtherNet/IP |
| 现场总线 | CANopen、Modbus |
| 串行 | Modbus RTU、RS-232、RS-485 |
| 工业以太网 | Modbus TCP、S7、MC、FINS |
| IT 协议 | OPC UA、MQTT、HTTP、SQL |
新增协议、查找设备参数模板详见 基础使用 → 通讯协议。
地址映射
IO 地址映射支持双向查询:
PLC 地址 (%IX0.0) ←→ 硬件端口 (EtherCAT Slave 2, PDO 0x6000:01, Bit 0)
详见 IO 地址映射。
地址优化
IDE 自动优化 IO 地址分配:
- 间隔合并 — 相邻地址合并成连续块,减少读写次数
- 边界对齐 — 按字节/字/双字对齐,提高访问效率
- 冲突检测 — 自动发现地址重叠
HAL 层设计
所有 IO 协议通过硬件抽象层 (HAL) 接入 PLC 扫描引擎:
IHardwareDriver 接口只有三个核心方法:
public interface IHardwareDriver
{
void Read(IOBuffer inputBuffer); // 读硬件 → 输入映像
void Write(IOBuffer outputBuffer); // 输出映像 → 写硬件
DriverStatus GetStatus(); // 健康状态
}
PLC 每个扫描周期调用所有驱动的 Read → 执行程序 → 调用所有 Write。驱动实现对上层透明, 用户不用关心底层协议。
IOBuffer 结构
IOBuffer 是一个连续的字节数组, PLC 程序通过 %IX/%IW/%IB/%ID 定位到其中的字节偏移:
IOBuffer:
Offset 0: IX0.0~0.7 (8 bits 的 DI)
Offset 1: IX1.0~1.7
...
Offset 100: IW100 (2 bytes)
Offset 200: ID200 (4 bytes REAL)
不同协议的驱动通过自己的 PDO/寄存器映射把数据搬运到 IOBuffer 的对应偏移。
地址空间规划
推荐地址空间规划约定:
| 范围 | 用途 |
|---|---|
| %IX0~%IX100 | EtherCAT 从站 DI |
| %IX100~%IX200 | Modbus / CANopen DI |
| %IW200~%IW500 | 模拟输入 |
| %ID500~%ID800 | 伺服反馈 |
| %QX0~%QX100 | EtherCAT DO |
| %QX100~%QX200 | Modbus / CANopen DO |
| %QW200~%QW500 | 模拟输出 |
| %QD500~%QD800 | 伺服指令 |
| %MX0~%MX500 | 内部布尔标志 |
| %MW500~%MW2000 | 内部字变量 |
| %MD2000~%MD4000 | 内部实数变量 |
固定规划便于维护, 也方便文档交付。
扫描周期选择
周期长度决定 IO 响应时效:
| 周期 | 场景 | 代价 |
|---|---|---|
| 250 μs | 高端运动控制 | CPU 占用最高 |
| 500 μs | 中高端运动 | 隔离核心要够 |
| 1 ms | 常见运动 + IO | 典型配置 |
| 2~5 ms | 纯 IO 采集 | CPU 轻松 |
| 10~50 ms | 慢速监控 | 最省 |
选错周期的症状:
- 太短 → CPU 持续 100%, 偶发抖动
- 太长 → 输出响应滞后, 运动精度不够
调优方法: 先试 1ms, 看 CPU 占用 → 占用 < 50% 可以缩短 → 占用 > 80% 要延长。
过程映像模式
有两种映像模式:
同步映像 (默认)
周期开始:
1. 所有驱动 Read → 输入映像
2. 执行 PLC 程序 (读输入, 计算, 写输出)
3. 所有驱动 Write ← 输出映像
周期结束
程序看到"一致快照"。这是默认模式, 满足绝大多数 PLC 语义。
异步映像 (高级)
部分协议 (如 MQTT 订阅) 不是周期性采样, 数据可以随时到达。异步映像模式下:
- 输入映像在任意时刻被协议栈更新
- 程序看到的是"最新值", 不保证周期内一致
仅少数需要极低延迟的事件驱动场景启用。
强制值
调试时, 可以锁定某个 IO 的值, 程序写入和硬件采集都被忽略:
// 强制 %IX0.0 永远为 TRUE
ioManager.ForceValue("%IX0.0", true);
// 强制 %QW100 固定为 5000
ioManager.ForceValue("%QW100", (short)5000);
// 解除强制
ioManager.ReleaseForce("%IX0.0");
强制值与正常值同时显示在 IO 监视面板, 强制项有黄色标记。
生产环境禁止使用强制值。强制输出可能驱动真实设备产生未预期动作 (如气缸误动、电机乱转)。只在离线调试或仿真时使用。
边沿与消抖
对于数字输入, 内置信号处理:
- 上升沿 / 下降沿 (
R_TRIG,F_TRIG标准 FB) - 消抖滤波: 信号必须稳定 N 个扫描周期才视为变化
- 脉冲捕获: 即使比扫描周期短的脉冲也不丢
消抖时间在每个 IO 点的属性里配置, 不影响其他点。
模拟量处理
模拟量原始值一般是 ADC/DAC 的整数, 需要工程量转换:
// 配置: 原始值 0~32767 对应 0~10V
mapping.Add(new IOPoint {
Address = "%IW100",
ScaleFactor = 10.0 / 32767.0,
Offset = 0,
Unit = "V"
});
// PLC 读到的 %IW100 可以直接用, 已经是工程量
还支持:
- 极限值报警: 超上下限触发报警
- 死区过滤: 小于阈值的波动视为噪声
- 一阶滤波: 低通平滑
- 线性化: 对热电偶等非线性传感器分段插值
掉线保护
每个 IO 设备有通讯超时监测:
device.TimeoutMs = 3000;
device.OnTimeout = () => {
// 设为通讯故障
device.Status = DeviceStatus.Offline;
// 设置默认值 (由配置决定)
// - FreezeLast: 保持最后值
// - SetToSafe: 设为安全值
// - Passthrough: 不变, 由程序决定
};
推荐安全值方案: 输出设为 0 (停止), PLC 程序判断通讯故障标志后走安全分支。
诊断
IDE 内置 IO 诊断面板:
- 所有 IO 设备的实时状态 (在线/离线/错误)
- 每台设备的错误计数 (RX error / Timeout / CRC)
- 最近 100 条设备事件日志
- 对 EtherCAT 从站: 显示 AL_STATUS, 各端口状态
- 对 Modbus: 显示响应时间直方图
- 对 CANopen: 显示 NMT 状态和 EMCY 历史