feat(canfd): 添加TX错误事件驱动的快速重发机制
添加TX错误事件标志和相关API以支持ARD模式下单发帧丢失的快速检测和重发, 实现停等式OTA在丢帧后的立即重发,避免等待ACK超时的延迟。 - 新增s_txErrorEvent数组跟踪各通道TX错误事件 - 添加readChannelErrorReg()读取CAN_ERR寄存器原始值 - 添加consumeTxError()消费TX错误事件并返回状态 - 在TX中断中检测MTE位设置错误事件标志 fix(motor-ota): 优化OTA重发策略和错误处理 调整OTA重发参数和逻辑,提升传输可靠性和响应速度。 - 将ACK超时重发间隔从500ms调整为100ms,提高响应速度 - 增加每包补发上限从2次到4次,保持总容错时间窗 - 实现TX错误驱动的快速重发,丢帧后立即按间隔补发 - 增加错误统计信息输出,包含重发和快速重发计数 - 提高仲裁段再同步跳跃宽度从1TQ到2TQ,增强总线容错能力
This commit is contained in:
@@ -273,6 +273,14 @@ static void setError(uint8_t code) {
|
||||
static constexpr uint32_t kStepTimeoutMs = 100U;
|
||||
/* 补发次数上限:首发后最多再补发 kMaxResends 次;仍无反馈则判该步失败并打印。 */
|
||||
static constexpr uint8_t kMaxResends = 2U;
|
||||
/* 数据阶段 ACK 超时重发间隔(ms):正常 ACK 往返 ~11ms(电机 BOOT 主循环 ~10ms),
|
||||
* 100ms 留 10 倍余量且避开电机偶发 flash 写慢。原 500ms 硬编码:11 次丢帧盲等
|
||||
* 共 ~5.5s,占整场传输过半时间。电机对同包号幂等(只 ACK 不重复写 flash),
|
||||
* 早发导致的双 ACK 由 processTransferAck 的重复 ACK 过滤兜底。 */
|
||||
static constexpr uint32_t kAckResendMs = 100U;
|
||||
/* 数据阶段每包补发上限:随 kAckResendMs 500→100 同步 2→4,保持总容错时间窗;
|
||||
* 握手阶段仍用 kMaxResends,不动 v1.4.51 抗干扰逻辑。超限后由 process() 整体超时兜底。 */
|
||||
static constexpr uint8_t kAckMaxResends = 4U;
|
||||
/* 数据传输每包间隔(ms):正式发固件时每包也留间隔,防电机单缓冲 RX 高速下丢包/CRC 错。
|
||||
* 现行:5ms/包(用户指定)。SPI 喂数必须能跟上;跟不上时靠水位门控暂停 CAN。 */
|
||||
static constexpr uint32_t kTransferGapMs = 5U;
|
||||
@@ -474,6 +482,8 @@ static void sendNextPacket() {
|
||||
s_pkt_pending = true;
|
||||
s_retry_count = 0; /* 新数据包:重置补发计数 */
|
||||
s_state_enter_ms = SysTickTimer::getMs(); /* 新数据包:重置整体超时 backstop */
|
||||
/* 丢弃上一包/握手阶段残留的 TX 错误事件,避免新包刚发就被陈旧标志误触快速重发。 */
|
||||
(void)CanFd::consumeTxError(s_channel);
|
||||
/* 末包 padding 日志在 sendCurrentPacket 的 TX 行之前打印,且用最短格式,
|
||||
* 避免 10ms 高频 TX 期间被 UART 吞掉(之前放在这里会被紧接的 TX pkt= 行挤掉)。 */
|
||||
sendCurrentPacket();
|
||||
@@ -492,6 +502,8 @@ static uint8_t s_crc_retry_count = 0;
|
||||
static uint16_t s_last_acked_pkt = 0xFFFFU; /* 上次确认的包号,用于识别重复 ACK */
|
||||
static uint32_t s_dup_ack_count = 0; /* 累计忽略的重复/未前进 ACK 帧数(诊断重传风暴) */
|
||||
static uint32_t s_crc_err_count = 0; /* 累计收到的 0x06 CRC 错误帧数(含电机重传) */
|
||||
static uint32_t s_ack_resend_total = 0; /* 累计 ACK 超时重发次数(整场不清零,丢帧率基线) */
|
||||
static uint32_t s_fast_resend_total = 0; /* 累计 TX 错误快速重发次数(MTE 事件驱动,免等超时) */
|
||||
|
||||
static void processTransferAck(const uint8_t *data, uint8_t len) {
|
||||
/* 擦除等待窗口(首包尚未发出:s_packets_acked==0 且 !s_pkt_pending):电机重传的旧
|
||||
@@ -567,9 +579,10 @@ if (next_pkt == 0xFFFFFFFFU) {
|
||||
s_packets_acked = s_total_packets;
|
||||
s_last_acked_pkt = s_total_packets;
|
||||
s_pkt_pending = false;
|
||||
printf("[MOTOR-OTA] FINISHED pkt=%u/%u dup=%lu crc=%lu t=%lu\r\n",
|
||||
printf("[MOTOR-OTA] FINISHED pkt=%u/%u dup=%lu crc=%lu resend=%lu fast=%lu t=%lu\r\n",
|
||||
(unsigned)s_packets_acked, (unsigned)s_total_packets,
|
||||
(unsigned long)s_dup_ack_count, (unsigned long)s_crc_err_count,
|
||||
(unsigned long)s_ack_resend_total, (unsigned long)s_fast_resend_total,
|
||||
(unsigned long)SysTickTimer::getMs());
|
||||
enterState(State::kFinished);
|
||||
pushStatus();
|
||||
@@ -611,9 +624,10 @@ if (next_pkt == 0xFFFFFFFFU) {
|
||||
* 与 0xD1 喂数抢总线,加重缓冲欠载(CANlog 可见周期性 ~500ms 空洞)。
|
||||
* 进度由 ESP32 feedTask 按已喂字节估算;终态再 push。 */
|
||||
if ((s_packets_acked & 0x1F) == 0) {
|
||||
printf("[MOTOR-OTA] 进度 pkt=%u/%u dup=%lu crc=%lu t=%lu\r\n",
|
||||
printf("[MOTOR-OTA] 进度 pkt=%u/%u dup=%lu crc=%lu resend=%lu fast=%lu t=%lu\r\n",
|
||||
(unsigned)s_packets_acked, (unsigned)s_total_packets,
|
||||
(unsigned long)s_dup_ack_count, (unsigned long)s_crc_err_count,
|
||||
(unsigned long)s_ack_resend_total, (unsigned long)s_fast_resend_total,
|
||||
(unsigned long)SysTickTimer::getMs());
|
||||
}
|
||||
}
|
||||
@@ -633,17 +647,42 @@ void onTxTick() {
|
||||
|
||||
const uint32_t now = SysTickTimer::getMs();
|
||||
|
||||
/* ACK 超时重发:包已发出(s_pkt_pending=true)但超过 500ms 未收到 ACK,重发。
|
||||
* 场景:CAN 总线 ERR_PASSIVE(REC>128) 导致电机 ACK 丢失,
|
||||
/* ACK 超时重发:包已发出(s_pkt_pending=true)但超过 kAckResendMs 未收到 ACK,重发。
|
||||
* 场景:OTA 期间 ARD=1 单发,总线错误(如 STUFF)直接击落数据帧/ACK 丢失,
|
||||
* GD32 等 ACK、电机等下一包 → 双方死锁。
|
||||
* ★ 注意:检查 s_pkt_pending(包已发等ACK),不是 s_ack_pending(ACK已到等处理)!
|
||||
* 重发后电机幂等处理(同包号只 ACK 不重复写 flash)。 */
|
||||
if (s_pkt_pending && !s_ack_pending && !s_crc_resend_pending) {
|
||||
if (now - s_last_send_ms >= 500U) {
|
||||
if (s_retry_count < kMaxResends) {
|
||||
/* 快速路径:TX 完成中断已确认当前帧被总线错误击落(MTE),免等
|
||||
* kAckResendMs 盲等,按 kTransferGapMs 节流立即重发。先判 gap 再 consume,
|
||||
* 避免过早清标志丢事件;重发次数与超时路径共用 kAckMaxResends 上限。 */
|
||||
if ((now - s_last_send_ms) >= kTransferGapMs
|
||||
&& s_retry_count < kAckMaxResends
|
||||
&& CanFd::consumeTxError(s_channel)) {
|
||||
s_retry_count++;
|
||||
s_fast_resend_total++;
|
||||
const uint32_t err = CanFd::readChannelErrorReg(s_channel);
|
||||
printf("[MOTOR-OTA] TX错快重发 pkt=%u retry=%u tec=%lu rec=%lu errn=%lu\r\n",
|
||||
(unsigned)s_packets_acked, (unsigned)s_retry_count,
|
||||
(unsigned long)((err >> 16) & 0xFFU),
|
||||
(unsigned long)((err >> 24) & 0xFFU),
|
||||
(unsigned long)((err >> 4) & 0x07U));
|
||||
sendCurrentPacket();
|
||||
s_last_send_ms = SysTickTimer::getMs();
|
||||
return;
|
||||
}
|
||||
if (now - s_last_send_ms >= kAckResendMs) {
|
||||
if (s_retry_count < kAckMaxResends) {
|
||||
s_retry_count++;
|
||||
printf("[MOTOR-OTA] ACK 超时重发 pkt=%u retry=%u\r\n",
|
||||
(unsigned)s_packets_acked, (unsigned)s_retry_count);
|
||||
s_ack_resend_total++;
|
||||
/* 事件时刻 CAN_ERR 快照:钉死丢帧时总线状态(TEC 涨=数据帧发丢,
|
||||
* REC 高=接收方向损坏),摆脱 1Hz [CAN-BUS] 采样混叠。仅日志,不做发送门控。 */
|
||||
const uint32_t err = CanFd::readChannelErrorReg(s_channel);
|
||||
printf("[MOTOR-OTA] ACK 超时重发 pkt=%u retry=%u tec=%lu rec=%lu errn=%lu\r\n",
|
||||
(unsigned)s_packets_acked, (unsigned)s_retry_count,
|
||||
(unsigned long)((err >> 16) & 0xFFU),
|
||||
(unsigned long)((err >> 24) & 0xFFU),
|
||||
(unsigned long)((err >> 4) & 0x07U));
|
||||
sendCurrentPacket();
|
||||
s_last_send_ms = SysTickTimer::getMs();
|
||||
}
|
||||
@@ -944,6 +983,8 @@ s_channel = can_channel;
|
||||
s_pkt_pending = false;
|
||||
s_dup_ack_count = 0;
|
||||
s_crc_err_count = 0;
|
||||
s_ack_resend_total = 0;
|
||||
s_fast_resend_total = 0;
|
||||
s_last_pad_valid = 0xFFFFU;
|
||||
|
||||
/* 不在 SPI 中断里同步发握手帧(电机跳 BOOT 期间 CAN 可能短暂断开)。
|
||||
|
||||
Reference in New Issue
Block a user