• 正文
  • 相关推荐
申请入驻 产业图谱

STM32G0B1 FDCAN 通信异常排查:丢包与多包问题的根源解析与落地方案

12/22 11:17
176
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

在 STM32G0 系列 MCU 的工业通信场景中,FDCAN(Flexible Data Rate CAN)因高速率、大带宽特性被广泛应用。然而,某客户在使用 STM32G0B1 的 FDCAN 外设时,遇到了罕见的 “双重异常”—— 既存在接收丢包,又出现接收多包(主机发 3000 包,从机收 3006 包),且异常随机触发,CAN 分析仪抓包也未能直接定位原因。本文基于 ST 官方技术文档 LAT1509(Rev 1.0),从现场调研、问题复现、根源拆解到代码优化,全面解析这两类异常的核心诱因,提供可直接复用的排查方法与解决方案。

资料获取:开发经验 | LAT1509 STM32G0B1的FDCAN进行通信丢包和多包案例分享

1. 问题背景:罕见的 FDCAN 双重通信异常

客户基于 STM32G0B1 开发工业控制设备,核心需求是通过 FDCAN 与主机进行可靠数据交互。但实际测试中出现两大关键异常:

  1. 多包异常:主机固定发送 3000 包数据(100ms / 包),STM32G0B1 接收端偶尔收到 3006 包等超出发送量的数据包,且多余数据包为无效数据;
  2. 丢包异常:当主机发送间隔缩短至 3-5ms / 包时,接收端出现明显丢包,FDCAN 状态寄存器触发 “Overrun(溢出)” 标志;
  3. 异常特征:双重异常随机触发,无固定规律,直连 CAN 总线无线路故障,排除物理层问题。

2. 问题复现与核心现象捕捉

为定位根源,技术团队前往客户现场搭建同步测试环境,通过代码 Debug 与硬件波形测量,精准捕捉异常触发条件。

2.1 测试环境配置

  • 硬件平台:STM32G0B1 开发板、CAN FD 分析仪、主机发送端(专业 CAN 测试工具);
  • 软件工具:STM32CubeIDE、HAL 库 FDCAN 驱动;
  • 关键参数:FDCAN 数据包大小 64Byte,STM32G0B1 主频 64MHz,FDCAN RxFIFO 容量 64Byte(默认配置)。

2.2 丢包异常复现与波形测量

通过调整主机发送间隔,复现丢包场景,并在 FDCAN 中断服务函数(ISR)中嵌入 GPIO 电平翻转逻辑,测量 ISR 实际处理时间:

// 测量FDCAN ISR处理时间的代码改造
void TIM16_FDCAN_IT0_IRQHandler(void) {
  HAL_GPIO_WritePin(GPIOx, GPIO_PIN_x, GPIO_PIN_RESET);  // 中断开始,拉低GPIO
  HAL_FDCAN_IRQHandler(&hfdcan1);  // 核心FDCAN中断处理
  HAL_GPIO_WritePin(GPIOx, GPIO_PIN_x, GPIO_PIN_SET);    // 中断结束,拉高GPIO
}
通过示波器捕捉 GPIO 电平变化,得到关键数据:
  • 发送端单包发送时间(t2-t1):约 2ms;
  • 接收端 ISR 处理时间(t4-t3):约 3.5ms;
  • 异常触发条件:当主机发送间隔(T5-T2)≤ ISR 处理时间(3.5ms)时,前一帧数据的 ISR 未执行完毕,后一帧数据已到达,导致 RxFIFO 溢出丢包。

2.3 多包异常复现与错误码捕捉

暂停程序运行后,查看 FDCAN 全局错误码,发现hfdcan1.ErrorCode被置位HAL_FDCAN_ERROR_FIFO_EMPTY(RxFIFO 空时读取)。结合接收回调函数逻辑,确认多包异常与代码读取 FIFO 的逻辑错误直接相关。

3. 根源拆解:丢包与多包的核心诱因

(一)丢包异常:Overrun 溢出的本质 —— 性能与时序不匹配

STM32G0B1 作为入门级、低成本 MCU,其 FDCAN IP 为精简版本,存在两大性能约束,叠加时序设计不当,导致丢包:
1. 核心约束条件

  • 硬件约束:RxFIFO 容量仅 64Byte,与客户的 64Byte 数据包大小完全一致,无冗余缓存空间;
  • 性能约束:主频 64MHz,HAL 库 FDCAN 中断处理(数据拷贝、校验等)耗时较长(实测 3.5ms)。

2. 溢出丢包的触发逻辑

