跳到主要内容

IO 地址映射

Darra PLC 的 IO 地址映射系统实现 IEC 61131-3 标准地址语法,支持 PLC 地址与硬件端口的双向映射,并提供冲突检测和自动优化功能。

IEC 地址语法

标准 IEC 61131-3 直接表示地址格式:

%<区域><宽度><字节偏移>[.<位偏移>]
组成部分可选值说明
区域I, Q, M, V输入/输出/标记/变量
宽度X, B, W, D位/字节/字/双字
字节偏移0~65535区域内字节地址
位偏移0~7仅 X (位) 宽度使用

地址示例

地址含义
%IX0.0输入区第 0 字节第 0 位 (DI)
%IX0.7输入区第 0 字节第 7 位
%IB0输入区第 0 字节 (8 个 DI)
%IW100输入区第 100-101 字节 (16 位模拟量)
%ID200输入区第 200-203 字节 (32 位浮点)
%QX0.0输出区第 0 字节第 0 位 (DO)
%QW0输出区第 0-1 字节 (16 位模拟输出)
%MW500标记区第 500-501 字节 (内部变量)

IOMapping — 声明式地址映射

IOMapping 管理 PLC 地址的声明与分配:

var mapping = new IOMapping();

// 声明 IO 点位
mapping.Add(new IOPoint {
Name = "工件到位传感器",
Address = "%IX0.0",
Type = IOType.DigitalInput,
Description = "光电传感器检测工件到位"
});

mapping.Add(new IOPoint {
Name = "夹爪开",
Address = "%QX0.0",
Type = IOType.DigitalOutput
});

mapping.Add(new IOPoint {
Name = "温度采集",
Address = "%IW100",
Type = IOType.AnalogInput,
ScaleFactor = 0.1, // 原始值 × 0.1 = 实际温度 (°C)
Unit = "°C"
});

IOAddressMapping — 双向映射

IOAddressMapping 建立 PLC 地址与物理硬件端口的双向映射关系:

var addrMap = new IOAddressMapping();

// PLC 地址 → 硬件端口
addrMap.Bind("%IX0.0", new HardwarePort {
Protocol = "EtherCAT",
SlaveIndex = 2,
PdoIndex = 0x6000,
SubIndex = 0x01,
BitOffset = 0
});

// 双向查询
var hwPort = addrMap.GetHardwarePort("%IX0.0"); // PLC → 硬件
var plcAddr = addrMap.GetPLCAddress(hwPort); // 硬件 → PLC

IOAddressValidator — 地址校验

在配置保存前自动校验地址合法性:

校验项说明错误示例
语法检查地址格式是否符合 IEC 标准%IX0.8 (位偏移超范围)
范围检查字节偏移是否超出区域容量%IW70000 (超出 64K)
类型匹配DI 必须映射到 I 区, DO 到 Q 区%QX0.0 映射到输入模块
对齐检查W/D 类型地址是否字节对齐%IW1 (奇数地址)

IOConflictChecker — 冲突检测

检测地址映射中的重叠和冲突:

var checker = new IOConflictChecker();
var conflicts = checker.Check(mapping);

foreach (var conflict in conflicts)
{
Console.WriteLine($"冲突: {conflict.Address1}{conflict.Address2} 地址重叠");
}

冲突类型

冲突类型说明
完全重叠两个点位使用同一地址
部分重叠不同宽度的地址区间交叉 (如 %IB0 与 %IX0.3)
隐式冲突字/双字地址覆盖了已分配的位地址
冲突处理

地址冲突不会阻止编译,但会在编译时产生警告。冲突的地址在运行时可能导致数据互相覆盖,应在部署前解决所有冲突。

AddressOptimizer — 地址优化

自动优化 IO 地址分配,减少通讯开销:

优化策略:

策略说明
间隔合并相邻地址间隔 < 128B 时合并为连续读写块
对齐填充在块边界填充至字对齐
排序优化按地址升序排列,减少寻址跳转
协议分组同一硬件设备的地址优先合并

IOVariableGenerator — 自动变量生成

从 IO 点位配置自动生成 PLC 变量表条目:

var generator = new IOVariableGenerator();
var variables = generator.Generate(mapping);

// 生成结果示例:
// VAR_GLOBAL
// 工件到位传感器 AT %IX0.0 : BOOL;
// 夹爪开 AT %QX0.0 : BOOL;
// 温度采集 AT %IW100 : INT;
// END_VAR

DeviceDescriptionImporter — 设备描述导入

支持从标准设备描述文件自动生成 IO 映射:

格式来源说明
ESI (XML)EtherCAT 从站PDO 定义 → IO 地址
EDSCANopen对象字典 → IO 地址
var importer = new DeviceDescriptionImporter();
var ioConfig = importer.ImportESI("Beckhoff_EL1008.xml");
// 自动生成 8 个 DI 点位映射

过程映像

IEC 61131-3 要求 PLC 使用"过程映像"的概念, 保证一个扫描周期内看到的 IO 数据是一致快照:

扫描周期 N 开始:
1. [输入映像更新]
硬件 DI → 驱动 Read → 输入映像缓冲区
(此时输入映像和硬件实际 DI 一致)

2. [程序执行]
程序读写 PLC 变量
所有对 %I* 的读 → 取输入映像
所有对 %Q* 的写 → 写输出映像 (不立即写硬件)

