ethercat-linux/README.md
Tony Cao 21b2d3bf99 J1900 <-> TwinCAT 1 kHz verification over EL6695 bridge
Re-implementation of rustbootd's ecat_el6695_* examples as a single binary
with fixes found in review and on hardware:

- DC-follow PLL hardened against period-2 hunting: slew-limited anchor
  (+/-50us/cycle), bistable-trap snap re-anchor, re-prime on stale deadline
- drift-free absolute-grid ticker mode; probe mode for timestamp forensics
- bounded-memory online stats (histograms), graceful SIGINT/SIGTERM shutdown
  with full report, error-streak abort
- timestamp plausibility filter comparing against the previous raw sample
  (avoids the deadlock after a startup outlier)
- XFC scope waveform options: --el2202 (with --el2202-dual), --el2262,
  --el1252 latch timestamp readback with per-channel edge statistics
- register access unified in regs.rs: named bit constants everywhere,
  read-modify-write for enable/activation bytes
- vendored patched ethercrab 0.7.1 (sdo_write_complete, send_raw_coe)

Verified on J1900 (PREEMPT_RT 6.6.135): 600k cycles/600s exact 1 kHz,
tx/rx zero errors, phase_err p50=164us std=5us; EL2202<->EL1252 loopback
edge interval mean 2000.24us std=24.65us.
2026-07-22 08:55:41 +08:00

