第2章 STM32F407 DMA控制器架构:双AHB总线与数据流映射

DMA控制器,说白了就是CPU的「数据搬运工」。CPU告诉它「把这里的数据搬到那里」,它就能自己干完,CPU可以腾出手来做更重要的事。但很多初学者一上来就对着参考手册发懵——什么双AHB、数据流、通道映射,看得头大。

别急,这一章我带你把这些概念一个一个拆开。搞懂了架构,后面调优才有底气。

2.1 双AHB总线架构:为什么是两条路?

STM32F407的DMA控制器挂在两条AHB总线上:AHB1和AHB2。这不是设计者闲得慌,而是有讲究的。

我打个比方:一条路只能单向通行,两条路就能双向同时跑。DMA1和DMA2各占一条总线,它们可以同时进行数据传输,互不干扰。

关键点:双AHB总线意味着DMA1和DMA2可以并行工作。如果你的系统里同时有ADC采集和SPI通信,让它们分别走不同的DMA控制器,吞吐量直接翻倍。

我在一个数据采集项目里就吃过亏。一开始把所有外设都挂在DMA1上,结果ADC和USART抢带宽,采集到的数据偶尔丢包。后来把USART挪到DMA2,问题立刻解决。嗯,这就是双总线的价值。

2.2 DMA1与DMA2的区别:不只是编号不同

很多人以为DMA1和DMA2就是两个一样的复制品。其实不然。它们能访问的外设范围不一样。

特性 DMA1 DMA2
挂载总线 AHB1 AHB2
可访问外设 ADC1/2/3、SPI/I2S、USART、I2C、定时器等 DCMI(摄像头接口)、SDIO、SPI/I2S、USART、I2C、定时器等
特有外设 ADC1/2/3 DCMI、SDIO
数据流数量 8个 8个
通道数量/流 8个 8个

你看,DMA2能访问DCMI和SDIO,DMA1不行。反过来,ADC的三个模块只能走DMA1。所以设计时得先查清楚:你的外设挂在哪个DMA上?

我的习惯:先把所有要用DMA的外设列出来,对照参考手册的DMA请求映射表,看看它们能不能分到不同的DMA控制器上。能分开就尽量分开,这是最基础的性能优化。

2.3 8个数据流与每个流8个通道的映射关系

这是DMA架构里最容易搞混的部分。我尽量讲清楚。

每个DMA控制器有8个数据流(Stream 0~7)。每个数据流有8个通道(Channel 0~7)。但注意:一个数据流同一时间只能使用一个通道。

你想想看,这就像一条高速公路有8条车道(数据流),每条车道可以走8种不同的车(通道),但同一时间一条车道上只能跑一种车。

那通道是干什么用的?通道决定了这个数据流能响应哪个外设的请求。

举个例子:

  • 数据流0的通道0:对应ADC1
  • 数据流0的通道1:对应SPI1_RX
  • 数据流0的通道2:对应USART1_TX
  • ……以此类推

你配置DMA时,需要做两件事:

  1. 选数据流:决定用哪条「车道」
  2. 选通道:告诉这条车道要服务哪个外设

我曾经踩过的坑:两个外设共用一个数据流的不同通道,以为能同时工作。结果发现数据流同一时间只能处理一个通道的请求,另一个外设只能排队等。后来我把它们分到不同的数据流上,问题才解决。

2.4 数据流与通道映射表(以DMA1为例)

下面这张表是DMA1的部分映射关系。完整版在参考手册的Table 45里,我这里只挑几个常用的给你看。

数据流 通道0 通道1 通道2 通道3 通道4 通道5 通道6 通道7
Stream 0 ADC1 SPI1_RX USART1_TX I2C1_TX 定时器2_UP 定时器3_CH1 — —
Stream 1 ADC2 SPI1_TX USART1_RX I2C1_RX 定时器2_CH1 定时器3_CH2 — —
Stream 2 ADC3 SPI2_RX USART2_TX I2C2_TX 定时器2_CH2 定时器3_CH3 — —
Stream 3 — SPI2_TX USART2_RX I2C2_RX 定时器2_CH3 定时器3_CH4 — —

看到没?同一个外设(比如USART1)的TX和RX,分别对应不同的数据流和通道。这样设计是为了让收发可以同时进行,互不阻塞。

2.5 数据流优先级与仲裁

当多个数据流同时请求传输时,谁先谁后?DMA控制器有一套优先级仲裁机制。

优先级分四级:

  • 非常高(Very High)
  • 高(High)
  • 中(Medium)
  • 低(Low)

每个数据流都可以独立设置优先级。如果两个数据流优先级相同,那就看编号——编号小的优先(Stream 0 > Stream 1)。

实战建议:把对实时性要求高的外设(比如音频I2S、摄像头DCMI)设为高优先级。把后台数据记录(比如SDIO写卡)设为低优先级。别把所有流都设成「非常高」,那样优先级等于没设。

2.6 架构总览图

下面这张SVG图把整个DMA控制器的架构串起来了。你对照着看,思路会更清晰。

STM32F407 DMA控制器架构总览 Cortex-M4 CPU AHB1 总线 DMA1 控制器 8个数据流 × 8个通道 DMA2 控制器 8个数据流 × 8个通道 DMA1 可访问外设 ADC1/2/3 SPI1/2 · USART1/2 · I2C1/2 DMA2 可访问外设 DCMI · SDIO SPI3/4 · USART3/4/5 · I2C3 DMA1 & DMA2 共享外设 定时器1~14 · SPI1/2 · USART1/2 I2C1/2 · DAC1/2 数据流与通道映射规则 • 每个数据流同一时间只能使用1个通道 • 通道编号决定该数据流响应哪个外设的DMA请求 • 优先级:软件设置(非常高/高/中/低)→ 同级时数据流编号小的优先

2.7 配置DMA的步骤总结

搞清楚了架构,配置DMA其实就四步:

  1. 查映射表:确定你的外设对应哪个DMA控制器、哪个数据流、哪个通道
  2. 开时钟:使能对应DMA控制器的时钟(RCC_AHB1ENR)
  3. 配参数:设置数据流的方向、地址、数据宽度、优先级、FIFO等
  4. 使能:先配好外设,再使能DMA数据流

一个小技巧:我习惯在代码里用宏定义把数据流和通道的映射关系写清楚。比如 #define DMA_STREAM_ADC1 DMA1_Stream0,这样半年后回来看代码,不用再翻手册。