feat(canfd): 添加通道使能控制和SPI握手信号支持
- 添加通道使能开关配置,通过sys/config.h中的CONFIG_CAN_CHx_ENABLE控制各通道 - 实现isChannelEnabled()方法用于查询通道使能状态 - 修正CAN1引脚映射配置,将RX/TX引脚对调并禁用重映射 - 修正错误计数器读取逻辑,REC字段从bit24开始提取而非bit8 - 修正busOff状态判断,使用CAN_ERR_BOERR标志位 - 调整滤波器配置时机,移到所有hardReset完成后防止共享寄存器被清零 - 在发送时增加总线健康检查,避免高错误计数时继续发送导致bus off - 添加SPI Ready握手信号(PB6)控制,改善主从机通信同步 fix(spi): 实现SPI命令处理中的READY信号握手协议 - 添加SPI Ready信号控制,PB6输出指示从机就绪状态 - CS下降沿立即拉低READY信号防主机抢跑 - 命令处理完成且无待处理命令时拉高READY信号 - 忽略无效事务后恢复READY信号避免超时 - 在繁忙期间保持READY信号低电平通知主机等待
This commit is contained in:
@@ -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)。
|
||||
|
||||
Reference in New Issue
Block a user