#ifndef DEVICE_CANFD_H #define DEVICE_CANFD_H #include #include "sys/config.h" /** @brief 每通道反馈快照中保存的最近一帧最大数据长度(本地诊断用)。 */ #define CANFD_FEEDBACK_LAST_DATA_MAX 32U /** @brief 每通道不重复 ID 快照表最大条目数(CONFIG_CANFD_ID_SNAPSHOT_MAX)。 */ #define CANFD_ID_SNAPSHOT_MAX CONFIG_CANFD_ID_SNAPSHOT_MAX /** @brief 快照条目中最大数据字节数(CONFIG_CANFD_ID_SNAPSHOT_DATA_MAX)。 */ #define CANFD_ID_SNAPSHOT_DATA_MAX CONFIG_CANFD_ID_SNAPSHOT_DATA_MAX /** * @brief CANFD 通信模块。 * @note 负责 GPIO/模式控制、波特率配置、异步接收缓存与按需发送能力。 */ class CanFd { public: /** * @brief CANFD 接收过滤模式枚举。 * @note 默认模式为 kModeAllPass,即三路控制器关闭硬件 ID 过滤,全部接收。 */ enum class Mode : uint8_t { kModeAllPass = 0U, /**< 全部接收模式:禁用硬件 ID/掩码匹配,所有 11/29 位帧都进入 FIFO0。 */ kModeFilterId = 1U, /**< 过滤模式:按 CONFIG_CAN_RX_FILTER_ID/MASK 做标准帧掩码匹配。 */ kModeBridge0To12 = 2U, /**< CAN0 桥接模式:硬件过滤等同 AllPass,CAN0 收到的每一帧按原帧格式自动转发到 CAN1 与 CAN2。 */ }; /** * @brief CAN 发送结果枚举,枚举值即 SPI 错误响应码,便于 handler 直接透传。 * @note 0xE0/0xE1 保留给 handler 入口校验(payload 过短/通道非法), * 不经 transmitFrame 返回;0xE2~0xE5 由 transmitFrame 区分四种失败原因。 */ enum class CanTxResult : uint8_t { kOk = 0x00U, /**< 发送成功,已进入硬件邮箱。 */ kErrBusOff = 0xE2U, /**< 通道处于 BUS_OFF,不发送。 */ kErrTecHigh = 0xE3U, /**< TEC > 96,暂停发送保护通道。 */ kErrMailbox = 0xE4U, /**< 3 个硬件邮箱全 PENDING,abort+重投仍失败。 */ kErrInvalid = 0xE5U, /**< 入口参数无效(channelIndex/dlc/data 等)。 */ }; /** * @brief CANFD 运行诊断统计信息。 */ struct Diag { uint32_t tx_count; uint32_t rx_count; uint32_t error_count; uint32_t rx_drop_count; uint32_t rx_fifo_full_count; uint32_t rx_fifo_overfull_count; uint32_t tx_abort_count; uint32_t last_rx_id; uint32_t last_tx_id; uint8_t last_rx_channel; uint8_t last_tx_channel; uint8_t last_payload_length; uint8_t last_rx_fd_flag; uint8_t last_rx_brs; uint8_t last_rx_esi; }; /** * @brief 单通道运行计数(每路独立累计)。 */ struct ChannelStats { uint32_t rx_count; /**< 该通道累计成功接收帧数。 */ uint32_t tx_count; /**< 该通道累计由硬件 MTFNERR 确认的成功发送帧数。 */ uint32_t rx_drop_count; /**< 该通道累计软件丢帧数。 */ uint32_t error_count; /**< 该通道累计错误计数(与发送邮箱失败合并)。 */ uint32_t last_rx_id; /**< 该通道最近一帧报文标识符。 */ uint8_t last_rx_dlc; /**< 该通道最近一帧报文数据长度。 */ uint8_t last_rx_fd_flag; /**< 该通道最近一帧 FD 帧标志。 */ uint8_t last_rx_brs; /**< 该通道最近一帧 BRS 标志。 */ uint8_t last_rx_esi; /**< 该通道最近一帧 ESI 标志。 */ uint8_t last_rx_data[CANFD_FEEDBACK_LAST_DATA_MAX]; uint8_t tec; /**< 最近一次采样时硬件发送错误计数器 (0~255)。 */ uint8_t rec; /**< 最近一次采样时硬件接收错误计数器 (0~255)。 */ uint8_t bus_state; /**< 总线状态位图:bit0=init_mode, bit1=sleep, bit2=error_passive, bit3=bus_off, bit4=error_warning。 */ }; /** * @brief 单通道 1Hz 反馈快照(每秒刷新一次)。 */ struct ChannelFeedback { uint32_t snapshot_seq; /**< 第几次刷新(自启动起递增)。 */ ChannelStats stats; /**< 抓拍时刻的该通道统计与最近一帧信息。 */ }; /** * @brief 单个不重复 ID 的最新帧快照。 */ struct IdSnapshotEntry { uint32_t id; /**< CAN 报文标识符。 */ uint32_t seq; /**< 该条目被更新的序号(自启动起递增)。 */ uint32_t rx_count; /**< 该 ID 累计接收帧数。 */ uint8_t dlc; /**< 最新一帧数据长度。 */ uint8_t fd_flag; /**< 最新一帧 FD 标志。 */ uint8_t brs; /**< 最新一帧 BRS 标志。 */ uint8_t esi; /**< 最新一帧 ESI 标志。 */ uint8_t data[CANFD_ID_SNAPSHOT_DATA_MAX]; /**< 最新一帧数据。 */ bool occupied; /**< 条目是否已被占用。 */ }; /** * @brief 每通道不重复 ID 快照表(1Hz 刷新,SPI 命令分页拉取)。 */ struct IdSnapshotTable { IdSnapshotEntry entries[CANFD_ID_SNAPSHOT_MAX]; uint8_t count; /**< 当前占用条目数。 */ uint32_t seq; /**< 全局更新序号(每次 refresh 时递增)。 */ }; static void init(); /** * @brief L0 轻量自检(不向物理总线发帧)。 * @retval true 所有已使能通道通过;false 无使能通道或任一路失败。 * @note 检查:硬件 init 结果、退出 init mode、收发器 Normal、无 BUS_OFF、TEC/REC 阈值。 */ static bool selfTestL0(); /** * @brief 查询指定通道是否已使能。 * @param[in] channelIndex 通道索引 0/1/2。 * @retval bool true=使能,false=禁用。 */ static bool isChannelEnabled(uint8_t channelIndex); /** * @brief 切换 CANFD 接收过滤模式。 */ static void setMode(Mode mode); /** @brief 查询当前 CANFD 接收过滤模式。 */ static Mode mode(); static bool hasRxFrame(); static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t *channel = nullptr); static CanTxResult transmitFrame(uint8_t channelIndex, uint32_t ext_id, const uint8_t *data, uint8_t dlc, bool periodic = false); /** @brief 立即停掉指定通道所有 PENDING 发送邮箱(阻止 auto_retrans 残留重发,供停等式协议用)。 */ static void abortPendingTx(uint8_t channelIndex); /** @brief 运行时切换指定通道 CAN 自动重发(ARD 位,需 init-mode);停等式 OTA 期间关闭、结束后恢复。 */ static void setChannelAutoRetrans(uint8_t channelIndex, bool enable); static const Diag *diag(); /** * @brief 刷新三路 1Hz 反馈快照。 * @note 通常由任务调度器在 1 秒节拍调用,将各通道实时计数与最近一帧信息抓拍到 s_feedback。 */ static void refreshFeedbackSnapshot(); /** * @brief 拷贝指定通道的最新反馈快照到调用方缓冲。 * @param[in] channelIndex 目标 CAN 通道号(0/1/2)。 * @param[out] out 目标快照缓冲;为空或通道非法时返回 false。 * @retval bool true 表示成功拷贝,false 表示参数非法。 */ static bool copyFeedbackSnapshot(uint8_t channelIndex, ChannelFeedback *out); /** * @brief 拷贝指定通道的不重复 ID 快照表。 * @param[in] channelIndex 目标通道 0/1/2。 * @param[out] out 输出缓冲。 * @retval true 成功。 */ static bool copyIdSnapshotTable(uint8_t channelIndex, IdSnapshotTable *out); static void handleRxInterrupt(uint8_t channelIndex); static void handleTxInterrupt(uint8_t channelIndex); private: static constexpr uint8_t kRxQueueDepth = 16U; struct RxFrame { uint8_t channel; uint32_t id; uint8_t data[64]; uint8_t length; }; struct RxQueue { RxFrame frames[kRxQueueDepth]; volatile uint8_t head; volatile uint8_t tail; volatile uint8_t count; }; static void initGpio(); /** * @brief 根据当前模式为指定控制器配置接收过滤器。 * @param[in] canPeriph 目标 CAN 外设实例。 * @param[in] filterNumber 过滤器编号。 * @param[in] mode 目标接收过滤模式。 */ static void configureRxFilter(uint32_t canPeriph, uint8_t filterNumber, Mode mode); static bool initNominalTiming(uint32_t canPeriph); static bool initFdMode(uint32_t canPeriph); static void printClockInfo(); static void initChannelHardware(uint8_t i); static void configureAllFilters(); static void printInitResult(); static void printErrorState(); static void runSelfTest(); static void runLoopbackTest(); static void dumpRegisters(); static Diag s_diag; static RxQueue s_rxQueues[3]; static uint8_t s_nextConsumeChannel; static volatile Mode s_mode; static ChannelStats s_channelStats[3]; static ChannelFeedback s_feedback[3]; /** 各通道硬件 init(时序+退出 init mode)是否成功 */ static bool s_channelHwOk[3]; static uint32_t s_feedbackSeq; static IdSnapshotTable s_idSnapshot[3]; }; #endif /* DEVICE_CANFD_H */