@@ -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 ) ;
}
/**