/** * @file motor_version.h * @brief 多电机版本缓存 — CAN 版本应答帧(0x00FF)识别、内存缓存、SPI 0xC6 推送序列化。 * * 设计要点(与 motor_state 同款模型): * - 仅在主循环上下文 (taskCanRxParse / pushStep) 中读写,无 ISR 竞态,不需要临界区。 * - GD32 不做语义解码,只缓存原始 24B 版本 payload,由前端解码。 * - 事件驱动:电机回报版本(0x000N00FF)时缓存并置 dirty,pushStep 经 ring 推 0xC6。 * 版本是按需查询、低频数据,不进 250ms 周期推送(0xC5),避免占用周期带宽。 */ #ifndef DEVICE_MOTOR_VERSION_H #define DEVICE_MOTOR_VERSION_H #include #include #include "sys/config.h" /* ── CAN ID 匹配规则 (29-bit 扩展帧) ── * 版本应答 = 0x000N00FF(DBC CANFD_GET_VERSION,发送方 Motor,与请求同 ID)。 * 电机 APP 仅在收到 base_id=0x00FF 时回报(CANFD_VERSION_ID)。 */ static constexpr uint32_t kMotorVersionIdMask = 0xFFF0FFFFU; /* 屏蔽 node nibble [19:16] */ static constexpr uint32_t kMotorVersionIdMatch = 0x000000FFU; /* 版本应答类型码 */ static constexpr uint8_t kMotorVersionMaxNodes = CONFIG_MOTOR_MAX_NODES; static constexpr uint8_t kMotorVersionDlc = 24U; /* ── 序列化常量 (0xC6 payload) ── * Header 2B: count + rsvd;每 entry 29B: node(1)+seq(4)+version(24)。 * (236-2)/29 = 8.06 → 单帧最多 8 个节点,版本按需查询、通常单节点,足够。 */ static constexpr uint8_t kMotorVersionHeaderLen = 2U; static constexpr uint8_t kMotorVersionEntrySize = 29U; /* 1+4+24 */ /// 单电机版本槽 struct MotorVersionSlot { uint8_t node_id; ///< 1-9, 0=空槽 uint8_t dirty; ///< 1=已缓存待推送, 0=已推送/空 uint8_t rsvd[2]; uint32_t seq; ///< 更新序号 (每次收到新应答 +1) uint8_t version[kMotorVersionDlc]; ///< 原始版本 payload }; /// 全局电机版本表 struct MotorVersionTable { MotorVersionSlot slots[kMotorVersionMaxNodes]; ///< 按 node_id-1 索引 uint32_t global_seq; ///< 全局更新序号 }; /// 电机版本管理命名空间 namespace MotorVersion { /// 获取全局版本表引用 MotorVersionTable &table(); /// CAN 帧到达时调用 (主循环上下文, taskCanRxParse 内);识别 0x00FF 并缓存 void onCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t len); /// 是否有待推送的版本更新 (任一槽 dirty) bool hasPending(); /// 序列化所有待推送版本到 payload 缓冲区,并清除已打包槽的 dirty。 /// @return 实际写入字节数, 0=无待推送/参数错误 uint16_t buildPayload(uint8_t *buf, uint16_t maxLen); } // namespace MotorVersion #endif /* DEVICE_MOTOR_VERSION_H */