Files
DCU_APP/GD32E508_DCU_APP/device/CANFD.cpp
T
cbb a52d94687b refactor(SPI): 实现全双工DMA传输以提升通信可靠性
- 配置DMA0_CH1用于SPI0_RX接收,替代原有的RBNE中断逐字节处理
- 添加startSpiRxDma、stopSpiDmas、drainSpiRxHw等DMA控制函数
- 在CS空闲期预装下一笔DMA传输,避免EXTI CS下降沿相对于SCK偏晚导致收0字节
- 与BOOT工程SpiOta的DMA配置对齐,确保通信稳定性

refactor(SPI): 优化CS边沿处理逻辑和命令解析时机

- 移除原有的EXTI5_9_IRQHandler用于SPI Ready引脚处理
- 将命令处理从异步任务调度改为在CS释放中断中同步完成
- 重构onCsAssert和onCsRelease逻辑,简化DMA启停流程
- 删除TASK_SPI_CMD_PROCESS任务类型,减少不必要的任务调度

feat(CANFD): 修改transmitFrame返回类型为枚举值以提供详细错误信息

- 将CanFd::transmitFrame返回类型从bool改为CanTxResult枚举
- 新增CanTxResult枚举定义,包含kOk、kErrBusOff、kErrTecHigh、kErrMailbox、kErrInvalid
- 更新SPI桥接命令处理中对transmitFrame返回值的处理逻辑
- 枚举值设计与SPI错误响应码对齐,便于直接透传错误信息

refactor(SPI): 实现全双工DMA传输以提升通信可靠性

- 配置DMA0_CH1用于SPI0_RX接收,替代原有的RBNE中断逐字节处理
- 添加startSpiRxDma、stopSpiDmas、drainSpiRxHw等DMA控制函数
- 在CS空闲期预装下一笔DMA传输,避免EXTI CS下降沿相对于SCK偏晚导致收0字节
- 与BOOT工程SpiOta的DMA配置对齐,确保通信稳定性

refactor(SPI): 优化CS边沿处理逻辑和命令解析时机

- 移除原有的EXTI5_9_IRQHandler用于SPI Ready引脚处理
- 将命令处理从异步任务调度改为在CS释放中断中同步完成
- 重构onCsAssert和onCsRelease逻辑,简化DMA启停流程
- 删除TASK_SPI_CMD_PROCESS任务类型,减少不必要的任务调度

feat(OTA): 添加BOOT版本号读取功能并优化版本信息获取

- 在ota_boot_request模块中添加readBootVersion函数
- 从OTA配置页Flash读取BOOT版本号信息
- 在SPI PING命令响应中使用新的版本读取接口
- 统一版本号获取方式,避免裸指针访问Flash地址

refactor(task): 调整诊断打印任务调度周期为5秒间隔

- 新增div_diag_print调度分频计数器用于诊断打印
- 将诊断打印任务从心跳任务中分离,独立控制执行周期
- 修改诊断打印函数注释,明确其由5秒节拍触发
- 优化任务调度逻辑,提高系统运行效率
2026-07-15 00:34:56 +08:00

