ethercat-linux/docs/8b-bridge-design.md
Tony Cao 309aaba342 EL6695 8-byte bidirectional exchange with per-byte verification
- 8B PDO config: ULINT-sized payload, no 0x1A01 diag PDO on SM3
  (device rejected SM3=8 with diag assigned: 'size not allowed, min/max 0xa')
- twin_layout_blob_sized(): single 64-bit mapping entry for 8B payloads
- Ticker: per-byte XOR 0x5A transform in 8B mode so TwinCAT can verify
  every byte individually (1KB path unchanged, const-guarded)
- TwinCAT automation scripts: deploy with RPC retry, LinkVariables-based
  whole-array relink (element links only carry 1 byte), ADS watch, diag
- Docs: 8B design, verification methodology, test report
2026-07-27 19:01:05 +08:00

227 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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,且在 `<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 被擦除**,配置与数据循环必须在同一会话内完成。
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 图案校验、偏移 264280 的时间戳/抖动字段)用编译期常量守卫(`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 条目 | `<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. 限制与后续工作
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` 中恢复多条目延续格式。