引言

在工业控制、汽车电子和机器人领域,CAN/FD-CAN 总线因其高可靠性和实时性被广泛使用。STM32H7 系列内置 FDCAN 控制器,支持 CAN FD 协议,最高速率可达 8Mbps。然而,多节点环境下,总线负载率过高会导致仲裁延迟增大,甚至丢帧。本文基于 STM32H743(主频 400MHz),实测不同负载下的总线行为,分析极限负载与仲裁延迟,并给出优化策略。

FDCAN 与仲裁机制

FDCAN 基于 CAN 2.0 和 CAN FD 协议,采用非破坏性逐位仲裁。每个节点发送帧时,先发送标识符(ID),ID 越小优先级越高。当多个节点同时发送时,显性位(0)覆盖隐性位(1),低 ID 节点赢得仲裁,高 ID 节点自动退出发送,等待总线空闲后重发。

仲裁延迟指从节点请求发送到实际占用总线的时间,受总线负载、优先级和位定时影响。在 FDCAN 中,仲裁段(Arbitration Phase)和数据段(Data Phase)可配置不同速率,典型配置为仲裁 1Mbps,数据 5Mbps。

测试环境与配置

硬件平台

  • 主控:STM32H743VIT6,主频 400MHz
  • 收发器:TJA1051(支持 CAN FD)
  • 节点数:3 个(节点 A、B、C),通过 120Ω 终端电阻连接
  • 示波器:R&S RTO2044,用于捕获总线波形

软件配置

使用 STM32CubeMX 生成工程,配置 FDCAN1 和 FDCAN2,参数如下:

  • 仲裁段速率:1Mbps
  • 数据段速率:5Mbps
  • 采样点:75%
  • 同步跳转宽度:1 TQ
  • 位时间:仲裁段 10 TQ,数据段 8 TQ

关键初始化代码(基于 HAL 库):

FDCAN_HandleTypeDef hfdcan1;
FDCAN_FilterTypeDef sFilterConfig;

void FDCAN_Init(void)
{
    hfdcan1.Instance = FDCAN1;
    hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;
    hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_BRS;
    hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
    hfdcan1.Init.AutoRetransmission = ENABLE;
    hfdcan1.Init.TransmitPause = DISABLE;
    hfdcan1.Init.ProtocolException = DISABLE;
    hfdcan1.Init.NominalPrescaler = 1;
    hfdcan1.Init.NominalSyncJumpWidth = 1;
    hfdcan1.Init.NominalTimeSeg1 = 6;  // 采样点 75%
    hfdcan1.Init.NominalTimeSeg2 = 3;
    hfdcan1.Init.DataPrescaler = 1;
    hfdcan1.Init.DataSyncJumpWidth = 1;
    hfdcan1.Init.DataTimeSeg1 = 5;
    hfdcan1.Init.DataTimeSeg2 = 2;
    hfdcan1.Init.StdFiltersNbr = 1;
    hfdcan1.Init.ExtFiltersNbr = 0;
    hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
    HAL_FDCAN_Init(&hfdcan1);

    sFilterConfig.IdType = FDCAN_STANDARD_ID;
    sFilterConfig.FilterIndex = 0;
    sFilterConfig.FilterType = FDCAN_FILTER_MASK;
    sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
    sFilterConfig.FilterID1 = 0x123;
    sFilterConfig.FilterID2 = 0x7FF;
    HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig);

    HAL_FDCAN_Start(&hfdcan1);
    HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
}

测试方法

设计三个节点:

  • 节点 A:发送 ID 0x100,周期 T1(可变)
  • 节点 B:发送 ID 0x200,周期 T2(可变)
  • 节点 C:发送 ID 0x300,周期 T3(可变)

每个节点发送 64 字节数据帧(CAN FD,BRS 使能)。通过调节发送周期,改变总线负载率。负载率计算公式:

负载率 = (每帧位时间 × 帧率) / 总线速率