3. [输出映像下发]
输出映像缓冲区 → 驱动 Write → 硬件 DO

扫描周期 N 结束

这样程序看到的是"一致的" IO, 不会在计算过程中途被输入变化打断, 避免状态机走错。

映像存储布局

输入映像和输出映像是两个连续的字节数组, %I / %Q 地址就是字节偏移:

InputImage: byte[8192]    (64 KB * 1/8 bit)
[0] = %IB0 = %IX0.0~0.7
[1] = %IB1
...
[100] = %IB100 (可作为 %IW100 前半)

OutputImage: byte[8192]
[0..] = %QB0..

Marker 区 (%M) 是第三个连续数组, 用于内部状态保存, 不与硬件交互。

映射到驱动

驱动的职责是把硬件数据搬运到映像指定的字节偏移。以 EtherCAT 为例:

Slave 1 (EL1008) PDO = [DI0..DI7] (1 byte)
Mapping.Add:
SourceOffset = Slave1.PDO.Offset (在 LRW 帧里的位置)
TargetAddress = %IB0 (输入映像第 0 字节)
Length = 1 byte

Slave 2 (EL3064) PDO = [AI1(int16), AI2(int16), AI3(int16), AI4(int16)]
Mapping.Add:
SourceOffset = Slave2.PDO.Offset
TargetAddress = %IW100
Length = 8 bytes

驱动每个周期执行这些映射, 把帧内字节复制到映像对应位置, 或反向。

地址冲突预防

常见冲突类型:

字节冲突

%IW100 覆盖 %IB100, %IB101
%IB100 又被声明为独立变量
→ 冲突

位冲突

%IB0 = 整字节声明 (8 个 DI)
%IX0.3 = 单独声明为另一个变量
→ 部分重叠, 语义歧义

跨设备冲突

EtherCAT Slave 1 映射到 %IX0.0~0.7
Modbus 网关也映射到 %IX0.0~0.7
→ 两个设备都写同一字节, 最后写的胜出

IOAddressValidator 在编译前检查, 发现冲突报警。

符号与地址双命名

IEC 61131-3 支持变量既有符号名又有地址:

VAR_GLOBAL
StartButton AT %IX0.0 : BOOL; (* 启动按钮 *)
TempSensor1 AT %IW100 : INT; (* 温度1 *)
MotorRun AT %QX1.0 : BOOL; (* 电机启动 *)
END_VAR

程序里两种方式都能用:

IF %IX0.0 OR StartButton THEN ...

IDE 会在符号重命名时自动更新代码引用, 减少硬编码风险。

自动分配算法

AddressAllocator 按设备类型分段自动分配:

Input:
[新增] Slave1 (EL1008): 8 DI
[新增] Slave2 (EL3064): 4 AI
[已有] Slave3: %IW200 (已占 4 WORD)

Algorithm:
DI 区: 起点 %IX0, 向上找第一个可用字节
→ 找到 %IX0
→ Slave1 分配 %IX0.0~0.7

AI 区: 起点 %IW100, 向上找 8 字节可用空间
→ 找到 %IW100 (未占)
→ Slave2 分配 %IW100~107
→ Slave3 保持 %IW200~207

手动调整的地址受保护, 自动分配不会覆盖。

混合字和位

IEC 允许位偏移落在 WORD 里:

%IX0.0   ← 同一字节
%IX0.15 ← WORD 的最高位 (在 Siemens S7 语法)

但 Darra PLC 的 IEC 实现只用字节+位 (0~7), 不用 WORD+位。跨 S7 导入时 IDE 自动转换 M0.15M1.7

大端 / 小端

Darra PLC 内部存储用小端 (x86 原生), 写入 %IW 时低字节在前:

int16 value = 0x1234;
memcpy(InputImage + 100, &value, 2);
// InputImage[100] = 0x34 (低)
// InputImage[101] = 0x12 (高)

但 Modbus / S7 / MC 等协议用大端, 驱动层在搬运时做字节翻转, 保证 PLC 程序看到的是正确数值。这一步对用户透明, 程序里 MW100 = 0x1234 就是 0x1234。

符号引用库

IDE 支持"引用库", 共享多个项目的标准地址:

StandardIO.dpllib:
EmergencyStop AT %IX100.0 : BOOL;
PowerOn AT %IX100.1 : BOOL;
SafetyDoor AT %IX100.2 : BOOL;
MainContactor AT %QX100.0 : BOOL;

多个项目引用同一个库, 地址规划统一, 避免手写重复。

导出映射报表

项目 → 导出 → IO 映射报表

输出 Excel 或 PDF:

PLC 地址符号名类型硬件位置单位量程注释
%IX0.0启动按钮BOOL机架1 槽0 通道0--绿色蘑菇头
%IW100炉温1INTSlave3 AI1°C0~600Pt100

用于交付电气工程师、设备手册。

排错

症状处理
编译提示"地址重叠"打开"地址冲突检查"视图, 按冲突表逐个解决
程序读到错的值检查字节序, Modbus 默认 ABCD
位地址超 .7Darra 只支持 0~7, 改用 WORD 访问
自动分配不稳定固定关键地址, 然后重新分配
导入 S7 地址失败S7 用 I0.0, IDE 需要 %IX0.0, 转换时注意