2026-07-06 19:13:56 +08:00
|
|
|
|
/**
|
2026-06-26 00:57:59 +08:00
|
|
|
|
* @file canfd.cpp
|
2026-05-10 17:28:14 +08:00
|
|
|
|
* @brief CANFD 通信模块实现。
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "main.h"
|
|
|
|
|
|
#include "device/canfd.h"
|
|
|
|
|
|
#include "sys/config.h"
|
2026-06-26 00:57:59 +08:00
|
|
|
|
#include "sys/critical.h"
|
2026-05-30 00:24:31 +08:00
|
|
|
|
#include "sys/task_manager.h"
|
2026-06-29 01:51:02 +08:00
|
|
|
|
#include <cstring>
|
2026-05-10 17:28:14 +08:00
|
|
|
|
|
|
|
|
|
|
namespace {
|
2026-05-10 21:27:51 +08:00
|
|
|
|
struct TxFrame {
|
2026-05-10 17:28:14 +08:00
|
|
|
|
can_trasnmit_message_struct tx_message;
|
|
|
|
|
|
uint8_t payload[64];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
struct TxQueue {
|
|
|
|
|
|
TxFrame frames[4];
|
|
|
|
|
|
uint8_t head;
|
|
|
|
|
|
uint8_t tail;
|
|
|
|
|
|
uint8_t count;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct CanChannelConfig {
|
|
|
|
|
|
uint32_t canPeriph;
|
|
|
|
|
|
rcu_periph_enum canRcu;
|
|
|
|
|
|
uint32_t txPort;
|
|
|
|
|
|
uint32_t txPin;
|
|
|
|
|
|
uint32_t rxPort;
|
|
|
|
|
|
uint32_t rxPin;
|
|
|
|
|
|
uint32_t slpPort;
|
|
|
|
|
|
uint32_t slpPin;
|
|
|
|
|
|
IRQn_Type rx0Irq;
|
|
|
|
|
|
uint8_t filterNumber;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
constexpr CanChannelConfig kChannels[] = {
|
2026-07-02 02:02:41 +08:00
|
|
|
|
{ CAN0, RCU_CAN0, GPIOA, GPIO_PIN_12, GPIOA, GPIO_PIN_11, GPIOA, GPIO_PIN_8, CAN0_RX0_IRQn, 0U },
|
2026-07-06 19:13:56 +08:00
|
|
|
|
{ CAN1, RCU_CAN1, GPIOB, GPIO_PIN_13, GPIOB, GPIO_PIN_12, GPIOB, GPIO_PIN_14, CAN1_RX0_IRQn, 14U },
|
2026-07-02 02:02:41 +08:00
|
|
|
|
{ CAN2, RCU_CAN2, GPIOE, GPIO_PIN_1, GPIOE, GPIO_PIN_0, GPIOB, GPIO_PIN_9, CAN2_RX0_IRQn, 2U },
|
2026-05-10 21:27:51 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
constexpr size_t kChannelCount = sizeof(kChannels) / sizeof(kChannels[0]);
|
2026-07-06 19:13:56 +08:00
|
|
|
|
|
|
|
|
|
|
/* ===== 通道使能开关:在 sys/config.h 中修改 CONFIG_CAN_CHx_ENABLE ===== */
|
|
|
|
|
|
constexpr bool kChannelEnabled[kChannelCount] = {
|
|
|
|
|
|
CONFIG_CAN_CH0_ENABLE,
|
|
|
|
|
|
CONFIG_CAN_CH1_ENABLE,
|
|
|
|
|
|
CONFIG_CAN_CH2_ENABLE,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
constexpr uint8_t kTxQueueDepth = 4U;
|
2026-05-30 00:24:31 +08:00
|
|
|
|
constexpr uint32_t kCanRxParseTaskMask = (1UL << TASK_CAN_RX_PARSE);
|
2026-07-02 02:02:41 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
2026-07-06 19:13:56 +08:00
|
|
|
|
result.rec = (result.err >> 24) & 0xFFU;
|
|
|
|
|
|
result.busOff = (result.err & CAN_ERR_BOERR) != 0U;
|
2026-07-02 02:02:41 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 17:28:14 +08:00
|
|
|
|
} // namespace
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
CanFd::Diag CanFd::s_diag{};
|
|
|
|
|
|
CanFd::RxQueue CanFd::s_rxQueues[3]{};
|
|
|
|
|
|
uint8_t CanFd::s_nextConsumeChannel = 0U;
|
2026-06-29 01:51:02 +08:00
|
|
|
|
volatile CanFd::Mode CanFd::s_mode = CanFd::Mode::kModeAllPass;
|
|
|
|
|
|
CanFd::ChannelStats CanFd::s_channelStats[3]{};
|
|
|
|
|
|
CanFd::ChannelFeedback CanFd::s_feedback[3]{};
|
|
|
|
|
|
uint32_t CanFd::s_feedbackSeq = 0U;
|
|
|
|
|
|
CanFd::IdSnapshotTable CanFd::s_idSnapshot[3]{};
|
2026-05-10 21:27:51 +08:00
|
|
|
|
static TxQueue s_txQueues[3]{};
|
2026-05-10 17:28:14 +08:00
|
|
|
|
|
2026-07-06 19:13:56 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 查询指定通道是否已使能。
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool CanFd::isChannelEnabled(uint8_t channelIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (channelIndex < kChannelCount) && kChannelEnabled[channelIndex];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 初始化三路 CANFD 相关 GPIO。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note 为 CAN0/CAN1/CAN2 的 TX、RX 及收发器 SLP 控制脚统一完成 GPIO 时钟与模式配置。
|
|
|
|
|
|
*/
|
2026-05-10 17:28:14 +08:00
|
|
|
|
void CanFd::initGpio()
|
|
|
|
|
|
{
|
|
|
|
|
|
rcu_periph_clock_enable(RCU_GPIOA);
|
2026-05-10 21:27:51 +08:00
|
|
|
|
rcu_periph_clock_enable(RCU_GPIOB);
|
|
|
|
|
|
rcu_periph_clock_enable(RCU_GPIOE);
|
2026-05-10 17:28:14 +08:00
|
|
|
|
rcu_periph_clock_enable(RCU_AF);
|
2026-07-02 02:02:41 +08:00
|
|
|
|
#ifdef GPIO_CAN1_REMAP
|
2026-07-06 19:13:56 +08:00
|
|
|
|
/* CAN1_REMAP=0: CAN1_RX/PB12, CAN1_TX/PB13
|
|
|
|
|
|
* GD32E50x_用户手册_Rev2.2.pdf AFIO_PCF0 bit22 */
|
|
|
|
|
|
gpio_pin_remap_config(GPIO_CAN1_REMAP, DISABLE);
|
2026-07-02 02:02:41 +08:00
|
|
|
|
#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
|
2026-07-06 19:13:56 +08:00
|
|
|
|
/* 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。 */
|
2026-05-10 17:28:14 +08:00
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
for(size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
const auto &ch = kChannels[i];
|
|
|
|
|
|
gpio_init(ch.rxPort, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, ch.rxPin);
|
|
|
|
|
|
gpio_init(ch.txPort, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, ch.txPin);
|
|
|
|
|
|
gpio_init(ch.slpPort, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, ch.slpPin);
|
|
|
|
|
|
}
|
2026-05-10 17:28:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
2026-05-30 00:24:31 +08:00
|
|
|
|
* @brief 为指定 CAN 控制器配置接收过滤器。
|
2026-05-10 21:27:51 +08:00
|
|
|
|
* @param[in] canPeriph 目标 CAN 外设实例。
|
|
|
|
|
|
* @param[in] filterNumber 过滤器编号。
|
2026-06-29 01:51:02 +08:00
|
|
|
|
* @param[in] mode 目标接收过滤模式。
|
2026-05-10 21:27:51 +08:00
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
2026-06-29 01:51:02 +08:00
|
|
|
|
* @note kModeAllPass 模式下使用 32 位掩码模式且 mask=0/list=0,硬件直接放行所有标准/扩展帧到 FIFO0;
|
|
|
|
|
|
* kModeFilterId 模式下按 CONFIG_CAN_RX_FILTER_ID/MASK 做标准帧掩码匹配。
|
2026-05-10 21:27:51 +08:00
|
|
|
|
*/
|
2026-06-29 01:51:02 +08:00
|
|
|
|
void CanFd::configureRxFilter(uint32_t canPeriph, uint8_t filterNumber, Mode mode)
|
2026-05-10 17:28:14 +08:00
|
|
|
|
{
|
|
|
|
|
|
can_filter_parameter_struct can_filter{};
|
2026-05-10 21:27:51 +08:00
|
|
|
|
can_filter.filter_number = filterNumber;
|
2026-05-10 17:28:14 +08:00
|
|
|
|
can_filter.filter_mode = CAN_FILTERMODE_MASK;
|
|
|
|
|
|
can_filter.filter_bits = CAN_FILTERBITS_32BIT;
|
2026-07-02 02:02:41 +08:00
|
|
|
|
if (mode == Mode::kModeAllPass || mode == Mode::kModeBridge0To12) {
|
2026-06-29 01:51:02 +08:00
|
|
|
|
/* 全部接收:列表与掩码都为 0,硬件不再对 ID 做比较。 */
|
|
|
|
|
|
can_filter.filter_list_high = 0x0000U;
|
|
|
|
|
|
can_filter.filter_list_low = 0x0000U;
|
|
|
|
|
|
can_filter.filter_mask_high = 0x0000U;
|
|
|
|
|
|
can_filter.filter_mask_low = 0x0000U;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
can_filter.filter_list_high = static_cast<uint16_t>((CONFIG_CAN_RX_FILTER_ID & 0x7FFU) << 5U);
|
|
|
|
|
|
can_filter.filter_list_low = 0x0000U;
|
|
|
|
|
|
can_filter.filter_mask_high = static_cast<uint16_t>((CONFIG_CAN_RX_FILTER_MASK & 0x7FFU) << 5U);
|
|
|
|
|
|
can_filter.filter_mask_low = static_cast<uint16_t>(1U << 2U);
|
|
|
|
|
|
}
|
2026-05-10 17:28:14 +08:00
|
|
|
|
can_filter.filter_fifo_number = CAN_FIFO0;
|
|
|
|
|
|
can_filter.filter_enable = ENABLE;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
can_filter_init(canPeriph, &can_filter);
|
2026-05-10 17:28:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 配置指定 CAN 控制器的标称仲裁段位时序。
|
|
|
|
|
|
* @param[in] canPeriph 目标 CAN 外设实例。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
2026-07-02 02:02:41 +08:00
|
|
|
|
* @note 时钟源为 APB1=90MHz;按 CONFIG_CAN_NOMINAL_* 宏配置,目标 1 Mbps。
|
|
|
|
|
|
* 实际波特率 = 90MHz / (CONFIG_CAN_NOMINAL_PRESCALER × (1+BS1+BS2))。
|
2026-05-10 21:27:51 +08:00
|
|
|
|
*/
|
2026-07-02 02:02:41 +08:00
|
|
|
|
bool CanFd::initNominalTiming(uint32_t canPeriph)
|
2026-05-10 17:28:14 +08:00
|
|
|
|
{
|
|
|
|
|
|
can_parameter_struct can_parameter{};
|
|
|
|
|
|
|
|
|
|
|
|
can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
|
|
|
|
|
|
can_parameter.time_triggered = DISABLE;
|
|
|
|
|
|
can_parameter.auto_bus_off_recovery = ENABLE;
|
|
|
|
|
|
can_parameter.auto_wake_up = DISABLE;
|
|
|
|
|
|
can_parameter.auto_retrans = ENABLE;
|
|
|
|
|
|
can_parameter.rec_fifo_overwrite = ENABLE;
|
|
|
|
|
|
can_parameter.trans_fifo_order = ENABLE;
|
|
|
|
|
|
can_parameter.working_mode = CAN_NORMAL_MODE;
|
2026-07-02 02:02:41 +08:00
|
|
|
|
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;
|
2026-05-10 17:28:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 配置指定 CAN 控制器的 CAN FD 数据段参数。
|
|
|
|
|
|
* @param[in] canPeriph 目标 CAN 外设实例。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
2026-07-02 02:02:41 +08:00
|
|
|
|
* @note 时钟源为 APB1=90MHz;按 CONFIG_CAN_DATA_* 宏配置,目标 2.0 Mbps。
|
|
|
|
|
|
* 实际数据段波特率 = 90MHz / (CONFIG_CAN_DATA_PRESCALER × (1+BS1+BS2))。
|
|
|
|
|
|
* BRS 受 CONFIG_CAN_FD_BRS_ENABLE 控制。
|
2026-05-10 21:27:51 +08:00
|
|
|
|
*/
|
2026-07-02 02:02:41 +08:00
|
|
|
|
bool CanFd::initFdMode(uint32_t canPeriph)
|
2026-05-10 17:28:14 +08:00
|
|
|
|
{
|
|
|
|
|
|
#ifdef GD32E508
|
|
|
|
|
|
can_fdframe_struct can_fd_parameter{};
|
|
|
|
|
|
can_fd_tdc_struct can_fd_tdc_parameter{};
|
|
|
|
|
|
|
|
|
|
|
|
can_struct_para_init(CAN_FD_FRAME_STRUCT, &can_fd_parameter);
|
|
|
|
|
|
can_fd_parameter.fd_frame = ENABLE;
|
|
|
|
|
|
can_fd_parameter.excp_event_detect = ENABLE;
|
|
|
|
|
|
can_fd_parameter.delay_compensation = ENABLE;
|
|
|
|
|
|
can_fd_tdc_parameter.tdc_filter = 0x04U;
|
|
|
|
|
|
can_fd_tdc_parameter.tdc_mode = CAN_TDCMOD_CALC_AND_OFFSET;
|
|
|
|
|
|
can_fd_tdc_parameter.tdc_offset = 0x04U;
|
|
|
|
|
|
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;
|
2026-07-02 02:02:41 +08:00
|
|
|
|
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;
|
2026-05-10 17:28:14 +08:00
|
|
|
|
#else
|
2026-05-10 21:27:51 +08:00
|
|
|
|
can_fd_function_enable(canPeriph);
|
2026-07-02 02:02:41 +08:00
|
|
|
|
return true;
|
2026-05-10 17:28:14 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 构造内部发送缓冲中的状态帧内容。
|
|
|
|
|
|
* @param[in] status 指向状态载荷的指针。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note 当前实现中状态帧构造逻辑已内联至 `enqueueTxFrame()`,此函数仅保留接口兼容性。
|
|
|
|
|
|
*/
|
2026-05-10 17:28:14 +08:00
|
|
|
|
void CanFd::buildStatusFrame(const StatusPayload *status)
|
|
|
|
|
|
{
|
2026-05-10 21:27:51 +08:00
|
|
|
|
if(status == nullptr) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 17:28:14 +08:00
|
|
|
|
const int16_t temp_x100 = static_cast<int16_t>(status->temperature * 100.0f);
|
|
|
|
|
|
const uint16_t vdda_x1000 = static_cast<uint16_t>(status->vdda * 1000.0f);
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
auto &frame = s_txQueues[0].frames[0];
|
|
|
|
|
|
auto &p = frame.payload;
|
2026-05-10 17:28:14 +08:00
|
|
|
|
p[0] = static_cast<uint8_t>(status->temp_raw & 0xFFU);
|
|
|
|
|
|
p[1] = static_cast<uint8_t>((status->temp_raw >> 8) & 0xFFU);
|
|
|
|
|
|
p[2] = static_cast<uint8_t>(status->vref_raw & 0xFFU);
|
|
|
|
|
|
p[3] = static_cast<uint8_t>((status->vref_raw >> 8) & 0xFFU);
|
|
|
|
|
|
p[4] = static_cast<uint8_t>(temp_x100 & 0xFF);
|
|
|
|
|
|
p[5] = static_cast<uint8_t>((temp_x100 >> 8) & 0xFF);
|
|
|
|
|
|
p[6] = static_cast<uint8_t>(vdda_x1000 & 0xFFU);
|
|
|
|
|
|
p[7] = static_cast<uint8_t>((vdda_x1000 >> 8) & 0xFFU);
|
|
|
|
|
|
p[8] = CONFIG_CAN_STATUS_TAG0;
|
|
|
|
|
|
p[9] = CONFIG_CAN_STATUS_TAG1;
|
|
|
|
|
|
p[10] = CONFIG_CAN_STATUS_TAG2;
|
|
|
|
|
|
p[11] = CONFIG_CAN_STATUS_TAG3;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
}
|
2026-05-10 17:28:14 +08:00
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 向指定 CAN 通道的发送 FIFO 入队一帧状态报文。
|
|
|
|
|
|
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
|
|
|
|
|
|
* @param[in] status 指向状态载荷的指针。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval bool true 表示成功入队,false 表示参数无效或该通道发送队列已满。
|
|
|
|
|
|
* @note 仅完成软件发送队列入队,不会立即触发硬件发送。
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool CanFd::enqueueTxFrame(uint8_t channelIndex, const StatusPayload *status)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(status == nullptr || channelIndex >= kChannelCount) {
|
|
|
|
|
|
return false;
|
2026-05-10 17:28:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
auto &queue = s_txQueues[channelIndex];
|
|
|
|
|
|
if(queue.count >= kTxQueueDepth) {
|
|
|
|
|
|
s_diag.tx_drop_count++;
|
2026-06-29 01:51:02 +08:00
|
|
|
|
s_channelStats[channelIndex].error_count++;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
return false;
|
2026-05-10 17:28:14 +08:00
|
|
|
|
}
|
2026-05-10 21:27:51 +08:00
|
|
|
|
|
2026-05-10 17:28:14 +08:00
|
|
|
|
buildStatusFrame(status);
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
auto &frame = queue.frames[queue.tail];
|
|
|
|
|
|
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &frame.tx_message);
|
2026-07-02 02:02:41 +08:00
|
|
|
|
frame.tx_message.tx_efid = CONFIG_CAN_STATUS_ID;
|
|
|
|
|
|
frame.tx_message.tx_ff = CAN_FF_EXTENDED;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
frame.tx_message.tx_ft = CAN_FT_DATA;
|
|
|
|
|
|
frame.tx_message.tx_dlen = CANFD_STATUS_DLEN;
|
|
|
|
|
|
frame.tx_message.fd_flag = ENABLE;
|
|
|
|
|
|
frame.tx_message.fd_brs = ENABLE;
|
|
|
|
|
|
frame.tx_message.fd_esi = CAN_ESI_DOMINANT;
|
|
|
|
|
|
|
|
|
|
|
|
const auto &src = s_txQueues[0].frames[0].payload;
|
|
|
|
|
|
auto &p = frame.payload;
|
|
|
|
|
|
for(uint8_t j = 0; j < CANFD_STATUS_DLEN; ++j) {
|
|
|
|
|
|
p[j] = src[j];
|
|
|
|
|
|
frame.tx_message.tx_data[j] = p[j];
|
2026-05-10 17:28:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
queue.tail = static_cast<uint8_t>((queue.tail + 1U) % kTxQueueDepth);
|
|
|
|
|
|
queue.count++;
|
2026-05-10 17:28:14 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 初始化三路 CANFD 控制器、过滤器、中断与软件队列。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
2026-05-30 00:24:31 +08:00
|
|
|
|
* @note 初始化后默认将三路 TCAN1057A 收发器置为 Normal,并使能 FIFO0 的非空/满/overfull 中断统计。
|
2026-05-10 21:27:51 +08:00
|
|
|
|
*/
|
|
|
|
|
|
void CanFd::init()
|
|
|
|
|
|
{
|
|
|
|
|
|
s_diag = {};
|
|
|
|
|
|
s_nextConsumeChannel = 0U;
|
2026-06-29 01:51:02 +08:00
|
|
|
|
s_mode = Mode::kModeAllPass;
|
|
|
|
|
|
for (size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
s_channelStats[i] = {};
|
|
|
|
|
|
s_feedback[i] = {};
|
|
|
|
|
|
}
|
|
|
|
|
|
s_feedbackSeq = 0U;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
|
|
|
|
|
|
initGpio();
|
|
|
|
|
|
|
|
|
|
|
|
for(size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
const auto &ch = kChannels[i];
|
2026-07-06 19:13:56 +08:00
|
|
|
|
if (!kChannelEnabled[i]) {
|
|
|
|
|
|
printf("[CAN] CH%u DISABLED, skip init\r\n", static_cast<unsigned>(i));
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2026-05-10 21:27:51 +08:00
|
|
|
|
rcu_periph_clock_enable(ch.canRcu);
|
|
|
|
|
|
gpio_bit_reset(ch.slpPort, ch.slpPin); /* STB=0 -> Normal */
|
2026-07-02 02:02:41 +08:00
|
|
|
|
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));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-06 19:13:56 +08:00
|
|
|
|
/* 滤波器配置延迟到循环结束后统一执行:
|
|
|
|
|
|
* CAN1 的 hardResetCan 含 SWRST,会清零 CAN0/CAN1 共享滤波器寄存器,
|
|
|
|
|
|
* 若在循环内配 CAN0 filter 后再做 CAN1 hardReset,CAN0 filter 会被破坏。 */
|
2026-07-02 02:02:41 +08:00
|
|
|
|
|
|
|
|
|
|
/* 退出 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;
|
2026-07-06 19:13:56 +08:00
|
|
|
|
const uint32_t recAfterExit = (errAfterExit >> 24) & 0xFFU;
|
2026-07-02 02:02:41 +08:00
|
|
|
|
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));
|
|
|
|
|
|
|
2026-05-30 00:24:31 +08:00
|
|
|
|
can_interrupt_enable(ch.canPeriph, CAN_INT_RFNE0 | CAN_INT_RFF0 | CAN_INT_RFO0);
|
2026-06-26 00:57:59 +08:00
|
|
|
|
nvic_irq_enable(ch.rx0Irq, CONFIG_IRQ_PREEMPT_CAN, static_cast<uint8_t>(i));
|
2026-05-10 21:27:51 +08:00
|
|
|
|
|
|
|
|
|
|
s_rxQueues[i].head = 0U;
|
|
|
|
|
|
s_rxQueues[i].tail = 0U;
|
|
|
|
|
|
s_rxQueues[i].count = 0U;
|
|
|
|
|
|
s_txQueues[i].head = 0U;
|
|
|
|
|
|
s_txQueues[i].tail = 0U;
|
|
|
|
|
|
s_txQueues[i].count = 0U;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
s_nextConsumeChannel = 0U;
|
2026-07-02 02:02:41 +08:00
|
|
|
|
|
2026-07-06 19:13:56 +08:00
|
|
|
|
/* ===== 统一配置滤波器(必须在所有 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)));
|
|
|
|
|
|
|
2026-07-02 02:02:41 +08:00
|
|
|
|
/* ===== 初始化结果汇总 ===== */
|
|
|
|
|
|
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));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-06 19:13:56 +08:00
|
|
|
|
/* ===== 初始化后错误计数器快照(不发帧,避免启动时累积 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 */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-02 02:02:41 +08:00
|
|
|
|
/* ===== Self-test (NORMAL mode, 发到物理总线测 ACK) ===== */
|
|
|
|
|
|
printf("=== CAN SELF-TEST (normal mode) ===\r\n");
|
|
|
|
|
|
for (size_t i = 0; i < kChannelCount; ++i) {
|
2026-07-06 19:13:56 +08:00
|
|
|
|
if (!kChannelEnabled[i]) {
|
|
|
|
|
|
printf("CH%u: SKIP(disabled)\r\n", static_cast<unsigned>(i));
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2026-07-02 02:02:41 +08:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-06 19:13:56 +08:00
|
|
|
|
|
2026-07-02 02:02:41 +08:00
|
|
|
|
/* ===== Loopback 回环测试 (内部 TX→RX,不走物理总线) ===== */
|
|
|
|
|
|
printf("=== CAN LOOPBACK (internal) ===\r\n");
|
|
|
|
|
|
for (size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
const auto &ch = kChannels[i];
|
|
|
|
|
|
|
2026-07-06 19:13:56 +08:00
|
|
|
|
if (!kChannelEnabled[i]) {
|
|
|
|
|
|
printf("CH%u: SKIP(disabled)\r\n", static_cast<unsigned>(i));
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-02 02:02:41 +08:00
|
|
|
|
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));
|
|
|
|
|
|
|
2026-07-06 19:13:56 +08:00
|
|
|
|
/* 回环前后寄存器快照,用于诊断 LCMOD 清除是否干净 */
|
|
|
|
|
|
uint32_t btBeforeExit = CAN_BT(ch.canPeriph);
|
|
|
|
|
|
uint32_t ctlBeforeExit = CAN_CTL(ch.canPeriph);
|
|
|
|
|
|
|
|
|
|
|
|
/* 回到 NORMAL 模式:进 init → 清 LCMOD+SCMOD → 退出 init */
|
2026-07-02 02:02:41 +08:00
|
|
|
|
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;
|
2026-07-06 19:13:56 +08:00
|
|
|
|
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)));
|
2026-07-02 02:02:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
printf("========================\r\n");
|
2026-07-06 19:13:56 +08:00
|
|
|
|
|
|
|
|
|
|
/* ===== 初始化后全寄存器快照:诊断 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");
|
2026-05-10 21:27:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-29 01:51:02 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 切换 CANFD 接收过滤模式。
|
|
|
|
|
|
* @param[in] mode 目标过滤模式。
|
|
|
|
|
|
* @note 切换时逐路重新配置过滤器;can_filter_init 内部会清 FCTL_FLD,无需手动进入 INITIAL 模式。
|
|
|
|
|
|
*/
|
|
|
|
|
|
void CanFd::setMode(Mode mode)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint32_t save = RT_CRITICAL_SAVE();
|
|
|
|
|
|
s_mode = mode;
|
|
|
|
|
|
for (size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
configureRxFilter(kChannels[i].canPeriph, kChannels[i].filterNumber, mode);
|
|
|
|
|
|
}
|
|
|
|
|
|
RT_CRITICAL_RESTORE(save);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 查询当前 CANFD 接收过滤模式。
|
|
|
|
|
|
*/
|
|
|
|
|
|
CanFd::Mode CanFd::mode()
|
|
|
|
|
|
{
|
|
|
|
|
|
return s_mode;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 执行兼容保留的 CAN 轮询入口。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note 当前三路接收均由 RX0 中断驱动,该函数仅保留旧调用兼容性。
|
|
|
|
|
|
*/
|
|
|
|
|
|
void CanFd::poll()
|
|
|
|
|
|
{
|
|
|
|
|
|
/* 兼容保留:三通道接收已由 RX0 中断驱动 */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 根据状态载荷为三路发送队列批量准备状态报文。
|
|
|
|
|
|
* @param[in] status 指向状态载荷的指针;为空时直接返回。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note 当前默认不会周期性调用;仅在外部显式触发发送测试或业务发送时使用。
|
|
|
|
|
|
*/
|
|
|
|
|
|
void CanFd::prepareStatus(const StatusPayload *status)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(status == nullptr) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for(size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
(void)enqueueTxFrame(static_cast<uint8_t>(i), status);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 根据状态载荷仅为指定通道准备一帧状态报文。
|
|
|
|
|
|
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
|
|
|
|
|
|
* @param[in] status 指向状态载荷的指针;为空时返回失败。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval bool true 表示成功入队,false 表示参数非法或发送队列已满。
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool CanFd::prepareStatusForChannel(uint8_t channelIndex, const StatusPayload *status)
|
|
|
|
|
|
{
|
|
|
|
|
|
return enqueueTxFrame(channelIndex, status);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 将当前各通道已准备好的状态报文送入硬件邮箱发送。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note 当前默认不会周期性调用;仅处理调用方显式准备过的发送队列内容。
|
|
|
|
|
|
*/
|
|
|
|
|
|
void CanFd::sendPrepared()
|
|
|
|
|
|
{
|
|
|
|
|
|
for(size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
auto &queue = s_txQueues[i];
|
|
|
|
|
|
if(queue.count == 0U) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto &frame = queue.frames[queue.head];
|
|
|
|
|
|
const uint8_t mailbox = can_message_transmit(kChannels[i].canPeriph, &frame.tx_message);
|
|
|
|
|
|
if(mailbox != CAN_NOMAILBOX) {
|
|
|
|
|
|
s_diag.tx_count++;
|
|
|
|
|
|
s_diag.last_payload_length = frame.tx_message.tx_dlen;
|
|
|
|
|
|
s_diag.last_tx_id = frame.tx_message.tx_sfid;
|
|
|
|
|
|
s_diag.last_tx_channel = static_cast<uint8_t>(i);
|
2026-06-29 01:51:02 +08:00
|
|
|
|
s_channelStats[i].tx_count++;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
queue.head = static_cast<uint8_t>((queue.head + 1U) % kTxQueueDepth);
|
|
|
|
|
|
queue.count--;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
s_diag.error_count++;
|
2026-06-29 01:51:02 +08:00
|
|
|
|
s_channelStats[i].error_count++;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 查询三路 CAN 通道是否存在待消费接收报文。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval bool true 表示至少一条接收队列非空,false 表示当前三路均无待消费报文。
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool CanFd::hasRxFrame()
|
|
|
|
|
|
{
|
2026-05-30 00:24:31 +08:00
|
|
|
|
bool found = false;
|
2026-06-26 00:57:59 +08:00
|
|
|
|
uint32_t save = RT_CRITICAL_SAVE();
|
2026-05-10 21:27:51 +08:00
|
|
|
|
for(size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
if(s_rxQueues[i].count != 0U) {
|
2026-05-30 00:24:31 +08:00
|
|
|
|
found = true;
|
|
|
|
|
|
break;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-26 00:57:59 +08:00
|
|
|
|
RT_CRITICAL_RESTORE(save);
|
2026-05-30 00:24:31 +08:00
|
|
|
|
return found;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 轮转读取并消费一帧接收报文。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] id 输出报文标识符;允许为空。
|
|
|
|
|
|
* @param[out] data 输出报文数据缓存;允许为空。
|
|
|
|
|
|
* @param[out] length 输出报文长度;允许为空。
|
|
|
|
|
|
* @param[out] channel 输出报文来源通道号;允许为空。
|
|
|
|
|
|
* @retval bool true 表示成功消费一帧报文,false 表示当前三路接收队列均为空。
|
|
|
|
|
|
* @note 消费顺序采用从 `s_nextConsumeChannel` 开始的轮转扫描,避免长期偏向固定通道。
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t *channel)
|
|
|
|
|
|
{
|
|
|
|
|
|
RxFrame localFrame{};
|
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
2026-06-26 00:57:59 +08:00
|
|
|
|
uint32_t save = RT_CRITICAL_SAVE();
|
2026-05-10 21:27:51 +08:00
|
|
|
|
for(size_t n = 0; n < kChannelCount; ++n) {
|
|
|
|
|
|
const uint8_t idx = static_cast<uint8_t>((s_nextConsumeChannel + n) % kChannelCount);
|
|
|
|
|
|
auto &queue = s_rxQueues[idx];
|
|
|
|
|
|
if(queue.count == 0U) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
localFrame = queue.frames[queue.head];
|
|
|
|
|
|
queue.head = static_cast<uint8_t>((queue.head + 1U) % kRxQueueDepth);
|
|
|
|
|
|
queue.count--;
|
|
|
|
|
|
s_nextConsumeChannel = static_cast<uint8_t>((idx + 1U) % kChannelCount);
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2026-06-26 00:57:59 +08:00
|
|
|
|
RT_CRITICAL_RESTORE(save);
|
2026-05-10 21:27:51 +08:00
|
|
|
|
|
|
|
|
|
|
if(!found) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(channel) *channel = localFrame.channel;
|
|
|
|
|
|
if(id) *id = localFrame.id;
|
|
|
|
|
|
if(length) *length = localFrame.length;
|
|
|
|
|
|
if(data) {
|
|
|
|
|
|
for(uint8_t i = 0; i < localFrame.length; ++i) {
|
|
|
|
|
|
data[i] = localFrame.data[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 17:07:25 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 直接向指定 CAN 通道发送一帧任意数据报文(扩展帧)。
|
|
|
|
|
|
* @param[in] channelIndex 目标 CAN 通道号(0/1/2)。
|
|
|
|
|
|
* @param[in] ext_id 扩展帧 ID(29 位)。
|
|
|
|
|
|
* @param[in] data 数据负载指针。
|
|
|
|
|
|
* @param[in] dlc 数据长度(1~64)。
|
|
|
|
|
|
* @retval bool true 表示成功入队硬件邮箱,false 表示参数无效或邮箱已满。
|
|
|
|
|
|
* @note 绕过软件发送队列,直接调用硬件发送;适用于 SPI 桥接等即时发送场景。
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
|
|
|
|
|
const uint8_t *data, uint8_t dlc)
|
|
|
|
|
|
{
|
2026-07-06 19:13:56 +08:00
|
|
|
|
if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]
|
|
|
|
|
|
|| data == nullptr || dlc == 0U || dlc > 64U) {
|
2026-06-13 17:07:25 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-06 19:13:56 +08:00
|
|
|
|
/* 总线健康检查: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% 错误被动阈值),暂停发送保护通道 */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 17:07:25 +08:00
|
|
|
|
can_trasnmit_message_struct tx_msg{};
|
|
|
|
|
|
can_struct_para_init(CAN_TX_MESSAGE_STRUCT, &tx_msg);
|
|
|
|
|
|
tx_msg.tx_efid = ext_id;
|
|
|
|
|
|
tx_msg.tx_ff = CAN_FF_EXTENDED;
|
|
|
|
|
|
tx_msg.tx_ft = CAN_FT_DATA;
|
|
|
|
|
|
tx_msg.tx_dlen = dlc;
|
|
|
|
|
|
#ifdef GD32E508
|
|
|
|
|
|
tx_msg.fd_flag = (dlc > 8U) ? ENABLE : DISABLE;
|
2026-07-02 02:02:41 +08:00
|
|
|
|
#if (CONFIG_CAN_FD_BRS_ENABLE != 0)
|
2026-06-13 17:07:25 +08:00
|
|
|
|
tx_msg.fd_brs = (dlc > 8U) ? ENABLE : DISABLE;
|
2026-07-02 02:02:41 +08:00
|
|
|
|
#else
|
|
|
|
|
|
tx_msg.fd_brs = DISABLE;
|
|
|
|
|
|
#endif
|
2026-06-13 17:07:25 +08:00
|
|
|
|
tx_msg.fd_esi = CAN_ESI_DOMINANT;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
for (uint8_t i = 0U; i < dlc; ++i) {
|
|
|
|
|
|
tx_msg.tx_data[i] = data[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-25 01:11:31 +08:00
|
|
|
|
uint8_t mailbox = can_message_transmit(canPeriph, &tx_msg);
|
|
|
|
|
|
|
2026-06-26 00:57:59 +08:00
|
|
|
|
/* 邮箱全占时保持非阻塞:本函数可能处于 SPI CS 中断链路,不能等待
|
|
|
|
|
|
* SysTick。若 auto_retrans 因总线无 ACK 卡住全部邮箱,只立即 abort
|
|
|
|
|
|
* 一个 PENDING 邮箱并重投一次,把中断占用时间控制在寄存器访问级别。 */
|
2026-06-25 01:11:31 +08:00
|
|
|
|
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) {
|
|
|
|
|
|
s_diag.tx_abort_count++;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
mailbox = can_message_transmit(canPeriph, &tx_msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 17:07:25 +08:00
|
|
|
|
if (mailbox != CAN_NOMAILBOX) {
|
|
|
|
|
|
s_diag.tx_count++;
|
|
|
|
|
|
s_diag.last_tx_id = ext_id;
|
|
|
|
|
|
s_diag.last_tx_channel = channelIndex;
|
|
|
|
|
|
s_diag.last_payload_length = dlc;
|
2026-06-29 01:51:02 +08:00
|
|
|
|
s_channelStats[channelIndex].tx_count++;
|
2026-06-13 17:07:25 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
s_diag.error_count++;
|
2026-06-29 01:51:02 +08:00
|
|
|
|
s_channelStats[channelIndex].error_count++;
|
2026-06-13 17:07:25 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取 CANFD 诊断统计信息只读指针。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval const CanFd::Diag* 返回内部诊断统计结构体只读指针。
|
|
|
|
|
|
*/
|
2026-05-10 17:28:14 +08:00
|
|
|
|
const CanFd::Diag *CanFd::diag()
|
|
|
|
|
|
{
|
|
|
|
|
|
return &s_diag;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 将三路 TCAN1057A 收发器置为 Normal 模式。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note 对所有通道统一将 SLP/STB 控制脚拉低。
|
|
|
|
|
|
*/
|
2026-05-10 17:28:14 +08:00
|
|
|
|
void CanFd::setNormalMode()
|
|
|
|
|
|
{
|
2026-05-10 21:27:51 +08:00
|
|
|
|
for(size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
gpio_bit_reset(kChannels[i].slpPort, kChannels[i].slpPin);
|
|
|
|
|
|
}
|
2026-05-10 17:28:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 将三路 TCAN1057A 收发器置为 Silent 模式。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note 对所有通道统一将 SLP/STB 控制脚拉高。
|
|
|
|
|
|
*/
|
2026-05-10 17:28:14 +08:00
|
|
|
|
void CanFd::setSilentMode()
|
|
|
|
|
|
{
|
2026-05-10 21:27:51 +08:00
|
|
|
|
for(size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
gpio_bit_set(kChannels[i].slpPort, kChannels[i].slpPin);
|
|
|
|
|
|
}
|
2026-05-10 17:28:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-02 02:02:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 处理指定通道的 RX0 中断接收。
|
|
|
|
|
|
* @param[in] channelIndex 目标 CAN 通道索引(0/1/2)。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
2026-05-30 00:24:31 +08:00
|
|
|
|
* @note 从对应控制器 FIFO0 循环取空所有待处理报文,更新诊断统计,写入该通道软件接收队列,并置位解析任务。
|
2026-05-10 21:27:51 +08:00
|
|
|
|
*/
|
|
|
|
|
|
void CanFd::handleRxInterrupt(uint8_t channelIndex)
|
2026-05-10 17:28:14 +08:00
|
|
|
|
{
|
2026-07-06 19:13:56 +08:00
|
|
|
|
if(channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]) return;
|
2026-05-10 21:27:51 +08:00
|
|
|
|
const auto &ch = kChannels[channelIndex];
|
|
|
|
|
|
|
2026-05-30 00:24:31 +08:00
|
|
|
|
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) {
|
2026-07-02 02:02:41 +08:00
|
|
|
|
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFF0);
|
2026-05-30 00:24:31 +08:00
|
|
|
|
s_diag.rx_fifo_full_count++;
|
|
|
|
|
|
}
|
|
|
|
|
|
if(fifoOverfull) {
|
2026-07-02 02:02:41 +08:00
|
|
|
|
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFO0);
|
2026-05-30 00:24:31 +08:00
|
|
|
|
s_diag.rx_fifo_overfull_count++;
|
|
|
|
|
|
}
|
2026-05-10 17:28:14 +08:00
|
|
|
|
|
2026-05-30 00:24:31 +08:00
|
|
|
|
bool hadTraffic = false;
|
|
|
|
|
|
while(can_interrupt_flag_get(ch.canPeriph, CAN_INT_FLAG_RFL0) != RESET) {
|
|
|
|
|
|
hadTraffic = true;
|
2026-05-10 17:28:14 +08:00
|
|
|
|
|
2026-05-30 00:24:31 +08:00
|
|
|
|
can_receive_message_struct rx_message{};
|
|
|
|
|
|
can_struct_para_init(CAN_RX_MESSAGE_STRUCT, &rx_message);
|
|
|
|
|
|
can_message_receive(ch.canPeriph, CAN_FIFO0, &rx_message);
|
|
|
|
|
|
|
|
|
|
|
|
s_diag.rx_count++;
|
|
|
|
|
|
s_diag.last_rx_channel = channelIndex;
|
|
|
|
|
|
s_diag.last_payload_length = rx_message.rx_dlen;
|
|
|
|
|
|
s_diag.last_rx_fd_flag = rx_message.fd_flag;
|
|
|
|
|
|
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;
|
2026-07-02 02:02:41 +08:00
|
|
|
|
/* 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++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-30 00:24:31 +08:00
|
|
|
|
|
2026-06-29 01:51:02 +08:00
|
|
|
|
/* 维护每通道独立计数与最近一帧快照,用于 1Hz 反馈给 ESP32。 */
|
|
|
|
|
|
auto &stats = s_channelStats[channelIndex];
|
|
|
|
|
|
stats.rx_count++;
|
|
|
|
|
|
stats.last_rx_id = s_diag.last_rx_id;
|
|
|
|
|
|
stats.last_rx_dlc = rx_message.rx_dlen;
|
|
|
|
|
|
stats.last_rx_fd_flag = rx_message.fd_flag;
|
|
|
|
|
|
stats.last_rx_brs = rx_message.fd_brs;
|
|
|
|
|
|
stats.last_rx_esi = rx_message.fd_esi;
|
|
|
|
|
|
const uint8_t copyLen = (rx_message.rx_dlen < CANFD_FEEDBACK_LAST_DATA_MAX)
|
|
|
|
|
|
? rx_message.rx_dlen : static_cast<uint8_t>(CANFD_FEEDBACK_LAST_DATA_MAX);
|
|
|
|
|
|
for (uint8_t i = 0U; i < copyLen; ++i) {
|
|
|
|
|
|
stats.last_rx_data[i] = rx_message.rx_data[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
for (uint8_t i = copyLen; i < CANFD_FEEDBACK_LAST_DATA_MAX; ++i) {
|
|
|
|
|
|
stats.last_rx_data[i] = 0U;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* 维护每通道不重复 ID 快照表,用于 1Hz 分页拉取给 ESP32。 */
|
|
|
|
|
|
{
|
|
|
|
|
|
auto &table = s_idSnapshot[channelIndex];
|
|
|
|
|
|
const uint32_t rxId = s_diag.last_rx_id;
|
|
|
|
|
|
/* 查找已有条目 */
|
|
|
|
|
|
uint8_t found = CANFD_ID_SNAPSHOT_MAX;
|
|
|
|
|
|
for (uint8_t si = 0U; si < table.count; ++si) {
|
|
|
|
|
|
if (table.entries[si].occupied && table.entries[si].id == rxId) {
|
|
|
|
|
|
found = si;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (found >= CANFD_ID_SNAPSHOT_MAX) {
|
|
|
|
|
|
/* 未找到:表未满则新建,满了淘汰最旧(index 0) */
|
|
|
|
|
|
if (table.count < CANFD_ID_SNAPSHOT_MAX) {
|
|
|
|
|
|
found = table.count;
|
|
|
|
|
|
table.count++;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
found = 0U;
|
|
|
|
|
|
/* 淘汰后整体左移一格让新条目在末尾 */
|
|
|
|
|
|
for (uint8_t si = 0U; si < CANFD_ID_SNAPSHOT_MAX - 1U; ++si) {
|
|
|
|
|
|
table.entries[si] = table.entries[si + 1U];
|
|
|
|
|
|
}
|
|
|
|
|
|
found = CANFD_ID_SNAPSHOT_MAX - 1U;
|
|
|
|
|
|
}
|
|
|
|
|
|
table.entries[found] = {};
|
|
|
|
|
|
table.entries[found].id = rxId;
|
|
|
|
|
|
table.entries[found].occupied = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
auto &entry = table.entries[found];
|
|
|
|
|
|
entry.rx_count++;
|
|
|
|
|
|
entry.dlc = rx_message.rx_dlen;
|
|
|
|
|
|
entry.fd_flag = rx_message.fd_flag;
|
|
|
|
|
|
entry.brs = rx_message.fd_brs;
|
|
|
|
|
|
entry.esi = rx_message.fd_esi;
|
|
|
|
|
|
const uint8_t snapCopy = (rx_message.rx_dlen < CANFD_ID_SNAPSHOT_DATA_MAX)
|
|
|
|
|
|
? rx_message.rx_dlen : static_cast<uint8_t>(CANFD_ID_SNAPSHOT_DATA_MAX);
|
|
|
|
|
|
for (uint8_t si = 0U; si < snapCopy; ++si) {
|
|
|
|
|
|
entry.data[si] = rx_message.rx_data[si];
|
|
|
|
|
|
}
|
|
|
|
|
|
for (uint8_t si = snapCopy; si < CANFD_ID_SNAPSHOT_DATA_MAX; ++si) {
|
|
|
|
|
|
entry.data[si] = 0U;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-30 00:24:31 +08:00
|
|
|
|
auto &queue = s_rxQueues[channelIndex];
|
|
|
|
|
|
if(queue.count >= kRxQueueDepth) {
|
|
|
|
|
|
s_diag.rx_drop_count++;
|
2026-06-29 01:51:02 +08:00
|
|
|
|
stats.rx_drop_count++;
|
2026-05-30 00:24:31 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2026-05-10 17:28:14 +08:00
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
auto &frame = queue.frames[queue.tail];
|
|
|
|
|
|
frame.channel = channelIndex;
|
|
|
|
|
|
frame.id = s_diag.last_rx_id;
|
|
|
|
|
|
frame.length = rx_message.rx_dlen;
|
|
|
|
|
|
for(uint8_t i = 0; i < rx_message.rx_dlen; ++i) {
|
|
|
|
|
|
frame.data[i] = rx_message.rx_data[i];
|
|
|
|
|
|
}
|
2026-05-30 00:24:31 +08:00
|
|
|
|
|
2026-05-10 21:27:51 +08:00
|
|
|
|
queue.tail = static_cast<uint8_t>((queue.tail + 1U) % kRxQueueDepth);
|
|
|
|
|
|
queue.count++;
|
|
|
|
|
|
}
|
2026-05-10 17:28:14 +08:00
|
|
|
|
|
2026-05-30 00:24:31 +08:00
|
|
|
|
if(fifoFull) {
|
|
|
|
|
|
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFF0);
|
|
|
|
|
|
}
|
|
|
|
|
|
if(fifoOverfull) {
|
|
|
|
|
|
can_interrupt_flag_clear(ch.canPeriph, CAN_INT_FLAG_RFO0);
|
|
|
|
|
|
}
|
|
|
|
|
|
if(hadTraffic) {
|
|
|
|
|
|
TaskManager::set(kCanRxParseTaskMask);
|
|
|
|
|
|
}
|
2026-05-10 21:27:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief CAN0 兼容中断处理入口。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note 兼容保留入口,内部统一转调 `handleRxInterrupt(0U)`。
|
|
|
|
|
|
*/
|
|
|
|
|
|
void CanFd::onRx0Interrupt()
|
|
|
|
|
|
{
|
|
|
|
|
|
handleRxInterrupt(0U);
|
2026-05-10 17:28:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-29 01:51:02 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 刷新三路 CAN 通道的 1Hz 反馈快照。
|
|
|
|
|
|
* @note 在临界区内拷贝实时计数与最近一帧信息到 s_feedback;保证 SPI 命令读到一致快照。
|
|
|
|
|
|
* 典型调用频率 1Hz,由任务调度器在心跳节拍触发。
|
|
|
|
|
|
*/
|
|
|
|
|
|
void CanFd::refreshFeedbackSnapshot()
|
|
|
|
|
|
{
|
2026-07-02 02:02:41 +08:00
|
|
|
|
/* 先在临界区内采集硬件寄存器,确保三通道快照一致 */
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-29 01:51:02 +08:00
|
|
|
|
s_feedbackSeq++;
|
|
|
|
|
|
for (size_t i = 0; i < kChannelCount; ++i) {
|
|
|
|
|
|
s_feedback[i].snapshot_seq = s_feedbackSeq;
|
|
|
|
|
|
s_feedback[i].stats = s_channelStats[i];
|
2026-07-02 02:02:41 +08:00
|
|
|
|
/* 写入硬件层错误状态供 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;
|
2026-06-29 01:51:02 +08:00
|
|
|
|
s_idSnapshot[i].seq = s_feedbackSeq;
|
|
|
|
|
|
}
|
2026-07-06 19:13:56 +08:00
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-29 01:51:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-06 19:13:56 +08:00
|
|
|
|
|
2026-06-29 01:51:02 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 拷贝指定通道的最新反馈快照。
|
|
|
|
|
|
* @param[in] channelIndex 目标 CAN 通道号(0/1/2)。
|
|
|
|
|
|
* @param[out] out 目标快照缓冲;为空或通道非法时返回 false。
|
|
|
|
|
|
* @retval bool true 表示成功拷贝。
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 拷贝指定通道的不重复 ID 快照表。
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool CanFd::copyIdSnapshotTable(uint8_t channelIndex, IdSnapshotTable *out)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (out == nullptr || channelIndex >= kChannelCount) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
uint32_t save = RT_CRITICAL_SAVE();
|
|
|
|
|
|
*out = s_idSnapshot[channelIndex];
|
|
|
|
|
|
RT_CRITICAL_RESTORE(save);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
bool CanFd::copyFeedbackSnapshot(uint8_t channelIndex, ChannelFeedback *out)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (out == nullptr || channelIndex >= kChannelCount) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
uint32_t save = RT_CRITICAL_SAVE();
|
|
|
|
|
|
*out = s_feedback[channelIndex];
|
|
|
|
|
|
RT_CRITICAL_RESTORE(save);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-05-10 17:28:14 +08:00
|
|
|
|
/* ----- 中断入口 C 包装 ------------------------------------------------- */
|
2026-05-10 21:27:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief CAN0 RX0 中断向量入口。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(0U)`。
|
|
|
|
|
|
*/
|
|
|
|
|
|
extern "C" void CAN0_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(0U); }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief CAN1 RX0 中断向量入口。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(1U)`。
|
|
|
|
|
|
*/
|
|
|
|
|
|
extern "C" void CAN1_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(1U); }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief CAN2 RX0 中断向量入口。
|
|
|
|
|
|
* @param[in] 无。
|
|
|
|
|
|
* @param[out] 无。
|
|
|
|
|
|
* @retval 无返回值。
|
|
|
|
|
|
* @note C 向量包装函数,内部转调 `CanFd::handleRxInterrupt(2U)`。
|
|
|
|
|
|
*/
|
|
|
|
|
|
extern "C" void CAN2_RX0_IRQHandler(void) { CanFd::handleRxInterrupt(2U); }
|