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:
@@ -1,4 +1,4 @@
|
||||
/*!
|
||||
/*!
|
||||
\file system_gd32e50x.c
|
||||
\brief CMSIS Cortex-M33 Device Peripheral Access Layer Source File for
|
||||
GD32E50x Device Series
|
||||
@@ -850,11 +850,11 @@ static void system_clock_120m_hxtal(void)
|
||||
RCU_APB1EN |= RCU_APB1EN_PMUEN;
|
||||
|
||||
/* HXTAL is stable */
|
||||
/* AHB = SYSCLK/4 */
|
||||
RCU_CFG0 |= RCU_AHB_CKSYS_DIV4;
|
||||
/* APB2 = AHB/1 */
|
||||
/* AHB = SYSCLK/2 (60 MHz) */
|
||||
RCU_CFG0 |= RCU_AHB_CKSYS_DIV2;
|
||||
/* APB2 = AHB/1 (60 MHz) */
|
||||
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
|
||||
/* APB1 = AHB/2 */
|
||||
/* APB1 = AHB/2 (30 MHz) */
|
||||
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
|
||||
|
||||
#if defined(GD32E50X_HD)
|
||||
@@ -867,13 +867,13 @@ static void system_clock_120m_hxtal(void)
|
||||
RCU_CFG0 |= RCU_PLL_MUL30;
|
||||
|
||||
#elif (defined(GD32E50X_CL) || defined(GD32E508))
|
||||
/* CK_PLL = (CK_PREDIV0) * 30 = 120 MHz */
|
||||
/* CK_PLL = (CK_PREDIV0) * 30 = 120 MHz */
|
||||
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5);
|
||||
RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL30);
|
||||
|
||||
/* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
|
||||
/* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */
|
||||
RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
|
||||
RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);
|
||||
RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8);
|
||||
|
||||
/* enable PLL1 */
|
||||
RCU_CTL |= RCU_CTL_PLL1EN;
|
||||
@@ -882,11 +882,11 @@ static void system_clock_120m_hxtal(void)
|
||||
}
|
||||
|
||||
#elif defined(GD32EPRT)
|
||||
/* CK_PLL = (CK_PREDIV0) * 30 = 120 MHz */
|
||||
/* CK_PLL = (CK_PREDIV0) * 30 = 120 MHz */
|
||||
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5);
|
||||
RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_PLL_MUL30);
|
||||
|
||||
/* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */
|
||||
/* CK_PREDIV0 = (CK_HXTAL)/2 *8 /8 = 4 MHz */
|
||||
RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
|
||||
RCU_CFG1 |= (RCU_PLLPRESRC_HXTAL | RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV8);
|
||||
|
||||
|
||||
@@ -60,9 +60,14 @@ void EscExmcBus::init()
|
||||
norsram_init.asyn_wait = DISABLE;
|
||||
norsram_init.nwait_polarity = EXMC_NWAIT_POLARITY_LOW;
|
||||
norsram_init.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;
|
||||
/* 时序:按 LAN9252 HBI 要求配置 read/write wait state */
|
||||
norsram_init.read_latency = EXMC_DEN_ALE_TR_6_HCLK; // 需按 LAN9252 HBI 时序实测调整
|
||||
norsram_init.write_latency = EXMC_DEN_ALE_TR_6_HCLK; // 需按 LAN9252 HBI 时序实测调整
|
||||
/* 时序:按 LAN9252 HBI 要求配置 read/write wait state
|
||||
* LAN9252 HBI 最小 read/write cycle = 100ns。
|
||||
* AHB=180MHz(RCU_MODIFY_UP 强制 /1),1 HCLK = 5.6ns。
|
||||
* 6 HCLK = 33ns → 不满足,需 ≥ 18 HCLK 才能达到 100ns。
|
||||
* 当前 ESC 未启用(selfTest FAIL),EXMC 库未纳入编译,此参数仅占位。
|
||||
* 启用 ESC 时需安装 EXMC 库并实测调整 HCLK 数。 */
|
||||
norsram_init.read_latency = EXMC_DEN_ALE_TR_6_HCLK; // 占位,启用 ESC 时需调整为 ≥18 HCLK
|
||||
norsram_init.write_latency = EXMC_DEN_ALE_TR_6_HCLK; // 占位,启用 ESC 时需调整为 ≥18 HCLK
|
||||
exmc_norsram_init(&norsram_init);
|
||||
exmc_norsram_enable(CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1);
|
||||
#else
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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] = {};
|
||||
|
||||
@@ -60,6 +60,40 @@
|
||||
#define CONFIG_SPI_APP_OTA_BOOT_ACK_OK 0xB5U
|
||||
/** @brief SPI 进入 Bootloader OTA 命令确认状态:失败。 */
|
||||
#define CONFIG_SPI_APP_OTA_BOOT_ACK_ERROR 0xE2U
|
||||
/* ============================================================
|
||||
* CAN-FD 位时序配置(三路 CAN0/CAN1/CAN2 共用)
|
||||
* 时钟链: SYSCLK=180MHz → AHB=/1=180MHz(RCU_MODIFY_UP 强制)
|
||||
* → APB1=/2=90MHz (CAN_CLK)
|
||||
*
|
||||
* 仲裁段目标: 1.0 Mbps = 90MHz / (9 × 10 TQ)
|
||||
* 数据段目标: 2.0 Mbps = 90MHz / (9 × 5 TQ)
|
||||
*
|
||||
* @note RCU_MODIFY_UP 宏在 system_clock_180m_hxtal() 末尾将 AHB 从 /4
|
||||
* 临时切到 /1,这是 GigaDevice 官方行为,不可删除。
|
||||
* 最终 AHB=180MHz, APB1=90MHz, APB2=180MHz, TIMER2_CLK=180MHz。
|
||||
* ============================================================ */
|
||||
|
||||
/** @brief 仲裁段预分频值。 */
|
||||
#define CONFIG_CAN_NOMINAL_PRESCALER 9U
|
||||
/** @brief 仲裁段再同步跳跃宽度。 */
|
||||
#define CONFIG_CAN_NOMINAL_SJW CAN_BT_SJW_1TQ
|
||||
/** @brief 仲裁段时间段 1(PHSEG1)。 */
|
||||
#define CONFIG_CAN_NOMINAL_BS1 CAN_BT_BS1_7TQ
|
||||
/** @brief 仲裁段时间段 2(PHSEG2)。 */
|
||||
#define CONFIG_CAN_NOMINAL_BS2 CAN_BT_BS2_2TQ
|
||||
|
||||
/** @brief 数据段预分频值。 */
|
||||
#define CONFIG_CAN_DATA_PRESCALER 9U
|
||||
/** @brief 数据段再同步跳跃宽度。 */
|
||||
#define CONFIG_CAN_DATA_SJW CAN_BT_SJW_1TQ
|
||||
/** @brief 数据段时间段 1。 */
|
||||
#define CONFIG_CAN_DATA_BS1 CAN_BT_BS1_3TQ
|
||||
/** @brief 数据段时间段 2。 */
|
||||
#define CONFIG_CAN_DATA_BS2 CAN_BT_BS2_1TQ
|
||||
|
||||
/** @brief BRS(比特率切换)使能开关:1=启用数据段加速,0=关闭仅经典 CAN。 */
|
||||
#define CONFIG_CAN_FD_BRS_ENABLE 1U
|
||||
|
||||
/** @brief CAN 接收过滤器目标标准帧 ID。 */
|
||||
#define CONFIG_CAN_RX_FILTER_ID CONFIG_CAN_STATUS_ID
|
||||
/** @brief CAN 接收过滤器标准帧掩码;`0x7FFU` 表示精确匹配 11-bit ID。 */
|
||||
@@ -81,7 +115,10 @@
|
||||
#define CONFIG_SCHED_TIMER_IRQn TIMER2_IRQn
|
||||
/** @brief 用户调度定时器时钟使能宏参数。 */
|
||||
#define CONFIG_SCHED_TIMER_RCU RCU_TIMER2
|
||||
/** @brief 用户调度定时器预分频值。 */
|
||||
/** @brief 用户调度定时器预分频值。
|
||||
* 时钟链: SYSCLK=180MHz → AHB=/1=180MHz(RCU_MODIFY_UP 强制) → APB1=/2=90MHz → TIMER2_CLK=APB1×2=180MHz。
|
||||
* 保持 100us tick: (PSC+1)×(PER+1)=18000 → 180×100 → PSC=179, PER=99。
|
||||
*/
|
||||
#define CONFIG_SCHED_TIMER_PRESCALER 179U
|
||||
/** @brief 用户调度定时器周期重装值。 */
|
||||
#define CONFIG_SCHED_TIMER_PERIOD 99U
|
||||
@@ -127,7 +164,7 @@
|
||||
/** @brief APP 主版本号。 */
|
||||
#define APP_FW_VERSION_MAJOR 1U
|
||||
/** @brief APP 次版本号。 */
|
||||
#define APP_FW_VERSION_MINOR 1U
|
||||
#define APP_FW_VERSION_MINOR 2U
|
||||
/** @brief APP 补丁版本号。 */
|
||||
#define APP_FW_VERSION_PATCH 2U
|
||||
/** @brief APP 构建编号。 */
|
||||
@@ -179,8 +216,9 @@
|
||||
#define CONFIG_ETHERCAT_SYNC_MODE 0
|
||||
|
||||
/** @brief periodicIsr 最大执行时间告警阈值(µs)。实测超过此值需评估减小 PDO 大小或优化 EXMC 时序。
|
||||
* 当前阈值 5µs:基于 32 字节 PDO + EXMC 6 HCLK 时序的估算上限。
|
||||
* preempt=0 ISR 阻塞窗口 ≤ 此阈值时,CAN/SPI 中断需容忍。 */
|
||||
* 当前阈值 5µs:基于 180MHz + 32 字节 PDO + EXMC 6 HCLK 时序的估算上限。
|
||||
* preempt=0 ISR 阻塞窗口 ≤ 此阈值时,CAN/SPI 中断需容忍。
|
||||
* @note 启用 ESC 后需根据实测 HCLK 数重新评估。 */
|
||||
#define CONFIG_ETHERCAT_ISR_MAX_US_THRESHOLD 5U
|
||||
|
||||
/** @brief 输入 PDO(TxPDO)在 PDI 内存区的起始偏移。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* @file hardfault.cpp
|
||||
* @brief HardFault 诊断处理——通过 USART0 输出故障寄存器信息。
|
||||
*
|
||||
@@ -56,7 +56,7 @@ static uint32_t detectApb2Clock(void) {
|
||||
uint32_t scss = (cfg0 >> 2) & 0x3U;
|
||||
uint32_t sysclk;
|
||||
if (scss == 0x2U) {
|
||||
sysclk = 180000000U; /* PLL = 180MHz(唯一配置的 PLL 目标频率) */
|
||||
sysclk = 180000000U; /* PLL = 180MHz(当前 PLL 目标频率) */
|
||||
} else {
|
||||
sysclk = 8000000U; /* IRC8M = 8MHz 或 HXTAL(未倍频时视为 8MHz) */
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ void TaskManager::tick100us()
|
||||
if(++g_scheduler.div_can_tx >= CONFIG_TASK_PERIOD_CAN_TX_TICKS) {
|
||||
g_scheduler.div_can_tx = 0U;
|
||||
g_scheduler.tick_100ms++;
|
||||
set(1UL << TASK_STATUS_LED);
|
||||
set((1UL << TASK_STATUS_LED) | (1UL << TASK_CAN_TX_SEND));
|
||||
}
|
||||
|
||||
if(++g_scheduler.div_heartbeat >= CONFIG_TASK_PERIOD_HEARTBEAT_TICKS) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* @file task_runner.cpp
|
||||
* @brief 任务执行器实现。
|
||||
*/
|
||||
@@ -152,7 +152,37 @@ void TaskRunner::taskCanRxPoll() { CanFd::poll(); }
|
||||
*/
|
||||
void TaskRunner::taskCanTxSend()
|
||||
{
|
||||
/* 当前阶段不做周期性发送调度。 */
|
||||
/* 启动后每 100ms 向三路 CAN 通道发送 CANFD+BRS 64 字节测试帧。
|
||||
* 帧格式:扩展帧 29-bit ID,CANFD+BRS,DLC=64。
|
||||
* 载荷布局:
|
||||
* [0] 通道号 0/1/2
|
||||
* [1] 保留
|
||||
* [2..5] 发送序号(小端 uint32,每通道独立递增)
|
||||
* [6..9] 系统运行秒数 g_app_uptime_sec(小端 uint32)
|
||||
* [10..63] 递增填充 0x00..0x35(固定模式,便于抓包校验) */
|
||||
static uint32_t seq[3] = {0U, 0U, 0U};
|
||||
|
||||
for (uint8_t ch = 0U; ch < 3U; ++ch) {
|
||||
uint8_t data[64];
|
||||
data[0] = ch;
|
||||
data[1] = 0U;
|
||||
const uint32_t s = seq[ch];
|
||||
data[2] = static_cast<uint8_t>(s & 0xFFU);
|
||||
data[3] = static_cast<uint8_t>((s >> 8) & 0xFFU);
|
||||
data[4] = static_cast<uint8_t>((s >> 16) & 0xFFU);
|
||||
data[5] = static_cast<uint8_t>((s >> 24) & 0xFFU);
|
||||
data[6] = static_cast<uint8_t>(g_app_uptime_sec & 0xFFU);
|
||||
data[7] = static_cast<uint8_t>((g_app_uptime_sec >> 8) & 0xFFU);
|
||||
data[8] = static_cast<uint8_t>((g_app_uptime_sec >> 16) & 0xFFU);
|
||||
data[9] = static_cast<uint8_t>((g_app_uptime_sec >> 24) & 0xFFU);
|
||||
for (uint8_t i = 10U; i < 64U; ++i) {
|
||||
data[i] = i;
|
||||
}
|
||||
|
||||
const uint32_t extId = 0x1FFFFF00U | static_cast<uint32_t>(ch);
|
||||
CanFd::transmitFrame(ch, extId, data, 64U);
|
||||
seq[ch]++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,6 +387,7 @@ void TaskRunner::runPending()
|
||||
if(pending & (1UL << TASK_ADC_PROCESS)) taskAdcProcess();
|
||||
if(pending & (1UL << TASK_CAN_RX_POLL)) taskCanRxPoll();
|
||||
if(pending & (1UL << TASK_CAN_RX_PARSE)) taskCanRxParse();
|
||||
if(pending & (1UL << TASK_CAN_TX_SEND)) taskCanTxSend();
|
||||
if(pending & (1UL << TASK_STATUS_LED)) taskStatusLed();
|
||||
if(pending & (1UL << TASK_HEARTBEAT)) taskHeartbeat();
|
||||
if(pending & (1UL << TASK_DIAGNOSTIC_PRINT)) taskDiagnosticPrint();
|
||||
|
||||
Reference in New Issue
Block a user