#ifndef __SNC_CAN_APP_H #define __SNC_CAN_APP_H #ifdef __cplusplus extern "C" { #endif #include #include #include "fdcan.h" #define SNC_ODOM_TIMEOUT_MS 300U /* ========================= 协议 CAN ID ========================= */ #define SNC_CAN_ID_HEARTBEAT 0x080U #define SNC_CAN_ID_CMD_VEL 0x100U #define SNC_CAN_ID_STATUS 0x181U #define SNC_CAN_ID_ACTUAL_RPM 0x182U #define SNC_CAN_ID_TARGET_RPM 0x183U #define SNC_CAN_ID_COMM_DIAG 0x184U #define SNC_CAN_ID_ODOM 0x200U /* ========================= 系统状态定义 ========================= */ typedef enum { SNC_SYSTEM_BOOTING = 0, SNC_SYSTEM_OPERATIONAL = 1, SNC_SYSTEM_SAFE_FAULT = 2 } SNC_SystemState_t; typedef enum { SNC_SYSTEM_HEALTH_OK = 0, SNC_SYSTEM_HEALTH_WARNING = 1, SNC_SYSTEM_HEALTH_FAULT = 2 } SNC_SystemHealth_t; /* ========================= 诊断位定义 ========================= */ typedef enum { SNC_DIAG_COMM_TIMEOUT = (1UL << 0), SNC_DIAG_CAN_BUS_OFF = (1UL << 1), SNC_DIAG_CMD_CRC_STORM = (1UL << 2), SNC_DIAG_CMD_CNT_STORM = (1UL << 3), SNC_DIAG_MOTOR_FL_STALL = (1UL << 4), SNC_DIAG_MOTOR_RL_STALL = (1UL << 5), SNC_DIAG_MOTOR_FR_STALL = (1UL << 6), SNC_DIAG_MOTOR_RR_STALL = (1UL << 7), SNC_DIAG_CONTROL_SATURATION = (1UL << 8) } SNC_DiagBits_t; /* ========================= 下位机状态快照 ========================= */ typedef struct { uint8_t state; uint8_t health; uint32_t diag_bits; uint8_t cmd_age_10ms; uint8_t status_counter; uint32_t last_update_ms; bool online; } SNC_ChassisStatus_t; /* ========================= 轮速数据 ========================= */ typedef struct { int16_t fl; int16_t rl; int16_t fr; int16_t rr; uint32_t last_update_ms; } SNC_WheelRpmFrame_t; /* ========================= 里程增量 ========================= */ typedef struct { int16_t fl_delta; int16_t rl_delta; int16_t fr_delta; int16_t rr_delta; uint32_t last_update_ms; } SNC_OdomDeltaFrame_t; /** * @brief 里程增量累加器 * * 解决 0x200 增量帧的"漏积分/重复积分"问题: * - ISR 收到每帧 0x200 时,将增量**累加**到此结构体(不覆盖) * - 消费者调用 SNC_CAN_ConsumeOdomDelta() 原子取走累计值并清零 * * 这样即使消费者频率 (100ms) 低于帧到达频率 (~60ms), * 也不会丢失任何增量;也不会重复消费同一份增量。 */ typedef struct { int32_t fl_accum; /**< 左前轮累计增量 ticks (int32 防溢出) */ int32_t rl_accum; /**< 左后轮累计增量 ticks */ int32_t fr_accum; /**< 右前轮累计增量 ticks */ int32_t rr_accum; /**< 右后轮累计增量 ticks */ uint32_t first_frame_ms; /**< 本次累加窗口内第一帧的时间戳 [ms] */ uint32_t last_frame_ms; /**< 本次累加窗口内最后一帧的时间戳 [ms] */ uint32_t span_ms; /**< 本次累计增量实际覆盖的时间窗 [ms] */ uint8_t frame_count; /**< 本次累加窗口内收到的帧数 */ } SNC_OdomDeltaAccum_t; /* ========================= 通信诊断 ========================= */ typedef struct { uint8_t valid_cmd_total_lsb; uint8_t crc_error_total_lsb; uint8_t counter_reject_total_lsb; uint8_t can_tx_drop_total_lsb; uint8_t busoff_total_lsb; uint8_t rx_overrun_total_lsb; uint8_t last_accepted_counter; uint8_t consecutive_counter_errors; uint8_t consecutive_crc_errors; uint32_t last_update_ms; } SNC_CommDiagFrame_t; /* ========================= 上位机应用层总状态 ========================= */ typedef struct { SNC_ChassisStatus_t status; SNC_WheelRpmFrame_t actual_rpm; SNC_WheelRpmFrame_t target_rpm; SNC_OdomDeltaFrame_t odom_delta; /**< 最近一帧快照 (保留兼容性, 勿用于积分) */ SNC_OdomDeltaAccum_t odom_accum; /**< 里程增量累加器 (正确消费入口) */ SNC_CommDiagFrame_t comm_diag; uint8_t tx_counter; uint8_t tx_ctrl_flags; uint32_t tx_total; uint32_t tx_fail_total; uint32_t rx_total; } SNC_CAN_AppContext_t; /* ========================= 全局上下文 ========================= */ extern SNC_CAN_AppContext_t g_snc_can_app; /* ========================= 初始化与任务 ========================= */ void SNC_CAN_AppInit(void); void SNC_CAN_20msTask(void); void SNC_CAN_100msTask(void); void SNC_CAN_PollOnlineState(uint32_t now_ms); /* ========================= 发送接口 ========================= */ HAL_StatusTypeDef SNC_CAN_SendHeartbeat(void); HAL_StatusTypeDef SNC_CAN_SendCmdVel(float vx_mps, float wz_radps, uint8_t ctrl_flags); /* ========================= 接收回调 ========================= */ void SNC_CAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t rxFifo0ITs); /* ========================= 数据读取接口 ========================= */ const SNC_CAN_AppContext_t *SNC_CAN_GetContext(void); /** * @brief 原子取走并清零里程增量累加器 * * 消费者(monitorTask)每周期调用一次,取走自上次消费以来 * ISR 累积的全部增量帧总和。返回后累加器归零,等待下一批帧。 * * @param[out] fl 左前轮累计增量 ticks (截断为 int16) * @param[out] rl 左后轮累计增量 ticks * @param[out] fr 右前轮累计增量 ticks * @param[out] rr 右后轮累计增量 ticks * @param[out] dt_ms 累加增量实际覆盖的时间窗 [ms] * 若无法可靠计算(如系统首帧)则为 0,可用默认帧间隔替代 * @return 本次取走的帧数;0 表示期间没有新帧到达 */ uint8_t SNC_CAN_ConsumeOdomDelta(int16_t *fl, int16_t *rl, int16_t *fr, int16_t *rr, uint32_t *dt_ms); /* ========================= 工具接口 ========================= */ uint8_t SNC_CAN_Crc8J1850(const uint8_t *data, uint16_t len); #ifdef __cplusplus } #endif #endif /* __SNC_CAN_APP_H */