# 引言 STM32H7 系列(如 STM32H743)凭借 480MHz 的 Cortex-M7 内核和双总线架构,成为高性能嵌入式应用的首选。然而,当 FDCAN(Flexible Data-rate CAN)和以太网(Ethernet)同时高负载传输时,DMA 带宽争用可能成为性能瓶颈。本文将从硬件架构出发,分析争用机制,并给出实用的优化策略。 # DMA 带宽争用的根源 STM32H7 采用多层 AXI 总线矩阵,外设通过 DMA 控制器访问 SRAM。FDCAN 和以太网 MAC 分别挂载在 DMA1/DMA2 或 MDMA 上,但共享同一内存总线。当两者同时发起大量传输时,总线仲裁器需分配带宽,导致以下问题: - **延迟增加**:低优先级 DMA 请求被高优先级阻塞,FDCAN 报文可能错过接收截止时间。 - **吞吐量下降**:以太网大帧传输占用连续带宽,挤压 FDCAN 的突发传输。 - **缓存一致性**:CPU 与 DMA 并发访问同一缓冲区时,可能产生数据不一致。 # 优化策略 ## 1. 合理配置 DMA 优先级 STM32H7 的 DMA 控制器支持 4 级优先级(低、中、高、最高)。根据实时性需求,为 FDCAN 分配更高优先级,因为 CAN 报文通常有严格时序要求。以太网数据可容忍稍大延迟。 ```c // 配置 FDCAN1 的 DMA 接收通道为最高优先级 hdma_fdcan1_rx.Init.Priority = DMA_PRIORITY_HIGH; HAL_DMA_Init(&hdma_fdcan1_rx); // 配置以太网 DMA 接收通道为中优先级 hdma_eth_rx.Init.Priority = DMA_PRIORITY_MEDIUM; HAL_DMA_Init(&hdma_eth_rx); ``` ## 2. 使用 MDMA 分担负载 MDMA(Master DMA)可在外设与内存间直接传输,绕过传统 DMA 通道,减少争用。将 FDCAN 数据通过 MDMA 搬运至专用 SRAM,以太网使用普通 DMA,可有效隔离带宽。 ```c // 初始化 MDMA 用于 FDCAN 数据搬运 MDMA_HandleTypeDef hmdma_fdcan; hmdma_fdcan.Init.Request = MDMA_REQUEST_DMA1_Stream0; // 示例,实际按需配置 hmdma_fdcan.Init.TransferTriggerMode = MDMA_BUFFER_TRANSFER; HAL_MDMA_Init(&hmdma_fdcan); ``` ## 3. 优化缓冲区布局 将 FDCAN 和以太网缓冲区分配到不同的 SRAM 区域(如 SRAM1 和 SRAM2),利用 AXI 总线矩阵的并行访问能力,减少冲突。 ```c // 使用 __attribute__ 指定内存段 __attribute__((section(".fdcan_buf"))) uint8_t fdcan_rx_buf[256]; __attribute__((section(".eth_buf"))) uint8_t eth_rx_buf[2048]; ``` 在链接脚本中定义对应段: ```c .fdcan_buf (NOLOAD) : { *(.fdcan_buf) } > SRAM1 .eth_buf (NOLOAD) : { *(.eth_buf) } > SRAM2 ``` ## 4. 调整 DMA 突发传输大小 增大 DMA 突发长度(Burst)可提高传输效率,但会占用更长的总线时间。需权衡:FDCAN 使用 4 字突发,以太网使用 8 字突发,以减少频繁仲裁。 ```c // FDCAN DMA 配置 hdma_fdcan_rx.Init.BurstLength = 4; // 以太网 DMA 配置 hdma_eth_rx.Init.BurstLength = 8; ``` ## 5. 启用缓存与写回策略 利用 Cortex-M7 的 D-Cache,将 DMA 缓冲区配置为 write-back,减少 CPU 访问延迟。但需注意维护缓存一致性,使用 `SCB_CleanDCache` 和 `SCB_InvalidateDCache`。 ```c // 在 DMA 接收前清理缓存 SCB_CleanDCache_by_Addr((uint32_t*)fdcan_rx_buf, sizeof(fdcan_rx_buf)); // 在 DMA 接收后使缓存失效 SCB_InvalidateDCache_by_Addr((uint32_t*)fdcan_rx_buf, sizeof(fdcan_rx_buf)); ``` # 完整代码示例 以下是一个简化示例,展示如何配置 FDCAN 和以太网并发传输,并应用上述优化。 ```c #include "stm32h7xx_hal.h" // 缓冲区定义 __attribute__((section(".fdcan_buf"))) uint8_t fdcan_rx_data[64]; __attribute__((section(".eth_buf"))) uint8_t eth_rx_data[1518]; // DMA 句柄 DMA_HandleTypeDef hdma_fdcan_rx; DMA_HandleTypeDef hdma_eth_rx; void SystemClock_Config(void); static void MX_FDCAN1_Init(void); static void MX_ETH_Init(void); static void MX_DMA_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_DMA_Init(); MX_FDCAN1_Init(); MX_ETH_Init(); // 启动 FDCAN 接收(使用 DMA) HAL_FDCAN_Start(&hfdcan1); HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0); HAL_FDCAN_Receive_DMA(&hfdcan1, FDCAN_RX_FIFO0, fdcan_rx_data, 8); // 启动以太网接收(使用 DMA) HAL_ETH_Start(&heth); HAL_ETH_Receive_DMA(&heth, eth_rx_data, 1518); while (1) { // 主循环处理数据 } } // DMA 初始化,设置优先级和突发长度 static void MX_DMA_Init(void) { __HAL_RCC_DMA1_CLK_ENABLE(); __HAL_RCC_DMA2_CLK_ENABLE(); // FDCAN RX DMA 配置 hdma_fdcan_rx.Instance = DMA1_Stream0; hdma_fdcan_rx.Init.Request = DMA_REQUEST_FDCAN1_RX; hdma_fdcan_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_fdcan_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_fdcan_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_fdcan_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_fdcan_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_fdcan_rx.Init.Mode = DMA_CIRCULAR; hdma_fdcan_rx.Init.Priority = DMA_PRIORITY_HIGH; hdma_fdcan_rx.Init.BurstLength = 4; HAL_DMA_Init(&hdma_fdcan_rx); // 以太网 RX DMA 配置 hdma_eth_rx.Instance = DMA2_Stream0; hdma_eth_rx.Init.Request = DMA_REQUEST_ETH_RX; hdma_eth_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_eth_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_eth_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_eth_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdma_eth_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; hdma_eth_rx.Init.Mode = DMA_CIRCULAR; hdma_eth_rx.Init.Priority = DMA_PRIORITY_MEDIUM; hdma_eth_rx.Init.BurstLength = 8; HAL_DMA_Init(&hdma_eth_rx); } ``` # 注意事项 - **优先级设置需谨慎**:过高优先级可能导致低优先级外设饿死,需根据实际负载测试。 - **MDMA 配置复杂**:MDMA 的请求映射因芯片型号而异,务必参考参考手册。 - **缓存一致性**:使用 D-Cache 时,必须确保 DMA 操作前后缓存正确清理/失效,否则数据错误。 - **内存段分配**:链接脚本需正确配置,否则编译报错或运行异常。 - **性能测试**:优化后应使用逻辑分析仪或性能计数器验证延迟和吞吐量,避免盲目调整。 # 结语 通过合理配置 DMA 优先级、使用 MDMA 分流、优化缓冲区布局和调整突发大小,可显著缓解 STM32H7 上 FDCAN 与以太网并发传输的带宽争用。实际项目中需结合具体负载反复调优,方能达到最佳性能。希望本文能为你提供实用的参考。