- twin_layout_blob_sized generalized: multi-entry TwinCAT line format (1 real @240bit + N continuation + remainder entry); 1024B path kept byte-identical; 800B = 27 entries verified on hardware - 800B constants (PAYLOAD/SM 800), deployed as el6695_rt_800b - 10-min + 5h stability runs: 18M cycles, zero errors, zero mismatches, PLC nMismatch frozen over the whole run (monitor_5h.csv evidence) - docs: 800B plan/design/verification/test-report chapters, jitter analysis (NFS rootfs stall primary cause), D2000 dev plan - scripts: 800B deploy/relink/verify, mismatch-delta, 5h monitor
13 KiB
EL6695 桥接 8 字节双向实时通讯 — 设计文档
版本: 1.1(新增 §8 800B 变体) 日期: 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,且在
<DataTypes>段中声明一致的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 被擦除,配置与数据循环必须在同一会话内完成。
- 扫描网段(INIT→PREOP);
- 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);
- 8B 模式:
- 写 PDO 赋值: 0x1C12=[0x1608], 0x1C13=[0x1A08](均不含诊断 PDO);
- 写 SM2/SM3 寄存器(start=0x1C00/0x8E00, len=8, ctrl=0x64/0x20)与 FMMU;
- INIT→PREOP→SAFEOP→OP;
- 数据循环: 读 SM3 区域 8B → XOR 0x5A → 写 SM2 区域 8B,周期统计。
4.3 关键常量(8B 构建)
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 交叉编译
$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 条目 | <Type GUID="{FE565D85-...}">ARRAY [0..7] OF BYTE</Type> |
<DataTypes> |
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. 限制与后续工作
- 无诊断 PDO: 8B 配置不含 "External device not connected" 等状态位。若需要,须在 SM3 同时赋值 0x1A01+0x1A08 并保持 SM3=10(TwinCAT 侧重算逻辑需相应修正)。
- 延迟: 往返固定 4 个 PLC 周期(≈4ms @1kHz),未做 DC 同步优化。
- 尺寸扩展: 本设计的修改方法(GUID 类型 + SM 记录 + J1900 常量)适用于任意 ≤240bit 的单条目尺寸;更大尺寸需在
twin_layout_blob_sized中恢复多条目延续格式。
8. 800B 变体(已实现并验证)
在 8B 验证通路基础上,扩展出 800 字节 PDO 工程 TwinCAT_EL6695_Primary_800B,作为实际应用的带宽档位,并验证了任意尺寸的映射 blob 生成规则。
8.1 与 8B 设计的差异
| 项 | 8B | 800B |
|---|---|---|
| PDO 条目类型 | ARRAY [0..7] OF BYTE |
ARRAY [0..799] OF BYTE(GUID {7D10FDFE-13E3-A012-D822-BE30F7D36D23},PLC 构建生成) |
| SM2/SM3 长度 | 8 / 8 | 800 / 800(0x0320) |
| 0x1A01 诊断 PDO | 不挂 SM3 | 同左(沿用,否则设备报 size not allowed) |
| 映射 blob | 单条目 64bit | 27 条目(见 §8.2) |
| 数据协议 | 每字节 XOR 0x5A + 历史环 | seq + f(x)=x·7+3 + 逐字节图案 + 反推校验(类 1KB) |
| 周期 | 1kHz(基线) | 1kHz |
8.2 多条目映射 blob 生成规则
EL6695 固件接受 TwinCAT 行格式:1 真实对象条目 @240bit + N 延续条目(0x0000:00)@240bit + 1 余量延续条目。
800 B = 6400 bit = 26 × 240 bit + 160 bit
blob = [27 (u16 LE)]
+ [240, 1, obj_lo, obj_hi] 真实对象 (0x6000:01 / 0x7000:01)
+ 25 × [240, 0, 0, 0] 延续
+ 1 × [160, 0, 0, 0] 余量延续
twin_layout_blob_sized(obj, n) 泛化实现:≤240bit 单条目;>240bit 按上式多条目;1024B 特判保持原 35 条目格式(字节级兼容)。
8.3 800B 数据布局
| 偏移 | 内容 | 方向 |
|---|---|---|
| [0..7] | TwinCAT nCycles → J1900 回 nCycles*7+3 |
双向 |
| [8..15] | J1900 本地周期号 | J→T |
| [16..263] | 逐字节图案 (nCycles+i) mod 256,J1900 原样回显 |
双向 |
| [264..267] | J1900 周期抖动 ns | J→T |
| [268..271] | J1900 图案失配计数 | J→T |
| [272..279] | J1900 回存的原始 tc_seq | J→T |
| [280..799] | 逐字节图案(同上) | 双向 |
设计要点:图案区必须避开 [264..279] 统计字段——首版实现按 [16..799] 连续图案,J1900 统计字段覆盖 16 字节导致 PLC 每周期 16 字节失配。
8.4 新增陷阱(800B 实测)
| # | 现象 | 根因 | 解决 |
|---|---|---|---|
| 8 | 激活时 EL6695 SAFEOP 失败,AL 状态码 0x0003 | 激活与 J1900 ticker 会话并发:relink 激活时 ticker 正在跑,桥两侧主站会话冲突 | 激活必须在 J1900 空闲时进行;失败后空载重新激活即恢复 |
| 9 | 数据通但 PLC bSync=False,每周期固定 16 字节失配 | 图案区 [16..799] 与 J1900 统计字段 [264..279] 重叠 | 图案区改为 [16..263]+[280..799](见 §8.3) |
8.5 800B 实测性能
| 指标 | 值 |
|---|---|
| J1900 exec(800B,同步写) | 均值 384µs / p99 400µs |
| FPRD(读 800B) | 均值 139µs / p99 156µs(8B 时为 71µs) |
| FPWR(写 800B) | 均值 244µs / p99 260µs(与 8B 基本持平) |
| 桥接 RTT(1kHz) | 4 周期(lag=4,同 8B/1KB 结构) |