@@ -1,4 +1,4 @@
/**
/**
* @file canfd.cpp
* @brief CANFD 通信模块实现。
*/
@@ -38,11 +38,19 @@ 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 , 14U } ,
{ CAN1 , RCU_CAN1 , GPIOB , GPIO_PIN_13 , GPIOB , GPIO_PIN_12 , 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 ] ) ;
/* ===== 通道使能开关:在 sys/config.h 中修改 CONFIG_CAN_CHx_ENABLE ===== */
constexpr bool kChannelEnabled [ kChannelCount ] = {
CONFIG_CAN_CH0_ENABLE ,
CONFIG_CAN_CH1_ENABLE ,
CONFIG_CAN_CH2_ENABLE ,
} ;
constexpr uint8_t kTxQueueDepth = 4U ;
constexpr uint32_t kCanRxParseTaskMask = ( 1UL < < TASK_CAN_RX_PARSE ) ;
@@ -98,8 +106,8 @@ static CanSelfTestResult runSelfTestFrame(uint8_t channelIndex, uint32_t extId,
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 ;
result . rec = ( result . err > > 24 ) & 0xFFU ;
result . busOff = ( result . err & CAN_ERR_BOERR ) ! = 0U ;
return result ;
}
@@ -152,6 +160,14 @@ uint32_t CanFd::s_feedbackSeq = 0U;
CanFd : : IdSnapshotTable CanFd : : s_idSnapshot [ 3 ] { } ;
static TxQueue s_txQueues [ 3 ] { } ;
/**
* @brief 查询指定通道是否已使能。
*/
bool CanFd : : isChannelEnabled ( uint8_t channelIndex )
{
return ( channelIndex < kChannelCount ) & & kChannelEnabled [ channelIndex ] ;
}
/**
* @brief 初始化三路 CANFD 相关 GPIO。
* @param[in] 无。
@@ -166,7 +182,9 @@ void CanFd::initGpio()
rcu_periph_clock_enable ( RCU_GPIOE ) ;
rcu_periph_clock_enable ( RCU_AF ) ;
# ifdef GPIO_CAN1_REMAP
gpio_pin_remap_config ( GPIO_CAN1_REMAP , ENABLE ) ;
/* CAN1_REMAP=0: CAN1_RX/PB12, CAN1_TX/PB13
* GD32E50x_用户手册_Rev2.2.pdf AFIO_PCF0 bit22 */
gpio_pin_remap_config ( GPIO_CAN1_REMAP , DISABLE ) ;
# endif
# ifdef AFIO_PE0_CAN2_CFG
gpio_afio_port_config ( AFIO_PE0_CAN2_CFG , ENABLE ) ;
@@ -174,6 +192,11 @@ void CanFd::initGpio()
# ifdef AFIO_PE1_CAN2_CFG
gpio_afio_port_config ( AFIO_PE1_CAN2_CFG , ENABLE ) ;
# endif
/* CAN0/CAN1 引脚选择由 AFIO_PCF0 的 remap 位控制:
* CAN0_REMAP[1:0]=00(复位默认) → CAN0_RX/PA11, CAN0_TX/PA12 (bit14:13)
* CAN1_REMAP=0(复位默认) → CAN1_RX/PB12, CAN1_TX/PB13 (bit22)
* AFIO_PCFA/PCFB 的 AFCFG 位不含 CAN0/CAN1 选项(GD32E50x_用户手册_Rev2.2.pdf
* 第8.5.17/8.5.18节),不可用 AFCFG 配置 CAN0/CAN1。 */
for ( size_t i = 0 ; i < kChannelCount ; + + i ) {
const auto & ch = kChannels [ i ] ;
@@ -378,6 +401,10 @@ void CanFd::init()
for ( size_t i = 0 ; i < kChannelCount ; + + i ) {
const auto & ch = kChannels [ i ] ;
if ( ! kChannelEnabled [ i ] ) {
printf ( " [CAN] CH%u DISABLED, skip init \r \n " , static_cast < unsigned > ( i ) ) ;
continue ;
}
rcu_periph_clock_enable ( ch . canRcu ) ;
gpio_bit_reset ( ch . slpPort , ch . slpPin ) ; /* STB=0 -> Normal */
const uint8_t rxLevelBefore = ( gpio_input_bit_get ( ch . rxPort , ch . rxPin ) ! = RESET ) ? 1U : 0U ;
@@ -408,12 +435,9 @@ void CanFd::init()
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 ) ;
/* 滤波器配置延迟到循环结束后统一执行:
* CAN1 的 hardResetCan 含 SWRST,会清零 CAN0/CAN1 共享滤波器寄存器,
* 若在循环内配 CAN0 filter 后再做 CAN1 hardReset, CAN0 filter 会被破坏。 */
/* 退出 init mode,确保进入 NORMAL 工作模式 */
CAN_CTL ( ch . canPeriph ) & = ~ CAN_CTL_IWMOD ;
@@ -441,7 +465,7 @@ void CanFd::init()
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 ;
const uint32_t recAfterExit = ( errAfterExit > > 24 ) & 0xFFU ;
printf ( " [CAN-DIAG] CH%u init_nom=%u init_fd=%u exit_ok=%u wait=%lu rx_before=%u rx_after=%u stb=%u STAT=0x%08lX ERR=0x%08lX TEC=%lu REC=%lu \r \n " ,
static_cast < unsigned > ( i ) ,
nominalOk ? 1U : 0U ,
@@ -469,6 +493,20 @@ void CanFd::init()
s_nextConsumeChannel = 0U ;
/* ===== 统一配置滤波器(必须在所有 hardResetCan 完成之后) =====
* CAN1 hardResetCan 的 SWRST 会清零 CAN0/CAN1 共享滤波器寄存器域。
* 若在循环内先配 CAN0 filter、后做 CAN1 hardReset, CAN0 的 filter 0 会被
* 硬件清零(CAN_FW bit0 = 0),导致 CAN0 无法接收任何帧。
* 修复方案:所有 hardReset 完成后,再统一配 HBC1F + 三路 filter。 */
can1_filter_start_bank ( 14U ) ; /* CAN0: bank 0-13, CAN1: bank 14-27 */
for ( size_t i = 0 ; i < kChannelCount ; + + i ) {
if ( ! kChannelEnabled [ i ] ) continue ;
configureRxFilter ( kChannels [ i ] . canPeriph , kChannels [ i ] . filterNumber , s_mode ) ;
}
printf ( " [CAN] filters configured post-init: HBC1F=%lu FW=0x%08lX \r \n " ,
static_cast < unsigned long > ( ( CAN_FCTL ( CAN0 ) > > 8 ) & 0x3FU ) ,
static_cast < unsigned long > ( CAN_FW ( CAN0 ) ) ) ;
/* ===== 初始化结果汇总 ===== */
printf ( " === CAN INIT RESULT === \r \n " ) ;
for ( size_t i = 0 ; i < kChannelCount ; + + i ) {
@@ -498,9 +536,30 @@ void CanFd::init()
static_cast < unsigned long > ( ( fw2 > > 2 ) & 1U ) ) ;
}
/* ===== 初始化后错误计数器快照(不发帧,避免启动时累积 TEC)===== */
printf ( " === CAN INIT ERR STATE === \r \n " ) ;
for ( size_t i = 0 ; i < kChannelCount ; + + i ) {
const auto & ch = kChannels [ i ] ;
uint32_t err = CAN_ERR ( ch . canPeriph ) ;
uint32_t tec = ( err > > 16 ) & 0xFFU ;
uint32_t rec = ( err > > 24 ) & 0xFFU ;
printf ( " CH%u: ERR=0x%08lX TEC=%lu REC=%lu boff=%u epv=%u ew=%u \r \n " ,
static_cast < unsigned > ( i ) ,
static_cast < unsigned long > ( err ) ,
static_cast < unsigned long > ( tec ) ,
static_cast < unsigned long > ( rec ) ,
( err & CAN_ERR_BOERR ) ? 1U : 0U , /* BOFF bit2 */
( err & CAN_ERR_PERR ) ? 1U : 0U , /* EPV bit1 */
( err & CAN_ERR_WERR ) ? 1U : 0U ) ; /* EWARN bit0 */
}
/* ===== Self-test (NORMAL mode, 发到物理总线测 ACK) ===== */
printf ( " === CAN SELF-TEST (normal mode) === \r \n " ) ;
for ( size_t i = 0 ; i < kChannelCount ; + + i ) {
if ( ! kChannelEnabled [ i ] ) {
printf ( " CH%u: SKIP(disabled) \r \n " , static_cast < unsigned > ( i ) ) ;
continue ;
}
const uint8_t ch = static_cast < uint8_t > ( i ) ;
uint8_t data8 [ 8 ] = { 0x50 , 0x51 , 0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 } ;
uint8_t data64 [ 64 ] ;
@@ -525,11 +584,17 @@ void CanFd::init()
# endif
}
/* ===== Loopback 回环测试 (内部 TX→RX,不走物理总线) ===== */
printf ( " === CAN LOOPBACK (internal) === \r \n " ) ;
for ( size_t i = 0 ; i < kChannelCount ; + + i ) {
const auto & ch = kChannels [ i ] ;
if ( ! kChannelEnabled [ i ] ) {
printf ( " CH%u: SKIP(disabled) \r \n " , static_cast < unsigned > ( i ) ) ;
continue ;
}
if ( CAN_STAT ( ch . canPeriph ) & CAN_STAT_IWS ) {
printf ( " CH%u: SKIP(stuck_init) \r \n " , static_cast < unsigned > ( i ) ) ;
continue ;
@@ -632,14 +697,74 @@ void CanFd::init()
brsOk ? " PASS " : " FAIL " ,
static_cast < unsigned long > ( tecLb ) ) ;
/* 回到 normal */
/* 回环前后寄存器快照,用于诊断 LCMOD 清除是否干净 */
uint32_t btBeforeExit = CAN_BT ( ch . canPeriph ) ;
uint32_t ctlBeforeExit = CAN_CTL ( ch . canPeriph ) ;
/* 回到 NORMAL 模式:进 init → 清 LCMOD+SCMOD → 退出 init */
CAN_CTL ( ch . canPeriph ) | = CAN_CTL_IWMOD ;
for ( volatile uint32_t t = 0 ; t < 100000U ; + + t ) { if ( CAN_STAT ( ch . canPeriph ) & CAN_STAT_IWS ) break ; }
CAN_BT ( ch . canPeriph ) & = ~ ( 3U < < 30 ) ; /* 清 LCMOD+SCMOD */
CAN_CTL ( ch . canPeriph ) & = ~ CAN_CTL_IWMOD ;
for ( volatile uint32_t t = 0 ; t < 100000U ; + + t ) { if ( ( CAN_STAT ( ch . canPeriph ) & CAN_STAT_IWS ) = = 0U ) break ; }
for ( volatile uint32_t t = 0 ; t < 200000U ; + + t ) {
if ( ( CAN_STAT ( ch . canPeriph ) & CAN_STAT_IWS ) = = 0U ) break ;
}
uint32_t errAfterLb = CAN_ERR ( ch . canPeriph ) ;
uint32_t tecAfterLb = ( errAfterLb > > 16 ) & 0xFFU ;
uint32_t recAfterLb = ( errAfterLb > > 24 ) & 0xFFU ;
uint32_t statAfterLb = CAN_STAT ( ch . canPeriph ) ;
printf ( " CH%u: post-lb TEC=%lu REC=%lu STAT=0x%08lX BT=0x%08lX CTL=0x%08lX \r \n " ,
static_cast < unsigned > ( i ) ,
static_cast < unsigned long > ( tecAfterLb ) ,
static_cast < unsigned long > ( recAfterLb ) ,
static_cast < unsigned long > ( statAfterLb ) ,
static_cast < unsigned long > ( CAN_BT ( ch . canPeriph ) ) ,
static_cast < unsigned long > ( CAN_CTL ( ch . canPeriph ) ) ) ;
}
printf ( " ======================== \r \n " ) ;
/* ===== 初始化后全寄存器快照:诊断 CAN0/CAN1 不收帧 ===== */
printf ( " === POST-INIT FULL REG DUMP === \r \n " ) ;
for ( size_t i = 0 ; i < kChannelCount ; + + i ) {
const auto & ch = kChannels [ i ] ;
uint32_t ctl = CAN_CTL ( ch . canPeriph ) ;
uint32_t stat = CAN_STAT ( ch . canPeriph ) ;
uint32_t bt = CAN_BT ( ch . canPeriph ) ;
uint32_t dbt = CAN_DBT ( ch . canPeriph ) ;
uint32_t err = CAN_ERR ( ch . canPeriph ) ;
uint32_t inten = CAN_INTEN ( ch . canPeriph ) ;
uint32_t fw = ( ch . canPeriph = = CAN2 ) ? CAN_FW ( CAN2 ) : CAN_FW ( CAN0 ) ;
uint32_t fctl = CAN_FCTL ( ch . canPeriph ) ;
uint32_t rfl0 = CAN_RFIFO0 ( ch . canPeriph ) & 0x3U ; /* RFL0 bits 1:0 */
bool normalMode = ( ( stat & CAN_STAT_IWS ) = = 0U ) & & ( ( bt & ( 3U < < 30 ) ) = = 0U ) ;
printf ( " CH%u: %s CTL=0x%08lX STAT=0x%08lX BT=0x%08lX DBT=0x%08lX \r \n "
" ERR=0x%08lX INTEN=0x%08lX FCTL=0x%08lX FW=0x%08lX RFL0=%lu \r \n " ,
static_cast < unsigned > ( i ) ,
normalMode ? " NORMAL " : " !!!NOT_NORMAL!!! " ,
static_cast < unsigned long > ( ctl ) ,
static_cast < unsigned long > ( stat ) ,
static_cast < unsigned long > ( bt ) ,
static_cast < unsigned long > ( dbt ) ,
static_cast < unsigned long > ( err ) ,
static_cast < unsigned long > ( inten ) ,
static_cast < unsigned long > ( fctl ) ,
static_cast < unsigned long > ( fw ) ,
static_cast < unsigned long > ( rfl0 ) ) ;
/* 逐位解析关键标志 */
printf ( " IWMOD=%u SLPWMOD=%u ARD=%u ABOR=%u | IWS=%u SLPWS=%u | LCMOD=%u SCMOD=%u \r \n " ,
( ctl & CAN_CTL_IWMOD ) ? 1U : 0U ,
( ctl & CAN_CTL_SLPWMOD ) ? 1U : 0U , /* bit1: sleep mode request */
( ctl & CAN_CTL_ARD ) ? 1U : 0U , /* bit4: auto retrans disable */
( ctl & CAN_CTL_ABOR ) ? 1U : 0U , /* bit6: auto bus-off recovery */
( stat & CAN_STAT_IWS ) ? 1U : 0U ,
( stat & CAN_STAT_SLPWS ) ? 1U : 0U ,
( bt & ( 1U < < 30 ) ) ? 1U : 0U , /* LCMOD */
( bt & ( 1U < < 31 ) ) ? 1U : 0U ) ; /* SCMOD */
}
printf ( " =============================== \r \n " ) ;
}
/**
@@ -818,10 +943,22 @@ bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t
bool CanFd : : transmitFrame ( uint8_t channelIndex , uint32_t ext_id ,
const uint8_t * data , uint8_t dlc )
{
if ( channelIndex > = kChannelCount | | data = = nullptr | | dlc = = 0U | | dlc > 64U ) {
if ( channelIndex > = kChannelCount | | ! kChannelEnabled [ channelIndex ]
| | data = = nullptr | | dlc = = 0U | | dlc > 64U ) {
return false ;
}
/* 总线健康检查:BUS_OFF 或 TEC 过高时跳过发送,防止测试帧把通道推向 BUS_OFF */
const uint32_t canPeriph = kChannels [ channelIndex ] . canPeriph ;
const uint32_t errReg = CAN_ERR ( canPeriph ) ;
if ( errReg & CAN_ERR_BOERR ) {
return false ; /* 通道处于 BUS_OFF,不发送 */
}
const uint32_t tec = ( errReg > > 16 ) & 0xFFU ;
if ( tec > 96U ) {
return false ; /* TEC 过高(>75% 错误被动阈值),暂停发送保护通道 */
}
can_trasnmit_message_struct tx_msg { } ;
can_struct_para_init ( CAN_TX_MESSAGE_STRUCT , & tx_msg ) ;
tx_msg . tx_efid = ext_id ;
@@ -841,7 +978,6 @@ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
tx_msg . tx_data [ i ] = data [ i ] ;
}
const uint32_t canPeriph = kChannels [ channelIndex ] . canPeriph ;
uint8_t mailbox = can_message_transmit ( canPeriph , & tx_msg ) ;
/* 邮箱全占时保持非阻塞:本函数可能处于 SPI CS 中断链路,不能等待
@@ -969,7 +1105,7 @@ static bool bridgeTransmitRaw(uint32_t canPeriph, const can_receive_message_stru
*/
void CanFd : : handleRxInterrupt ( uint8_t channelIndex )
{
if ( channelIndex > = kChannelCount ) return ;
if ( channelIndex > = kChannelCount | | ! kChannelEnabled [ channelIndex ] ) return ;
const auto & ch = kChannels [ channelIndex ] ;
const bool fifoFull = ( can_interrupt_flag_get ( ch . canPeriph , CAN_INT_FLAG_RFF0 ) ! = RESET ) ;
@@ -1165,8 +1301,33 @@ void CanFd::refreshFeedbackSnapshot()
s_feedback [ i ] . stats . bus_state = busState ;
s_idSnapshot [ i ] . seq = s_feedbackSeq ;
}
/* BUS_OFF 状态变化检测:仅在状态切换时打印,避免刷屏 */
static uint8_t s_prevBusState [ 3 ] = { 0xFF , 0xFF , 0xFF } ; /* 0xFF=首次 */
for ( size_t i = 0 ; i < kChannelCount ; + + i ) {
uint8_t cur = s_feedback [ i ] . stats . bus_state ;
if ( cur ! = s_prevBusState [ i ] ) {
const char * stateStr = " NORMAL " ;
if ( cur & ( 1U < < 0 ) ) stateStr = " INIT_MODE " ;
else if ( cur & ( 1U < < 3 ) ) stateStr = " BUS_OFF " ;
else if ( cur & ( 1U < < 2 ) ) stateStr = " ERR_PASSIVE " ;
else if ( cur & ( 1U < < 4 ) ) stateStr = " ERR_WARN " ;
printf ( " [CAN-BUS] CH%u: %s -> %s TEC=%lu REC=%lu seq=%lu \r \n " ,
static_cast < unsigned > ( i ) ,
( s_prevBusState [ i ] = = 0xFF ) ? " ? " :
( s_prevBusState [ i ] & ( 1U < < 3 ) ) ? " BUS_OFF " :
( s_prevBusState [ i ] & ( 1U < < 2 ) ) ? " ERR_PASSIVE " :
( s_prevBusState [ i ] & ( 1U < < 4 ) ) ? " ERR_WARN " : " NORMAL " ,
stateStr ,
static_cast < unsigned long > ( hw_tec [ i ] ) ,
static_cast < unsigned long > ( hw_rec [ i ] ) ,
static_cast < unsigned long > ( s_feedbackSeq ) ) ;
s_prevBusState [ i ] = cur ;
}
}
}
/**
* @brief 拷贝指定通道的最新反馈快照。
* @param[in] channelIndex 目标 CAN 通道号(0/1/2)。