127 lines
7.1 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-rt — J1900 ↔ TwinCAT 1 kHz 通讯验证EL6695 EtherCAT 桥)
J1900 (Linux, PREEMPT_RT) 经 EL6695 EtherCAT 桥与 TwinCAT Windows PC 之间
1 kHz 双向过程数据1024 字节/方向)通讯验证程序。参考
`rustbootd/gateway-workspace``ecat_el6695_*` 示例重新实现,并修复了
评审中发现的问题(见文末)。
## 拓扑
```
J1900(eth1) → EK1100 → EBUS → EL6695(X2/primary) → X1 → EK1100 → TwinCAT 主机
```
- SM2 @0x1C001024BJ1900 → TwinCATTwinCAT 侧 `aIn1`
- SM3 @0x8E001026Bticker 模式2B 诊断 + 1024B
1046Bfollow/probe 模式22B 0x1A02 时间戳 + 1024BTwinCAT → J1900
- 每周期fprd SM3 → 原样回显 → fpwr SM2本地周期号写在字节 8..16
TwinCAT PLC 用字节 0..8 的自身序列号计算往返)
## 构建与部署
```bash
# Windows + Git Bash交叉编译 musl 静态二进制
RUSTFLAGS="-C linker=rust-lld -C link-self-contained=y" \
cargo build --release --target x86_64-unknown-linux-musl
# 部署(经跳板,按实际地址调整)
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
```
## 运行J1900 上root
```bash
el6695_rt eth1 --mode follow --duration 600 # DC 跟随(推荐)
el6695_rt eth1 --mode ticker --duration 60 # 本地 1ms 自由跑
el6695_rt eth1 --mode probe --duration 10 --debug-ts 40 # 时间戳探针
```
参数:`--phase-us N`follow 模式网格相位,默认 500、`--cycle-us N`、
`--core N`(绑核,默认 2、`--prio N`SCHED_FIFO默认 80
`--quiet`(不打每秒状态行)。`--duration 0` = 跑到 Ctrl-C/SIGTERM 为止,
信号触发优雅退出并打印完整统计。
`--el2202`(默认关闭):每周期翻转 EL2202 数字输出0x01/0x00产生
~500Hz 方波供示波器观察发送时序抖动。`--el2202-dual`:双通道同相翻转
(写 0x05/0x00输出 1 → EL1252 回采、输出 2 → 示波器,同一信号两种
视角。**注意 EL2202 过程字节语义**EEPROM PDO 0x1600/0x1601 实证):
`bit0=通道1数据, bit1=通道1三态, bit2=通道2数据, bit3=通道2三态`——
写 0x03 会把通道 1 置为高阻(三态),双通道必须写 0x05。实现手动
配置 EL2202 SM0输出物理地址 0x0F001 字节ctrl 0x44enable 0x09
按 EEPROM裸 AL 驱动进 OP写回读验证后才启用翻转帧使单圈执行
时间从 ~410µs 增至 ~650µs仍在 1ms 预算内)。**注意:输出端子可能
接着真实执行器,确认接线后再启用。** 简单 DO 端子无邮箱且 SM/FMMU
在组 PREOP→SAFEOP 转换时才被编程(我们跳过该转换),这是必须手动配
SM0 的原因。
`--el2262`默认关闭EL2262 XFC 输出 DC 同步锁定方波(方案 B
配置 DC 同步单元SYNC0/1 周期 1ms、+100ms 启动、0x0980=0x03并手动
配 SM00x1000/1B/0x64/0x09输出沿锁定 DC 1ms 网格,可作示波器或
EL1252 回采的基准。
`--el1252`默认关闭EL1252 锁存时间戳回采(方案 C。每周期读
ESC 寄存器 0x09AE 起 34 字节status + 双通道 Latch Pos/Neg u64
统计两通道上升沿间隔与沿相位。详见 docs/scope-waveform-design.md §8。
帧预算A+C 或 B+C 组合单圈 ~898µsA+B+C 不要同开(超 1ms
## 三种模式
- **ticker**`clock_nanosleep(TIMER_ABSTIME)` 绝对时间网格,无漂移;
SM3 布局为 0x1A01 诊断字。
- **follow**(默认):软件 PLL 跟随 TwinCAT DC 时钟0x1A02 外部时间戳),
读点锁在 TC 网格边界 + phase。与参考实现的差异见下。
- **probe**ticker 节拍 + follow 布局,不做 PLL 反馈,只 dump 原始
时间戳相位(诊断用)。
## 相对参考实现rustbootd ecat_el6695_*)的修复
1. **DC 跟随 PLL 的周期二狩猎(最重要)**:参考实现每个周期用原始
采样直接重算锚点deadbeat`floor(ext)+1ms+phase`)。实测该环路是
带延迟的反馈系统TwinCAT 发送相位与我们的帧时序相互耦合),存在
多个周期轨道:相位关系漂移(实测 ~155ppm到不利区间时周期时间
在 0.4/1.6ms 间交替、相位误差双峰跨度 >1ms。参考二进制只是恰好落
在稳定轨道上。本实现:锚点变化 slew 限幅 ±50µs/周期,且检测到
卡死(误差连续 100 周期顶在同一限幅边界,即"读到上一帧"的双稳态
陷阱)时一次性 snap 重锚定deadline 落后 >2ms 时 re-prime同时
丢弃被污染的 offset。修复后此前必现狩猎的 phase=500 配置:
**30000 周期/30s 精确 1kHzphase_err p50=164µs、std 4.4µs
period_jitter p99.9=0.2µs**锁相质量优于参考实现std 6~10µs
2. **合理性与稳定性评测发现的参考实现其它问题**
- `bridge_dump` 用自旋凑满 1ms无绝对时间锚点超时即永久相移 →
本实现 ticker 模式用绝对时间网格。
- 统计用逐样本 Vec 再排序(~30MB/小时/指标)→ 有界直方图 + Welford。
- 无信号处理Ctrl-C 丢失全部统计 → SIGINT/SIGTERM 优雅退出。
- 配置代码在 4+ 个 example 中复制粘贴 ~300 行 → 单一 `bridge` 模块。
- 错误只计数不上报,死链静默累积 → 连续 1000 周期错误即中止。
- 时间戳合理性过滤与"上一个原始样本"比较(而非上一个已接受样本),
单个坏样本最多造成两次拒绝、自动恢复(过滤与已接受值比较会在启动
离群值后永久死锁——实测踩中)。
- 移除模拟内存 loopback 线程/mutexCH368/RFM2G 模拟遗留RT 路径
直接回显。
- 删除循环内嵌 PLC 时间戳的端到端延迟指标(跨时钟域且依赖 PLC 程序
内嵌格式,实测数据无意义;端到端延迟应在 TwinCAT 侧用 PLC RTT 统计
测量)。
## 保留的参考实现经验(全部实测有效)
- EL6695 用户 OD 在主站更替时被固件清空,每次上线自检自配。
- 映射对象必须 Complete Access 单条邮箱消息写(`vendor/ethercrab` 补丁
`sdo_write_complete`),标准分段流程被固件拒绝 MBXERR_INVALIDSIZE。
- PDO 布局逐字节复制 TwinCAT 线格式0x1608/0x1A08 各 35 条目
(首条目 @240bit + 33 条 0x0000 延续 + 1 条 @32bit)。
- SM 用裸寄存器配置AL 状态机裸 fpwr/fprd 驱动SAFEOP 后邮箱坏死,
数据循环内禁止 SDO。
- RT 手段PREEMPT_RT 内核 + SCHED_FIFO 80 + 绑核 + mlockall + 栈
prefaultmusl 静态链接下 `sched_setscheduler` 需走 raw syscall。
## 目录
- `src/main.rs` — 主循环ticker/follow/probe、PLL、信号处理
- `src/bridge.rs` — EL6695 自检自配、SM 裸配置、AL 状态机
- `src/rt.rs` — RT 提升、绝对时间睡眠、Ticker
- `src/stats.rs` — 有界在线统计(直方图 + Welford
- `vendor/ethercrab` — 带 `sdo_write_complete`/`send_raw_coe` 补丁的
ethercrab 0.7.1(复制自 rustbootd/gateway-workspace/vendor