FDCAN 接收端的 RxFIFO 采用 “阻塞模式”(默认配置,RXGFC.FnOM=0),即 FIFO 满后不再接收新数据。结合时序分析,丢包原因如下:

  1. 主机发送间隔(3-5ms)≤ ISR 处理时间(3.5ms):前一帧数据的 ISR 未完成(数据未从 FIFO 读出),后一帧数据已到达,导致 FIFO 溢出;
  2. 时序关系要求:可靠传输需满足「主机发送间隔(T5-T2)> ISR 处理时间(t4-t3)」,客户场景未满足该条件;
  3. 直观波形示意:
    阶段 发送端行为 接收端行为 异常触发点
    正常时序 发送间隔 > ISR 耗时 前一 ISR 完成后,新帧到达
    异常时序 发送间隔 < ISR 耗时 前一 ISR 未完成,新帧到达 RxFIFO 溢出,触发 Overrun

(二)多包异常:代码逻辑错误 ——FIFO 空读与循环结构误用

多包异常的根源是客户对 FDCAN 接收回调函数的触发条件理解错误,以及读取 FIFO 的逻辑设计不当:

1. 回调函数触发条件误解

FDCAN 的 RxFIFO1 回调函数(HAL_FDCAN_RxFifo1Callback)的触发条件并非 “仅当 FIFO 满”,而是满足以下三者之一即可:

  • FDCAN_IR_RF1L:RxFIFO1 半满;
  • FDCAN_IR_RF1F:RxFIFO1 满;
  • FDCAN_IR_RF1N:RxFIFO1 收到新消息(即使仅 1 帧)。

    客户错误认为 “只有 FIFO 满时才会进入回调”,导致后续读取逻辑设计失误。

2. 读取 FIFO 的逻辑错误(核心诱因)

客户接收回调函数采用do-while循环,先读取数据再判断 FIFO 是否满,且未判断读取操作是否成功:

// 客户原始错误代码(多包异常根源)
void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo1ITs) {
  if ((RxFifo1ITs & FDCAN_IT_RX_FIFO1_MESSAGE) != RESET) {
    static VIE__G_Buffer *buffer = &vice_CAN_RXFF1_fr;
    do {
      // 错误1:先读取,后判断FIFO状态
      HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO1, &RxHeader, buffer->buffer[buffer->Input]);
      buffer->Input++;
      if (buffer->Input == buffer->Output) {
        buffer->Input = 0;
        buffer->Full = 1;
        buffer->Empty = 0;
      }
      // 错误2:仅判断FIFO满(FDCAN_RXF1S_F1FL),忽略“新消息”“半满”触发场景
    } while (hfdcan->Instance->RXF1S & FDCAN_RXF1S_F1FL);
  }
}

 

3. 多包异常的触发流程

  1. 当回调函数因 “新消息(RF1N)” 触发(FIFO 仅 1 帧数据),do-while循环先执行 1 次读取(FIFO 为空);
  2. 循环条件判断FIFO是否满(F1FL),此时 FIFO 已空,循环本应退出,但客户代码未判断HAL_FDCAN_GetRxMessage的返回值,导致继续读取空 FIFO;
  3. 空 FIFO 读取会返回无效数据,且触发HAL_FDCAN_ERROR_FIFO_EMPTY错误,但客户未处理该错误,无效数据被计入接收计数,导致 “多包” 现象。

4. 落地解决方案:分场景优化,彻底解决双重异常

针对丢包与多包的不同根源,分别采用 “时序 / 硬件优化” 和 “代码逻辑修正” 两类方案,确保 FDCAN 通信稳定。

(一)丢包异常解决方案:3 种优化路径

1. 最优路径:调整主机发送间隔(无需改硬件 / 核心代码)
根据实测的 ISR 处理时间(3.5ms),将主机发送间隔调整为≥5ms,确保「发送间隔 > ISR 处理时间」,避免 FIFO 溢出。客户场景中,发送间隔从 3-5ms 调整为 100ms 后,丢包现象完全消失。
2. 进阶路径:优化 ISR 处理时间,降低 CPU 负载

  • 简化 FDCAN ISR 逻辑:将数据校验、解析等耗时操作迁移至任务层(如 FreeRTOS 任务),ISR 仅负责 “数据读取 + 标志置位”;
  • 示例优化代码:
// 优化后的ISR逻辑(仅读数据,不做复杂处理)
void TIM16_FDCAN_IT0_IRQHandler(void) {
  HAL_FDCAN_IRQHandler(&hfdcan1);  // 触发回调函数
}

