ethercat-linux/docs/8b-bridge-design.md

289 lines
13 KiB
Markdown
Raw Permalink Normal View History

# 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 被擦除**,配置与数据循环必须在同一会话内完成。
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` 中恢复多条目延续格式。
---
## 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) |
| 10 | 手写 `<DcData>` 十六进制被 TwinCAT 归一化为全零 | DC 模式选择不接受 legacy hex 直写 | **用 XSD 结构化形式 `<Dc><Data AssignActivate="#x320"/></Dc>`**,TwinCAT 读取后自动生成正确 DcData(`2003...`,即 0x0320)并启用 DC-Synchron(2026-08-09 已验证) |
### 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 结构) |