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压根不支持这个模式。
4.2 传输方向:三种模式
DMA的传输方向其实就三种,但很多人搞混。我画个图帮你理解。
说白了,这三种方向对应了三种典型场景:
- 内存到外设:你准备好一个数据缓冲区,DMA自动把数据喂给USART发送。我习惯用这种方式做串口打印,CPU几乎零负担。
- 外设到内存:外设(比如ADC)产生数据,DMA自动存到内存数组里。你想想看,如果不用DMA,你得在中断里一个个读数据,多浪费CPU。
- 内存到内存:把一块内存的数据拷贝到另一块。比如图像处理中的帧缓冲切换。注意:只有DMA2支持这个模式。
4.3 传输模式:普通模式 vs 循环模式
DMA传输模式就两种:普通模式和循环模式。但选错了,你的系统可能跑飞。
| 特性 | 普通模式(Normal) | 循环模式(Circular) |
|---|---|---|
| 传输次数 | 一次传输完成后停止 | 传输完成后自动重新开始 |
| 适用场景 | 单次数据发送/接收 | 连续数据流(如ADC连续采样) |
| 中断触发 | 传输完成中断一次 | 每次传输完成都触发中断 |
| 配置复杂度 | 简单 | 需要处理缓冲区半满/全满中断 |
普通模式很好理解:你设置好传输数量(比如100个字节),DMA搬完就停。下次要传输,得重新使能DMA。
循环模式就有点意思了。它会在传输完成后自动把地址指针重置到起始位置,然后继续传输。我经常用循环模式做ADC连续采样——DMA不断把数据填到缓冲区,CPU只需要在缓冲区半满或全满时处理数据。
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个数据 | 内存到内存拷贝 |
4.5 综合配置建议
好了,讲了这么多,我总结一个实际项目中的配置流程:
- 选DMA控制器:高速外设用DMA2,低速用DMA1。
- 选通道:查手册确认外设对应的DMA通道。
- 定方向:根据数据流向选择内存→外设、外设→内存或内存→内存。
- 选模式:连续数据流用循环模式,单次传输用普通模式。
- 配FIFO:高速外设开启FIFO,阈值设为半满或全满。
- 设突发:从4拍开始测试,找到最优值。
你想想看,如果这些配置都对了,DMA基本不会出问题。但如果你跳过了某一步,比如忘了配FIFO阈值,那调试起来就头疼了。