1495 lines
61 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file canfd.cpp
* @brief CANFD 通信模块实现。
*/
#include "main.h"
#include "device/canfd.h"
#include "sys/config.h"
#include "sys/critical.h"
#include "sys/systick.h"
#include "sys/task_manager.h"
#include <cstring>
namespace {
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 txIrq;
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_TX_IRQn, CAN0_RX0_IRQn, 0U },
{ CAN1, RCU_CAN1, GPIOB, GPIO_PIN_13, GPIOB, GPIO_PIN_12, GPIOB, GPIO_PIN_14, CAN1_TX_IRQn, CAN1_RX0_IRQn, 14U },
{ CAN2, RCU_CAN2, GPIOE, GPIO_PIN_1, GPIOE, GPIO_PIN_0, GPIOB, GPIO_PIN_9, CAN2_TX_IRQn, CAN2_RX0_IRQn, 2U },
};
constexpr size_t kChannelCount = sizeof(kChannels) / sizeof(kChannels[0]);
/* ===== 通道使能开关:在 sys/config.h 中修改 CONFIG_CAN_CHx_ENABLE ===== */
constexpr bool kChannelEnabled[kChannelCount] = {
CONFIG_CAN_CH0_ENABLE,
CONFIG_CAN_CH1_ENABLE,
CONFIG_CAN_CH2_ENABLE,
};
constexpr uint32_t kCanRxParseTaskMask = (1UL << TASK_CAN_RX_PARSE);
static const char *canLastErrorName(uint32_t errReg)
{
static const char *const names[] = {
"NONE", "STUFF", "FORM", "ACK", "BIT_RECESSIVE", "BIT_DOMINANT", "CRC", "SOFTWARE"
};
return names[(errReg >> 4U) & 0x07U];
}
struct CanSelfTestResult {
bool txReq;
uint8_t mailbox;
uint8_t txState;
uint32_t stat;
uint32_t err;
uint32_t tec;
uint32_t rec;
bool busOff;
};
static CanSelfTestResult runSelfTestFrame(uint8_t channelIndex, uint32_t extId, const uint8_t *data, uint8_t len, bool fd, bool brs)
{
CanSelfTestResult result{};
result.mailbox = CAN_NOMAILBOX;
result.txState = CAN_TRANSMIT_FAILED;
if (channelIndex >= kChannelCount || data == nullptr || len == 0U || len > 64U) {
return result;
}
can_trasnmit_message_struct tx_msg{};
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &tx_msg);
tx_msg.tx_efid = extId;
tx_msg.tx_ff = CAN_FF_EXTENDED;
tx_msg.tx_ft = CAN_FT_DATA;
tx_msg.tx_dlen = len;
#ifdef GD32E508
tx_msg.fd_flag = fd ? ENABLE : DISABLE;
tx_msg.fd_brs = (fd && brs) ? ENABLE : DISABLE;
tx_msg.fd_esi = CAN_ESI_DOMINANT;
#endif
for (uint8_t i = 0U; i < len; ++i) {
tx_msg.tx_data[i] = data[i];
}
const uint32_t canPeriph = kChannels[channelIndex].canPeriph;
result.mailbox = can_message_transmit(canPeriph, &tx_msg);
result.txReq = (result.mailbox != CAN_NOMAILBOX);
for (volatile uint32_t dly = 0; dly < 40000U; ++dly) { }
if (result.txReq) {
result.txState = static_cast<uint8_t>(can_transmit_states(canPeriph, result.mailbox));
if (result.txState == CAN_TRANSMIT_PENDING) {
(void)can_transmission_stop(canPeriph, result.mailbox);
}
}
result.stat = CAN_STAT(canPeriph);
result.err = CAN_ERR(canPeriph);
result.tec = (result.err >> 16) & 0xFFU;
result.rec = (result.err >> 24) & 0xFFU;
result.busOff = (result.err & CAN_ERR_BOERR) != 0U;
return result;
}
/**
* @brief 对 CAN 外设执行完整硬件级复位(SWRST + RCU 电源周期)。
* @note 解决 GD32E508 CAN1 反复初始化后卡死在 init mode 的问题。
* 步骤:软件复位 → 长延时 → 关时钟 → 长延时 → 开时钟 → 长延时 → 残留状态清理。
*/
static void hardResetCan(uint32_t canPeriph, rcu_periph_enum canRcu)
{
/* 1. 触发 SWRST 软件复位 */
CAN_CTL(canPeriph) |= CAN_CTL_SWRST;
for (volatile uint32_t d = 0; d < 100000U; ++d) {}
/* 2. 确认 SWRST 已被硬件自动清零;若未清零则强制写 0 并等待 */
if (CAN_CTL(canPeriph) & CAN_CTL_SWRST) {
CAN_CTL(canPeriph) &= ~CAN_CTL_SWRST;
for (volatile uint32_t d = 0; d < 50000U; ++d) {}
}
/* 3. 关 RCU 时钟,等待外设域完全放电 */
rcu_periph_clock_disable(canRcu);
for (volatile uint32_t d = 0; d < 200000U; ++d) {} /* ~1.1ms @180MHz */
/* 4. 重新开时钟,等待外设域上电稳定 */
rcu_periph_clock_enable(canRcu);
for (volatile uint32_t d = 0; d < 200000U; ++d) {} /* ~1.1ms @180MHz */
/* 5. can_deinit() 走标准固件库复位流程 */
can_deinit(canPeriph);
/* 6. 若复位后 IWMOD 仍然置位(外设残留状态),强制清 init mode */
if (CAN_CTL(canPeriph) & CAN_CTL_IWMOD) {
CAN_CTL(canPeriph) &= ~CAN_CTL_IWMOD;
for (volatile uint32_t d = 0; d < 100000U; ++d) {
if ((CAN_STAT(canPeriph) & CAN_STAT_IWS) == 0U) break;
}
}
}
} // 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]{};
/**
* @brief 查询指定通道是否已使能。
*/
bool CanFd::isChannelEnabled(uint8_t channelIndex)
{
return (channelIndex < kChannelCount) && kChannelEnabled[channelIndex];
}
/**
* @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);
#ifdef GPIO_CAN0_PARTIAL_REMAP
/* Bootloader uses CAN0 on PB8/PB9 and jumps to APP without a system reset,
* so AFIO_PCF0 survives can_deinit(). Restore APP mapping PA11/P12 here. */
gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, DISABLE);
#endif
#ifdef GPIO_CAN0_FULL_REMAP
gpio_pin_remap_config(GPIO_CAN0_FULL_REMAP, DISABLE);
#endif
#ifdef GPIO_CAN1_REMAP
/* CAN1_REMAP=0 selects PB12=RX and PB13=TX on GD32E508. */
gpio_pin_remap_config(GPIO_CAN1_REMAP, DISABLE);
#endif
#ifdef AFIO_PE0_CAN2_CFG
gpio_afio_port_config(AFIO_PE0_CAN2_CFG, ENABLE);
#endif
#ifdef AFIO_PE1_CAN2_CFG
gpio_afio_port_config(AFIO_PE1_CAN2_CFG, ENABLE);
#endif
printf("[CAN-GPIO] AFIO_PCF0=0x%08lX CAN0=PA11/PA12 CAN1=PB12/PB13\r\n",
static_cast<unsigned long>(AFIO_PCF0));
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 || mode == Mode::kModeBridge0To12) {
/* 全部接收:列表与掩码都为 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<uint16_t>((CONFIG_CAN_RX_FILTER_ID & 0x7FFU) << 5U);
can_filter.filter_list_low = 0x0000U;
can_filter.filter_mask_high = static_cast<uint16_t>((CONFIG_CAN_RX_FILTER_MASK & 0x7FFU) << 5U);
can_filter.filter_mask_low = static_cast<uint16_t>(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 时钟源为 APB1=90MHz;按 CONFIG_CAN_NOMINAL_* 宏配置,目标 1 Mbps。
* 实际波特率 = 90MHz / (CONFIG_CAN_NOMINAL_PRESCALER × (1+BS1+BS2))。
*/
bool 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 = CONFIG_CAN_NOMINAL_SJW;
can_parameter.time_segment_1 = CONFIG_CAN_NOMINAL_BS1;
can_parameter.time_segment_2 = CONFIG_CAN_NOMINAL_BS2;
can_parameter.prescaler = CONFIG_CAN_NOMINAL_PRESCALER;
return can_init(canPeriph, &can_parameter) == SUCCESS;
}
/**
* @brief 配置指定 CAN 控制器的 CAN FD 数据段参数。
* @param[in] canPeriph 目标 CAN 外设实例。
* @param[out] 无。
* @retval 无返回值。
* @note 时钟源为 APB1=90MHz;按 CONFIG_CAN_DATA_* 宏配置,目标 2.0 Mbps。
* 实际数据段波特率 = 90MHz / (CONFIG_CAN_DATA_PRESCALER × (1+BS1+BS2))。
* BRS 受 CONFIG_CAN_FD_BRS_ENABLE 控制。
*/
bool 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 = CONFIG_CAN_DATA_PRESCALER;
can_fd_parameter.data_resync_jump_width = CONFIG_CAN_DATA_SJW;
can_fd_parameter.data_time_segment_1 = CONFIG_CAN_DATA_BS1;
can_fd_parameter.data_time_segment_2 = CONFIG_CAN_DATA_BS2;
return can_fd_init(canPeriph, &can_fd_parameter) == SUCCESS;
#else
can_fd_function_enable(canPeriph);
return true;
#endif
}
/**
* @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();
printf("[CAN-CLOCK] SYS=%luHz APB1=%luHz nominal=1Mbps data=2Mbps\r\n",
static_cast<unsigned long>(SystemCoreClock),
static_cast<unsigned long>(rcu_clock_freq_get(CK_APB1)));
for(size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
if (!kChannelEnabled[i]) {
printf("[CAN] CH%u DISABLED, skip init\r\n", static_cast<unsigned>(i));
continue;
}
rcu_periph_clock_enable(ch.canRcu);
gpio_bit_reset(ch.slpPort, ch.slpPin); /* STB=0 -> Normal */
const uint8_t rxLevelBefore = (gpio_input_bit_get(ch.rxPort, ch.rxPin) != RESET) ? 1U : 0U;
const uint8_t stbLevel = (gpio_output_bit_get(ch.slpPort, ch.slpPin) != RESET) ? 1U : 0U;
/* 强制复位 + 重试机制:使用 hardResetCan 完整电源周期解决 CAN1 卡死问题 */
bool nominalOk = false;
bool fdOk = false;
for (uint8_t retry = 0U; retry < 5U; ++retry) {
hardResetCan(ch.canPeriph, ch.canRcu);
nominalOk = initNominalTiming(ch.canPeriph);
fdOk = initFdMode(ch.canPeriph);
if (nominalOk && fdOk) {
break; /* 初始化成功,无需重试 */
}
/* can_init 失败后外设可能卡在 init mode,先强制退出让下次重试从干净状态开始 */
CAN_CTL(ch.canPeriph) &= ~CAN_CTL_IWMOD;
for (volatile uint32_t d = 0; d < 50000U; ++d) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) break;
}
printf("[CAN-WARN] CH%u init failed (nom=%u fd=%u STAT=0x%08lX), retry %u/5\r\n",
static_cast<unsigned>(i),
nominalOk ? 1U : 0U, fdOk ? 1U : 0U,
static_cast<unsigned long>(CAN_STAT(ch.canPeriph)),
static_cast<unsigned>(retry + 1U));
}
/* 滤波器配置延迟到循环结束后统一执行:
* CAN1 的 hardResetCan 含 SWRST,会清零 CAN0/CAN1 共享滤波器寄存器,
* 若在循环内配 CAN0 filter 后再做 CAN1 hardReset,CAN0 filter 会被破坏。 */
/* 退出 init mode,确保进入 NORMAL 工作模式 */
CAN_CTL(ch.canPeriph) &= ~CAN_CTL_IWMOD;
uint32_t exitWait = 0U;
for (; exitWait < 200000U; ++exitWait) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) {
break;
}
}
/* 兜底:若仍然卡在 init mode,做一次额外的 SWRST + 强制退出 */
if (CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) {
CAN_CTL(ch.canPeriph) |= CAN_CTL_SWRST;
for (volatile uint32_t d = 0; d < 50000U; ++d) {}
CAN_CTL(ch.canPeriph) &= ~(CAN_CTL_SWRST | CAN_CTL_IWMOD);
for (volatile uint32_t d = 0; d < 200000U; ++d) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) break;
}
printf("[CAN-WARN] CH%u needed extra SWRST to exit init mode (STAT=0x%08lX)\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(CAN_STAT(ch.canPeriph)));
}
const uint32_t statAfterExit = CAN_STAT(ch.canPeriph);
const uint32_t errAfterExit = CAN_ERR(ch.canPeriph);
const uint8_t rxLevelAfter = (gpio_input_bit_get(ch.rxPort, ch.rxPin) != RESET) ? 1U : 0U;
const uint32_t tecAfterExit = (errAfterExit >> 16) & 0xFFU;
const uint32_t recAfterExit = (errAfterExit >> 24) & 0xFFU;
printf("[CAN-DIAG] CH%u init_nom=%u init_fd=%u exit_ok=%u wait=%lu rx_before=%u rx_after=%u stb=%u STAT=0x%08lX ERR=0x%08lX TEC=%lu REC=%lu\r\n",
static_cast<unsigned>(i),
nominalOk ? 1U : 0U,
fdOk ? 1U : 0U,
((statAfterExit & CAN_STAT_IWS) == 0U) ? 1U : 0U,
static_cast<unsigned long>(exitWait),
static_cast<unsigned>(rxLevelBefore),
static_cast<unsigned>(rxLevelAfter),
static_cast<unsigned>(stbLevel),
static_cast<unsigned long>(statAfterExit),
static_cast<unsigned long>(errAfterExit),
static_cast<unsigned long>(tecAfterExit),
static_cast<unsigned long>(recAfterExit));
CAN_TSTAT(ch.canPeriph) = CAN_TSTAT_MTF0 | CAN_TSTAT_MTF1 | CAN_TSTAT_MTF2;
can_interrupt_enable(ch.canPeriph, CAN_INT_TME | CAN_INT_RFNE0 | CAN_INT_RFF0 | CAN_INT_RFO0);
nvic_irq_enable(ch.txIrq, CONFIG_IRQ_PREEMPT_CAN, static_cast<uint8_t>(i));
nvic_irq_enable(ch.rx0Irq, CONFIG_IRQ_PREEMPT_CAN, static_cast<uint8_t>(i));
s_rxQueues[i].head = 0U;
s_rxQueues[i].tail = 0U;
s_rxQueues[i].count = 0U;
}
s_nextConsumeChannel = 0U;
/* ===== 统一配置滤波器(必须在所有 hardResetCan 完成之后) =====
* CAN1 hardResetCan 的 SWRST 会清零 CAN0/CAN1 共享滤波器寄存器域。
* 若在循环内先配 CAN0 filter、后做 CAN1 hardReset,CAN0 的 filter 0 会被
* 硬件清零(CAN_FW bit0 = 0),导致 CAN0 无法接收任何帧。
* 修复方案:所有 hardReset 完成后,再统一配 HBC1F + 三路 filter。 */
can1_filter_start_bank(14U); /* CAN0: bank 0-13, CAN1: bank 14-27 */
for (size_t i = 0; i < kChannelCount; ++i) {
if (!kChannelEnabled[i]) continue;
configureRxFilter(kChannels[i].canPeriph, kChannels[i].filterNumber, s_mode);
}
printf("[CAN] filters configured post-init: HBC1F=%lu FW=0x%08lX\r\n",
static_cast<unsigned long>((CAN_FCTL(CAN0) >> 8) & 0x3FU),
static_cast<unsigned long>(CAN_FW(CAN0)));
/* ===== 初始化结果汇总 ===== */
printf("=== CAN INIT RESULT ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
uint32_t stat = CAN_STAT(ch.canPeriph);
uint32_t bt = CAN_BT(ch.canPeriph);
uint32_t dbt = CAN_DBT(ch.canPeriph);
bool ok = (stat & CAN_STAT_IWS) == 0U;
printf("CH%u: %s BT=0x%08lX DBT=0x%08lX\r\n",
static_cast<unsigned>(i),
ok ? "OK" : "FAIL(init_mode)",
static_cast<unsigned long>(bt),
static_cast<unsigned long>(dbt));
}
/* 滤波器一行 */
{
uint32_t fw = CAN_FW(CAN0);
uint32_t fw2 = CAN_FW(CAN2);
uint32_t hbc = (CAN_FCTL(CAN0) >> 8) & 0x3FU;
printf("FILTER: HBC1F=%lu CAN01_FW=0x%08lX(b0=%lu b2=%lu b14=%lu) CAN2_FW=0x%08lX(b2=%lu)\r\n",
static_cast<unsigned long>(hbc),
static_cast<unsigned long>(fw),
static_cast<unsigned long>((fw >> 0) & 1U),
static_cast<unsigned long>((fw >> 2) & 1U),
static_cast<unsigned long>((fw >> 14) & 1U),
static_cast<unsigned long>(fw2),
static_cast<unsigned long>((fw2 >> 2) & 1U));
}
/* ===== 初始化后错误计数器快照(不发帧,避免启动时累积 TEC)===== */
printf("=== CAN INIT ERR STATE ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
uint32_t err = CAN_ERR(ch.canPeriph);
uint32_t tec = (err >> 16) & 0xFFU;
uint32_t rec = (err >> 24) & 0xFFU;
printf("CH%u: ERR=0x%08lX TEC=%lu REC=%lu boff=%u epv=%u ew=%u\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(err),
static_cast<unsigned long>(tec),
static_cast<unsigned long>(rec),
(err & CAN_ERR_BOERR) ? 1U : 0U, /* BOFF bit2 */
(err & CAN_ERR_PERR) ? 1U : 0U, /* EPV bit1 */
(err & CAN_ERR_WERR) ? 1U : 0U); /* EWARN bit0 */
}
/* ===== Self-test (NORMAL mode, 发到物理总线测 ACK) ===== */
#if (CONFIG_CAN_STARTUP_BUS_TEST_ENABLE != 0)
printf("=== CAN SELF-TEST (normal mode) ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
if (!kChannelEnabled[i]) {
printf("CH%u: SKIP(disabled)\r\n", static_cast<unsigned>(i));
continue;
}
const uint8_t ch = static_cast<uint8_t>(i);
uint8_t data8[8] = {0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57};
uint8_t data64[64];
for (uint8_t j = 0; j < 64U; ++j) data64[j] = 0xA0 + (j & 0x0F);
const CanSelfTestResult c = runSelfTestFrame(ch, 0x1FFFFF00U | i, data8, 8, false, false);
const CanSelfTestResult fd = runSelfTestFrame(ch, 0x1FFFFF10U | i, data64, 64, true, false);
#if (CONFIG_CAN_FD_BRS_ENABLE != 0)
const CanSelfTestResult brs = runSelfTestFrame(ch, 0x1FFFFF20U | i, data64, 64, true, true);
printf("CH%u: classic=%s(fd=0) fd_nobrs=%s(fd=1) fd_brs=%s(brs=1) TEC=%lu\r\n",
static_cast<unsigned>(i),
c.tec == 0 ? "ACK" : "NO_ACK",
fd.tec == 0 ? "ACK" : "NO_ACK",
brs.tec == 0 ? "ACK" : "NO_ACK",
static_cast<unsigned long>(brs.tec > fd.tec ? brs.tec : fd.tec));
#else
printf("CH%u: classic=%s fd_nobrs=%s TEC=%lu\r\n",
static_cast<unsigned>(i),
c.tec == 0 ? "ACK" : "NO_ACK",
fd.tec == 0 ? "ACK" : "NO_ACK",
static_cast<unsigned long>(fd.tec));
#endif
}
#else
printf("=== CAN SELF-TEST SKIPPED (CONFIG_CAN_STARTUP_BUS_TEST_ENABLE=0) ===\r\n");
#endif
/* ===== Loopback 回环测试 (内部 TX→RX,不走物理总线) ===== */
#if (CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE != 0)
printf("=== CAN LOOPBACK (internal) ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
if (!kChannelEnabled[i]) {
printf("CH%u: SKIP(disabled)\r\n", static_cast<unsigned>(i));
continue;
}
if (CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) {
printf("CH%u: SKIP(stuck_init)\r\n", static_cast<unsigned>(i));
continue;
}
/* 进入 loopback 模式:用固件库 API,失败则用寄存器直接操作兜底 */
ErrStatus r1 = can_working_mode_set(ch.canPeriph, CAN_MODE_INITIALIZE);
ErrStatus r2 = can_monitor_mode_set(ch.canPeriph, CAN_LOOPBACK_MODE);
ErrStatus r3 = can_working_mode_set(ch.canPeriph, CAN_MODE_NORMAL);
bool lbkmOk = (CAN_BT(ch.canPeriph) & (1U << 30)) != 0U;
/* 如果 API 失败,尝试直接操作寄存器 */
if (!lbkmOk) {
CAN_CTL(ch.canPeriph) |= CAN_CTL_IWMOD; /* 强制进入 init mode */
for (volatile uint32_t t = 0; t < 100000U; ++t) {
if (CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) break;
}
CAN_BT(ch.canPeriph) |= (1U << 30); /* 直接设 LCMOD=1 */
CAN_CTL(ch.canPeriph) &= ~CAN_CTL_IWMOD; /* 退出 init mode */
for (volatile uint32_t t = 0; t < 100000U; ++t) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) break;
}
lbkmOk = (CAN_BT(ch.canPeriph) & (1U << 30)) != 0U;
}
if (!lbkmOk) {
printf("CH%u: SKIP(lbk_fail API:%d/%d/%d reg:LCMOD=0)\r\n",
static_cast<unsigned>(i), r1, r2, r3);
/* 恢复 normal */
CAN_CTL(ch.canPeriph) |= CAN_CTL_IWMOD;
for (volatile uint32_t t = 0; t < 100000U; ++t) { if (CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) break; }
CAN_BT(ch.canPeriph) &= ~(3U << 30);
CAN_CTL(ch.canPeriph) &= ~CAN_CTL_IWMOD;
for (volatile uint32_t t = 0; t < 100000U; ++t) { if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) break; }
continue;
}
/* --- 经典 CAN --- */
const uint32_t rxB1 = s_channelStats[i].rx_count;
{
can_trasnmit_message_struct tx{};
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &tx);
tx.tx_sfid = 0x100U + i; tx.tx_ff = CAN_FF_STANDARD; tx.tx_ft = CAN_FT_DATA; tx.tx_dlen = 8;
for (uint8_t j = 0; j < 8U; ++j) tx.tx_data[j] = 0x55 + j;
can_message_transmit(ch.canPeriph, &tx);
}
for (volatile uint32_t t = 0; t < 500000U; ++t) {
if (s_channelStats[i].rx_count > rxB1 || can_receive_message_length_get(ch.canPeriph, CAN_FIFO0)) break;
}
/* 消费 FIFO 中可能残留的帧 */
while (can_receive_message_length_get(ch.canPeriph, CAN_FIFO0) > 0U) {
can_receive_message_struct rx{}; can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx);
can_message_receive(ch.canPeriph, CAN_FIFO0, &rx);
}
bool cOk = (s_channelStats[i].rx_count - rxB1) > 0U;
/* --- FD no BRS --- */
const uint32_t rxB2 = s_channelStats[i].rx_count;
{
can_trasnmit_message_struct tx{};
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &tx);
tx.tx_sfid = 0x200U + i; tx.tx_ff = CAN_FF_STANDARD; tx.tx_ft = CAN_FT_DATA; tx.tx_dlen = 64;
tx.fd_flag = ENABLE; tx.fd_brs = DISABLE; tx.fd_esi = CAN_ESI_DOMINANT;
for (uint8_t j = 0; j < 64U; ++j) tx.tx_data[j] = 0xAA + (j & 0x0F);
can_message_transmit(ch.canPeriph, &tx);
}
for (volatile uint32_t t = 0; t < 500000U; ++t) {
if (s_channelStats[i].rx_count > rxB2 || can_receive_message_length_get(ch.canPeriph, CAN_FIFO0)) break;
}
while (can_receive_message_length_get(ch.canPeriph, CAN_FIFO0) > 0U) {
can_receive_message_struct rx{}; can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx);
can_message_receive(ch.canPeriph, CAN_FIFO0, &rx);
}
bool fdOk = (s_channelStats[i].rx_count - rxB2) > 0U;
/* --- FD BRS --- */
const uint32_t rxB3 = s_channelStats[i].rx_count;
{
can_trasnmit_message_struct tx{};
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &tx);
tx.tx_sfid = 0x300U + i; tx.tx_ff = CAN_FF_STANDARD; tx.tx_ft = CAN_FT_DATA; tx.tx_dlen = 64;
tx.fd_flag = ENABLE; tx.fd_brs = ENABLE; tx.fd_esi = CAN_ESI_DOMINANT;
for (uint8_t j = 0; j < 64U; ++j) tx.tx_data[j] = 0xBB + (j & 0x0F);
can_message_transmit(ch.canPeriph, &tx);
}
for (volatile uint32_t t = 0; t < 500000U; ++t) {
if (s_channelStats[i].rx_count > rxB3 || can_receive_message_length_get(ch.canPeriph, CAN_FIFO0)) break;
}
while (can_receive_message_length_get(ch.canPeriph, CAN_FIFO0) > 0U) {
can_receive_message_struct rx{}; can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx);
can_message_receive(ch.canPeriph, CAN_FIFO0, &rx);
}
bool brsOk = (s_channelStats[i].rx_count - rxB3) > 0U;
uint32_t tecLb = (CAN_ERR(ch.canPeriph) >> 16) & 0xFFU;
printf("CH%u: classic=%s fd=%s fd_brs=%s TEC=%lu\r\n",
static_cast<unsigned>(i),
cOk ? "PASS" : "FAIL",
fdOk ? "PASS" : "FAIL",
brsOk ? "PASS" : "FAIL",
static_cast<unsigned long>(tecLb));
/* 回环前后寄存器快照,用于诊断 LCMOD 清除是否干净 */
uint32_t btBeforeExit = CAN_BT(ch.canPeriph);
uint32_t ctlBeforeExit = CAN_CTL(ch.canPeriph);
/* 回到 NORMAL 模式:进 init → 清 LCMOD+SCMOD → 退出 init */
CAN_CTL(ch.canPeriph) |= CAN_CTL_IWMOD;
for (volatile uint32_t t = 0; t < 100000U; ++t) { if (CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) break; }
CAN_BT(ch.canPeriph) &= ~(3U << 30); /* 清 LCMOD+SCMOD */
CAN_CTL(ch.canPeriph) &= ~CAN_CTL_IWMOD;
for (volatile uint32_t t = 0; t < 200000U; ++t) {
if ((CAN_STAT(ch.canPeriph) & CAN_STAT_IWS) == 0U) break;
}
uint32_t errAfterLb = CAN_ERR(ch.canPeriph);
uint32_t tecAfterLb = (errAfterLb >> 16) & 0xFFU;
uint32_t recAfterLb = (errAfterLb >> 24) & 0xFFU;
uint32_t statAfterLb = CAN_STAT(ch.canPeriph);
printf("CH%u: post-lb TEC=%lu REC=%lu STAT=0x%08lX BT=0x%08lX CTL=0x%08lX\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(tecAfterLb),
static_cast<unsigned long>(recAfterLb),
static_cast<unsigned long>(statAfterLb),
static_cast<unsigned long>(CAN_BT(ch.canPeriph)),
static_cast<unsigned long>(CAN_CTL(ch.canPeriph)));
}
printf("========================\r\n");
#else
printf("=== CAN LOOPBACK SKIPPED (CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE=0) ===\r\n");
#endif
/* ===== 初始化后全寄存器快照:诊断 CAN0/CAN1 不收帧 ===== */
printf("=== POST-INIT FULL REG DUMP ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
uint32_t ctl = CAN_CTL(ch.canPeriph);
uint32_t stat = CAN_STAT(ch.canPeriph);
uint32_t bt = CAN_BT(ch.canPeriph);
uint32_t dbt = CAN_DBT(ch.canPeriph);
uint32_t err = CAN_ERR(ch.canPeriph);
uint32_t inten = CAN_INTEN(ch.canPeriph);
uint32_t fw = (ch.canPeriph == CAN2) ? CAN_FW(CAN2) : CAN_FW(CAN0);
uint32_t fctl = CAN_FCTL(ch.canPeriph);
uint32_t rfl0 = CAN_RFIFO0(ch.canPeriph) & 0x3U; /* RFL0 bits 1:0 */
bool normalMode = ((stat & CAN_STAT_IWS) == 0U) && ((bt & (3U << 30)) == 0U);
printf("CH%u: %s CTL=0x%08lX STAT=0x%08lX BT=0x%08lX DBT=0x%08lX\r\n"
" ERR=0x%08lX INTEN=0x%08lX FCTL=0x%08lX FW=0x%08lX RFL0=%lu\r\n",
static_cast<unsigned>(i),
normalMode ? "NORMAL" : "!!!NOT_NORMAL!!!",
static_cast<unsigned long>(ctl),
static_cast<unsigned long>(stat),
static_cast<unsigned long>(bt),
static_cast<unsigned long>(dbt),
static_cast<unsigned long>(err),
static_cast<unsigned long>(inten),
static_cast<unsigned long>(fctl),
static_cast<unsigned long>(fw),
static_cast<unsigned long>(rfl0));
/* 逐位解析关键标志 */
printf(" IWMOD=%u SLPWMOD=%u ARD=%u ABOR=%u | IWS=%u SLPWS=%u | LCMOD=%u SCMOD=%u\r\n",
(ctl & CAN_CTL_IWMOD) ? 1U : 0U,
(ctl & CAN_CTL_SLPWMOD) ? 1U : 0U, /* bit1: sleep mode request */
(ctl & CAN_CTL_ARD) ? 1U : 0U, /* bit4: auto retrans disable */
(ctl & CAN_CTL_ABOR) ? 1U : 0U, /* bit6: auto bus-off recovery */
(stat & CAN_STAT_IWS) ? 1U : 0U,
(stat & CAN_STAT_SLPWS) ? 1U : 0U,
(bt & (1U << 30)) ? 1U : 0U, /* LCMOD */
(bt & (1U << 31)) ? 1U : 0U); /* SCMOD */
}
printf("===============================\r\n");
}
/**
* @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 查询三路 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<uint8_t>((s_nextConsumeChannel + n) % kChannelCount);
auto &queue = s_rxQueues[idx];
if(queue.count == 0U) {
continue;
}
localFrame = queue.frames[queue.head];
queue.head = static_cast<uint8_t>((queue.head + 1U) % kRxQueueDepth);
queue.count--;
s_nextConsumeChannel = static_cast<uint8_t>((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)。
* @param[in] periodic 是否为周期性发送;true 时仅允许一个邮箱保持自动重发,
* 不主动 abort 旧帧,且跳过 TEC 保护以匹配诊断行为。
* @retval CanTxResult kOk 表示发送请求已进入硬件邮箱;其它值表示具体失败原因。
* @note 绕过软件发送队列,直接调用硬件发送;适用于 SPI 桥接等即时发送场景。
* 网页发送数仅在 TX 中断确认 MTFNERR 后增加,不能用本函数返回值判断总线 ACK。
*/
CanFd::CanTxResult CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
const uint8_t *data, uint8_t dlc, bool periodic)
{
if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]
|| data == nullptr || dlc == 0U || dlc > 64U) {
return CanTxResult::kErrInvalid;
}
/* 总线健康检查:BUS_OFF 或 TEC 过高时跳过发送,防止测试帧把通道推向 BUS_OFF */
const uint32_t canPeriph = kChannels[channelIndex].canPeriph;
const uint32_t errReg = CAN_ERR(canPeriph);
if (errReg & CAN_ERR_BOERR) {
static uint32_t s_lastBusOffLogSeq[kChannelCount] = {0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU};
if (s_lastBusOffLogSeq[channelIndex] != s_feedbackSeq) {
printf("[CAN-TX-REJECT] CH%u BUS_OFF ERR=0x%08lX ERRN=%lu(%s) TEC=%lu REC=%lu TSTAT=0x%08lX\r\n",
static_cast<unsigned>(channelIndex),
static_cast<unsigned long>(errReg),
static_cast<unsigned long>((errReg >> 4U) & 0x07U),
canLastErrorName(errReg),
static_cast<unsigned long>((errReg >> 16U) & 0xFFU),
static_cast<unsigned long>((errReg >> 24U) & 0xFFU),
static_cast<unsigned long>(CAN_TSTAT(canPeriph)));
s_lastBusOffLogSeq[channelIndex] = s_feedbackSeq;
}
return CanTxResult::kErrBusOff; /* 通道处于 BUS_OFF,不发送 */
}
const uint32_t tec = (errReg >> 16) & 0xFFU;
if (tec > 96U && !periodic) {
static uint32_t s_lastTecLogSeq[kChannelCount] = {0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU};
if (s_lastTecLogSeq[channelIndex] != s_feedbackSeq) {
printf("[CAN-TX-REJECT] CH%u TEC_HIGH ERR=0x%08lX ERRN=%lu(%s) TEC=%lu REC=%lu TSTAT=0x%08lX\r\n",
static_cast<unsigned>(channelIndex),
static_cast<unsigned long>(errReg),
static_cast<unsigned long>((errReg >> 4U) & 0x07U),
canLastErrorName(errReg),
static_cast<unsigned long>(tec),
static_cast<unsigned long>((errReg >> 24U) & 0xFFU),
static_cast<unsigned long>(CAN_TSTAT(canPeriph)));
s_lastTecLogSeq[channelIndex] = s_feedbackSeq;
}
return CanTxResult::kErrTecHigh; /* TEC 过高(>75% 错误被动阈值),暂停发送保护通道 */
}
/* 周期发送只允许一个邮箱保持自动重发。无 ACK 时同一帧会持续产生
* 总线波形;不要每 1ms 再填满其余邮箱,更不能主动 abort 旧帧,否则
* TX 完成中断会把软件中止累计为发送失败,掩盖真实 TEC/ERRN。 */
if (periodic) {
for (uint8_t mb = CAN_MAILBOX0; mb <= CAN_MAILBOX2; ++mb) {
if (can_transmit_states(canPeriph, mb) == CAN_TRANSMIT_PENDING) {
return CanTxResult::kErrMailbox;
}
}
}
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;
#if (CONFIG_CAN_FD_BRS_ENABLE != 0)
tx_msg.fd_brs = (dlc > 8U) ? ENABLE : DISABLE;
#else
tx_msg.fd_brs = DISABLE;
#endif
tx_msg.fd_esi = CAN_ESI_DOMINANT;
#endif
for (uint8_t i = 0U; i < dlc; ++i) {
tx_msg.tx_data[i] = data[i];
}
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.last_tx_id = ext_id;
s_diag.last_tx_channel = channelIndex;
s_diag.last_payload_length = dlc;
return CanTxResult::kOk;
}
s_diag.error_count++;
s_channelStats[channelIndex].error_count++;
static uint32_t s_lastMailboxLogSeq[kChannelCount] = {0xFFFFFFFFU, 0xFFFFFFFFU, 0xFFFFFFFFU};
if (s_lastMailboxLogSeq[channelIndex] != s_feedbackSeq) {
const uint32_t errAfter = CAN_ERR(canPeriph);
printf("[CAN-TX-REJECT] CH%u MAILBOX ERR=0x%08lX ERRN=%lu(%s) TEC=%lu REC=%lu TSTAT=0x%08lX\r\n",
static_cast<unsigned>(channelIndex),
static_cast<unsigned long>(errAfter),
static_cast<unsigned long>((errAfter >> 4U) & 0x07U),
canLastErrorName(errAfter),
static_cast<unsigned long>((errAfter >> 16U) & 0xFFU),
static_cast<unsigned long>((errAfter >> 24U) & 0xFFU),
static_cast<unsigned long>(CAN_TSTAT(canPeriph)));
s_lastMailboxLogSeq[channelIndex] = s_feedbackSeq;
}
return CanTxResult::kErrMailbox;
}
/**
* @brief 检查指定 CAN 通道当前是否处于健康可发送状态。
* @param[in] channelIndex 通道索引 0/1/2。
* @retval bool true=健康(未 BUS_OFF 且 TEC<=96),false=应避免发送。
* @note 主要用于 taskCanTxSend 等周期性测试帧发送前判断,防止病态通道被
* 持续重发的测试帧反复推回 BUS_OFF,干扰硬件 auto_bus_off_recovery。
*/
bool CanFd::isChannelHealthy(uint8_t channelIndex)
{
if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]) {
return false;
}
const uint32_t canPeriph = kChannels[channelIndex].canPeriph;
const uint32_t errReg = CAN_ERR(canPeriph);
if (errReg & CAN_ERR_BOERR) {
return false;
}
const uint32_t tec = (errReg >> 16) & 0xFFU;
return tec <= 96U;
}
/**
* @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 透传式发送:按接收帧的原格式(标准/扩展、CAN-FD/经典、BRS/ESI、DLC)原样转发到目标 CAN 通道。
* @param[in] canPeriph 目标 CAN 外设实例。
* @param[in] rx 刚从 FIFO0 读出的接收帧。
* @retval bool true 表示成功入队硬件邮箱;false 表示三个邮箱均忙且无法 abort 重投。
* @note 仅供桥接模式在 RX0 中断中调用;非阻塞,与 transmitFrame 共用 abort+重投策略,时间占用控制在寄存器访问级别。
*/
static bool bridgeTransmitRaw(uint32_t canPeriph, const can_receive_message_struct &rx)
{
can_trasnmit_message_struct tx_msg{};
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &tx_msg);
tx_msg.tx_ff = rx.rx_ff;
if (rx.rx_ff == CAN_FF_STANDARD) {
tx_msg.tx_sfid = rx.rx_sfid;
} else {
tx_msg.tx_efid = rx.rx_efid;
}
tx_msg.tx_ft = CAN_FT_DATA;
tx_msg.tx_dlen = rx.rx_dlen;
#ifdef GD32E508
tx_msg.fd_flag = rx.fd_flag;
#if (CONFIG_CAN_FD_BRS_ENABLE != 0)
tx_msg.fd_brs = rx.fd_brs;
#else
tx_msg.fd_brs = DISABLE;
#endif
tx_msg.fd_esi = rx.fd_esi;
#endif
const uint8_t copyLen = (rx.rx_dlen <= 64U) ? rx.rx_dlen : 64U;
for (uint8_t i = 0U; i < copyLen; ++i) {
tx_msg.tx_data[i] = rx.rx_data[i];
}
uint8_t mailbox = can_message_transmit(canPeriph, &tx_msg);
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) {
break;
}
}
mailbox = can_message_transmit(canPeriph, &tx_msg);
}
return mailbox != CAN_NOMAILBOX;
}
/**
* @brief 处理指定通道的 RX0 中断接收。
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
* @param[out] 无。
* @retval 无返回值。
* @note 从对应控制器 FIFO0 循环取空所有待处理报文,更新诊断统计,写入该通道软件接收队列,并置位解析任务。
*/
void CanFd::handleRxInterrupt(uint8_t channelIndex)
{
if(channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]) 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) {
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFF0);
s_diag.rx_fifo_full_count++;
}
if(fifoOverfull) {
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFO0);
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;
/* CAN0 桥接模式:将刚收到的原帧透传到 CAN1 与 CAN2,保留 11/29 位、FD、BRS、ESI。 */
if (channelIndex == 0U && s_mode == Mode::kModeBridge0To12) {
for (uint8_t bridgeCh = 1U; bridgeCh <= 2U; ++bridgeCh) {
const uint32_t bridgePeriph = kChannels[bridgeCh].canPeriph;
const bool ok = bridgeTransmitRaw(bridgePeriph, rx_message);
if (ok) {
s_diag.last_tx_id = s_diag.last_rx_id;
s_diag.last_tx_channel = bridgeCh;
s_diag.last_payload_length = rx_message.rx_dlen;
} else {
s_diag.error_count++;
s_channelStats[bridgeCh].error_count++;
}
}
}
/* 维护每通道独立计数与最近一帧快照,用于 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<uint8_t>(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<uint8_t>(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<uint8_t>((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 处理指定通道的发送完成中断。
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
* @note 只有 MTFNERR 置位才表示完整发送且未出错;邮箱受理、仲裁丢失、
* 主动中止和发送错误均不能计入网页“发送数”。
*/
void CanFd::handleTxInterrupt(uint8_t channelIndex)
{
if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]) {
return;
}
const uint32_t canPeriph = kChannels[channelIndex].canPeriph;
const uint32_t tstat = CAN_TSTAT(canPeriph);
constexpr uint32_t finishedBits[] = {
CAN_TSTAT_MTF0, CAN_TSTAT_MTF1, CAN_TSTAT_MTF2
};
constexpr uint32_t successBits[] = {
CAN_TSTAT_MTFNERR0, CAN_TSTAT_MTFNERR1, CAN_TSTAT_MTFNERR2
};
constexpr uint32_t errorBits[] = {
CAN_TSTAT_MTE0, CAN_TSTAT_MTE1, CAN_TSTAT_MTE2
};
for (size_t mailbox = 0U; mailbox < 3U; ++mailbox) {
if ((tstat & finishedBits[mailbox]) == 0U) {
continue;
}
if ((tstat & successBits[mailbox]) != 0U) {
s_diag.tx_count++;
s_channelStats[channelIndex].tx_count++;
} else if ((tstat & errorBits[mailbox]) != 0U) {
s_diag.error_count++;
s_channelStats[channelIndex].error_count++;
}
}
CAN_TSTAT(canPeriph) = tstat & (CAN_TSTAT_MTF0 | CAN_TSTAT_MTF1 | CAN_TSTAT_MTF2);
}
/**
* @brief 刷新三路 CAN 通道的 1Hz 反馈快照。
* @note 在临界区内拷贝实时计数与最近一帧信息到 s_feedback;保证 SPI 命令读到一致快照。
* 典型调用频率 1Hz,由任务调度器在心跳节拍触发。
*/
void CanFd::refreshFeedbackSnapshot()
{
/* 先在临界区内采集硬件寄存器,确保三通道快照一致 */
uint32_t hw_tec[3] = {};
uint32_t hw_rec[3] = {};
uint32_t hw_stat[3] = {};
uint32_t hw_err[3] = {};
uint32_t hw_rfl0[3] = {};
{
uint32_t save = RT_CRITICAL_SAVE();
for (size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
const uint32_t err_val = CAN_ERR(ch.canPeriph);
const uint32_t stat_val = CAN_STAT(ch.canPeriph);
hw_tec[i] = (err_val >> 16) & 0xFFU;
hw_rec[i] = (err_val >> 24) & 0xFFU;
hw_stat[i] = stat_val;
hw_err[i] = err_val;
hw_rfl0[i] = can_receive_message_length_get(ch.canPeriph, CAN_FIFO0);
}
RT_CRITICAL_RESTORE(save);
}
s_feedbackSeq++;
/* 每 5 秒打印一次 seq 心跳;诊断期间同时输出实际错误通道的最后错误类型。 */
if (s_feedbackSeq % 5U == 0U) {
printf("[CAN-FB] refresh seq=%lu rx0=%lu rx1=%lu rx2=%lu\r\n",
(unsigned long)s_feedbackSeq,
(unsigned long)s_channelStats[0].rx_count,
(unsigned long)s_channelStats[1].rx_count,
(unsigned long)s_channelStats[2].rx_count);
for (size_t i = 0; i < kChannelCount; ++i) {
if (hw_tec[i] == 0U && hw_rec[i] == 0U
&& (hw_err[i] & (CAN_ERR_WERR | CAN_ERR_PERR | CAN_ERR_BOERR)) == 0U) {
continue;
}
printf("[CAN-HW-ERR] CH%u ERR=0x%08lX ERRN=%lu(%s) TEC=%lu REC=%lu TSTAT=0x%08lX\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(hw_err[i]),
static_cast<unsigned long>((hw_err[i] >> 4U) & 0x07U),
canLastErrorName(hw_err[i]),
static_cast<unsigned long>(hw_tec[i]),
static_cast<unsigned long>(hw_rec[i]),
static_cast<unsigned long>(CAN_TSTAT(kChannels[i].canPeriph)));
}
}
for (size_t i = 0; i < kChannelCount; ++i) {
s_feedback[i].snapshot_seq = s_feedbackSeq;
s_feedback[i].stats = s_channelStats[i];
/* 写入硬件层错误状态供 SPI 反馈使用 */
s_feedback[i].stats.tec = static_cast<uint8_t>(hw_tec[i]);
s_feedback[i].stats.rec = static_cast<uint8_t>(hw_rec[i]);
/* 总线状态位图:bit0=init, bit1=sleep, bit2=error_passive, bit3=bus_off, bit4=error_warning
* init/sleep 来自 CAN_STAT,error_passive/bus_off/error_warning 来自 CAN_ERR */
uint8_t busState = 0U;
if (hw_stat[i] & CAN_STAT_IWS) busState |= (1U << 0); /* 初始化模式 */
if (hw_stat[i] & CAN_STAT_SLPWS) busState |= (1U << 1); /* 睡眠模式 */
if (hw_err[i] & CAN_ERR_PERR) busState |= (1U << 2); /* 错误被动 */
if (hw_err[i] & CAN_ERR_BOERR) busState |= (1U << 3); /* 总线关闭 */
if (hw_err[i] & CAN_ERR_WERR) busState |= (1U << 4); /* 错误警告 */
s_feedback[i].stats.bus_state = busState;
s_idSnapshot[i].seq = s_feedbackSeq;
}
/* BUS_OFF 状态变化检测:仅在状态切换时打印,避免刷屏 */
static uint8_t s_prevBusState[3] = {0xFF, 0xFF, 0xFF}; /* 0xFF=首次 */
for (size_t i = 0; i < kChannelCount; ++i) {
uint8_t cur = s_feedback[i].stats.bus_state;
if (cur != s_prevBusState[i]) {
const char *stateStr = "NORMAL";
if (cur & (1U << 0)) stateStr = "INIT_MODE";
else if (cur & (1U << 3)) stateStr = "BUS_OFF";
else if (cur & (1U << 2)) stateStr = "ERR_PASSIVE";
else if (cur & (1U << 4)) stateStr = "ERR_WARN";
printf("[CAN-BUS] CH%u: %s -> %s ERR=0x%08lX ERRN=%lu(%s) TEC=%lu REC=%lu seq=%lu\r\n",
static_cast<unsigned>(i),
(s_prevBusState[i] == 0xFF) ? "?" :
(s_prevBusState[i] & (1U << 3)) ? "BUS_OFF" :
(s_prevBusState[i] & (1U << 2)) ? "ERR_PASSIVE" :
(s_prevBusState[i] & (1U << 4)) ? "ERR_WARN" : "NORMAL",
stateStr,
static_cast<unsigned long>(hw_err[i]),
static_cast<unsigned long>((hw_err[i] >> 4U) & 0x07U),
canLastErrorName(hw_err[i]),
static_cast<unsigned long>(hw_tec[i]),
static_cast<unsigned long>(hw_rec[i]),
static_cast<unsigned long>(s_feedbackSeq));
s_prevBusState[i] = cur;
}
}
}
/**
* @brief 运行时 BUS_OFF / 严重错误被动状态软件恢复。
* @note 由 1Hz 心跳任务调用。若某通道持续 2 秒以上处于 BUS_OFF 或 TEC>96,
* 通过收发器静默-唤醒切换让总线获得 128×11 个连续隐性位,协助硬件
* auto_bus_off_recovery 退出 BUS_OFF。本函数不执行 CAN 控制器 hardReset,
* 避免破坏 CAN0/CAN1 共享的 filter 配置;若切换后仍不健康,说明物理层
* 问题未消除,需要排查终端电阻/ACK/波特率/接线。
*/
void CanFd::recoverBusOffIfNeeded()
{
static uint32_t s_unhealthySinceSeq[kChannelCount] = {0U, 0U, 0U};
static bool s_recoveryInProgress[kChannelCount] = {false, false, false};
for (size_t i = 0; i < kChannelCount; ++i) {
if (!kChannelEnabled[i]) {
continue;
}
const auto &ch = kChannels[i];
const uint32_t errReg = CAN_ERR(ch.canPeriph);
const bool busOff = (errReg & CAN_ERR_BOERR) != 0U;
const uint32_t tec = (errReg >> 16) & 0xFFU;
const bool healthy = !busOff && (tec <= 96U);
if (healthy) {
if (s_unhealthySinceSeq[i] != 0U) {
printf("[CAN-RECOVER] CH%u healthy again (TEC=%lu) seq=%lu\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(tec),
static_cast<unsigned long>(s_feedbackSeq));
s_unhealthySinceSeq[i] = 0U;
}
s_recoveryInProgress[i] = false;
continue;
}
if (s_unhealthySinceSeq[i] == 0U) {
s_unhealthySinceSeq[i] = s_feedbackSeq;
}
/* 仅在连续 2 秒不健康后才干预,避免对短暂波动过度反应 */
const uint32_t elapsed = s_feedbackSeq - s_unhealthySinceSeq[i];
if (elapsed < 2U) {
continue;
}
if (s_recoveryInProgress[i]) {
/* 上一轮恢复后仍在下一周期观察到不健康:物理层问题未解决,不要刷屏 */
static uint32_t s_lastStillBadPrintSeq[kChannelCount] = {0U, 0U, 0U};
if ((s_feedbackSeq - s_lastStillBadPrintSeq[i]) >= 10U) {
printf("[CAN-RECOVER] CH%u still unhealthy after toggle (TEC=%lu bus_off=%u) seq=%lu; check physical layer\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(tec),
busOff ? 1U : 0U,
static_cast<unsigned long>(s_feedbackSeq));
s_lastStillBadPrintSeq[i] = s_feedbackSeq;
}
s_recoveryInProgress[i] = false; /* 允许下一轮再次尝试 */
continue;
}
printf("[CAN-RECOVER] CH%u attempting recovery (TEC=%lu bus_off=%u elapsed=%lus)\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>(tec),
busOff ? 1U : 0U,
static_cast<unsigned long>(elapsed));
/* 步骤:让收发器进入 standby 再切回 normal,强制总线出现连续隐性位,
* 帮助硬件 ABOR 完成 128×11 个连续隐性位的恢复序列。 */
gpio_bit_set(ch.slpPort, ch.slpPin);
SysTickTimer::delayMs(20U);
gpio_bit_reset(ch.slpPort, ch.slpPin);
SysTickTimer::delayMs(10U);
const uint32_t errAfterToggle = CAN_ERR(ch.canPeriph);
const bool stillBad = ((errAfterToggle & CAN_ERR_BOERR) != 0U) ||
(((errAfterToggle >> 16) & 0xFFU) > 96U);
if (stillBad) {
printf("[CAN-RECOVER] CH%u toggle done but still unhealthy (TEC=%lu bus_off=%u); need physical-layer fix\r\n",
static_cast<unsigned>(i),
static_cast<unsigned long>((errAfterToggle >> 16) & 0xFFU),
((errAfterToggle & CAN_ERR_BOERR) != 0U) ? 1U : 0U);
} else {
printf("[CAN-RECOVER] CH%u recovered by transceiver toggle\r\n",
static_cast<unsigned>(i));
}
s_recoveryInProgress[i] = true;
}
}
/**
* @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 CAN0 TX 完成中断向量入口。 */
extern "C" void CAN0_TX_IRQHandler(void) { CanFd::handleTxInterrupt(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 CAN1 TX 完成中断向量入口。 */
extern "C" void CAN1_TX_IRQHandler(void) { CanFd::handleTxInterrupt(1U); }
/**
* @brief CAN2 RX0 中断向量入口。
* @param[in] 无。
* @param[out] 无。
* @retval 无返回值。
* @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(2U)`。
*/
extern "C" void CAN2_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(2U); }
/** @brief CAN2 TX 完成中断向量入口。 */
extern "C" void CAN2_TX_IRQHandler(void) { CanFd::handleTxInterrupt(2U); }