# EL6695 桥接 8 字节双向实时通讯 — 设计文档 > **版本**: 1.0 > **日期**: 2026-07-27 > **状态**: 已实现并验证通过 > **前置文档**: `1kb-verification-progress.md`(1KB 版本演进史) --- ## 1. 系统概述 在 TwinCAT PC 与 J1900(无 TwinCAT 授权的 Linux RT 工控机)之间,通过 Beckhoff **EL6695 EtherCAT 桥接端子**实现 1 kHz 实时数据交换。本设计将过程数据(PDO)从 1KB 缩减为 **8 字节**,以降低总线负载并验证 EL6695 虚拟 PDO 的可配置性。 ### 1.1 硬件拓扑 ``` TwinCAT PC (Windows, TwinCAT 3.1.4026) │ 网卡: 以太网 2 (TwinCAT-Intel PCI, MAC 00-15-17-8A-B3-DA) │ IP: 169.254.176.217 / AmsNetId: 169.254.176.217.1.1 ▼ EK1100 (总线耦合器) ├─ EL2202 (2ch DO, 信号源) ├─ EL1252 (2ch DI + DC 锁存) ├─ EL2262 (2ch DO, DC 同步) ├─ EL2008 (8ch DO) ├─ EL6851-0010 (DMX) ├─ EL6022 (RS422/RS485) ├─ EL6695 (EtherCAT 桥接端子, Primary 侧) ◄── 本设计核心 └─ EL9011 (总线末端) EL6695 X1 (Secondary 侧) │ ▼ J1900 (Linux 6.6.135-rt74, PREEMPT_RT) IP: 192.168.68.100 (经跳板机 192.168.58.8 SSH 访问) eth1 直连 EL6695 次要端口 ``` ### 1.2 角色分工 | 侧 | 角色 | 运行实体 | 职责 | |---|---|---|---| | TwinCAT PC | EtherCAT 主站 (Primary) | TwinCAT 实时内核 + PLC (PlcTask, 1ms) | 写 aOut1(8B 测试图案)→ 读 aIn1 并校验 | | J1900 | EtherCAT 主站 (Secondary) | `el6695_rt`(Rust/ethercrab, FIFO 80, 核 2) | 读 rx(8B)→ 逐字节 XOR 0x5A → 写 tx | EL6695 内部含双 EtherCAT 从站控制器与共享 DPRAM,两侧各有独立的 SM/FMMU 寄存器组,固件在两侧 DPRAM 区间转发过程数据。 --- ## 2. EL6695 配置设计 ### 2.1 PDO 设计 EL6695 的 IO PDO 在 ESI 中标记为 `Virtual="1"`,尺寸由主站配置决定: | PDO | 索引 | 方向(Primary 视角) | SyncMan | 条目 | 尺寸 | |---|---|---|---|---|---| | IO Outputs | 0x1608 | TwinCAT → J1900 | SM2 | 0x7000:01, `ARRAY [0..7] OF BYTE` | 8 B | | IO Inputs | 0x1A08 | J1900 → TwinCAT | SM3 | 0x6000:01, `ARRAY [0..7] OF BYTE` | 8 B | **重要设计决策:不为 SM3 分配 0x1A01 "SYNC Inputs" 诊断 PDO。** 1KB 配置中 SM3 长度为 1026 = 2B 诊断 + 1024B payload。EL6695 固件按 PDO 赋值计算 SM 长度要求;TwinCAT 在 8B 配置下重算 SM3 长度时未计入 2B 诊断,导致设备拒绝(`sync manager 3: size not allowed (min 0xa, max 0xa)`)。取消诊断 PDO 赋值后,两侧统一为纯 8B payload。代价:失去 "TxPDO toggle / External device not connected" 等在线状态位。 ### 2.2 SyncManager 配置(Primary 侧, tsproj) | SM | 起始地址 | 长度 | ctrl | 用途 | |---|---|---|---|---| | SM0 | 0x1000 | 1024 | 0x26 | 邮箱输出(CoE/EoE/FoE) | | SM1 | 0x1600 | 1024 | 0x22 | 邮箱输入 | | SM2 | 0x1C00 | **8** | 0x64 | 过程数据输出(TwinCAT→J1900) | | SM3 | 0x8E00 | **8** | 0x20 | 过程数据输入(J1900→TwinCAT) | tsproj 中的十六进制记录(24 字节,激活时由 TwinCAT 校验/重算): ``` SM2: 001c08006400010003000000000000000000001c64010000 SM3: 008e08002000010004000000000000000200008e20010000 ``` ### 2.3 类型系统与 GUID 编码 TwinCAT 对 PDO 条目类型的解析方式直接决定配置能否生效: - **基类型**(如 `ULINT`)按名称解析; - **数组类型**必须提供 GUID,且在 `` 段中声明一致的 `GUID + 名称`; - TwinCAT 为 PLC 生成的数组类型使用**随机 GUID**(CoCreateGuid); - 历史 TwinCAT 项目中的 `{18071995-0000-0000-0000-00XX00YYYYYY}` 形式 GUID,后缀 `YYYYYY` = 元素个数(十六进制),如 `000300000400` = 1024 元素 BYTE 数组。**手写该命名空间的 GUID 会被 TwinCAT 类型系统错误回退**(名称被改写、SM 被清零),不可使用。 本设计的最终方案:**直接复用 PLC 构建时生成的数组类型**: `{FE565D85-927B-2D08-1043-124F5F921E35}` = `ARRAY [0..7] OF BYTE`,PDO 条目与 PLC 变量类型完全一致。 ### 2.4 PLC ↔ IO 链接 | 链接 | VarA | VarB | 尺寸 | |---|---|---|---| | 输入 | `PlcTask Inputs^GVL_Sync.aIn1` | `IO Inputs^IO Inputs` | 8 B | | 输出 | `PlcTask Outputs^GVL_Sync.aOut1` | `IO Outputs^IO Outputs` | 8 B | **关键教训**:链接指向数组元素 `IO Inputs[0]` 时,TwinCAT 按两侧较小者建链(1 字节);指向不存在的路径时映射 0 字节。必须通过自动化 API **`ITcSysManager.LinkVariables`** 以整数组路径创建链接(脚本 `scripts/tc-relink.ps1`)。 --- ## 3. 数据协议设计(8B 逐字节验证协议) 设计目标:证明 **8 个字节中的每一个**都真实地完成了 TwinCAT → J1900 → 软件处理 → TwinCAT 的完整环路,而非只有低位计数器字节在动。 ### 3.1 发送图案(TwinCAT,每周期) ``` aOut1[i] = (nCycles + i*32) mod 256, i = 0..7 ``` - 8 个字节互不相同(步进 32,模 256 下 8 个值不重叠); - 每个字节每周期都变化; - 图案周期 256 个 PLC 周期,任意 16 个连续周期的 8 字节组**全局唯一**(支撑延迟匹配)。 ### 3.2 J1900 变换 ``` tx[i] = rx[i] XOR 0x5A, i = 0..7 ``` 逐字节独立的读-算-写。XOR 0x5A 是软件才能完成的变换,可证明数据经过了 J1900 的处理而非硬件透传;同时逐字节可逆,便于 TwinCAT 校验。 ### 3.3 TwinCAT 校验(发送历史环) PLC 维护 16 槽发送历史环 `aHist[0..15, 0..7]`,每周期在槽位 `nCycles mod 16` 保存刚发送的 8 字节: ``` bSync = TRUE ⟺ ∃ h∈[0,15]: ∀i∈[0,7]: (aIn1[i] XOR 0x5A) == aHist[h, i] nMatchLag = 匹配槽对应的延迟周期数 (实测稳定为 4) ``` 历史环设计天然容忍桥接管线延迟,无需预设固定延迟值;16 槽 >> 实测延迟 4 周期,且不会误判(256 周期内图案唯一)。 ### 3.4 派生指标 | 变量 | 含义 | |---|---| | `bSync` | 本周期回显 8 字节整组匹配成功 | | `nMatchLag` | 桥接往返延迟(PLC 周期数) | | `nReturnedSeq` | 回显对应的原始发送周期序号 | | `nMismatch` | 失配周期累计计数 | | `bProcessingOk` | = bSync(J1900 处理证明) | | `nRttMin/Max/Sum/Count` | RTT 统计 | --- ## 4. J1900 侧软件设计(`el6695_rt`) ### 4.1 架构 Rust + ethercrab 0.7(本地 patch: `sdo_write_complete` 单报文 Complete Access 下载,EL6695 固件强制要求)。1 kHz ticker 线程绑定核 2,SCHED_FIFO 80,mlockall。 ### 4.2 配置流程(ConfigOwner 角色) EL6695 固件约束:**主站切换时用户 OD 被擦除**,配置与数据循环必须在同一会话内完成。 1. 扫描网段(INIT→PREOP); 2. Complete Access 写 PDO 映射: - 8B 模式: `twin_layout_blob_sized(obj, 8)` — 单条目 `[64, 1, obj_lo, obj_hi]`(64 bit 挂在 0x6000:01 / 0x7000:01 上); - 1KB 模式: 35 条目 TwinCAT 行格式(1×240bit 真实对象 + 33×240bit 延续 + 1×32bit); 3. 写 PDO 赋值: 0x1C12=[0x1608], 0x1C13=[0x1A08](均不含诊断 PDO); 4. 写 SM2/SM3 寄存器(start=0x1C00/0x8E00, len=8, ctrl=0x64/0x20)与 FMMU; 5. INIT→PREOP→SAFEOP→OP; 6. 数据循环: 读 SM3 区域 8B → XOR 0x5A → 写 SM2 区域 8B,周期统计。 ### 4.3 关键常量(8B 构建) ```rust const SM_TX_ADDR: u16 = 0x1C00; // SM2: J1900 -> TwinCAT const SM_TX_LEN: usize = 8; const SM_RX_ADDR: u16 = 0x8E00; // SM3: TwinCAT -> J1900 const SM_RX_LEN: usize = 8; const SM_RX_PLD_LEN: usize = 8; const PAYLOAD_LEN: usize = 8; ``` 1KB 专属逻辑(256B 图案校验、偏移 264–280 的时间戳/抖动字段)用编译期常量守卫(`if PAYLOAD_LEN >= 280` 等),8B 构建时死代码消除。 ### 4.4 交叉编译 ```powershell $env:RUSTFLAGS="-C linker=rust-lld" # musl 目标无系统 cc,用 rust-lld cargo build --release --target x86_64-unknown-linux-musl --bin el6695-rt scp -J tonycao@192.168.58.8 target/x86_64-unknown-linux-musl/release/el6695-rt ` root@192.168.68.100:/root/gateway/el6695_rt_8b_xor ``` --- ## 5. TwinCAT 侧配置(tsproj 关键修改) | 位置 | 修改 | |---|---| | PDO 0x1608/0x1A08 条目 | `ARRAY [0..7] OF BYTE` | | `` | PLC 构建自动注册 `{FE565D85-...}` 类型 | | SM2 记录 | `001c080064...`(len=8) | | SM3 记录 | `008e080020...`(len=8) | | 0x1A01 Pdo 标签 | 移除 `SyncMan="3"` 属性(取消诊断 PDO 赋值) | | GVL_Sync | `aIn1/aOut1 : ARRAY[0..7] OF BYTE`,新增 `aHist`, `nMatchLag` | | MAIN | 逐字节图案 + 历史环校验(见 §3) | 备份: `TwinCATProject1.tsproj.bak-1k` 为可用的 1KB 配置。 --- ## 6. 已解决的典型陷阱(排查手册) | # | 现象 | 根因 | 解决 | |---|---|---|---| | 1 | 只有 1 字节过 bridge | 链接指向数组元素 `[0]`,按 1 字节建链 | `LinkVariables` 整数组链接 | | 2 | 改整数组名后变 0 字节 | 链接路径无法解析 | 同上,用 API 而非手改 XML | | 3 | 手写 GUID `000300000008` 后名称被改回 [0..1023]、SM3 清零 | TwinCAT 类型系统对该命名空间 GUID 回退 | 复用 PLC 生成的随机 GUID 类型 | | 4 | SM3 反复被清零,事件日志 `size not allowed (min 0xa, max 0xa)` | 0x1A01 诊断 PDO(2B)在赋值中,设备要求 SM3=10,TwinCAT 重算为 8 | 取消 0x1A01 的 SM3 赋值,两侧统一 8B | | 5 | J1900 配置后 TwinCAT 激活又清零 | EL6695 固件在主站切换时擦除用户 OD | 先激活 TwinCAT,再启动 ticker(同会话) | | 6 | `RPC_E_CALL_REJECTED` 持续拒绝 | VS 构建期间 COM 忙 | 全部 COM 调用加重试;先等 30s 再操作 | | 7 | bSync 用当前 nCycles 校验永假 | 桥接 ~4 周期管线延迟 | 发送历史环匹配 | --- ## 7. 限制与后续工作 1. **无诊断 PDO**: 8B 配置不含 "External device not connected" 等状态位。若需要,须在 SM3 同时赋值 0x1A01+0x1A08 并保持 SM3=10(TwinCAT 侧重算逻辑需相应修正)。 2. **延迟**: 往返固定 4 个 PLC 周期(≈4ms @1kHz),未做 DC 同步优化。 3. **尺寸扩展**: 本设计的修改方法(GUID 类型 + SM 记录 + J1900 常量)适用于任意 ≤240bit 的单条目尺寸;更大尺寸需在 `twin_layout_blob_sized` 中恢复多条目延续格式。