/* * motor_state.h - 被动接收 GD32 多电机状态推送(SPI TYPE 0xC5) * * 与 CAN 0xC1 / ADC 0xC4 同模型:GD32 250ms 主动按铃推送(独立任务位),ESP32 takeEvent 侧解析入库。 * 是否推送由 GD32 CONFIG_MOTOR_STATE_ENABLE 决定;ESP32 无本地开关,有帧即解析。 * * payload 格式(每页): * [0] page, [1] total_pages, [2] entry_count, [3] active_count * [4..] 每个 entry 44B: * [0] node_id, [1] channel, [2] online, [3] dlc, * [4..7] rx_count (LE), [8..11] seq (LE), * [12..43] data[32] (原始 STATUS payload) */ #ifndef MOTOR_STATE_H #define MOTOR_STATE_H #include #include #include #ifdef __cplusplus extern "C" { #endif #define MOTOR_MAX_NODES 9 #define MOTOR_STATUS_DLC 32 #define MOTOR_ENTRY_SIZE 44 #define MOTOR_PAGE_HEADER 4 #define MOTOR_TOTAL_PAGES 2 /** @brief 单电机状态快照。 */ typedef struct { uint8_t node_id; ///< 1-9, 0=空槽 uint8_t channel; ///< CAN 通道 (0/1/2) uint8_t online; ///< 1=在线, 0=离线 uint8_t dlc; ///< STATUS 帧 DLC (正常=32) uint32_t rx_count; ///< 累计收到帧数 uint32_t seq; ///< GD32 侧更新序号 uint8_t data[MOTOR_STATUS_DLC]; ///< 原始 STATUS payload } motor_slot_t; /** @brief 全局电机状态表。 */ typedef struct { motor_slot_t slots[MOTOR_MAX_NODES]; ///< 按 node_id-1 索引 uint8_t active_count; ///< 在线电机数 uint8_t boot_active; ///< 1=总线上有电机在 BOOT(0x103 心跳,来自 0xC5 头 bit7) uint8_t valid_slots; ///< 有效槽位数(node_id!=0) int64_t recv_ms; ///< ESP32 最近收到时 esp_timer ms bool have_data; ///< 至少收到过一帧 } motor_state_table_t; /** * @brief 处理一帧 0xC5 EVENT payload(由 can_fb 任务 takeEvent 分发)。 * @return true 表示已接受并更新。 */ bool onMotorStateEvent(const uint8_t *payload, size_t len); /** * @brief 拷贝最新电机状态表(线程安全)。 * @param[out] out 输出缓冲 * @return true 表示至少收到过一帧 */ bool getLatestMotorState(motor_state_table_t *out); /** @brief 距离上次收到的毫秒数;无数据返回 UINT32_MAX。 */ uint32_t motorStateAgeMs(void); #ifdef __cplusplus } #endif #endif /* MOTOR_STATE_H */