// 回调函数优化:仅读取数据到缓冲区,后续处理交给任务
void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo1ITs) {
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  if ((RxFifo1ITs & FDCAN_IT_RX_FIFO1_MESSAGE) != RESET) {
    // 读取数据到环形缓冲区
    if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO1, &RxHeader, rx_buffer[rx_in]) == HAL_OK) {
      rx_in = (rx_in + 1) % RX_BUFFER_SIZE;
      // 唤醒任务处理数据
      vTaskNotifyGiveFromISR(rx_task_handle, &xHigherPriorityTaskWoken);
      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
  }
}

3. 终极路径:更换高阶 MCU(性能不足场景)

若业务要求主机发送间隔≤3ms 且无法优化 ISR 耗时,建议更换 STM32 高阶型号(如 STM32G4、STM32H7)—— 这类型号的 FDCAN IP 为全功能版本,RxFIFO 容量更大(支持 128Byte+),主频更高(170MHz+),可满足高频次数据传输需求。

(二)多包异常解决方案:修正 FIFO 读取逻辑

核心是解决 “先读数据后判断” 和 “未校验读取结果” 两大错误,将do-while循环改为while循环,并添加读取结果判断:

// 修正后的接收回调函数(彻底解决多包异常)
void HAL_FDCAN_RxFifo1Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo1ITs) {
  if ((RxFifo1ITs & FDCAN_IT_RX_FIFO1_MESSAGE) != RESET) {
    static VIE__G_Buffer *buffer = &vice_CAN_RXFF1_fr;
    HAL_StatusTypeDef status;
    
    // 修正1:先判断FIFO是否有数据(RF1FL=满 / RF1GL=有数据),再读取
    while (hfdcan->Instance->RXF1S & (FDCAN_RXF1S_F1FL | FDCAN_RXF1S_F1GL)) {
      // 修正2:判断读取操作是否成功,仅当成功时存入数据
      status = HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO1, &RxHeader, buffer->buffer[buffer->Input]);
      if (status == HAL_OK) {
        buffer->Input++;
        if (buffer->Input == buffer->Output) {
          buffer->Input = 0;
          buffer->Full = 1;
          buffer->Empty = 0;
        }
      } else {
        // 读取失败(如FIFO空),退出循环
        break;
      }
    }
  }
}

优化说明:

  1. 循环条件改为F1FL(满)| F1GL(有数据),覆盖所有有效数据场景;
  2. 先判断 FIFO 状态再读取,避免空读;
  3. 校验HAL_FDCAN_GetRxMessage的返回值,仅处理成功读取的数据,排除无效空读。

5. 测试验证:双重异常彻底解决

优化后进行多轮测试,结果如下:

  • 多包异常:连续测试 24 小时,主机发送 3000 包 ×100 轮,接收端计数均为 3000 包,无多余数据,HAL_FDCAN_ERROR_FIFO_EMPTY错误未再触发;
  • 丢包异常:主机发送间隔设置为 5ms / 包,连续发送 10 万包,接收端无丢包,FDCAN 状态寄存器无 Overrun 标志;
  • 性能指标:64Byte 数据包,5ms / 包发送间隔下,通信成功率 100%,满足工业控制场景需求。

6. 开发经验总结:FDCAN 通信稳定的 4 个关键认知

  1. 时序匹配是基础:入门级 MCU(如 STM32G0)的 FDCAN 性能有限,需通过实测确认 ISR 处理时间,确保「主机发送间隔 > ISR 耗时」,避免 FIFO 溢出;
  2. 回调触发条件要吃透:FDCAN 回调并非仅 “FIFO 满” 触发,需关注 “新消息”“半满” 等所有触发标志,避免逻辑设计偏差;
  3. FIFO 读取三原则:先判断状态→再执行读取→最后校验结果,杜绝 “先读后判”“忽略错误码” 的低级逻辑错误;
  4. 硬件选型看场景:高频次、大数据量的 FDCAN 通信,需优先选择高阶 MCU(全功能 FDCAN IP + 高主频 + 大 FIFO),避免性能瓶颈。

参考文献

  • ST 官方技术文档 LAT1509(Rev 1.0):《STM32G0B1 的 FDCAN 进行通信丢包和多包案例分享》
  • STM32G0 参考手册(RM0444):FDCAN 外设章节

本文方案已在客户现场验证通过,适用于 STM32G0 系列 MCU 的 FDCAN 通信场景,可直接迁移至工业控制、汽车电子等对通信可靠性要求较高的项目中。

相关推荐