# 引言 在工业控制、车载网关等场景中,STM32H7 常需同时处理 FDCAN(灵活数据速率 CAN)和以太网通信。当主频提升至 400MHz 时,虽然 CPU 算力充足,但外设并发访问共享资源(如 AHB 总线、SRAM、DMA 控制器)会引发时序冲突,导致 FDCAN 报文丢失或以太网帧延迟超时。本文从硬件架构出发,剖析冲突根源,并提供可落地的优化方案。 # 1. 冲突根源分析 ## 1.1 总线矩阵竞争 STM32H7 采用多主多从总线矩阵,CPU、DMA1/2、以太网 MAC、FDCAN 等主设备可并行访问不同从设备,但访问同一 SRAM 或 AHB 外设时需仲裁。FDCAN 和以太网 DMA 均可能同时访问 AHB SRAM(如 DTCM 或 AXI SRAM),导致等待周期增加。 - **关键路径**:以太网 DMA 突发传输(Burst)长度可达 8 字,占用总线时间较长;FDCAN 每次传输仅 1-2 字,但频率高,两者竞争时 FDCAN 易被饿死。 - **实测现象**:当以太网流量达 90% 带宽时,FDCAN 发送延迟从 20μs 增至 200μs,甚至触发 TEC(发送错误计数)增加。 ## 1.2 中断优先级与嵌套 FDCAN 和以太网中断默认优先级相同,若同时触发,CPU 需按硬件编号顺序响应,造成不确定延迟。此外,若中断处理函数中执行耗时操作(如内存拷贝),会阻塞另一外设的中断服务。 ## 1.3 缓存一致性问题 STM32H7 的 CPU 有 L1-Cache,而 DMA 不经过 Cache。当 CPU 修改发送缓冲区后,若未执行 Clean 操作,DMA 可能读到旧数据;反之,DMA 写入接收缓冲区后,CPU 读取时可能命中脏 Cache。 # 2. 优化策略 ## 2.1 中断优先级分组与抢占 将 FDCAN 中断设为最高优先级(抢占优先级 0),以太网设为次高(抢占优先级 1),并确保中断处理函数短小精悍。使用 NVIC 优先级分组 3(4 位抢占,0 位子优先级)。 ```c // 配置 NVIC 优先级分组 HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_3); // FDCAN1 中断:抢占优先级 0,子优先级 0 HAL_NVIC_SetPriority(FDCAN1_IT0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(FDCAN1_IT0_IRQn); // 以太网中断:抢占优先级 1,子优先级 0 HAL_NVIC_SetPriority(ETH_IRQn, 1, 0); HAL_NVIC_EnableIRQ(ETH_IRQn); ``` ## 2.2 DMA 通道分离与双缓冲 FDCAN 使用 DMA1 通道,以太网使用 DMA2 通道,避免同一 DMA 控制器的内部仲裁。同时,为 FDCAN 接收配置双缓冲区(Ping-Pong),防止 DMA 写入时 CPU 读取冲突。 ```c // FDCAN 接收双缓冲配置(基于 HAL 库) FDCAN_RxHeaderTypeDef RxHeader; uint8_t RxData[2][8]; // 双缓冲 uint8_t ActiveBuf = 0; void FDCAN_RxCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo) { if (HAL_FDCAN_GetRxMessage(hfdcan, RxFifo, &RxHeader, RxData[ActiveBuf]) == HAL_OK) { // 处理数据 ProcessRxData(RxData[ActiveBuf]); ActiveBuf ^= 1; // 切换缓冲区 } } ``` ## 2.3 内存屏障与 Cache 维护 在 DMA 传输前后使用 `__DMB()` 和 `SCB_CleanDCache_by_Addr()` 确保数据一致性。 ```c // 发送前:确保 CPU 写入可见 SCB_CleanDCache_by_Addr((uint32_t*)TxData, sizeof(TxData)); __DMB(); HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData); // 接收后:使 Cache 失效,读取最新 DMA 数据 SCB_InvalidateDCache_by_Addr((uint32_t*)RxData[ActiveBuf], sizeof(RxData[ActiveBuf])); __DMB(); ``` ## 2.4 时序调度:时间片轮转 若应用允许,可采用时间片轮转,将 FDCAN 发送集中在以太网空闲窗口。例如,使用定时器中断触发 FDCAN 批量发送,避免与以太网 DMA 突发重叠。 ```c // 定时器回调:每 1ms 触发一次 FDCAN 发送 void TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM6) { // 检查以太网 DMA 是否忙碌 if (HAL_ETH_GetDMAFlagStatus(&heth, ETH_DMA_FLAG_BUSY) == RESET) { SendPendingFDCANMessages(); } } } ``` # 3. 完整代码示例 以下为综合配置片段,展示 FDCAN 和以太网初始化及中断处理。 ```c // 初始化 FDCAN1 FDCAN_HandleTypeDef hfdcan1; 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 = 2; hfdcan1.Init.NominalSyncJumpWidth = 1; hfdcan1.Init.NominalTimeSeg1 = 13; hfdcan1.Init.NominalTimeSeg2 = 2; hfdcan1.Init.DataPrescaler = 1; hfdcan1.Init.DataSyncJumpWidth = 1; hfdcan1.Init.DataTimeSeg1 = 13; hfdcan1.Init.DataTimeSeg2 = 2; hfdcan1.Init.StdFiltersNbr = 1; hfdcan1.Init.ExtFiltersNbr = 0; hfdcan1.Init.RxFifo0ElmtsNbr = 8; hfdcan1.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_MAX; hfdcan1.Init.RxFifo1ElmtsNbr = 0; hfdcan1.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_MAX; hfdcan1.Init.TxEventsNbr = 0; hfdcan1.Init.TxBuffersNbr = 4; hfdcan1.Init.TxEventFifoElmtsNbr = 0; HAL_FDCAN_Init(&hfdcan1); HAL_FDCAN_Start(&hfdcan1); HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0); } // 初始化以太网(使用 DMA) ETH_HandleTypeDef heth; void ETH_Init(void) { heth.Instance = ETH; heth.Init.MACAddr[0] = 0x00; heth.Init.MACAddr[1] = 0x11; heth.Init.MACAddr[2] = 0x22; heth.Init.MACAddr[3] = 0x33; heth.Init.MACAddr[4] = 0x44; heth.Init.MACAddr[5] = 0x55; heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; heth.Init.TxDesc = TxDesc; heth.Init.RxDesc = RxDesc; heth.Init.RxBuffLen = 1524; HAL_ETH_Init(&heth); HAL_ETH_Start(&heth); } // 中断处理:FDCAN 优先 void FDCAN1_IT0_IRQHandler(void) { HAL_FDCAN_IRQHandler(&hfdcan1); } void ETH_IRQHandler(void) { HAL_ETH_IRQHandler(&heth); } ``` # 4. 注意事项 - **时钟配置**:确保 FDCAN 内核时钟和以太网 MAC 时钟来自不同 PLL,避免频率耦合。 - **DMA 描述符**:以太网 DMA 描述符应放在 AXI SRAM 且 32 字节对齐,FDCAN 缓冲区放在 DTCM 以减少总线冲突。 - **调试技巧**:使用逻辑分析仪抓取 FDCAN TX 和以太网 TX 信号,观察冲突窗口;或利用 STM32 的调试计数器(DWT)测量中断延迟。 - **实时性验证**:在最大负载下运行 24 小时,监控 FDCAN 错误计数和以太网丢包率,确保优化有效。 # 结语 通过中断优先级调整、DMA 通道分离、缓存维护和时序调度,STM32H7 在 400MHz 下可稳定支持 FDCAN 与以太网并发传输。实际项目中需根据具体流量模型微调参数,建议结合 RTOS 的优先级翻转机制进一步优化。希望本文能为你解决类似问题提供参考。