添加了s_pmSuspended状态标志来管理LAN9252的电源管理模式,在芯片进入D1-D3 睡眠状态时阻止HBI访问以避免唤醒问题。同步修改了ethercat_sync中断处理程序, 在睡眠挂起期间跳过HBI相关操作确保芯片能够正常维持睡眠状态。 fix(spi): 提升SPI CS EXTI抢占优先级避免帧丢失 将SPI CS EXTI中断优先级从1提升至0,使其能够抢占PDI/SYNC1等关键中断,确 保SPI帧释放处理能够在微秒级完成,避免因延时导致的ESP32 CS间隔超时和帧 丢失问题。 feat(ssc): 实现OP输入新鲜度不变量监控机制 添加了输入映射序列号监控机制,当PDI路径中断时能够检测到输入数据冻结并 自动触发强制刷新,解决结构洞问题导致的输入数据停止更新现象。 chore(pdo): 更新PDO输入字节注释说明布局变更历史
21 KiB
21 KiB
feature, status, updated, branch, commits
| feature | status | updated | branch | commits |
|---|---|---|---|---|
| tiered-pdo-mapping | designed | 2026-09-12 | master (DCU_APP) + main (tools/ecat) — 无 worktree,沿用工作树 | <pending delivery> |
Tiered PDO Mapping(按电机数分档收缩 SM3)
Report
(交付后填写)
[S1] Problem
输入 PDO SM3 恒为 600B,与本拍实际密排电机数无关:
- 3 电机时仍占用 600B SM 与 ~984B 过程数据帧,100Mbps 上单帧线路时钟地板约 81µs。
- 从站稀疏写(1.6.22)只省 PRAM 搬运,不减线长。
- 用户场景:电机数长期固定(常见 3 台),偶发插拔;希望档内无感、跨档自动重映射,且重映射 <500ms、不 NRST、不撞 1s 过程数据看门狗。
现链路硬约束:
- SSC
PREOP→SAFEOP调APPL_GenerateMapping得nPdInputSize,CheckSmSettings要求 ESC SM3.Length == nPdInputSize(ecatslv.c:509)。 - SOEM
Ibytes来自 CoE PDO 映射位数,不是从站运行时 tier;主站不改写 SM/FMMU/Ibytes则线帧不变。 - 完整
do_op_start(重枚举 + 250×4ms 喂帧 + 最长数秒等 OP)无法满足 <500ms。 decode_input密排魔数@35=0xA5已可区分布局,但 段3@544 / 段5@584 写死,短 SM 下会读越界/错位。
[S2] Design
S2.1 容量档与线格式
唯一真源(从站头 + 主站常量,数值必须一致):
ECAT_PDO_IN_MAX = 600
ECAT_PDO_IN_PREFIX = 40 // 段1 32B + 段2 头 8B,槽区起点 MOTW_OFF_SLOTS=40
ECAT_PDO_IN_XRX = 40 // 段3 长快照
ECAT_PDO_IN_DIAG = 16 // 段5 诊断(实际用 12B,对齐到 8)
ECAT_MOT_SLOT_SIZE = 56
in_bytes(cap) = 40 + cap*56 + 40 + 16 = 96 + 56*cap
| 档 | count 范围 | cap | in_bytes | 线过程数据 out+in | 约线路时钟* |
|---|---|---|---|---|---|
| A | 0–3 | 3 | 264 | 384+264=648 | ~52µs |
| B | 4–6 | 6 | 432 | 384+432=816 | ~65µs |
| C | 7–9 | 9 | 600 | 384+600=984 | ~81µs(现状) |
*仅 eth 过程数据字节的 100Mbps 线时钟粗算,不含头/IFG 的完整帧模型见 ECAT_BEAT_TAIL_REPORT。
布局语义(密排,魔数 @35=0xA5 不变):
[0..31] 段1 桥状态(与现网一致)
[32..39] 段2 头:count/active/boot/magic/gseq
[40 .. 40+n*56) 密排槽,n = count ≤ cap
[40+cap*56 .. +40) 段3 xrx
[+40 .. +16) 段5 诊断(load/torn/seq/overfull/reltimeout/reject)
- 档内(count 在同一 cap 槽位预算内变化):只更新
count与槽内容,不改 SM、不 PREOP。 - 跨档:走 S2.3 轻量重映射;两端同时切到目标
in_bytes。 - 非密排(magic≠0xA5 或 ilen≥640 旧布局)路径保持原样,本特性不碰。
主站由 Ibytes 反推 cap(与 tier 一一对应):
cap = (Ibytes - 96) / 56 // 264→3, 432→6, 600→9
xrx_off = 40 + cap*56
diag_off = xrx_off + 40
禁止再写死 544/584。
S2.2 从站契约(DCU_APP,目标版本 1.6.34)
- SII/EEPROM 不改:
gen_eeprom.py仍PD_IN_SIZE=600,冷启动 SM3 默认 600,与旧主站兼容。 APPL_GenerateMapping(dc_app.c,PREOP→SAFEOP时调用):- 读 ESC 当前 SM3
Length(GetSyncMan(PROCESS_DATA_IN))。 - 若
Length∈ {264, 432, 600} →*pInputSize = Length(从站跟随主站)。 - 否则 →
*pInputSize = ECAT_PDO_IN_MAX(600,保底)。 *pOutputSize仍 384,不变。
- 读 ESC 当前 SM3
APPL_InputMapping:memcpy(pInputData, …, nPdInputSize),不再写死 600。- 密排填充 / 稀疏写 / 差分写(
ethercat_can_bridge.cpp+ diff/sparse 开关路径):- 段3/段5 物理偏移改为
40 + cap*56起,其中cap = (nPdInputSize - 96) / 56(在 Safe/OP 内nPdInputSize已被 GenerateMapping 更新)。 - 稀疏写范围同步:段A=
0 .. 40+count*56,段B=xrx_off .. nPdInputSize。 count > cap时本拍只打包前cap个(并保持现有“撑满回退整包”仅在 cap=9 时语义不变);正常调度不应出现,出现时记诊断。
- 段3/段5 物理偏移改为
- 对象字典 0x6000:仍按 600B 定义(SDO 可读全镜像);过程数据长度以 SM/
nPdInputSize为准。不引入多 TxPDO 映射子索引(v1 范围外)。 - 版本:
config.hpatch → 34,注释写明 tiered SM3 跟随主站。
从站不主动改 SM 长度,避免与主站写顺序竞态。
S2.3 主站契约(tools/ecat / ecat_native.c)
常量与状态
tier_in_bytes[] = {264, 432, 600} // cap 3/6/9
g_tier_cap // 当前会话生效 cap,默认 9
g_tier_seen_count / g_tier_since // 滞回:观察到的新档 count 与起始时刻
g_tier_last_remap / g_tier_remaps // 诊断
count 取密排头 g_head.wcount(或 inp[32]),不用 active(离线电机仍占槽报最后一轮,属于“槽预算”)。
首次进 OP
do_op_start仍走完整config_init+config_map(CoE 得 Ibytes=600)→ 首拍恒 Tier C。- 首拍 decode 得到
count后进入滞回观察;不与 SAFEOP→OP 竞态。
跨档轻量重映射 do_tier_remap(target_cap)
前置:g_bus_open && g_op && 循环已 loop_stop(调用方持 g_bus_lock)。
1. 记录 DC 形态 (dc_cycle_us, dc_shift_us) —— 与 wedge 恢复同源
2. OP → PREOP|ACK(不 INIT、不 config_init 重枚举)
- statecheck PREOP ≤400ms;失败则回退完整 do_op_start 一次
3. 写 0x0420 = 10000(1s WD,与 do_op_start 同款)
4. 改写 SM3:
- ec_slave[1].SM[3].SMlength = htoes(target_in_bytes) // StartAddr 仍 0x1880,flags 不动(3-buffer)
- ecx_FPWR / ec_FPWRw 写 ESC SM3 寄存器(与 config_map 写 SM 同基址)
5. 改写输入 FMMU 逻辑长度(仅输入 FMMU,LogStart 不变,LogLength = target_in_bytes)
6. 同步 SOEM 进程数据尺寸:
- ec_slave[1].Ibytes / Ibits = target_in_bytes / *8
- ec_group[0].Ibytes 调整,保证 send/receive_processdata 帧长 = O+new_I
- g_expected_wkc 重算(inputsWKC 对短映射通常仍 1,需实读确认)
7. PREOP → SAFEOP|ACK(≤400ms)—— 此时从站 GenerateMapping 读回新 SM3 长度
8. 短喂帧:1ms 节拍 pump ~150 次(≈150ms,≪1s WD),再请求 OP
9. OP|ACK,继续 pump 直到 AL=OP,总预算 ≤400ms;失败一次则完整 do_op_start
10. g_tier_cap = target_cap;g_rxb_layout 保持 dense(3);清空槽表按 do_op_start 会话语义
(可选:保留 g_slots,避免 remap 窗口 UI 闪空——v1 与 do_op_start 一致选择清空+重建)
目标耗时: PREOP/SAFEOP/OP 状态检查各一次成功路径通常 10–50ms,加 150ms 喂帧,合计 远低于 500ms;全失败回退完整路径仍可恢复(不保证 <500ms,记日志)。
禁止: NRST、ec_close、改邮箱 SM、改输出 SM2 长度。
滞回与触发点
- 仅在 循环运行且 OP 时评估;放在
wedge_worker100ms 节拍(或 pdo_worker 每 N 拍采样后置标志,由 wedge 线程统一处理——不得在 pdo_worker 内 loop_stop 自己)。 - 规则:
need_cap = cap_for_count(count)- 若
need_cap == g_tier_cap→ 清观察状态 - 若
need_cap != g_tier_cap:- 首次记下
g_tier_seen_count与g_tier_since - 同一 need_cap 连续 ≥500ms 才执行 remap
- 期间 count 又回到原档 → 清观察
- 首次记下
pm/hold置位期间不 remap(与楔死看门狗一致)。- remap 进行中
g_tier_busy:wedge 楔死恢复、/api/motor/start、SDO 独占路由互斥(复用g_bus_lock)。
decode_input 密排分支
- 魔数判定不变。
cap = (ilen - 96) / 56,非法(不能整除或 cap∉{3,6,9})时:按 cap=9 且off+size>ilen则 break 的现有保护,不崩溃;记 layout 异常。- 段3/段5 偏移用
xrx_off/diag_off,替换XRX_DENSE_OFF_*/DIAG_OFF_*_WIN常量中的 544/584(常量改为宏函数或运行时变量)。
API / 观测
/api/motor/state或 busKv 增加:tier_cap、tier_in_bytes、tier_remaps、tier_since(可选)。/api/pdo/tier:{"cap":3|6|9}手动钉档(立即 remap,不走 500ms 滞回);{"auto":true}恢复自动。v1 至少实现手动钉档读写。/api/open启动日志:输入 %dB打印真实Ibytes(remap 后),去掉写死「600B」文案。
楔死恢复 / start / sdo
wedge_worker、/api/motor/state快路径、/api/motor/start:重拉do_op_start后 重置g_tier_cap=9,再允许滞回把档缩回去(简单、可预期)。/api/sdo仍loop_stop不恢复——tier 状态在下次 start 时回到 9,可接受。
S2.4 前端 / 文档(次要)
lab/spi_test.html:bus 总线卡展示tier in_bytes(若 API 提供);layout=dense 分支的段5 注释改为“偏移随 cap”。lab/pdo_layout.html:统计卡从「600 B SM3」改为「264/432/600 分档」;不阻塞固件交付,可同 PR 或 follow-up。- 注释清理:避免再写“线速两布局同为 600B”这类在 tier 生效后错误的句子。
S2.5 错误与回退
| 情况 | 行为 |
|---|---|
| SM3 改写 FPWR 失败 | abort remap,保持旧 Ibytes,日志 WARN;下一滞回窗重试 |
| SAFEOP 失败(SSC 拒 SM) | 读 ALstatuscode;完整 do_op_start(回到 600)并记 tier_remap_fail |
| OP 失败 | 同上 |
| count 突然 > cap(从站 bug/撕裂) | decode 已有越界 break;从站侧 count>cap 截断;不立即 remap,等滞回 |
| 首拍 Ibytes 仍 600 且 count=3 | 正常进入滞回,500ms 后缩到 264 |
| 主站新、从站旧(GenerateMapping 恒 600) | 主站写 SM=264 后 SSC CheckSm 失败 → 回退 600,功能与今日等价 |
| 从站新、主站旧 | SM 仍被旧主站写 600,从站跟随 600,与今日等价 |
S2.6 不做(Out of Scope 见 S3)但需预留
- 输出 SM2 分档
- 多 TxPDO 赋值(0x1C13 子索引选映射)
- EEPROM/ESI 为每档发一份 SII
- 改 1-buffer 换更大窗口
S2.7 文件级改动清单(Implement 对照表)
行号以 2026-09-12 工作树为准,实现时以符号为准、行号仅作导航。
A. 从站 DCU_APP
| # | 文件 | 改什么 |
|---|---|---|
| A1 | GD32E508_DCU_APP/comm/ethercat_pdo_size.h |
新增分档纯宏(C/C++ 共用):ECAT_PDO_IN_TIERS {264,432,600}、ECAT_PDO_TIER_CAPS {3,6,9}、ECAT_PDO_IN_PREFIX=40、ECAT_PDO_IN_XRX=40、ECAT_PDO_IN_DIAG=16、ECAT_PDO_IN_BYTES_FOR_CAP(cap)=(96+56*(cap))、ECAT_PDO_CAP_FOR_IN_BYTES(ib)。静态断言:三档均 ≤600 且 %8==0,cap=9 时等于现行 ECAT_PDO_IN_BYTES。ECAT_PDO_IN_BYTES 保留为 max=600(数组/EEPROM 上限),语义注释改为「上限,运行期可小于」。 |
| A2 | GD32E508_DCU_APP/comm/ethercat_can_bridge.h |
段3/段5 枚举保持 cap=9 数值(544/584…)作默认/静态断言基准;新增运行时偏移接口:uint16_t ecatMotXrxOff(uint8_t cap) / ecatMotDiagOff(uint8_t cap) / ecatPdoInBytesForCap(uint8_t cap)(inline 或声明+cpp)。static_assert 改为只约束 cap=9 与 max 的首尾相接。注释标明:短档时段3/段5 前移,主站必须按 Ibytes 解。 |
| A3 | GD32E508_DCU_APP/comm/ethercat_can_bridge.cpp buildInput @286–333 |
段3 填充:ECAT_XRX_OFF_* → xrx = ecatMotXrxOff(cap) 相对写。memset(in,0,kEcatPdoInBytes) 可改为只清 in_bytes(或仍清 600,多清无害但稀疏/diff 会多比——建议只清 in_bytes)。cap 来源见 A5。段1/段2 偏移不变(0–39 + slots)。 |
| A4 | GD32E508_DCU_APP/comm/ethercat_can_bridge.cpp fillMotorRound @119+ |
逻辑不变;若 count > cap 截断到 cap(防越界写到段3)。影子区仍按 max 分配。 |
| A5 | GD32E508_DCU_APP/comm/ssc/port/dc_app.c APPL_GenerateMapping @355–360 |
读 GetSyncMan(PROCESS_DATA_IN)(channel=3)Length;∈{264,432,600} 则 *pInputSize=Length,否则 600。随后调用新增 EcatPdo_OnPdoInBytes(*pInputSize)(或写 EthercatPdo 静态),通知填充层当前 in_bytes/cap。输出仍 384。 |
| A6 | GD32E508_DCU_APP/comm/ssc/port/dc_app.c APPL_InputMapping @408–453 |
EcatPdo_WriteInputs 之后:memcpy(pInputData, …, nPdInputSize)(替换写死 ECAT_PDO_IN_BYTES)。 |
| A7 | GD32E508_DCU_APP/comm/ethercat_pdo.h / .cpp |
新增静态 s_inBytes/s_cap(默认 600/9)+ OnPdoInBytes(uint16)。fillDiagTail @114–155:ECAT_DIAG_OFF_* → diag=ecatMotDiagOff(cap) 相对。sparseEscWritePlan @166–194:ECAT_XRX_OFF_CH/kPdoInBytes → 运行时 xrx/in_bytes;「撑满」条件改为 headEnd >= xrx。escSeg2WriteLen @199–216、diff 三区 @258–279:同改。影子数组 s_escSh2/s_escSh3 保持按 600 上限分配,只比较/写 in_bytes 内长度。 |
| A8 | GD32E508_DCU_APP/sys/config.h @152 |
APP_FW_VERSION_PATCH 33→34,追加 1.6.34 注释:SM3 分档跟随主站(GenerateMapping 读回 SM3.Length)。 |
| A9 | GD32E508_DCU_APP/comm/ssc/port/gen_eeprom.py |
不改 PD_IN_SIZE=600(v1 不刷 EEPROM)。可在文件头加一行注释:「运行期主站可把 SM3 改为 264/432,SII 仍 600」。 |
| A10 | ethercat_diff_stats.cpp |
若 kInBytes 宏绑死 600:改为读 EthercatPdo::inBytes() 或仍按 max 快照——差分仪表在短档下勿拿 600 去比 in_bytes 内容;实现时优先 observe 用当前 in_bytes。 |
B. 主站 tools/ecat
| # | 文件 | 改什么 |
|---|---|---|
| B1 | ecat_native.c 顶部常量 @70–133 |
新增:TIER_IN_BYTES[]、tier_cap_for_count(n)、tier_in_bytes(cap)、dense_xrx_off(ilen)、dense_diag_off(ilen)(cap=(ilen-96)/56)。保留 XRX_DENSE_OFF_*/DIAG_OFF_*_WIN 仅作文档/默认 cap=9;decode 路径改走函数。 |
| B2 | ecat_native.c 状态 @190–226 附近 |
新增:g_tier_cap(默认9)、g_tier_auto(默认1)、g_tier_busy、g_tier_need_cap、g_tier_since、g_tier_remaps、g_tier_remap_fail。 |
| B3 | ecat_native.c decode_input @450–530 |
密排分支:xch/xda/diag 用 dense_*_off(ilen)。若 (ilen-96)%56!=0 或 cap∉{3,6,9}:仍按魔数密排处理但 off+size>ilen 则 break(已有),并置 layout 异常标志。段5 读取位置同步改(@559–560 现用 DIAG_OFF_LOAD_WIN)。 |
| B4 | ecat_native.c 新函数 do_tier_remap(int target_cap, …) |
放在 do_op_start/do_op_stop 附近。实现 S2.3 十步;内部用 ecx_FPWR/ec_FPWRw:SM3 长度字 @ ESC 0x081A(ECT_REG_SM0+3*8+2),整段 SM 重写可抄 ethercatconfig.c ecx_map_sm 的 FPWR 模式;输入 FMMU:读当前 FMMU 表,只改输入那条的 LogLength=target_in,FPWR 回去;ec_slave[1].Ibytes=Ibits/8、ec_group[0].Ibytes 收缩/回扩;g_expected_wkc 重算。成功:g_tier_cap=target;失败:do_op_start 回落并 g_tier_cap=9。 |
| B5 | ecat_native.c wedge_worker @3057–3100 |
在 need 楔死分支之前增加 tier 滞回评估(仅当 g_loop_running && g_op && !g_tier_busy && !pm_hold && g_tier_auto):用最新 g_head.wcount;同一 need_cap≠g_tier_cap 持续 ≥0.5s → loop_stop + do_tier_remap + loop_start。楔死恢复成功路径:g_tier_cap=9、清观察。 |
| B6 | ecat_native.c do_op_start 成功出口 @1178–1207 |
g_tier_cap=9、清滞回观察。日志 输入 %dB 已打真实 Ibytes(@1180)——确认 remap 后再 start 也正确。 |
| B7 | ecat_native.c /api/motor/start @1987–2046 |
成功 loop_start 后同样 g_tier_cap=9(若走的是未 remap 的 do_op_start)。日志去掉写死「输入 600B」文案 @2039。 |
| B8 | ecat_native.c /api/motor/state @2365–2403 |
快路径楔死恢复成功后 g_tier_cap=9。build_state_json 增加 tier_cap/tier_in_bytes/tier_remaps(函数体搜 build_state_json)。 |
| B9 | ecat_native.c 新路由 /api/pdo/tier |
GET:返回 {ok, auto, tier_cap, tier_in_bytes, remaps, last_ms}。POST body {"cap":3|6|9} 或 {"auto":false|true}:钉档时持 g_bus_lock、loop_stop、do_tier_remap、loop_start;auto 只改标志。 |
| B10 | ecat_native.c pdo_worker @848–852 |
无需改 Ibytes 来源(已读 ec_slave[1].Ibytes)。禁止在此 remap。可选:每 N 拍把 g_head.wcount 时间戳写给 wedge(直接读 g_head 已无锁跨线程——与现状一致,保持)。 |
| B11 | build_new.bat / 工程 |
无新源文件则不用改;若 do_tier_remap 拆到新 .c 再加入 bat。 |
C. 前端 tools/ESP32(不阻塞固件)
| # | 文件 | 改什么 |
|---|---|---|
| C1 | lab/spi_test.html |
总线/KV 区:若 API 返回 tier_in_bytes 则显示「SM3 264/432/600 · 档」。dense 段5 注释 @1198 改为「偏移随 cap」。CSS/JS 引用若外置需 ?v=(本页自包含则内联即可)。 |
| C2 | lab/pdo_layout.html |
统计卡 @243–244、图例 @819/@841、说明 @264:由「600 B SM3」改为分档说明;图可标三档长度。 |
| C3 | ECAT_BEAT_TAIL_REPORT.html 等历史报告 |
不改(已归档结论);避免改写历史数字。 |
D. 明确不改
ESP32_COMM/**gen_eeprom.py的 PD_IN_SIZE、DCU_ESI.xmlsoem-src/**(只调用,不改库;FMMU/SM 用现成 FPWR)- 输出 SM2、邮箱 SM0/1
E. 实现顺序(文件依赖)
A1 → A2 → A5/A6(GenerateMapping+InputMapping)
→ A3/A4/A7(填充与稀疏/差分,依赖 A5 通知 in_bytes)
→ A8 版本
B1 → B3 decode → B4 remap → B5/B6/B7 滞回与重置 → B8/B9 API
C1/C2 可后置
双端可并行:A 与 B 仅共享数值表;联调以 V3–V8 为准。
[S3] Out of Scope
- ESP32_COMM 固件(不解析 EtherCAT PDO)
- 输出 PDO 384B 收缩
- 刷 EEPROM / 冷启动依赖的 SII 多映射
- 电机 10+ 节点
- 用 tier 直接调 PDO 周期(0.25ms 等)
- 提交/合并(用户明确不自动 commit)
Tasks
- T1: A1+A2+A5+A6 从站长度宏 + GenerateMapping 跟随 SM3 + InputMapping 按 nPdInputSize —— acceptance: 静态断言 264/432/600;主站手写 SM3=264 后 PREOP→SAFEOP 不报 0x001A/INVALIDSMINCFG;
nPdInputSize变 264 (covers: S2.2 S2.7-A) - T2: A3+A4+A7 从站密排/稀疏/差分偏移 cap 相对 —— acceptance: 264B SM 下段3/段5 不越界,主站能读 load/torn;count≤3 稀疏写两段正确 (covers: S2.2 S2.7-A; depends: T1)
- T3: A8 版本 1.6.34 —— acceptance: PATCH=34 + 注释 (covers: S2.2; depends: T1)
- T4: B1+B3 主站 decode cap 相对 —— acceptance: ilen=264+magic 槽/段3/段5 正确;ilen=600 回归一致 (covers: S2.1 S2.3 S2.7-B)
- T5: B4 do_tier_remap + Ibytes/FMMU/group —— acceptance: 600→264→600 各一次 AL=OP、wkc 正、成功路径日志 <500ms (covers: S2.3; depends: T4)
- T6: B5+B6+B7 滞回 + start/wedge 重置 —— acceptance: 稳定 3 电机 0.5–1.0s 进 264;4 电机进 432;楔死后回 9 再缩回 (covers: S2.3; depends: T5)
- T7: B8+B9
/api/pdo/tier与 state 字段 —— acceptance: POST cap 立即 remap;GET 含 tier_* (covers: S2.3; depends: T5) - T8: V1–V10 双端编译+台架 —— acceptance: cbuild/build_native 通过;见 Verification (covers: S2.2 S2.3; depends: T1–T7)
- T9: C1+C2 前端分档展示 —— acceptance: 页面显示当前档;不阻塞 T8 (covers: S2.4; depends: T7)
Verification(交付证据)
| 步骤 | 命令/操作 | 期望 |
|---|---|---|
| V1 | DCU cbuild(既有 Release+GD32E508VE) | 0 error |
| V2 | build_new.bat / VS2022 cl 原生主站 |
产出 ecat_native.new.exe |
| V3 | 冷启动 600 进 OP(3 电机) | 与今日一致,layout=dense |
| V4 | 等待 ≥1s | 自动 remap 到 264,日志含耗时 |
| V5 | /api/motor/state + spi_test |
Ibytes/tier=264,count=3,wkc 稳定 |
| V6 | rx 线时(粗) | 过程数据字节 648 vs 984,rx 段应下降(允许 ±15µs 噪声) |
| V7 | 手动 /api/pdo/tier cap=9 |
回到 600 |
| V8 | 拔/加第 4 台(或模拟 count) | 滞回后 432 |
| V9 | 旧从站 + 新主站 | SAFEOP 失败路径回 600,总线仍可用 |
| V10 | 楔死/PM hold | hold 中不 remap;恢复后 tier 策略符合 S2.3 |
Journey log(设计期)
- 线时收益来自 SM3 变短,稀疏写不够——与 1.6.22/1.6.25 结论一致。
- SSC 长度双源校验 ⇒ 必须主从同数;v1 选「主站写 SM,从站 GenerateMapping 读回」,避免 EEPROM 与多 PDO 映射。
- 首拍恒 600 是为了兼容 config_map/CoE,用 500ms 滞回消化一次 remap,比改 CoE 映射表风险低。
- 重映射不能走完整
do_op_start(重枚举+长喂帧)——独立轻量路径 + 失败回退完整路径。 - 段3/段5 偏移写死是主站解码的隐雷;Ibytes 分支(ilen<600→slim)会把 264 误判成瘦身布局——必须先用魔数 0xA5 拦下,再按 cap 排偏移。