DMA控制器基础:工作原理、传输类型与传输模式
DMA,全称Direct Memory Access,直接存储器访问。说白了,就是让外设和内存之间能直接“对话”,不用CPU在旁边一句句传话。我刚开始做嵌入式时,总觉得DMA是个黑盒子,后来亲手调过几块板子,才真正摸清了它的脾气。
DMA工作原理:谁在干活?
先看一张图,把DMA的工作流程理清楚。
流程其实就四步:
- CPU配置DMA:告诉DMA控制器,数据从哪来、到哪去、传多少。
- 外设发起请求:比如UART收到一个字节,就拉一下DMA请求线。
- DMA接管总线:DMA控制器拿到总线控制权,开始搬运数据。
- 传输完成通知:搬完了,DMA发个中断给CPU,说“活干完了”。
三种传输类型
DMA能搬的数据类型,按数据流向分三种。每种我在项目里都踩过坑,一个一个说。
| 传输类型 | 数据流向 | 典型场景 | 我踩过的坑 |
|---|---|---|---|
| 内存到内存 | 内存A → 内存B | 数据拷贝、图像处理 | 源和目的地址没对齐,性能暴跌 |
| 外设到内存 | 外设寄存器 → 内存 | UART接收、ADC采样 | 外设数据宽度和内存宽度不匹配 |
| 内存到外设 | 内存 → 外设寄存器 | DAC输出、SPI发送 | 外设FIFO满了,DMA还在发 |
内存到内存(M2M)
这个最简单,就是从一个内存地址拷到另一个。我记得有一次做图像传感器驱动,需要把RAW数据从临时缓冲区搬到处理缓冲区。用CPU memcpy?太慢了。换成DMA M2M传输,CPU直接解放出来做图像算法。
外设到内存(P2M)
这是最常用的类型。外设产生数据,DMA自动搬走,CPU完全不用管。比如UART接收,每来一个字节,DMA就把它存到内存缓冲区里。
这里有个关键点:外设的数据宽度。比如UART是8位,但你的内存缓冲区是32位对齐的。DMA怎么处理?
- 可以设置DMA的源数据宽度为8位,目的数据宽度为32位。
- DMA会自动做数据拼接,4个8位拼成一个32位。
- 但要注意字节序!大端小端搞反了,数据就全乱了。
内存到外设(M2P)
这个和外设到内存正好相反。CPU把数据准备好,DMA负责往外设送。比如SPI发送一个数据包,DMA从内存里取数据,一个一个喂给SPI的发送寄存器。
嗯,这里要注意外设的FIFO深度。如果外设FIFO只有4个字节,你DMA一次传100个字节,外设FIFO满了之后,DMA还在发,数据就丢了。解决办法是开启外设的DMA请求流控,或者用DMA的硬件握手信号。
三种传输模式
传输模式决定了DMA怎么搬、搬完一次之后怎么办。三种模式各有各的用处。
| 传输模式 | 行为描述 | 适用场景 | 配置要点 |
|---|---|---|---|
| 普通模式 | 传完指定长度就停,需要CPU重新触发 | 一次性数据传输 | 每次传输完都要重新使能DMA |
| 循环模式 | 传完自动从头开始,无限循环 | 连续数据流(音频、视频) | 注意缓冲区覆盖问题 |
| 双缓冲模式 | 两个缓冲区交替使用,一个在传,一个给CPU用 | 高速数据采集、实时处理 | 两个缓冲区要连续且对齐 |
普通模式
最基础的模式。DMA配置好源、目的、长度,启动,传完,停。下次再传,CPU得重新配置一遍。
我刚开始做驱动时,就用的普通模式。每次UART收到一包数据,DMA传完,中断里重新配置DMA。后来发现,如果数据包来得太快,中断还没处理完,下一包就来了,DMA还没准备好,数据就丢了。
循环模式
这个模式很巧妙。DMA传完指定长度后,自动把地址指针重置到起始地址,继续传。无限循环,直到你手动停止。
你想想看,这特别适合音频播放。比如你要播放一段16位、44.1kHz的音频,DMA循环模式从缓冲区取数据送给DAC,CPU只需要往缓冲区里填数据就行。DMA自己在那循环,CPU完全不用管。
但有个坑:缓冲区覆盖。如果CPU填数据的速度赶不上DMA读数据的速度,DMA就会读到脏数据。解决办法是用双缓冲模式。
双缓冲模式
这个模式是我最喜欢的。它有两个缓冲区,DMA正在用A缓冲区往外传数据,CPU往B缓冲区里填数据。等A传完了,DMA自动切到B,CPU切到A。两个缓冲区乒乓操作,互不干扰。
我曾经在一个高速数据采集项目里用过双缓冲。ADC以10Msps采样,每采样一个16位数据,DMA就搬到一个缓冲区。一个缓冲区满了,DMA切到另一个,同时CPU处理已满的缓冲区。这样数据流完全不断,CPU也有充足时间处理数据。
小结一下
DMA的核心价值,就是把CPU从数据搬运的苦活里解放出来。三种传输类型对应不同的数据流向,三种传输模式对应不同的工作节奏。选对了,系统性能能翻倍;选错了,调试起来能让你怀疑人生。
我个人建议,新手先从外设到内存的普通模式开始练手,比如用DMA接收UART数据。跑通了,再尝试循环模式和双缓冲模式。一口吃不成胖子,DMA这东西,得慢慢摸透它的脾气。