# 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 @0x1C00:1024B,J1900 → TwinCAT(TwinCAT 侧 `aIn1`) - SM3 @0x8E00:1026B(ticker 模式:2B 诊断 + 1024B)或 1046B(follow/probe 模式:22B 0x1A02 时间戳 + 1024B),TwinCAT → 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(输出物理地址 0x0F00,1 字节,ctrl 0x44,enable 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)并手动 配 SM0(0x1000/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µs,A+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 精确 1kHz,phase_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 线程/mutex(CH368/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 + 栈 prefault;musl 静态链接下 `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)