/** * @file canfd.cpp * @brief CANFD 通信模块实现。 */ #include "main.h" #include "device/canfd.h" #include "sys/config.h" #include "sys/critical.h" #include "sys/task_manager.h" #include namespace { struct TxFrame { can_trasnmit_message_struct tx_message; uint8_t payload[64]; }; struct TxQueue { TxFrame frames[4]; uint8_t head; uint8_t tail; uint8_t count; }; struct CanChannelConfig { uint32_t canPeriph; rcu_periph_enum canRcu; uint32_t txPort; uint32_t txPin; uint32_t rxPort; uint32_t rxPin; uint32_t slpPort; uint32_t slpPin; IRQn_Type rx0Irq; uint8_t filterNumber; }; constexpr CanChannelConfig kChannels[] = { { CAN0, RCU_CAN0, GPIOA, GPIO_PIN_12, GPIOA, GPIO_PIN_11, GPIOA, GPIO_PIN_8, CAN0_RX0_IRQn, 0U }, { CAN1, RCU_CAN1, GPIOB, GPIO_PIN_12, GPIOB, GPIO_PIN_13, GPIOB, GPIO_PIN_14, CAN1_RX0_IRQn, 1U }, { CAN2, RCU_CAN2, GPIOE, GPIO_PIN_1, GPIOE, GPIO_PIN_0, GPIOB, GPIO_PIN_9, CAN2_RX0_IRQn, 2U }, }; constexpr size_t kChannelCount = sizeof(kChannels) / sizeof(kChannels[0]); constexpr uint8_t kTxQueueDepth = 4U; constexpr uint32_t kCanRxParseTaskMask = (1UL << TASK_CAN_RX_PARSE); } // namespace CanFd::Diag CanFd::s_diag{}; CanFd::RxQueue CanFd::s_rxQueues[3]{}; uint8_t CanFd::s_nextConsumeChannel = 0U; volatile CanFd::Mode CanFd::s_mode = CanFd::Mode::kModeAllPass; CanFd::ChannelStats CanFd::s_channelStats[3]{}; CanFd::ChannelFeedback CanFd::s_feedback[3]{}; uint32_t CanFd::s_feedbackSeq = 0U; CanFd::IdSnapshotTable CanFd::s_idSnapshot[3]{}; static TxQueue s_txQueues[3]{}; /** * @brief 初始化三路 CANFD 相关 GPIO。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note 为 CAN0/CAN1/CAN2 的 TX、RX 及收发器 SLP 控制脚统一完成 GPIO 时钟与模式配置。 */ void CanFd::initGpio() { rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_GPIOE); rcu_periph_clock_enable(RCU_AF); for(size_t i = 0; i < kChannelCount; ++i) { const auto &ch = kChannels[i]; gpio_init(ch.rxPort, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, ch.rxPin); gpio_init(ch.txPort, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, ch.txPin); gpio_init(ch.slpPort, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, ch.slpPin); } } /** * @brief 为指定 CAN 控制器配置接收过滤器。 * @param[in] canPeriph 目标 CAN 外设实例。 * @param[in] filterNumber 过滤器编号。 * @param[in] mode 目标接收过滤模式。 * @param[out] 无。 * @retval 无返回值。 * @note kModeAllPass 模式下使用 32 位掩码模式且 mask=0/list=0,硬件直接放行所有标准/扩展帧到 FIFO0; * kModeFilterId 模式下按 CONFIG_CAN_RX_FILTER_ID/MASK 做标准帧掩码匹配。 */ void CanFd::configureRxFilter(uint32_t canPeriph, uint8_t filterNumber, Mode mode) { can_filter_parameter_struct can_filter{}; can_filter.filter_number = filterNumber; can_filter.filter_mode = CAN_FILTERMODE_MASK; can_filter.filter_bits = CAN_FILTERBITS_32BIT; if (mode == Mode::kModeAllPass) { /* 全部接收:列表与掩码都为 0,硬件不再对 ID 做比较。 */ can_filter.filter_list_high = 0x0000U; can_filter.filter_list_low = 0x0000U; can_filter.filter_mask_high = 0x0000U; can_filter.filter_mask_low = 0x0000U; } else { can_filter.filter_list_high = static_cast((CONFIG_CAN_RX_FILTER_ID & 0x7FFU) << 5U); can_filter.filter_list_low = 0x0000U; can_filter.filter_mask_high = static_cast((CONFIG_CAN_RX_FILTER_MASK & 0x7FFU) << 5U); can_filter.filter_mask_low = static_cast(1U << 2U); } can_filter.filter_fifo_number = CAN_FIFO0; can_filter.filter_enable = ENABLE; can_filter_init(canPeriph, &can_filter); } /** * @brief 配置指定 CAN 控制器的标称仲裁段位时序。 * @param[in] canPeriph 目标 CAN 外设实例。 * @param[out] 无。 * @retval 无返回值。 * @note 当前按 1 Mbps 仲裁段参数完成普通 CAN 初始化。 */ void CanFd::initNominalTiming(uint32_t canPeriph) { can_parameter_struct can_parameter{}; can_struct_para_init(CAN_INIT_STRUCT, &can_parameter); can_parameter.time_triggered = DISABLE; can_parameter.auto_bus_off_recovery = ENABLE; can_parameter.auto_wake_up = DISABLE; can_parameter.auto_retrans = ENABLE; can_parameter.rec_fifo_overwrite = ENABLE; can_parameter.trans_fifo_order = ENABLE; can_parameter.working_mode = CAN_NORMAL_MODE; can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; can_parameter.time_segment_1 = CAN_BT_BS1_7TQ; can_parameter.time_segment_2 = CAN_BT_BS2_2TQ; can_parameter.prescaler = 6U; can_init(canPeriph, &can_parameter); } /** * @brief 配置指定 CAN 控制器的 CAN FD 数据段参数。 * @param[in] canPeriph 目标 CAN 外设实例。 * @param[out] 无。 * @retval 无返回值。 * @note 在 GD32E508 平台下启用 FD、BRS 与 TDC;非该平台则仅使能 FD 功能。 */ void CanFd::initFdMode(uint32_t canPeriph) { #ifdef GD32E508 can_fdframe_struct can_fd_parameter{}; can_fd_tdc_struct can_fd_tdc_parameter{}; can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter); can_fd_parameter.fd_frame = ENABLE; can_fd_parameter.excp_event_detect = ENABLE; can_fd_parameter.delay_compensation = ENABLE; can_fd_tdc_parameter.tdc_filter = 0x04U; can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET; can_fd_tdc_parameter.tdc_offset = 0x04U; can_fd_parameter.p_delay_compensation = &can_fd_tdc_parameter; can_fd_parameter.iso_bosch = CAN_FDMOD_ISO; can_fd_parameter.esi_mode = CAN_ESIMOD_HARDWARE; can_fd_parameter.data_prescaler = 6U; can_fd_parameter.data_resync_jump_width = CAN_BT_SJW_1TQ; can_fd_parameter.data_time_segment_1 = CAN_BT_BS1_3TQ; can_fd_parameter.data_time_segment_2 = CAN_BT_BS2_1TQ; can_fd_init(canPeriph, &can_fd_parameter); #else can_fd_function_enable(canPeriph); #endif } /** * @brief 构造内部发送缓冲中的状态帧内容。 * @param[in] status 指向状态载荷的指针。 * @param[out] 无。 * @retval 无返回值。 * @note 当前实现中状态帧构造逻辑已内联至 `enqueueTxFrame()`,此函数仅保留接口兼容性。 */ void CanFd::buildStatusFrame(const StatusPayload *status) { if(status == nullptr) { return; } const int16_t temp_x100 = static_cast(status->temperature * 100.0f); const uint16_t vdda_x1000 = static_cast(status->vdda * 1000.0f); auto &frame = s_txQueues[0].frames[0]; auto &p = frame.payload; p[0] = static_cast(status->temp_raw & 0xFFU); p[1] = static_cast((status->temp_raw >> 8) & 0xFFU); p[2] = static_cast(status->vref_raw & 0xFFU); p[3] = static_cast((status->vref_raw >> 8) & 0xFFU); p[4] = static_cast(temp_x100 & 0xFF); p[5] = static_cast((temp_x100 >> 8) & 0xFF); p[6] = static_cast(vdda_x1000 & 0xFFU); p[7] = static_cast((vdda_x1000 >> 8) & 0xFFU); p[8] = CONFIG_CAN_STATUS_TAG0; p[9] = CONFIG_CAN_STATUS_TAG1; p[10] = CONFIG_CAN_STATUS_TAG2; p[11] = CONFIG_CAN_STATUS_TAG3; } /** * @brief 向指定 CAN 通道的发送 FIFO 入队一帧状态报文。 * @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。 * @param[in] status 指向状态载荷的指针。 * @param[out] 无。 * @retval bool true 表示成功入队,false 表示参数无效或该通道发送队列已满。 * @note 仅完成软件发送队列入队,不会立即触发硬件发送。 */ bool CanFd::enqueueTxFrame(uint8_t channelIndex, const StatusPayload *status) { if(status == nullptr || channelIndex >= kChannelCount) { return false; } auto &queue = s_txQueues[channelIndex]; if(queue.count >= kTxQueueDepth) { s_diag.tx_drop_count++; s_channelStats[channelIndex].error_count++; return false; } buildStatusFrame(status); auto &frame = queue.frames[queue.tail]; can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &frame.tx_message); frame.tx_message.tx_sfid = CONFIG_CAN_STATUS_ID; frame.tx_message.tx_efid = 0U; frame.tx_message.tx_ff = CAN_FF_STANDARD; frame.tx_message.tx_ft = CAN_FT_DATA; frame.tx_message.tx_dlen = CANFD_STATUS_DLEN; #ifdef GD32E508 frame.tx_message.fd_flag = ENABLE; frame.tx_message.fd_brs = ENABLE; frame.tx_message.fd_esi = CAN_ESI_DOMINANT; #endif const auto &src = s_txQueues[0].frames[0].payload; auto &p = frame.payload; for(uint8_t j = 0; j < CANFD_STATUS_DLEN; ++j) { p[j] = src[j]; frame.tx_message.tx_data[j] = p[j]; } queue.tail = static_cast((queue.tail + 1U) % kTxQueueDepth); queue.count++; return true; } /** * @brief 初始化三路 CANFD 控制器、过滤器、中断与软件队列。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note 初始化后默认将三路 TCAN1057A 收发器置为 Normal,并使能 FIFO0 的非空/满/overfull 中断统计。 */ void CanFd::init() { s_diag = {}; s_nextConsumeChannel = 0U; s_mode = Mode::kModeAllPass; for (size_t i = 0; i < kChannelCount; ++i) { s_channelStats[i] = {}; s_feedback[i] = {}; } s_feedbackSeq = 0U; initGpio(); for(size_t i = 0; i < kChannelCount; ++i) { const auto &ch = kChannels[i]; rcu_periph_clock_enable(ch.canRcu); gpio_bit_reset(ch.slpPort, ch.slpPin); /* STB=0 -> Normal */ can_deinit(ch.canPeriph); initNominalTiming(ch.canPeriph); initFdMode(ch.canPeriph); configureRxFilter(ch.canPeriph, ch.filterNumber, s_mode); can_interrupt_enable(ch.canPeriph, CAN_INT_RFNE0 | CAN_INT_RFF0 | CAN_INT_RFO0); nvic_irq_enable(ch.rx0Irq, CONFIG_IRQ_PREEMPT_CAN, static_cast(i)); s_rxQueues[i].head = 0U; s_rxQueues[i].tail = 0U; s_rxQueues[i].count = 0U; s_txQueues[i].head = 0U; s_txQueues[i].tail = 0U; s_txQueues[i].count = 0U; } s_nextConsumeChannel = 0U; } /** * @brief 切换 CANFD 接收过滤模式。 * @param[in] mode 目标过滤模式。 * @note 切换时逐路重新配置过滤器;can_filter_init 内部会清 FCTL_FLD,无需手动进入 INITIAL 模式。 */ void CanFd::setMode(Mode mode) { uint32_t save = RT_CRITICAL_SAVE(); s_mode = mode; for (size_t i = 0; i < kChannelCount; ++i) { configureRxFilter(kChannels[i].canPeriph, kChannels[i].filterNumber, mode); } RT_CRITICAL_RESTORE(save); } /** * @brief 查询当前 CANFD 接收过滤模式。 */ CanFd::Mode CanFd::mode() { return s_mode; } /** * @brief 执行兼容保留的 CAN 轮询入口。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note 当前三路接收均由 RX0 中断驱动,该函数仅保留旧调用兼容性。 */ void CanFd::poll() { /* 兼容保留:三通道接收已由 RX0 中断驱动 */ } /** * @brief 根据状态载荷为三路发送队列批量准备状态报文。 * @param[in] status 指向状态载荷的指针;为空时直接返回。 * @param[out] 无。 * @retval 无返回值。 * @note 当前默认不会周期性调用;仅在外部显式触发发送测试或业务发送时使用。 */ void CanFd::prepareStatus(const StatusPayload *status) { if(status == nullptr) { return; } for(size_t i = 0; i < kChannelCount; ++i) { (void)enqueueTxFrame(static_cast(i), status); } } /** * @brief 根据状态载荷仅为指定通道准备一帧状态报文。 * @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。 * @param[in] status 指向状态载荷的指针;为空时返回失败。 * @param[out] 无。 * @retval bool true 表示成功入队,false 表示参数非法或发送队列已满。 */ bool CanFd::prepareStatusForChannel(uint8_t channelIndex, const StatusPayload *status) { return enqueueTxFrame(channelIndex, status); } /** * @brief 将当前各通道已准备好的状态报文送入硬件邮箱发送。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note 当前默认不会周期性调用;仅处理调用方显式准备过的发送队列内容。 */ void CanFd::sendPrepared() { for(size_t i = 0; i < kChannelCount; ++i) { auto &queue = s_txQueues[i]; if(queue.count == 0U) { continue; } auto &frame = queue.frames[queue.head]; const uint8_t mailbox = can_message_transmit(kChannels[i].canPeriph, &frame.tx_message); if(mailbox != CAN_NOMAILBOX) { s_diag.tx_count++; s_diag.last_payload_length = frame.tx_message.tx_dlen; s_diag.last_tx_id = frame.tx_message.tx_sfid; s_diag.last_tx_channel = static_cast(i); s_channelStats[i].tx_count++; queue.head = static_cast((queue.head + 1U) % kTxQueueDepth); queue.count--; } else { s_diag.error_count++; s_channelStats[i].error_count++; } } } /** * @brief 查询三路 CAN 通道是否存在待消费接收报文。 * @param[in] 无。 * @param[out] 无。 * @retval bool true 表示至少一条接收队列非空,false 表示当前三路均无待消费报文。 */ bool CanFd::hasRxFrame() { bool found = false; uint32_t save = RT_CRITICAL_SAVE(); for(size_t i = 0; i < kChannelCount; ++i) { if(s_rxQueues[i].count != 0U) { found = true; break; } } RT_CRITICAL_RESTORE(save); return found; } /** * @brief 轮转读取并消费一帧接收报文。 * @param[in] 无。 * @param[out] id 输出报文标识符;允许为空。 * @param[out] data 输出报文数据缓存;允许为空。 * @param[out] length 输出报文长度;允许为空。 * @param[out] channel 输出报文来源通道号;允许为空。 * @retval bool true 表示成功消费一帧报文,false 表示当前三路接收队列均为空。 * @note 消费顺序采用从 `s_nextConsumeChannel` 开始的轮转扫描,避免长期偏向固定通道。 */ bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t *channel) { RxFrame localFrame{}; bool found = false; uint32_t save = RT_CRITICAL_SAVE(); for(size_t n = 0; n < kChannelCount; ++n) { const uint8_t idx = static_cast((s_nextConsumeChannel + n) % kChannelCount); auto &queue = s_rxQueues[idx]; if(queue.count == 0U) { continue; } localFrame = queue.frames[queue.head]; queue.head = static_cast((queue.head + 1U) % kRxQueueDepth); queue.count--; s_nextConsumeChannel = static_cast((idx + 1U) % kChannelCount); found = true; break; } RT_CRITICAL_RESTORE(save); if(!found) { return false; } if(channel) *channel = localFrame.channel; if(id) *id = localFrame.id; if(length) *length = localFrame.length; if(data) { for(uint8_t i = 0; i < localFrame.length; ++i) { data[i] = localFrame.data[i]; } } return true; } /** * @brief 直接向指定 CAN 通道发送一帧任意数据报文(扩展帧)。 * @param[in] channelIndex 目标 CAN 通道号(0/1/2)。 * @param[in] ext_id 扩展帧 ID(29 位)。 * @param[in] data 数据负载指针。 * @param[in] dlc 数据长度(1~64)。 * @retval bool true 表示成功入队硬件邮箱,false 表示参数无效或邮箱已满。 * @note 绕过软件发送队列,直接调用硬件发送;适用于 SPI 桥接等即时发送场景。 */ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id, const uint8_t *data, uint8_t dlc) { if (channelIndex >= kChannelCount || data == nullptr || dlc == 0U || dlc > 64U) { return false; } can_trasnmit_message_struct tx_msg{}; can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &tx_msg); tx_msg.tx_efid = ext_id; tx_msg.tx_ff = CAN_FF_EXTENDED; tx_msg.tx_ft = CAN_FT_DATA; tx_msg.tx_dlen = dlc; #ifdef GD32E508 tx_msg.fd_flag = (dlc > 8U) ? ENABLE : DISABLE; tx_msg.fd_brs = (dlc > 8U) ? ENABLE : DISABLE; tx_msg.fd_esi = CAN_ESI_DOMINANT; #endif for (uint8_t i = 0U; i < dlc; ++i) { tx_msg.tx_data[i] = data[i]; } const uint32_t canPeriph = kChannels[channelIndex].canPeriph; uint8_t mailbox = can_message_transmit(canPeriph, &tx_msg); /* 邮箱全占时保持非阻塞:本函数可能处于 SPI CS 中断链路,不能等待 * SysTick。若 auto_retrans 因总线无 ACK 卡住全部邮箱,只立即 abort * 一个 PENDING 邮箱并重投一次,把中断占用时间控制在寄存器访问级别。 */ if (mailbox == CAN_NOMAILBOX) { for (uint8_t mb = CAN_MAILBOX0; mb <= CAN_MAILBOX2; ++mb) { if (can_transmit_states(canPeriph, mb) != CAN_TRANSMIT_PENDING) { continue; } if (can_transmission_stop(canPeriph, mb) == SUCCESS) { s_diag.tx_abort_count++; break; } } mailbox = can_message_transmit(canPeriph, &tx_msg); } if (mailbox != CAN_NOMAILBOX) { s_diag.tx_count++; s_diag.last_tx_id = ext_id; s_diag.last_tx_channel = channelIndex; s_diag.last_payload_length = dlc; s_channelStats[channelIndex].tx_count++; return true; } s_diag.error_count++; s_channelStats[channelIndex].error_count++; return false; } /** * @brief 获取 CANFD 诊断统计信息只读指针。 * @param[in] 无。 * @param[out] 无。 * @retval const CanFd::Diag* 返回内部诊断统计结构体只读指针。 */ const CanFd::Diag *CanFd::diag() { return &s_diag; } /** * @brief 将三路 TCAN1057A 收发器置为 Normal 模式。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note 对所有通道统一将 SLP/STB 控制脚拉低。 */ void CanFd::setNormalMode() { for(size_t i = 0; i < kChannelCount; ++i) { gpio_bit_reset(kChannels[i].slpPort, kChannels[i].slpPin); } } /** * @brief 将三路 TCAN1057A 收发器置为 Silent 模式。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note 对所有通道统一将 SLP/STB 控制脚拉高。 */ void CanFd::setSilentMode() { for(size_t i = 0; i < kChannelCount; ++i) { gpio_bit_set(kChannels[i].slpPort, kChannels[i].slpPin); } } /** * @brief 处理指定通道的 RX0 中断接收。 * @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。 * @param[out] 无。 * @retval 无返回值。 * @note 从对应控制器 FIFO0 循环取空所有待处理报文,更新诊断统计,写入该通道软件接收队列,并置位解析任务。 */ void CanFd::handleRxInterrupt(uint8_t channelIndex) { if(channelIndex >= kChannelCount) return; const auto &ch = kChannels[channelIndex]; const bool fifoFull = (can_interrupt_flag_get(ch.canPeriph, CAN_INT_FLAG_RFF0) != RESET); const bool fifoOverfull = (can_interrupt_flag_get(ch.canPeriph, CAN_INT_FLAG_RFO0) != RESET); if(fifoFull) { s_diag.rx_fifo_full_count++; } if(fifoOverfull) { s_diag.rx_fifo_overfull_count++; } bool hadTraffic = false; while(can_interrupt_flag_get(ch.canPeriph, CAN_INT_FLAG_RFL0) != RESET) { hadTraffic = true; can_receive_message_struct rx_message{}; can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message); can_message_receive(ch.canPeriph, CAN_FIFO0, &rx_message); s_diag.rx_count++; s_diag.last_rx_channel = channelIndex; s_diag.last_payload_length = rx_message.rx_dlen; s_diag.last_rx_fd_flag = rx_message.fd_flag; s_diag.last_rx_brs = rx_message.fd_brs; s_diag.last_rx_esi = rx_message.fd_esi; s_diag.last_rx_id = (rx_message.rx_ff == CAN_FF_STANDARD) ? rx_message.rx_sfid : rx_message.rx_efid; /* 维护每通道独立计数与最近一帧快照,用于 1Hz 反馈给 ESP32。 */ auto &stats = s_channelStats[channelIndex]; stats.rx_count++; stats.last_rx_id = s_diag.last_rx_id; stats.last_rx_dlc = rx_message.rx_dlen; stats.last_rx_fd_flag = rx_message.fd_flag; stats.last_rx_brs = rx_message.fd_brs; stats.last_rx_esi = rx_message.fd_esi; const uint8_t copyLen = (rx_message.rx_dlen < CANFD_FEEDBACK_LAST_DATA_MAX) ? rx_message.rx_dlen : static_cast(CANFD_FEEDBACK_LAST_DATA_MAX); for (uint8_t i = 0U; i < copyLen; ++i) { stats.last_rx_data[i] = rx_message.rx_data[i]; } for (uint8_t i = copyLen; i < CANFD_FEEDBACK_LAST_DATA_MAX; ++i) { stats.last_rx_data[i] = 0U; } /* 维护每通道不重复 ID 快照表,用于 1Hz 分页拉取给 ESP32。 */ { auto &table = s_idSnapshot[channelIndex]; const uint32_t rxId = s_diag.last_rx_id; /* 查找已有条目 */ uint8_t found = CANFD_ID_SNAPSHOT_MAX; for (uint8_t si = 0U; si < table.count; ++si) { if (table.entries[si].occupied && table.entries[si].id == rxId) { found = si; break; } } if (found >= CANFD_ID_SNAPSHOT_MAX) { /* 未找到:表未满则新建,满了淘汰最旧(index 0) */ if (table.count < CANFD_ID_SNAPSHOT_MAX) { found = table.count; table.count++; } else { found = 0U; /* 淘汰后整体左移一格让新条目在末尾 */ for (uint8_t si = 0U; si < CANFD_ID_SNAPSHOT_MAX - 1U; ++si) { table.entries[si] = table.entries[si + 1U]; } found = CANFD_ID_SNAPSHOT_MAX - 1U; } table.entries[found] = {}; table.entries[found].id = rxId; table.entries[found].occupied = true; } auto &entry = table.entries[found]; entry.rx_count++; entry.dlc = rx_message.rx_dlen; entry.fd_flag = rx_message.fd_flag; entry.brs = rx_message.fd_brs; entry.esi = rx_message.fd_esi; const uint8_t snapCopy = (rx_message.rx_dlen < CANFD_ID_SNAPSHOT_DATA_MAX) ? rx_message.rx_dlen : static_cast(CANFD_ID_SNAPSHOT_DATA_MAX); for (uint8_t si = 0U; si < snapCopy; ++si) { entry.data[si] = rx_message.rx_data[si]; } for (uint8_t si = snapCopy; si < CANFD_ID_SNAPSHOT_DATA_MAX; ++si) { entry.data[si] = 0U; } } auto &queue = s_rxQueues[channelIndex]; if(queue.count >= kRxQueueDepth) { s_diag.rx_drop_count++; stats.rx_drop_count++; continue; } auto &frame = queue.frames[queue.tail]; frame.channel = channelIndex; frame.id = s_diag.last_rx_id; frame.length = rx_message.rx_dlen; for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) { frame.data[i] = rx_message.rx_data[i]; } queue.tail = static_cast((queue.tail + 1U) % kRxQueueDepth); queue.count++; } if(fifoFull) { can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFF0); } if(fifoOverfull) { can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFO0); } if(hadTraffic) { TaskManager::set(kCanRxParseTaskMask); } } /** * @brief CAN0 兼容中断处理入口。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note 兼容保留入口,内部统一转调 `handleRxInterrupt(0U)`。 */ void CanFd::onRx0Interrupt() { handleRxInterrupt(0U); } /** * @brief 刷新三路 CAN 通道的 1Hz 反馈快照。 * @note 在临界区内拷贝实时计数与最近一帧信息到 s_feedback;保证 SPI 命令读到一致快照。 * 典型调用频率 1Hz,由任务调度器在心跳节拍触发。 */ void CanFd::refreshFeedbackSnapshot() { uint32_t save = RT_CRITICAL_SAVE(); s_feedbackSeq++; for (size_t i = 0; i < kChannelCount; ++i) { s_feedback[i].snapshot_seq = s_feedbackSeq; s_feedback[i].stats = s_channelStats[i]; s_idSnapshot[i].seq = s_feedbackSeq; } RT_CRITICAL_RESTORE(save); } /** * @brief 拷贝指定通道的最新反馈快照。 * @param[in] channelIndex 目标 CAN 通道号(0/1/2)。 * @param[out] out 目标快照缓冲;为空或通道非法时返回 false。 * @retval bool true 表示成功拷贝。 */ /** * @brief 拷贝指定通道的不重复 ID 快照表。 */ bool CanFd::copyIdSnapshotTable(uint8_t channelIndex, IdSnapshotTable *out) { if (out == nullptr || channelIndex >= kChannelCount) { return false; } uint32_t save = RT_CRITICAL_SAVE(); *out = s_idSnapshot[channelIndex]; RT_CRITICAL_RESTORE(save); return true; } bool CanFd::copyFeedbackSnapshot(uint8_t channelIndex, ChannelFeedback *out) { if (out == nullptr || channelIndex >= kChannelCount) { return false; } uint32_t save = RT_CRITICAL_SAVE(); *out = s_feedback[channelIndex]; RT_CRITICAL_RESTORE(save); return true; } /* ----- 中断入口 C 包装 ------------------------------------------------- */ /** * @brief CAN0 RX0 中断向量入口。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(0U)`。 */ extern "C" void CAN0_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(0U); } /** * @brief CAN1 RX0 中断向量入口。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(1U)`。 */ extern "C" void CAN1_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(1U); } /** * @brief CAN2 RX0 中断向量入口。 * @param[in] 无。 * @param[out] 无。 * @retval 无返回值。 * @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(2U)`。 */ extern "C" void CAN2_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(2U); }