# 引言 在工业控制、汽车电子和机器人领域,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 库): ```c 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): ```c 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% 以下,并采用冗余设计。本文实测数据为嵌入式开发者提供了可靠的参考。