feat(canfd): 修复CAN发送问题。实现CANFD发送完成中断处理和通道健康检查
- 添加CANFD发送完成中断处理函数,确保只有MTFNERR置位时才计数发送成功 - 新增通道健康检查功能,防止病态通道被持续重发的测试帧反复推回BUS_OFF - 重构CANFD配置结构体,添加TX中断配置项并修正GPIO映射设置 - 移除原有的软件发送队列机制,改为直接硬件发送模式 - 更新项目配置文件中的CANFD源文件路径命名 fix(canfd): 修复CANFD发送计数和错误处理逻辑 - 修正发送计数只在硬件MTFNERR确认后增加,而非请求进入邮箱时 - 改进错误日志输出,添加详细的错误类型和通道状态信息 - 优化总线恢复机制,通过收发器静默-唤醒协助硬件自动恢复 - 添加条件编译开关控制启动时自检和回环测试功能
This commit is contained in:
@@ -73,6 +73,12 @@ constexpr uint8_t CONFIG_SPI_APP_OTA_BOOT_ACK_ERROR = 0xE2U;
|
||||
constexpr uint32_t CONFIG_SPI_READY_PORT = GPIOB;
|
||||
/** @brief SPI Ready 握手输出引脚。 */
|
||||
constexpr uint32_t CONFIG_SPI_READY_PIN = GPIO_PIN_6;
|
||||
|
||||
/** @brief ESP32 硬件复位控制端口(PC9 接 ESP32 EN/RESET)。 */
|
||||
constexpr uint32_t CONFIG_ESP32_RESET_PORT = GPIOC;
|
||||
/** @brief ESP32 硬件复位控制引脚。 */
|
||||
constexpr uint32_t CONFIG_ESP32_RESET_PIN = GPIO_PIN_9;
|
||||
|
||||
/* ============================================================
|
||||
* CAN-FD 位时序配置(三路 CAN0/CAN1/CAN2 共用)
|
||||
* 时钟链: SYSCLK=180MHz → AHB=/1=180MHz(RCU_MODIFY_UP 强制)
|
||||
@@ -108,6 +114,11 @@ constexpr uint32_t CONFIG_CAN_DATA_BS2 = CAN_BT_BS2_1TQ;
|
||||
* @note 保留 #define:canfd.cpp 中用 #if 做编译期分支。 */
|
||||
#define CONFIG_CAN_FD_BRS_ENABLE 1U
|
||||
|
||||
/** @brief 启动时向物理总线发送 classic/FD/BRS 探测帧;正常固件默认关闭。 */
|
||||
#define CONFIG_CAN_STARTUP_BUS_TEST_ENABLE 0
|
||||
/** @brief 启动时切换控制器到内部回环执行自检;正常固件默认关闭。 */
|
||||
#define CONFIG_CAN_STARTUP_LOOPBACK_TEST_ENABLE 0
|
||||
|
||||
/** @brief CAN0 通道使能开关:true=启用初始化/收发/自测,false=完全禁用。 */
|
||||
constexpr bool CONFIG_CAN_CH0_ENABLE = true;
|
||||
/** @brief CAN1 通道使能开关。 */
|
||||
@@ -121,11 +132,6 @@ constexpr uint32_t CONFIG_CAN_RX_FILTER_ID = CONFIG_CAN_STATUS_ID;
|
||||
constexpr uint32_t CONFIG_CAN_RX_FILTER_MASK = 0x7FFU;
|
||||
/** @brief 单次 CAN 接收解析任务最多消费的软件队列帧数。 */
|
||||
constexpr uint32_t CONFIG_CAN_RX_PARSE_BUDGET = 48U;
|
||||
/** @brief CAN 状态载荷中调试标识字符 0~3。 */
|
||||
constexpr uint8_t CONFIG_CAN_STATUS_TAG0 = 0x43U;
|
||||
constexpr uint8_t CONFIG_CAN_STATUS_TAG1 = 0x41U;
|
||||
constexpr uint8_t CONFIG_CAN_STATUS_TAG2 = 0x4EU;
|
||||
constexpr uint8_t CONFIG_CAN_STATUS_TAG3 = 0x46U;
|
||||
|
||||
/** @brief 用户调度定时器实例。 */
|
||||
constexpr uint32_t CONFIG_SCHED_TIMER_INSTANCE = TIMER2;
|
||||
@@ -151,8 +157,10 @@ constexpr uint32_t CONFIG_TASK_PERIOD_VREF_SAMPLE_TICKS = 5U;
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_TEMP_SAMPLE_TICKS = 10U;
|
||||
/** @brief 调度基准节拍:10ms 计数周期,单位 100us tick。 */
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_CAN_RX_TICKS = 100U;
|
||||
/** @brief 调度基准节拍:CAN 发送/状态灯周期,单位 100us tick。 */
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_CAN_TX_TICKS = 1000U;
|
||||
/** @brief 调度基准节拍:CAN 周期发送任务周期,10 tick = 1ms。 */
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_CAN_TX_TICKS = 10U;
|
||||
/** @brief 调度基准节拍:状态灯任务周期,1000 tick = 100ms。 */
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_STATUS_LED_TICKS = 1000U;
|
||||
/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_HEARTBEAT_TICKS = 10000U;
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ void AppInitializer::init()
|
||||
nvic_priority_group_set(CONFIG_NVIC_PRIORITY_GROUP);
|
||||
printf("[APP] init: SysTick...\r\n");
|
||||
SysTickTimer::initMs();
|
||||
/* ESP32 EN/RESET (PC9) 直接配置为推挽输出高电平,保持 ESP32 正常运行 */
|
||||
rcu_periph_clock_enable(RCU_GPIOC);
|
||||
gpio_bit_set(CONFIG_ESP32_RESET_PORT, CONFIG_ESP32_RESET_PIN);
|
||||
gpio_init(CONFIG_ESP32_RESET_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, CONFIG_ESP32_RESET_PIN);
|
||||
printf("[APP] init: USART0...\r\n");
|
||||
Usart0::configurePins();
|
||||
Usart0::init();
|
||||
|
||||
@@ -31,6 +31,7 @@ void TaskManager::init()
|
||||
g_scheduler.div_temp_sample = 0U;
|
||||
g_scheduler.div_can_rx = 0U;
|
||||
g_scheduler.div_can_tx = 0U;
|
||||
g_scheduler.div_status_led = 0U;
|
||||
g_scheduler.div_heartbeat = 0U;
|
||||
}
|
||||
|
||||
@@ -94,8 +95,14 @@ void TaskManager::tick100us()
|
||||
|
||||
if(++g_scheduler.div_can_tx >= CONFIG_TASK_PERIOD_CAN_TX_TICKS) {
|
||||
g_scheduler.div_can_tx = 0U;
|
||||
g_scheduler.tick_1ms++;
|
||||
set(1UL << TASK_CAN_TX_SEND);
|
||||
}
|
||||
|
||||
if(++g_scheduler.div_status_led >= CONFIG_TASK_PERIOD_STATUS_LED_TICKS) {
|
||||
g_scheduler.div_status_led = 0U;
|
||||
g_scheduler.tick_100ms++;
|
||||
set((1UL << TASK_STATUS_LED) | (1UL << TASK_CAN_TX_SEND));
|
||||
set(1UL << TASK_STATUS_LED);
|
||||
}
|
||||
|
||||
if(++g_scheduler.div_heartbeat >= CONFIG_TASK_PERIOD_HEARTBEAT_TICKS) {
|
||||
|
||||
@@ -144,54 +144,48 @@ void TaskRunner::taskAdcProcess()
|
||||
void TaskRunner::taskCanRxPoll() { CanFd::poll(); }
|
||||
|
||||
/**
|
||||
* @brief 执行预留的 CAN 发送执行任务。
|
||||
* @brief 执行 CAN 周期发送任务:每 1ms 在每个使能通道上发送一帧 CANFD 测试帧。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 当前阶段不做周期性发送调度;若后续需要显式验证某一路发送能力,可由调用方先 prepare,再直接调用 `CanFd::sendPrepared()`。
|
||||
* @note 帧格式:扩展帧 29-bit ID,CAN-FD,DLC=64,BRS 受 CONFIG_CAN_FD_BRS_ENABLE 控制。
|
||||
* 载荷布局:
|
||||
* [0] 固定标识 0xC0
|
||||
* [1] 通道号
|
||||
* [2..5] 发送序号(小端 uint32)
|
||||
* [6..7] 系统运行秒数低 16 位
|
||||
* [8..63] 填充 0xA0 + (index & 0x0F)
|
||||
* periodic=true:仅允许一个邮箱保持自动重发,不主动 abort 旧帧,
|
||||
* 跳过 TEC 保护以匹配诊断行为。
|
||||
*/
|
||||
void TaskRunner::taskCanTxSend()
|
||||
{
|
||||
/* 启动后每 100ms 轮流发送一路 CAN 通道的 CANFD+BRS 64 字节测试帧。
|
||||
* 三路通道时分复用:CH0→CH1→CH2→CH0→...,每 100ms 只发一路,
|
||||
* 避免多路同时发送导致总线仲裁冲突(当多路并接到同一总线时)。
|
||||
* 帧格式:扩展帧 29-bit ID,CANFD+BRS,DLC=64。
|
||||
* 载荷布局:
|
||||
* [0] 通道号 0/1/2
|
||||
* [1] 保留
|
||||
* [2..5] 发送序号(小端 uint32,每通道独立递增)
|
||||
* [6..9] 系统运行秒数 g_app_uptime_sec(小端 uint32)
|
||||
* [10..63] 递增填充,按通道偏移便于抓包区分 */
|
||||
static uint32_t seq[3] = {0U, 0U, 0U};
|
||||
static uint8_t round = 0U; /* 0=CH0, 1=CH1, 2=CH2, 轮流 */
|
||||
static uint32_t seq = 0U;
|
||||
|
||||
const uint8_t ch = round;
|
||||
round = (round + 1U) % 3U;
|
||||
for (uint8_t ch = 0U; ch < 3U; ++ch) {
|
||||
if (!CanFd::isChannelEnabled(ch)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 跳过已禁用的通道 */
|
||||
if (!CanFd::isChannelEnabled(ch)) {
|
||||
return;
|
||||
uint8_t data[64];
|
||||
data[0] = 0xC0U;
|
||||
data[1] = ch;
|
||||
const uint32_t s = seq;
|
||||
data[2] = static_cast<uint8_t>(s & 0xFFU);
|
||||
data[3] = static_cast<uint8_t>((s >> 8) & 0xFFU);
|
||||
data[4] = static_cast<uint8_t>((s >> 16) & 0xFFU);
|
||||
data[5] = static_cast<uint8_t>((s >> 24) & 0xFFU);
|
||||
data[6] = static_cast<uint8_t>(g_app_uptime_sec & 0xFFU);
|
||||
data[7] = static_cast<uint8_t>((g_app_uptime_sec >> 8) & 0xFFU);
|
||||
for (uint8_t i = 8U; i < 64U; ++i) {
|
||||
data[i] = static_cast<uint8_t>(0xA0U + (i & 0x0FU));
|
||||
}
|
||||
|
||||
const uint32_t extId = 0x18FF5000U | static_cast<uint32_t>(ch);
|
||||
if (CanFd::transmitFrame(ch, extId, data, sizeof(data), true)) {
|
||||
seq++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t data[64];
|
||||
data[0] = ch;
|
||||
data[1] = 0U;
|
||||
const uint32_t s = seq[ch];
|
||||
data[2] = static_cast<uint8_t>(s & 0xFFU);
|
||||
data[3] = static_cast<uint8_t>((s >> 8) & 0xFFU);
|
||||
data[4] = static_cast<uint8_t>((s >> 16) & 0xFFU);
|
||||
data[5] = static_cast<uint8_t>((s >> 24) & 0xFFU);
|
||||
data[6] = static_cast<uint8_t>(g_app_uptime_sec & 0xFFU);
|
||||
data[7] = static_cast<uint8_t>((g_app_uptime_sec >> 8) & 0xFFU);
|
||||
data[8] = static_cast<uint8_t>((g_app_uptime_sec >> 16) & 0xFFU);
|
||||
data[9] = static_cast<uint8_t>((g_app_uptime_sec >> 24) & 0xFFU);
|
||||
for (uint8_t i = 10U; i < 64U; ++i) {
|
||||
data[i] = static_cast<uint8_t>(i + ch * 0x40U);
|
||||
}
|
||||
|
||||
const uint32_t extId = 0x1FFFFF00U | static_cast<uint32_t>(ch);
|
||||
CanFd::transmitFrame(ch, extId, data, 64U);
|
||||
seq[ch]++;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,18 +217,6 @@ void TaskRunner::taskCanRxParse()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行预留的 CAN 发送准备任务。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 当前阶段三路 CANFD 仅要求底层收发能力可用,不做周期性业务状态广播。
|
||||
*/
|
||||
void TaskRunner::taskCanTxPrepare()
|
||||
{
|
||||
/* 当前阶段不做周期性业务状态广播。 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行状态灯维护任务。
|
||||
* @param[in] 无。
|
||||
|
||||
@@ -39,9 +39,7 @@ private:
|
||||
static void taskCanRxPoll();
|
||||
/** @brief 解析并消费接收帧。 */
|
||||
static void taskCanRxParse();
|
||||
/** @brief 预留的 CAN 发送准备任务;当前不主动生成周期业务帧。 */
|
||||
static void taskCanTxPrepare();
|
||||
/** @brief 预留的 CAN 发送执行任务;当前仅保留接口,不参与周期调度。 */
|
||||
/** @brief CAN 周期发送任务;每 1ms 每通道发送一帧测试报文。 */
|
||||
static void taskCanTxSend();
|
||||
/** @brief 状态灯维护任务(当前预留)。 */
|
||||
static void taskStatusLed();
|
||||
|
||||
Reference in New Issue
Block a user