feat(canfd): 添加CANFD自检功能和桥接模式支持

- 添加完整的CANFD初始化流程,包括硬复位机制解决CAN1卡死问题
- 实现CANFD自检功能,包含物理总线测试和内部回环测试
- 支持CAN0到CAN1/CAN2的透传桥接模式
- 添加详细的初始化诊断信息输出
- 修复CAN滤波器配置和GPIO映射问题
- 增强错误处理和状态监控功能

perf(system): 优化系统时钟配置提高性能

- 将AHB时钟从SYSCLK/4调整为SYSCLK/2 (60MHz)
- 优化PLL预分频配置参数
- 更新相关时序注释以反映实际频率变化

docs(esc): 更新EXMC总线时序配置注释

- 添加LAN9252 HBI时序要求详细说明
- 注明当前ESC未启用状态下的占位符说明
- 提供实际计算得出的HCLK延迟要求
This commit is contained in:
cbb
2026-07-02 02:02:41 +08:00
parent b171767023
commit 6aa21a1f9d
11 changed files with 608 additions and 57 deletions
+480 -29
View File
@@ -1,4 +1,4 @@
/**
/**
* @file canfd.cpp
* @brief CANFD 通信模块实现。
*/
@@ -37,14 +37,109 @@ struct CanChannelConfig {
};
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 },
{ 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, 14U },
{ 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);
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 >> 8) & 0xFFU;
result.busOff = (result.stat & (1U << 8)) != 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{};
@@ -70,6 +165,15 @@ void CanFd::initGpio()
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_AF);
#ifdef GPIO_CAN1_REMAP
gpio_pin_remap_config(GPIO_CAN1_REMAP, ENABLE);
#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
for(size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
@@ -95,7 +199,7 @@ void CanFd::configureRxFilter(uint32_t canPeriph, uint8_t filterNumber, Mode mod
can_filter.filter_number = filterNumber;
can_filter.filter_mode = CAN_FILTERMODE_MASK;
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
if (mode == Mode::kModeAllPass) {
if (mode == Mode::kModeAllPass || mode == Mode::kModeBridge0To12) {
/* 全部接收:列表与掩码都为 0,硬件不再对 ID 做比较。 */
can_filter.filter_list_high = 0x0000U;
can_filter.filter_list_low = 0x0000U;
@@ -117,9 +221,10 @@ void CanFd::configureRxFilter(uint32_t canPeriph, uint8_t filterNumber, Mode mod
* @param[in] canPeriph 目标 CAN 外设实例。
* @param[out] 无。
* @retval 无返回值。
* @note 当前按 1 Mbps 仲裁段参数完成普通 CAN 初始化。
* @note 时钟源为 APB1=90MHz;按 CONFIG_CAN_NOMINAL_* 宏配置,目标 1 Mbps。
* 实际波特率 = 90MHz / (CONFIG_CAN_NOMINAL_PRESCALER × (1+BS1+BS2))。
*/
void CanFd::initNominalTiming(uint32_t canPeriph)
bool CanFd::initNominalTiming(uint32_t canPeriph)
{
can_parameter_struct can_parameter{};
@@ -131,11 +236,11 @@ void CanFd::initNominalTiming(uint32_t canPeriph)
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);
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;
}
/**
@@ -143,9 +248,11 @@ void CanFd::initNominalTiming(uint32_t canPeriph)
* @param[in] canPeriph 目标 CAN 外设实例。
* @param[out] 无。
* @retval 无返回值。
* @note 在 GD32E508 平台下启用 FD、BRS 与 TDC;非该平台则仅使能 FD 功能。
* @note 时钟源为 APB1=90MHz;按 CONFIG_CAN_DATA_* 宏配置,目标 2.0 Mbps。
* 实际数据段波特率 = 90MHz / (CONFIG_CAN_DATA_PRESCALER × (1+BS1+BS2))。
* BRS 受 CONFIG_CAN_FD_BRS_ENABLE 控制。
*/
void CanFd::initFdMode(uint32_t canPeriph)
bool CanFd::initFdMode(uint32_t canPeriph)
{
#ifdef GD32E508
can_fdframe_struct can_fd_parameter{};
@@ -161,13 +268,14 @@ void CanFd::initFdMode(uint32_t canPeriph)
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);
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
}
@@ -228,16 +336,13 @@ bool CanFd::enqueueTxFrame(uint8_t channelIndex, const StatusPayload *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_efid = CONFIG_CAN_STATUS_ID;
frame.tx_message.tx_ff = CAN_FF_EXTENDED;
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;
@@ -275,10 +380,82 @@ void CanFd::init()
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);
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));
}
/* GD32E508: CAN0/CAN1 共享 28 个滤波器 bank,必须显式设置 CAN1 起始 bank */
if (ch.canPeriph == CAN1) {
can1_filter_start_bank(14U); /* CAN0: bank 0-13, CAN1: bank 14-27 */
}
configureRxFilter(ch.canPeriph, ch.filterNumber, s_mode);
/* 退出 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 >> 8) & 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_interrupt_enable(ch.canPeriph, CAN_INT_RFNE0 | CAN_INT_RFF0 | CAN_INT_RFO0);
nvic_irq_enable(ch.rx0Irq, CONFIG_IRQ_PREEMPT_CAN, static_cast<uint8_t>(i));
@@ -291,6 +468,178 @@ void CanFd::init()
}
s_nextConsumeChannel = 0U;
/* ===== 初始化结果汇总 ===== */
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));
}
/* ===== Self-test (NORMAL mode, 发到物理总线测 ACK) ===== */
printf("=== CAN SELF-TEST (normal mode) ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
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
}
/* ===== Loopback 回环测试 (内部 TX→RX,不走物理总线) ===== */
printf("=== CAN LOOPBACK (internal) ===\r\n");
for (size_t i = 0; i < kChannelCount; ++i) {
const auto &ch = kChannels[i];
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));
/* 回到 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); /* 清 LCMOD+SCMOD */
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; }
}
printf("========================\r\n");
}
/**
@@ -481,7 +830,11 @@ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
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) {
@@ -559,6 +912,54 @@ void CanFd::setSilentMode()
}
}
/**
* @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)。
@@ -574,9 +975,11 @@ void CanFd::handleRxInterrupt(uint8_t 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++;
}
@@ -595,6 +998,23 @@ void CanFd::handleRxInterrupt(uint8_t channelIndex)
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.tx_count++;
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;
s_channelStats[bridgeCh].tx_count++;
} else {
s_diag.error_count++;
s_channelStats[bridgeCh].error_count++;
}
}
}
/* 维护每通道独立计数与最近一帧快照,用于 1Hz 反馈给 ESP32。 */
auto &stats = s_channelStats[channelIndex];
@@ -706,14 +1126,45 @@ void CanFd::onRx0Interrupt()
*/
void CanFd::refreshFeedbackSnapshot()
{
uint32_t save = RT_CRITICAL_SAVE();
/* 先在临界区内采集硬件寄存器,确保三通道快照一致 */
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++;
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;
}
RT_CRITICAL_RESTORE(save);
}
/**
+7 -3
View File
@@ -1,4 +1,4 @@
#ifndef DEVICE_CANFD_H
#ifndef DEVICE_CANFD_H
#define DEVICE_CANFD_H
#include <stdint.h>
@@ -28,6 +28,7 @@ public:
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。 */
};
/**
@@ -66,6 +67,9 @@ public:
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。 */
};
/**
@@ -182,8 +186,8 @@ private:
* @param[in] mode 目标接收过滤模式。
*/
static void configureRxFilter(uint32_t canPeriph, uint8_t filterNumber, Mode mode);
static void initNominalTiming(uint32_t canPeriph);
static void initFdMode(uint32_t canPeriph);
static bool initNominalTiming(uint32_t canPeriph);
static bool initFdMode(uint32_t canPeriph);
static void buildStatusFrame(const StatusPayload *status);
static bool enqueueTxFrame(uint8_t channelIndex, const StatusPayload *status);
@@ -19,7 +19,10 @@
* [13..16] tx_count (LE) 通道累计发送
* [17..20] drop_count (LE)
* [21..24] error_count (LE)
* [25..] 条目逐条拼接:
* [25] tec 硬件发送错误计数器 (0~255)
* [26] rec 硬件接收错误计数器 (0~255)
* [27] bus_state 总线状态位图:bit0=init,bit1=sleep,bit2=error_passive,bit3=bus_off,bit4=error_warning
* [28..] 条目逐条拼接:
* id(4LE) + dlc(1) + flags(1) + reserved(2) + data(dlc)
* 每条 = 8 + dlc 字节
*
@@ -43,7 +46,7 @@ namespace {
constexpr uint8_t kFeedbackChannelCount = 3U;
constexpr uint16_t kFeedbackPayloadLen = 123U; /* kTxBufSize=128 - overhead=5 */
constexpr uint8_t kPageEntries = 8U;
constexpr uint16_t kFixedHeaderLen = 25U; /* ch(1)+page(1)+total(1)+cnt(1)+mode(1)+seq(4)+rx(4)+tx(4)+drop(4)+err(4) */
constexpr uint16_t kFixedHeaderLen = 28U; /* ch(1)+page(1)+total(1)+cnt(1)+mode(1)+seq(4)+rx(4)+tx(4)+drop(4)+err(4)+tec(1)+rec(1)+bus_state(1) */
void writeU32Le(uint8_t *dst, uint32_t value)
{
@@ -119,6 +122,9 @@ private:
writeU32Le(&payload[13], snap.stats.tx_count);
writeU32Le(&payload[17], snap.stats.rx_drop_count);
writeU32Le(&payload[21], snap.stats.error_count);
payload[25] = snap.stats.tec;
payload[26] = snap.stats.rec;
payload[27] = snap.stats.bus_state;
/* 打包 ID 条目 */
uint16_t offset = kFixedHeaderLen;
@@ -170,6 +176,7 @@ private:
switch (requested) {
case 0U: target = CanFd::Mode::kModeAllPass; break;
case 1U: target = CanFd::Mode::kModeFilterId; break;
case 2U: target = CanFd::Mode::kModeBridge0To12; break;
default: {
uint8_t err = 0xE1U;
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
@@ -313,6 +313,7 @@ void AppSpiCommand::init()
m_txLen = 0U;
m_txPos = 0U;
m_logPending = false;
m_txActiveThisCs = false;
m_commandCount = 0U;
memset(m_txBuf, 0xFF, sizeof(m_txBuf));
memset(m_logBuf, 0, sizeof(m_logBuf));
@@ -495,6 +496,7 @@ void AppSpiCommand::onCsAssert()
m_csActive = true;
m_rxLen = 0U;
m_txPos = 0U;
m_txActiveThisCs = false;
/* 停止 DMA 并清理旧状态 */
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
@@ -516,6 +518,7 @@ void AppSpiCommand::onCsAssert()
/* 启动 DMA 发送响应帧(与 BOOT 完全一致) */
if (m_txLen > 0U) {
m_txActiveThisCs = true;
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
startSpiTxDma(m_txBuf, m_txLen);
} else {
@@ -545,8 +548,19 @@ void AppSpiCommand::onCsRelease()
if (shouldIgnoreTransaction(m_rxBuf, m_rxLen)) {
m_rxLen = 0U;
if (m_txActiveThisCs) {
m_txLen = 0U;
m_txPos = 0U;
memset(m_txBuf, 0xFF, sizeof(m_txBuf));
}
m_txActiveThisCs = false;
return;
}
m_txLen = 0U;
m_txPos = 0U;
m_txActiveThisCs = false;
memset(m_txBuf, 0xFF, sizeof(m_txBuf));
m_cmdPending = true;
TaskManager::set(1UL << TASK_SPI_CMD_PROCESS);
}
@@ -1,4 +1,4 @@
#ifndef DEVICE_APP_SPI_COMMAND_H
#ifndef DEVICE_APP_SPI_COMMAND_H
#define DEVICE_APP_SPI_COMMAND_H
#include <cstdint>
@@ -70,6 +70,7 @@ private:
volatile uint8_t m_txPos = 0U;
volatile bool m_logPending = false;
volatile bool m_cmdPending = false; /**< ISR 置位,标记有待主循环解析的 SPI 命令。 */
bool m_txActiveThisCs = false;
uint8_t m_rxBuf[kRxBufSize] = {};
uint8_t m_txBuf[kTxBufSize] = {};
char m_logBuf[kLogBufSize] = {};