Files
cbb b171767023 feat(ethercat): 添加EtherCAT从站功能支持并优化同步机制
- 添加CAN反馈和ID快照SPI命令文件
- 移除EXMC组件依赖,精简项目配置
- 添加RAM函数段链接脚本,优化Flash等待周期抖动
- 实现EscExmcBus类,提供16位并行总线访问LAN9252
- 实现EscLan9252驱动,支持寄存器和PDI内存访问
- 实现EthercatSlave类,采用三缓冲机制进行PDO数据交换
- 实现EthercatSync同步管理,支持SYNC0/SYNC1中断和定时器模式
- 添加ISR执行时间统计和诊断功能
- 分离周期路径和事件路径通知机制,避免RMW竞态
2026-06-29 01:51:02 +08:00

8.2 KiB

Tasks

  • Task 1: 阶段 S1 — 分离输入/输出 PDO 的 PDI 偏移(修复严重 S1)

    • SubTask 1.1: 在 sys/config.h 的 EtherCAT 配置区新增 CONFIG_ESC_PDI_INPUT_OFFSET 与 CONFIG_ESC_PDI_OUTPUT_OFFSET 两个 constexpr 或 #define 常量(默认占位值 0x1000U / 0x1200U,注释说明需与主站 ESI 匹配)
    • SubTask 1.2: 修改 comm/ethercat_slave.cpp::periodicIsr() 中 EscLan9252::readPdi() 调用,将 offset 参数由 0x0000U 改为 CONFIG_ESC_PDI_INPUT_OFFSET
    • SubTask 1.3: 修改 comm/ethercat_slave.cpp::periodicIsr() 中 EscLan9252::writePdi() 调用,将 offset 参数由 0x0000U 改为 CONFIG_ESC_PDI_OUTPUT_OFFSET
    • SubTask 1.4: 修改 comm/esc_lan9252.h 中 readPdi/writePdi/initOnlyReadPdi/initOnlyWritePdi(M6 重命名后)的接口注释,说明 offset 参数语义为"SyncManager 内存区相对偏移,输入/输出必须分离"
  • Task 2: 阶段 S2 — 实现 process() 框架级 PDO 桥接(修复严重 S2)

    • SubTask 2.1: 在 comm/ethercat_slave.h 的 EthercatSlave 类中新增 static PdoBuffer s_appInputs 与 static PdoBuffer s_appOutputs 私有静态成员声明
    • SubTask 2.2: 在 comm/ethercat_slave.h 的 EthercatSlave 类 public 区新增 getApplicationInputs()(返回 const PdoBuffer*)与 setApplicationOutputs(const PdoBuffer*) 两个公共静态接口声明
    • SubTask 2.3: 在 comm/ethercat_slave.cpp 中定义 s_appInputs / s_appOutputs 静态成员(初始化为空 PdoBuffer)
    • SubTask 2.4: 实现 process():先调用 getLatestInputs(),若返回非空则拷贝到 s_appInputs;然后调用 setOutputs(&s_appOutputs) 推入输出三缓冲(保留原 TODO 注释说明应用对象字典映射待后续 spec)
    • SubTask 2.5: 实现 getApplicationInputs():返回 &s_appInputs
    • SubTask 2.6: 实现 setApplicationOutputs(const PdoBuffer *outputs):非空检查后拷贝 *outputs 到 s_appOutputs
  • Task 3: 阶段 S3 — ESC 就绪状态守卫增强(修复严重 S3,仅守卫)

    • SubTask 3.1: 在 comm/ethercat_slave.h 新增 static uint8_t s_alState 私有静态成员声明,与 static uint8_t getAlState() 公共接口声明;新增 AL 状态码常量(可选:定义为 constexpr 或 #define,如 AL_STATE_INIT=0x01、AL_STATE_PREOP=0x02、AL_STATE_SAFEOP=0x04、AL_STATE_OP=0x08)
    • SubTask 3.2: 在 comm/ethercat_slave.cpp 定义 s_alState 静态成员(初始化 0)
    • SubTask 3.3: 修改 EthercatSlave::init():在读取芯片 ID 后,若 ID 匹配则进一步读取 AL 状态寄存器(0x0130)并保存到 s_alState(注意:M6 重命名后用 initOnlyReadReg 而非 safeReadReg)
    • SubTask 3.4: 修改 isEscReady() 内联实现:返回 (s_chipId == 0x87654321U) && (s_alState >= AL_STATE_SAFEOP)(注:AL 状态寄存器低 4 位为状态码,SAFEOP=0x04,OP=0x08,故 >= 0x04 兼容 SAFEOP 与 OP)
    • SubTask 3.5: 修改 EthercatSlave::init() 的诊断打印:ID 匹配但 AL < SAFEOP 时打印"ESC detected but AL state=0x%02X < SAFEOP, SYNC0 will not fire, full ESC config requires SSC integration";ID 匹配且 AL ≥ SAFEOP 时打印现有"ESC ready, AL state=0x%04X"
    • SubTask 3.6: 实现 getAlState() 返回 s_alState
    • SubTask 3.7: 在 comm/esc_lan9252.cpp::EscLan9252::init() 现有 TODO 注释处扩展为完整 TODO 块,列出:SyncManager 初始化(SM0/1/2/3 配置寄存器)、FMMU 配置、AL Control 写入(OP 状态请求)、SYNC0 生成寄存器(0x0981-0x0982 周期、0x0983 脉冲长度等),并说明需 SSC 或等价协议栈代码
  • Task 4: 阶段 M4 — TIMER0 降级模式保留 ESC IRQ handler(修复中危 M4)

    • SubTask 4.1: 修改 comm/ethercat_sync.cpp 的 extern "C" 中断入口包装区:#else(TIMER0 降级)分支在 TIMER0_IRQHandler 定义之后,补充 EXTI1_IRQHandler 与 EXTI2_IRQHandler 的定义(与 #if 分支一致,调用 onEscIrqExti/onSync1Exti)
    • SubTask 4.2: 修改 EthercatSync::init() 的 #else(TIMER0 降级)分支:在 TIMER0 配置之前或之后,补充 EXTI1(ESC IRQ)与 EXTI2(SYNC1)的 gpio_exti_source_select/exti_init/exti_interrupt_enable/exti_interrupt_flag_clear/nvic_irq_enable 配置(代码与 #if 分支的 EXTI1/EXTI2 配置段相同)
    • SubTask 4.3: 确认 onEscIrqExti 与 onSync1Exti 实现无需改动(降级模式下行为一致)
  • Task 5: 阶段 M5 — 输出三缓冲写入索引改为 (readIdx+2)%3(修复中危 M5)

    • SubTask 5.1: 修改 comm/ethercat_slave.cpp::setOutputs():write_idx 由 (s_outputReadIdx + 1U) % 3U 改为 (s_outputReadIdx + 2U) % 3U
    • SubTask 5.2: 修改 setOutputs() 实现内注释:删除"无需临界区"表述,改为"2 周期宽限下拷贝竞态窗口消除,前提是主循环处理时间 < 2 × 周期时间"
    • SubTask 5.3: 修改 comm/ethercat_slave.h 中 s_outputs 成员的注释:说明 setOutputs() 写入 (readIdx+2)%3,与 ISR 当前读取位置隔 2 个缓冲,提供 2 周期宽限
  • Task 6: 阶段 M6 — safe* 接口重命名为 initOnly*(修复中危 M6)

    • SubTask 6.1: 修改 comm/esc_lan9252.h:safeReadReg/safeWriteReg/safeReadPdi/safeWritePdi 四个成员函数声明重命名为 initOnlyReadReg/initOnlyWriteReg/initOnlyReadPdi/initOnlyWritePdi,注释中移除"safe"字样,统一为"INIT-ONLY"
    • SubTask 6.2: 修改 comm/esc_lan9252.cpp:四个函数定义同步重命名
    • SubTask 6.3: 修改 comm/ethercat_slave.cpp::EthercatSlave::init() 中调用 safeReadReg(0x0130U) 的代码,改为 initOnlyReadReg(0x0130U)(注:此调用点在 Task 3 S3 实现中可能已重命名,需协调)
    • SubTask 6.4: 修改 comm/esc_lan9252.cpp::readChipId() 中调用 safeReadReg 的代码,改为 initOnlyReadReg
    • SubTask 6.5: 全项目 grep safeReadReg/safeWriteReg/safeReadPdi/safeWritePdi,确认无任何残留(含声明、定义、调用、注释)
  • Task 7: 阶段 M7 — s_periodicSeq 加宽为 uint16_t(修复中危 M7)

    • SubTask 7.1: 修改 comm/ethercat_slave.h:s_periodicSeq 类型由 volatile uint8_t 改为 volatile uint16_t;s_processedSeq 类型由 uint8_t 改为 uint16_t
    • SubTask 7.2: 修改 comm/ethercat_slave.cpp 中两个静态成员定义的类型,与头文件一致
    • SubTask 7.3: 确认 hasPendingPeriodic() 与 markPeriodicProcessed() 实现无需改动(比较与赋值操作对 uint16_t 同样适用)
    • SubTask 7.4: 确认诊断打印中无 s_periodicSeq 直接格式化输出(若有,确认 %u/%lu 格式符适配 uint16_t)
  • Task 8: 编译验证与一致性检查

    • SubTask 8.1: 静态代码审查确认:全项目无 safeReadReg/safeWriteReg/safeReadPdi/safeWritePdi 残留;periodicIsr() 中 readPdi/writePdi 使用不同 offset;setOutputs() 使用 (readIdx+2)%3;s_periodicSeq 为 uint16_t;降级模式下 EXTI1_IRQHandler/EXTI2_IRQHandler 已定义
    • SubTask 8.2: 静态代码审查确认:process() 内部调用 getLatestInputs() 与 setOutputs();getApplicationInputs()/setApplicationOutputs() 已实现;isEscReady() 含 AL 状态守卫
    • SubTask 8.3: 若可编译:全量编译通过,无 error(⚠️ 受 EXMC pack 解析问题阻塞,改为静态代码审查,详见下方验证)
    • SubTask 8.4: 若可编译:map 文件确认新增静态成员 s_appInputs/s_appOutputs/s_alState 已分配(⚠️ 待编译环境修复后验证)

Task Dependencies

  • Task 1(S1 PDO 偏移)、Task 5(M5 输出索引)、Task 7(M7 序列号加宽)相互独立,可并行实施(改动 ethercat_slave.cpp/config.h 不同代码段,无交叉)
  • Task 6(M6 重命名)应早于 Task 3(S3 AL 状态守卫),因 S3 SubTask 3.3 调用 initOnlyReadReg,需先完成重命名
  • Task 2(S2 process 桥接)与 Task 3(S3 AL 守卫)均修改 ethercat_slave.h/.cpp,建议串行实施避免冲突
  • Task 4(M4 降级模式)独立,改动 ethercat_sync.cpp,可与其他任务并行
  • Task 8(编译验证)依赖 Task 1-7 全部完成

推荐执行顺序

  1. Task 6(M6 重命名)— 先改名,避免后续任务调用旧名
  2. Task 1(S1 PDO 偏移)+ Task 5(M5 索引)+ Task 7(M7 加宽)— 并行,改动不同代码段
  3. Task 4(M4 降级模式)— 独立文件,可并行
  4. Task 2(S2 process 桥接)
  5. Task 3(S3 AL 守卫)— 依赖 M6 重命名完成
  6. Task 8(编译验证)