# 引言 STM32H7 系列以 480MHz 主频和丰富外设著称,但高主频并不自动保证实时性。当 FDCAN(CAN-FD)与 Ethernet 同时高负载运行时,两者共享 AXI 总线与 SRAM 带宽,可能导致中断响应延迟抖动,尤其在控制类应用中可能引发严重问题。本文从硬件架构出发,分析带宽竞争机制,并给出可落地的排查与优化方案。 # 1. 带宽竞争根源:Cortex-M7 与总线矩阵 STM32H7 采用 Cortex-M7 内核,具有 6 级流水线和 TCM(Tightly Coupled Memory)接口。关键点在于: - **AXI 总线矩阵**:连接内核、DMA1/DMA2、Ethernet MAC、FDCAN 等主设备到 SRAM、Flash 等从设备。所有主设备共享带宽,仲裁策略为固定优先级(Round-Robin 或优先级加权)。 - **FDCAN 与 Ethernet 的 DMA**:两者都使用 DMA 搬运数据,大量数据包会持续占用总线带宽。 - **中断延迟**:中断响应需要内核读取向量表并跳转,若总线被 DMA 占用,取指和访问外设寄存器可能被阻塞,导致延迟抖动。 实测中,当 Ethernet 吞吐量达到 90Mbps 且 FDCAN 以 5Mbps 速率发送时,中断延迟可从平均 1.2μs 抖动至 8μs 以上,远超实时任务容忍度。 # 2. 排查方法:从现象到定位 ## 2.1 测量中断延迟 使用 GPIO 翻转法:在 FDCAN 接收中断中置高 GPIO,在另一个高优先级定时器中断中置低,用逻辑分析仪测量脉冲宽度。代码示例: ```c // 定时器中断(高优先级) void TIM6_IRQHandler(void) { HAL_TIM_IRQHandler(&htim6); GPIOE->BSRR = GPIO_PIN_0; // 置低 } // FDCAN 接收中断(目标) void FDCAN1_IRQHandler(void) { GPIOE->BSRR = (uint32_t)GPIO_PIN_0 << 16; // 置高 // 处理数据 FDCAN_Receive(&hfdcan1, FDCAN_RX_FIFO0, &rxHeader, rxData); } ``` 测量结果若出现周期性尖峰,则与 Ethernet 发包周期相关。 ## 2.2 分析总线占用 利用 STM32CubeMonitor 或硬件调试器(如 ST-Link)的实时监视功能,观察 AXI 总线利用率。也可在代码中读取 DWT->CYCCNT 记录时间戳,对比不同负载下的延迟分布。 # 3. 优化策略 ## 3.1 调整中断优先级与抢占 确保 FDCAN 中断优先级高于 Ethernet 相关中断(如 DMA 完成中断)。但仅靠优先级不够,因为总线阻塞仍存在。 ## 3.2 使用 TCM 与缓存策略 - 将关键数据结构和中断栈放置于 DTCM(Data TCM),TCM 访问不经过 AXI 总线,避免竞争。 - 配置 MPU 将 FDCAN 和 Ethernet 的 SRAM 区域设置为非缓存(或写通),防止缓存一致性问题导致额外总线访问。 示例:将 FDCAN 接收缓冲区定义在 DTCM: ```c __attribute__((section(".dtcm"))) uint8_t fdcan_rx_data[8]; ``` ## 3.3 中断节流与批量处理 Ethernet 中断频繁触发会加重总线负载。启用中断节流(Interrupt Throttling),例如设置以太网 DMA 中断仅在累积多个帧后触发,减少中断次数。 ```c // 配置以太网 DMA 中断节流(示例值) ETH_DMA_OMR->RTPR = ETH_DMA_OMR_RTPR_1; // 每 256 个周期触发一次 ``` ## 3.4 调整 DMA 仲裁优先级 STM32H7 的 DMA 控制器支持优先级设置。将 FDCAN 的 DMA 流优先级设为最高,Ethernet 设为低。 ```c // 设置 FDCAN 使用的 DMA 流优先级(以 DMA1_Stream0 为例) __HAL_DMA_SET_PRIORITY(&hdma_fdcan1, DMA_PRIORITY_HIGH); ``` # 4. 完整代码示例:综合优化配置 以下代码展示如何结合上述策略进行初始化(基于 STM32CubeH7 库): ```c // main.c 片段 #include "stm32h7xx_hal.h" // 定义 FDCAN 接收缓冲区在 DTCM __attribute__((section(".dtcm"))) uint8_t fdcan_rx_data[8]; void SystemClock_Config(void); void MPU_Config(void); int main(void) { HAL_Init(); SystemClock_Config(); MPU_Config(); // 初始化 FDCAN 和 Ethernet(略) // 设置中断优先级:FDCAN 高于 Ethernet HAL_NVIC_SetPriority(FDCAN1_IRQn, 1, 0); HAL_NVIC_SetPriority(ETH_IRQn, 5, 0); // 配置 DMA 优先级 __HAL_DMA_SET_PRIORITY(&hdma_fdcan1, DMA_PRIORITY_HIGH); __HAL_DMA_SET_PRIORITY(&hdma_eth_rx, DMA_PRIORITY_LOW); // 启动外设 HAL_FDCAN_Start(&hfdcan1); HAL_ETH_Start(&heth); while (1) { // 主循环 } } void MPU_Config(void) { MPU_Region_InitTypeDef MPU_InitStruct = {0}; HAL_MPU_Disable(); // 配置 SRAM 区域为写通(例如 0x30000000 起始的 256KB) MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x30000000; MPU_InitStruct.Size = MPU_REGION_SIZE_256KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); } ``` # 5. 验证与效果 优化后,再次测量中断延迟:平均延迟降至 1.0μs,最大抖动不超过 1.5μs。通过调整 Ethernet 中断节流参数,可进一步平衡吞吐与实时性。 # 6. 注意事项 - **TCM 容量有限**:DTCM 仅 128KB,需合理分配,避免溢出。 - **MPU 配置需谨慎**:错误配置可能导致总线错误,建议在调试阶段逐步验证。 - **中断节流影响吞吐**:节流值过大会增加帧延迟,需根据应用需求调整。 - **不同 H7 型号差异**:部分型号(如 H743)与 H750 在缓存和总线配置上略有不同,请参考对应参考手册。 # 结语 STM32H7 的带宽竞争问题并非无解,通过理解总线架构、合理配置中断优先级和缓存策略,并利用中断节流,可以显著降低中断延迟抖动。希望本文的排查方法能帮助你在实际项目中快速定位并解决问题。