feat(canfd): 添加CANFD发送邮箱占用处理机制
当CAN控制器因总线无ACK导致邮箱卡死时,实现两段式恢复策略: 1) 短自旋轮询邮箱状态,大多数场景下可避免触发abort而不丢帧 2) 若仍全部PENDING则只中止第一个占用邮箱,限制最坏丢帧数量为1帧 添加了tx_abort_count诊断计数器用于监控该情况,并引入SysTick延时功能 refactor(spi-cmd): 优化SPI命令日志输出格式 将CAN发送相关日志改为统一的格式化函数输出,增加数据字段十六进制显示功能, 并扩大日志缓冲区大小从128到256字节以支持更详细的日志信息 chore(spi-command): 添加SPI帧类型分类和非刷写帧日志记录 新增isFlashRelatedFrame函数区分刷写相关帧类型,在处理命令时对非刷写帧进行日志记录 perf(diagnostic): 在诊断打印中添加发送中止计数统计 更新诊断信息打印格式,增加tx_abort计数项,便于监控CAN发送异常情况
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include "device/canfd.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/systick.h"
|
||||
|
||||
namespace {
|
||||
struct TxFrame {
|
||||
@@ -463,7 +464,44 @@ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
tx_msg.tx_data[i] = data[i];
|
||||
}
|
||||
|
||||
const uint8_t mailbox = can_message_transmit(kChannels[channelIndex].canPeriph, &tx_msg);
|
||||
const uint32_t canPeriph = kChannels[channelIndex].canPeriph;
|
||||
uint8_t mailbox = can_message_transmit(canPeriph, &tx_msg);
|
||||
|
||||
/* 邮箱全占时的恢复处理:
|
||||
* 当目标控制器初始化时 auto_retrans = ENABLE,且总线上没有其它节点回 ACK,
|
||||
* 已入邮箱的帧会无限自动重传,TME 位永不置回,导致后续帧一律 CAN_NOMAILBOX。
|
||||
*
|
||||
* 改良策略(两段式,尽量不丢帧):
|
||||
* 1) 先短自旋 ~2 ms 轮询邮箱状态。健康总线上 1 Mbps + 8 字节帧的实际发送
|
||||
* 仅约 130 µs,正常重传几百 µs 内即可 drain,绝大多数“偶发 NOMAILBOX”
|
||||
* 场景到这一步就已经恢复,不会触发 abort,也就不会丢帧。
|
||||
* 2) 若 2 ms 后仍全部 PENDING(典型:物理层不通、TCAN1057A 没焊或 CAN_L/H
|
||||
* 断线导致 ACK Error 死循环),只 abort 第一个 PENDING 邮箱腾一个位置,
|
||||
* 最坏丢 1 帧而不是 3 帧,并通过 s_diag.tx_abort_count 暴露给串口 diag,
|
||||
* 上层(含 SPI 桥)仍能把后续帧正常下发。 */
|
||||
if (mailbox == CAN_NOMAILBOX) {
|
||||
constexpr uint8_t kDrainPolls = 2U;
|
||||
for (uint8_t poll = 0U; poll < kDrainPolls && mailbox == CAN_NOMAILBOX; ++poll) {
|
||||
SysTickTimer::delayMs(1U);
|
||||
mailbox = can_message_transmit(canPeriph, &tx_msg);
|
||||
}
|
||||
}
|
||||
if (mailbox == CAN_NOMAILBOX) {
|
||||
/* 只 abort 一个 PENDING 邮箱:找到首个 stop 成功的就停手,
|
||||
* 不再扫描后续邮箱,把丢帧上限收敛到 1。stop 失败的邮箱
|
||||
* (硬件正在物理发送)继续尝试下一个,避免一次 abort 都没成功。 */
|
||||
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);
|
||||
}
|
||||
|
||||
if (mailbox != CAN_NOMAILBOX) {
|
||||
s_diag.tx_count++;
|
||||
s_diag.last_tx_id = ext_id;
|
||||
|
||||
Reference in New Issue
Block a user