1. 什么是DMA?说白了就是让数据自己跑
大家好,我是你们的嵌入式系统讲师。今天咱们聊聊DMA——直接存储器访问。
这名字听着挺唬人,对吧?其实说白了,DMA就是一块硬件,专门负责搬数据。CPU呢,就可以腾出手来干正事。
我刚开始做嵌入式开发那会儿,总觉得DMA是个高大上的东西,碰都不敢碰。后来被逼急了——项目里数据量太大,CPU忙不过来,系统卡得跟幻灯片似的。没办法,硬着头皮学DMA。结果发现,嗯,这东西其实没那么玄乎。
1.1 没有DMA的时候,CPU在干嘛?
先看个最简单的场景:你要从ADC(模数转换器)读一批数据,存到内存里。
没有DMA的时候,代码大概是这样的:
// 伪代码:CPU亲自搬数据
for (i = 0; i < 1000; i++) {
while(!(ADC->SR & ADC_FLAG_EOC)); // 等ADC转换完成
buffer[i] = ADC->DR; // CPU亲自读数据,再写到内存
}
你想想看,CPU每读一个数据,就要等ADC转换完。这期间CPU啥也干不了,就干等着。1000个数据,CPU就傻等1000次。
我见过一个项目,工程师用这种轮询方式读ADC,CPU占用率直接飙到80%以上。系统其他任务全被拖垮了。这就是典型的「CPU被数据搬运累死」的案例。
1.2 DMA来了:数据自己走
DMA的工作原理,其实特别简单。我画个图你就明白了:
你看这个图,CPU只需要配置一下DMA,告诉它「从哪搬、搬到哪、搬多少」。然后DMA就自己干活了。CPU呢?该干嘛干嘛去。
数据流是这样的:
- 外设触发:ADC转换完成,给DMA发个信号
- DMA响应:DMA收到信号,从外设寄存器读数据
- DMA写入:直接把数据写到内存里
- 通知CPU:全部搬完了,给CPU发个中断,「活干完了,你来处理吧」
核心要点:DMA搬运数据时,CPU完全不用参与。数据从外设到内存,或者从内存到外设,都是DMA自己搞定。
1.3 DMA到底怎么「绕过」CPU的?
这个问题我经常被问到。其实关键在于总线仲裁。
系统总线上挂着好几个设备:CPU、DMA、外设、内存。谁想用总线,得先申请。CPU和DMA都有这个资格。
DMA工作的时候,会向总线仲裁器申请「总线控制权」。仲裁器一看,CPU这会儿没在用总线(比如在执行内部运算),就把总线交给DMA。DMA拿到总线,直接在外设和内存之间传数据。
整个过程,CPU压根不知道总线上发生了什么。它还在那算它的数据呢。
我的经验:我曾经在一个项目中,用DMA搬运UART接收的数据。CPU只需要在DMA完成中断里处理数据,平时完全不用管。结果CPU占用率从70%降到了15%。那感觉,就像给系统装了个涡轮增压。
1.4 DMA的三种搬运模式
DMA不是只会一种搬法。根据场景不同,有三种模式:
| 模式 | 怎么搬 | 典型场景 |
|---|---|---|
| 内存→内存 | 从一块内存搬到另一块内存 | 数据拷贝、缓冲区移动 |
| 外设→内存 | 从外设寄存器搬到内存 | ADC采集、UART接收 |
| 内存→外设 | 从内存搬到外设寄存器 | DAC输出、SPI发送 |
我个人用得最多的是「外设→内存」模式。比如采集音频数据,ADC每转换完一个样本,DMA就自动搬到缓冲区。等缓冲区满了,CPU一次性处理。效率极高。
1.5 避坑指南:DMA不是万能的
我曾经踩过的坑:有一次做高速数据采集,DMA配置好了,结果数据老是丢。查了两天才发现——DMA传输速度太快,内存带宽不够,导致数据覆盖。
用DMA要注意几点:
- 内存带宽有限:DMA和CPU共用总线,DMA搬得太猛,CPU反而被拖慢
- 缓存一致性问题:DMA直接操作内存,CPU的缓存里可能是旧数据。需要手动刷新缓存
- DMA通道有限:芯片里的DMA通道就那么几个,别指望所有外设都用DMA
嗯,这些坑后面章节会详细讲。今天先把概念理清楚。
1.6 小结
DMA说白了就是:让硬件帮你搬数据,CPU专心做计算。
我刚开始学的时候,总觉得DMA配置复杂,不如自己写循环。后来发现,数据量一上来,CPU根本扛不住。DMA不是锦上添花,而是雪中送炭。
下一节,咱们会深入DMA的寄存器配置,看看怎么在代码里把DMA用起来。到时候我会拿STM32的DMA控制器做例子,手把手教你配。
一句话记住DMA:CPU只管「算」,DMA只管「搬」。各司其职,效率翻倍。