feat(ethercat): 添加EtherCAT从站功能支持并优化同步机制

- 添加CAN反馈和ID快照SPI命令文件
- 移除EXMC组件依赖,精简项目配置
- 添加RAM函数段链接脚本,优化Flash等待周期抖动
- 实现EscExmcBus类,提供16位并行总线访问LAN9252
- 实现EscLan9252驱动,支持寄存器和PDI内存访问
- 实现EthercatSlave类,采用三缓冲机制进行PDO数据交换
- 实现EthercatSync同步管理,支持SYNC0/SYNC1中断和定时器模式
- 添加ISR执行时间统计和诊断功能
- 分离周期路径和事件路径通知机制,避免RMW竞态
This commit is contained in:
cbb
2026-06-29 01:51:02 +08:00
parent 1612e326ee
commit b171767023
31 changed files with 2177 additions and 290 deletions
@@ -0,0 +1,83 @@
# Verification Checklist
## 阶段 S1:输入/输出 PDO 的 PDI 偏移分离(严重 S1)
- [x] `sys/config.h` 中新增 `CONFIG_ESC_PDI_INPUT_OFFSET` 与 `CONFIG_ESC_PDI_OUTPUT_OFFSET` 两个常量,且数值不同
- [x] `comm/ethercat_slave.cpp::periodicIsr()` 中 `readPdi()` 调用使用 `CONFIG_ESC_PDI_INPUT_OFFSET`
- [x] `comm/ethercat_slave.cpp::periodicIsr()` 中 `writePdi()` 调用使用 `CONFIG_ESC_PDI_OUTPUT_OFFSET`
- [x] `periodicIsr()` 中 `readPdi` 与 `writePdi` 的 offset 参数不再共用 `0x0000U`
- [x] `comm/esc_lan9252.h` 中 PDI 接口注释说明 offset 语义为"SyncManager 内存区相对偏移,输入/输出必须分离"
## 阶段 S2:process() 框架级 PDO 桥接(严重 S2)
- [x] `comm/ethercat_slave.h` 中 `EthercatSlave` 类含 `s_appInputs` 与 `s_appOutputs` 私有静态成员声明
- [x] `comm/ethercat_slave.h` 中 `EthercatSlave` 类 public 区含 `getApplicationInputs()` 与 `setApplicationOutputs(const PdoBuffer*)` 声明
- [x] `comm/ethercat_slave.cpp` 中 `s_appInputs` / `s_appOutputs` 已定义并初始化
- [x] `process()` 实现内调用 `getLatestInputs()` 拷贝到 `s_appInputs`
- [x] `process()` 实现内调用 `setOutputs(&s_appOutputs)` 推入输出三缓冲
- [x] `getApplicationInputs()` 返回 `&s_appInputs`(只读指针)
- [x] `setApplicationOutputs()` 含非空检查并将入参拷贝到 `s_appOutputs`
- [x] `process()` 注释保留 TODO 说明应用对象字典映射待后续 spec
## 阶段 S3:ESC 就绪状态含 AL 状态守卫(严重 S3,仅守卫)
- [x] `comm/ethercat_slave.h` 含 `s_alState` 私有静态成员声明与 `getAlState()` 公共接口声明
- [x] `comm/ethercat_slave.h` 含 AL 状态码常量定义(至少含 `AL_STATE_SAFEOP=0x04`)
- [x] `comm/ethercat_slave.cpp` 中 `s_alState` 已定义并初始化
- [x] `EthercatSlave::init()` 在芯片 ID 匹配后读取 AL 状态寄存器(0x0130)保存到 `s_alState`
- [x] `isEscReady()` 返回值包含 AL 状态判断(`s_alState >= AL_STATE_SAFEOP`)
- [x] `isEscReady()` 在芯片 ID 不匹配时短路返回 false(不读 AL 状态)
- [x] `EthercatSlave::init()` 诊断打印区分三种情况:ID 不匹配、ID 匹配但 AL < SAFEOP、ID 匹配且 AL ≥ SAFEOP
- [x] `getAlState()` 返回 `s_alState`
- [x] `comm/esc_lan9252.cpp::EscLan9252::init()` 含完整 TODO 注释块,列出 SyncManager/FMMU/AL Control/SYNC0 配置步骤,并说明需 SSC 集成
## 阶段 M4:TIMER0 降级模式保留 ESC 事件中断(中危 M4)
- [x] `comm/ethercat_sync.cpp` 的 `#else`(TIMER0 降级)分支含 `EXTI1_IRQHandler` 定义
- [x] `comm/ethercat_sync.cpp` 的 `#else` 分支含 `EXTI2_IRQHandler` 定义
- [x] `EthercatSync::init()` 的 `#else` 分支含 EXTI1 的 `gpio_exti_source_select`/`exti_init`/`exti_interrupt_enable`/`exti_interrupt_flag_clear`/`nvic_irq_enable` 配置
- [x] `EthercatSync::init()` 的 `#else` 分支含 EXTI2 的对应配置
- [x] `#else` 分支不含 `EXTI0_IRQHandler` 定义(被 `TIMER0_IRQHandler` 替代)
- [x] `onEscIrqExti` 与 `onSync1Exti` 实现未改动
## 阶段 M5:输出三缓冲写入索引提供 2 周期宽限(中危 M5)
- [x] `comm/ethercat_slave.cpp::setOutputs()` 中 `write_idx` 表达式为 `(s_outputReadIdx + 2U) % 3U`
- [x] `setOutputs()` 内注释无"无需临界区"表述
- [x] `setOutputs()` 内注释含"2 周期宽限"与"前提是主循环处理时间 < 2 × 周期时间"说明
- [x] `comm/ethercat_slave.h` 中 `s_outputs` 成员注释说明写入索引为 `(readIdx+2)%3` 与 2 周期宽限语义
- [x] `periodicIsr()` 中 `s_outputReadIdx` 递增逻辑未改动(仍为 `(readIdx + 1) % 3`)
## 阶段 M6:`safe*` 接口重命名为 `initOnly*`(中危 M6)
- [x] `comm/esc_lan9252.h` 中无 `safeReadReg`/`safeWriteReg`/`safeReadPdi`/`safeWritePdi` 声明
- [x] `comm/esc_lan9252.h` 中含 `initOnlyReadReg`/`initOnlyWriteReg`/`initOnlyReadPdi`/`initOnlyWritePdi` 声明
- [x] `comm/esc_lan9252.cpp` 中无 `safe*` 函数定义
- [x] `comm/esc_lan9252.cpp` 中含 `initOnly*` 函数定义
- [x] `comm/esc_lan9252.h` 中 `initOnly*` 接口注释含"INIT-ONLY"字样,无"safe"字样
- [x] `comm/ethercat_slave.cpp::EthercatSlave::init()` 中 AL 状态读取调用 `initOnlyReadReg`(非 `safeReadReg`)
- [x] `comm/esc_lan9252.cpp::readChipId()` 中调用 `initOnlyReadReg`(非 `safeReadReg`)
- [x] 全项目 grep `safeReadReg`/`safeWriteReg`/`safeReadPdi`/`safeWritePdi`,无任何匹配(含注释)
## 阶段 M7:`s_periodicSeq` 加宽为 `uint16_t`(中危 M7)
- [x] `comm/ethercat_slave.h` 中 `s_periodicSeq` 类型为 `volatile uint16_t`
- [x] `comm/ethercat_slave.h` 中 `s_processedSeq` 类型为 `uint16_t`
- [x] `comm/ethercat_slave.cpp` 中 `s_periodicSeq` 定义类型为 `volatile uint16_t`
- [x] `comm/ethercat_slave.cpp` 中 `s_processedSeq` 定义类型为 `uint16_t`
- [x] `hasPendingPeriodic()` 实现未改动(仍为 `s_periodicSeq != s_processedSeq`)
- [x] `markPeriodicProcessed()` 实现未改动(仍为 `s_processedSeq = s_periodicSeq`)
## 编译验证与一致性检查
- [x] 静态审查:全项目 grep `safeReadReg`/`safeWriteReg`/`safeReadPdi`/`safeWritePdi`,确认无残留
- [x] 静态审查:`periodicIsr()` 中 `readPdi`/`writePdi` 使用不同 offset 常量
- [x] 静态审查:`setOutputs()` 使用 `(readIdx+2)%3`
- [x] 静态审查:`s_periodicSeq` 与 `s_processedSeq` 均为 `uint16_t`
- [x] 静态审查:降级模式下 `EXTI1_IRQHandler`/`EXTI2_IRQHandler` 已定义
- [x] 静态审查:`process()` 内调用 `getLatestInputs()` 与 `setOutputs()`
- [x] 静态审查:`getApplicationInputs()`/`setApplicationOutputs()` 已实现
- [x] 静态审查:`isEscReady()` 含 AL 状态守卫
- [ ] 全量编译通过,无 error(⚠️ 待编译环境修复 EXMC pack 阻塞后验证)
- [ ] 若可编译:map 文件确认新增静态成员 `s_appInputs`/`s_appOutputs`/`s_alState` 已分配(⚠️ 待编译环境修复后验证)
## 风险闭环确认
- [x] 严重 S1(PDO 偏移重叠):输入/输出 PDO 使用独立 offset,数据不再相互覆盖,闭环
- [x] 严重 S2(process 为空):process() 框架级桥接实现,数据通路在框架层闭合,闭环
- [x] 严重 S3(ESC 寄存器初始化缺失):isEscReady() 增加 AL 状态守卫,SYNC0 不会触发的事实在运行期可观测;完整 ESC 配置留 SSC 集成,本 spec 范围内闭环
- [x] 中危 M4(降级模式丢失 ESC IRQ):EXTI1/EXTI2 handler 在降级模式下保留,ESC 事件路径可用,闭环
- [x] 中危 M5(输出三缓冲竞态):写入索引改为 `(readIdx+2)%3`,2 周期宽限消除拷贝竞态,闭环
- [x] 中危 M6(safe* 命名误导):重命名为 `initOnly*`,命名与实现语义一致,闭环
- [x] 中危 M7(序列号回绕):加宽为 `uint16_t`,6.55s 回绕周期覆盖任何合理主循环阻塞,闭环