Files
DCU_DesktopController/docs/compose/spec/ecat-rx-stuck.md
T

68 lines
3.4 KiB
Markdown
Raw Normal View History

---
feature: ecat-rx-stuck
status: in-progress
updated: 2026-09-16
branch: main
commits:
---
# EtherCAT 从机帧接收异常(输入全 0 / 发帧后才流动)
## Report
真机验收(2026-09-16,主站 kick 已上线):`motor/start` **不发业务帧** 即
`wkc=3/3`、`rxraw.layout=dense`、magic=0xA5、gseq=429、motors=1、wr=0。
日志:`OP 后输入已流动(magic=1 diag=1 rx 431→431),踢帧 8 拍`。
## [S1] Problem
EtherCAT 进入 OP 后,主站 `/api/motor/pdoin` 与 `/api/motor/rxraw` **整包 600B 全 0**:
无 dense magic(0xA5)、gseq=0、桥 rx_count=0、段 5 诊断 seq=0、`app/load.valid=false`。
现场 `wkc=2/3`(期望 3)。用户一旦经 `/api/motor/send` 发出任意帧,输入立刻流动:
`wkc=3/3`、magic=A5、电机槽/桥状态出现。之后 `wr` 楔死恢复次数持续上涨。
真机复现(2026-09-16,GD32 APP 1.6.41 + 主站 native):
1. `motor/start` → 日志「OP 后输入无流动…重走 INIT→OP」→ 仍全 0
2. `motor/send` 一帧 → 同一秒内 pdoin 出现 dense 布局与真实电机状态
3. SPI `/spi/speedtest` 正常(ESP32↔GD32 链路本身无病)
## [S2] Design
### 根因(主)
从站 SSC 在 **SM-Synchron** 下应用与输入映射靠 SM2 事件(PDI_Isr)驱动;
MainLoop 的 free-run 分支在 `bEcatFirstOutputsReceived==TRUE` 后整段跳过。
主站 idle 输出 `seq=0` 时,从站 `processOutput`/冷启动路径迟迟不把输入 PDO
真正填进 ESC——`buildInput`+`fillDiagTail` 依赖 `EcatPdo_Process()` 被调用。
主站一旦发送(seq 沿变化)→ outputProbe 全量读 → 冷启动/应用路径苏醒 → 输入开始流动。
### 次因
1. **主站 OP 活性判据过窄**(`do_op_start`):只看两次 `pump_cycles` 之间
`bridge.rx_count` 是否变化。无 CAN 流量时 rx_count 恒 0 → 必判「无流动」→
无意义重走 INIT→OP;有流量时 12ms 窗口也可能采不到 → 同样误判。
2. **从站 1.6.33 新鲜度救援只写不填**(`EcatDbg_OpInputFreshness`):输入冻结时
只调 `PDO_InputMapping()`,从空的 `s_inBuf` 拷 0 写 ESC,等于把「冻结」固化成「全 0」。
3. 从站 1.6.41 warm-jump 跳过 RST# 的路径本身可接受(BYTE_TEST 门),但与上述
「应用未醒」叠加后,首帧前输入窗口长期全 0。
### 修复契约
| 侧 | 行为 |
|----|------|
| 主站 | 进 OP 后、起循环前:用 **dlc=0 + seq 沿** 的空输出连打数拍,制造 outputProbe 边沿、不发 CAN。活性判据改为「输入含 dense magic 或 diag seq 非 0 或 rx_count 变化」。 |
| 从站 | `EcatDbg_OpInputFreshness` 强制刷新前先 `EcatPdo_Process()`,再 `PDO_InputMapping()`。 |
| 从站版本 | patch → 1.6.42(仅救援路径,无布局变更,可与主站独立升级)。 |
## [S3] Out of Scope
- 不改 PDO 线格式 / SII / 600B 布局
- 不改 SPI 协议(0xE8 撕裂已有 ESP32 侧未提交修复,另案)
- 不回退 1.6.41 warm-jump 探针策略(BYTE_TEST 门保留)
- 不改电机/CAN 桥业务语义
## Tasks
- [x] T1: 主站 OP 后 seq 沿踢帧 + 活性判据加宽 — acceptance: 不发业务帧时 pdoin 出现 0xA5 且 gseq 可前进 (covers: S2)
- [x] T2: 从站新鲜度救援先 Process 再 InputMapping — acceptance: PDI 停摆时主站读到非全 0 输入 (covers: S2)
- [x] T3: 真机复验 start 后不 send 即有输入 — acceptance: motor/start 后 2s 内 rxraw.layout=dense (covers: S2; depends: T1)