4. DMA基础概念:DMA控制器架构与传输模式

各位同学,今天我们来聊聊DMA。说实话,很多初学者觉得DMA就是个“后台搬运工”,配置几个寄存器就能跑。但如果你真这么想,后面调试的时候大概率会踩坑。我个人习惯是把DMA的架构和传输模式先理清楚,再动手写代码。

4.1 DMA控制器架构:DMA1与DMA2

STM32内部通常有两个DMA控制器,分别叫DMA1和DMA2。它们不是简单的“主备”关系,而是各有分工。

特性 DMA1 DMA2
总线挂载 AHB总线(低速外设总线) AHB总线(高速外设总线)
通道数量 7个通道 5个通道
支持外设 USART、SPI、I2C、TIM等 USART、SPI、I2C、TIM、ADC、DAC等
内存到内存 不支持 支持
优先级 低(默认) 高(默认)

你看这个表格,DMA2比DMA1多了一个“内存到内存”的能力。为什么?因为DMA2挂载在更高速的总线上,适合做大数据量的内存拷贝。我在项目中遇到过,有人用DMA1做内存到内存传输,结果死活不工作——查了半天手册才发现,DMA1压根不支持这个模式。

避坑指南: 我曾经在F103项目里,把ADC采集的数据用DMA1传输,结果发现采样率一高就丢数据。后来换成DMA2的通道1,问题就解决了。记住:高速外设(ADC、DAC)优先用DMA2,低速外设(USART、I2C)用DMA1就够了。

4.2 传输方向:三种模式

DMA的传输方向其实就三种,但很多人搞混。我画个图帮你理解。

DMA三种传输方向示意图 内存(SRAM) 外设(USART/SPI) 内存(SRAM) ① 内存→外设 ② 外设→内存 ③ 内存→内存(仅DMA2) 说明: • 内存→外设:常用于发送数据(如USART发送缓冲区) • 外设→内存:常用于接收数据(如ADC采集结果存储) • 内存→内存:用于数据拷贝、缓冲区搬移(仅DMA2支持)

说白了,这三种方向对应了三种典型场景:

  • 内存到外设:你准备好一个数据缓冲区,DMA自动把数据喂给USART发送。我习惯用这种方式做串口打印,CPU几乎零负担。
  • 外设到内存:外设(比如ADC)产生数据,DMA自动存到内存数组里。你想想看,如果不用DMA,你得在中断里一个个读数据,多浪费CPU。
  • 内存到内存:把一块内存的数据拷贝到另一块。比如图像处理中的帧缓冲切换。注意:只有DMA2支持这个模式。
个人经验: 我建议你在配置传输方向时,先确认外设的“请求信号”是否与DMA通道匹配。比如USART1的发送请求对应DMA1通道4,接收对应通道5。配错了,DMA根本不会启动。

4.3 传输模式:普通模式 vs 循环模式

DMA传输模式就两种:普通模式和循环模式。但选错了,你的系统可能跑飞。

特性 普通模式(Normal) 循环模式(Circular)
传输次数 一次传输完成后停止 传输完成后自动重新开始
适用场景 单次数据发送/接收 连续数据流(如ADC连续采样)
中断触发 传输完成中断一次 每次传输完成都触发中断
配置复杂度 简单 需要处理缓冲区半满/全满中断

普通模式很好理解:你设置好传输数量(比如100个字节),DMA搬完就停。下次要传输,得重新使能DMA。

循环模式就有点意思了。它会在传输完成后自动把地址指针重置到起始位置,然后继续传输。我经常用循环模式做ADC连续采样——DMA不断把数据填到缓冲区,CPU只需要在缓冲区半满或全满时处理数据。

关键点: 循环模式下,缓冲区大小必须是传输数据量的整数倍。比如你每次传输4个字节,缓冲区大小最好是4的倍数。否则地址会错位,数据就乱了。

4.4 FIFO与突发传输

这部分是DMA性能调优的核心。很多工程师忽略了FIFO和突发传输的配置,导致DMA效率低下。

4.4.1 FIFO(先进先出缓冲区)

DMA内部有一个4字深的FIFO(具体深度取决于芯片型号)。它的作用是什么?说白了就是“缓冲”。

  • 直接模式(Direct Mode):数据不经过FIFO,直接从外设搬到内存。延迟低,但容易丢数据。
  • FIFO模式:数据先存到FIFO,攒够一定数量再一次性搬走。适合高速外设。

我记得在做一个高速SPI项目时,外设时钟跑到18MHz,直接模式总是丢数据。后来改成FIFO模式,把阈值设为半满(2个字),问题就解决了。

4.4.2 突发传输(Burst Transfer)

突发传输允许DMA在一次总线请求中连续传输多个数据。比如“4拍突发”就是一次请求传4个数据。

突发大小 说明 适用场景
单次(Single) 每次请求传1个数据 低速外设、低功耗场景
4拍(INCR4) 每次请求传4个数据 中等速度外设(如SPI)
8拍(INCR8) 每次请求传8个数据 高速外设(如ADC、DAC)
16拍(INCR16) 每次请求传16个数据 内存到内存拷贝
注意: 突发传输不是越大越好。我曾经在F4上配置16拍突发,结果外设跟不上节奏,导致数据溢出。建议从4拍开始测试,逐步增加,直到出现丢数据为止,再回退一档。

4.5 综合配置建议

好了,讲了这么多,我总结一个实际项目中的配置流程:

  1. 选DMA控制器:高速外设用DMA2,低速用DMA1。
  2. 选通道:查手册确认外设对应的DMA通道。
  3. 定方向:根据数据流向选择内存→外设、外设→内存或内存→内存。
  4. 选模式:连续数据流用循环模式,单次传输用普通模式。
  5. 配FIFO:高速外设开启FIFO,阈值设为半满或全满。
  6. 设突发:从4拍开始测试,找到最优值。

你想想看,如果这些配置都对了,DMA基本不会出问题。但如果你跳过了某一步,比如忘了配FIFO阈值,那调试起来就头疼了。

我的习惯: 每次配置DMA后,我都会先跑一个简单的回环测试——把数据从内存发到外设,再收回来对比。这样能快速验证配置是否正确。别等到整个系统跑起来才发现DMA没工作,那排查成本就高了。