使用示波器捕获总线波形,测量从节点请求发送(通过 GPIO 触发)到帧起始(SOF)的时间差作为仲裁延迟。同时,统计每个节点发送成功帧数和重发次数,计算丢帧率。

实测结果与分析

负载率与仲裁延迟

| 负载率 | 平均仲裁延迟 (μs) | 最大仲裁延迟 (μs) | 丢帧率 | |--------|-------------------|-------------------|--------| | 30% | 12 | 25 | 0% | | 50% | 28 | 60 | 0% | | 70% | 55 | 120 | 0.1% | | 80% | 90 | 200 | 0.5% | | 90% | 150 | 350 | 2% |

从数据可见,负载率低于 70% 时,仲裁延迟较小且丢帧率几乎为零;超过 80% 后,延迟急剧增加,丢帧率上升。这是因为高负载下,多个节点同时发送概率增大,低优先级帧频繁重发,导致总线冲突加剧。

仲裁延迟分布

在 70% 负载下,测量 1000 帧的仲裁延迟分布如下:

  • 0-50μs:65%
  • 50-100μs:25%
  • 100-200μs:8%
  • 200μs:2%

大部分帧延迟在 100μs 内,但存在长尾,可能影响实时性要求高的应用。

极限负载分析

通过逐步增加帧率,发现当负载率超过 92% 时,总线进入饱和状态,节点 A(低 ID)几乎独占总线,节点 C 的发送成功率降至 70% 以下。此时,FDCAN 的自动重传机制导致总线持续被高优先级帧占用,低优先级帧饿死。

优化建议

  • 调整优先级分配:将实时性要求高的节点分配低 ID,避免低优先级帧长时间等待。
  • 使用 TDC(Transmitter Delay Compensation):在高速数据段(>5Mbps)启用 TDC,提高采样点精度,减少位错误。
  • 合理配置采样点:根据总线长度和节点数调整采样点,通常 75%-80% 较优。
  • 减少数据长度:在满足需求下,缩短数据字段,降低每帧位时间,提高有效吞吐。
  • 启用发送暂停(Transmit Pause):在低优先级节点上启用,避免连续重发导致总线风暴。

完整代码示例

以下为节点 A 的发送任务示例(使用 RTOS):

void vSenderTask(void *argument)
{
    FDCAN_TxHeaderTypeDef TxHeader;
    uint8_t TxData[64];
    uint32_t tick = HAL_GetTick();

    TxHeader.Identifier = 0x100;
    TxHeader.IdType = FDCAN_STANDARD_ID;
    TxHeader.TxFrameType = FDCAN_DATA_FRAME;
    TxHeader.DataLength = FDCAN_DLC_BYTES_64;
    TxHeader.FDFormat = FDCAN_FD_CAN;
    TxHeader.BitRateSwitch = FDCAN_BRS_ON;
    TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;

    while (1)
    {
        // 填充数据
        for (int i = 0; i < 64; i++) TxData[i] = (uint8_t)(tick + i);

        // 发送前触发 GPIO 高电平,用于测量仲裁延迟
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
        if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData) != HAL_OK)
        {
            Error_Handler();
        }
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);

        // 周期发送,例如 1ms
        osDelay(1);
    }
}

注意事项

  • 测量仲裁延迟时,需确保 GPIO 触发与发送请求同步,避免软件延迟影响结果。
  • 高负载测试时,注意观察总线错误计数器(ECR),若错误过多可能进入 Bus-Off 状态。
  • 实际应用中,建议预留 20% 的带宽余量,以应对突发负载。
  • FDCAN 的 FIFO 深度有限,若发送队列溢出,可启用中断或 DMA 提高吞吐。

总结

STM32H7 在 400MHz 下,FDCAN 总线在负载率低于 70% 时表现稳定,仲裁延迟可控;超过 80% 后性能急剧下降。通过合理配置优先级、采样点和启用 TDC,可有效提升总线利用率。对于高实时性应用,建议将负载率控制在 60% 以下,并采用冗余设计。本文实测数据为嵌入式开发者提供了可靠的参考。