1、课程导学与DMA基础
1.1 这门课要解决什么问题?
做嵌入式开发久了,你会发现一个很头疼的问题:外设驱动代码写死了。
比如你写了一个 SPI 驱动,里面直接调了 HAL_SPI_Transmit。过两天换了个芯片,或者想加个 DMA 加速,得把整个驱动翻出来重写。我早期做项目时就吃过这个亏——一个温控模块,因为 SPI 驱动和 DMA 逻辑耦合太紧,换主控芯片时改了整整两周。
这门课的核心思路就一句话:把「外设操作」和「数据传输方式」拆开。你写驱动时只关心「我要发什么数据」,至于用轮询还是 DMA,那是上层配置的事。这样模块才能复用,代码才能移植。
我们会基于 STM32 的 HAL 库,从最基础的 DMA 讲起,一步步带你实现一个解耦的驱动框架。今天这第一节课,先把 DMA 的底子打牢。
1.2 DMA 到底是什么?
DMA,全称 Direct Memory Access,直接存储器访问。说白了,就是一个专门负责搬数据的小工。
没有 DMA 的时候,CPU 得亲自搬数据:从外设读一个字节,存到内存,再读下一个……整个过程 CPU 被死死拖住,啥也干不了。有了 DMA,你只需要告诉它:「从 ADC 的数据寄存器搬 100 个字节到内存缓冲区」,它自己就干完了,搬完再通知你一声。
核心理解:DMA 让 CPU 从「搬运工」变成「管理者」。CPU 只负责下指令、处理结果,中间的数据搬运全交给 DMA。
我在一个数据采集项目里用过这个特性。ADC 以 1MHz 采样,每 1µs 产生一个数据。如果用 CPU 搬,系统根本跑不了别的任务。换成 DMA 后,CPU 只在 DMA 搬完一整批数据后才去处理,负载从 90% 降到了 5%。
1.3 DMA 传输三要素
任何 DMA 传输,都离不开三个东西:源地址、目的地址、触发源。你想想看,搬数据总得知道从哪搬、搬到哪、什么时候开始搬吧?
| 要素 | 说明 | 常见例子 |
|---|---|---|
| 源地址 | 数据从哪来 | 外设数据寄存器(如 USART->DR)、内存数组 |
| 目的地址 | 数据搬到哪去 | 内存缓冲区、外设数据寄存器 |
| 触发源 | 什么时候开始搬 | 定时器溢出、ADC 转换完成、USART 接收非空 |
举个例子:你用 USART 接收一串数据。
- 源地址:USART 的接收数据寄存器(USART1->DR)
- 目的地址:你定义的一个数组
rx_buffer[100] - 触发源:USART 接收到一个字节(RXNE 标志)
每次 USART 收到一个字节,DMA 就自动把它搬到 rx_buffer 里。CPU 全程不用管。
个人习惯:我一般把源和目的都配置成「递增模式」——内存地址自动加 1,外设地址固定不变。这样连续传输时不用手动更新地址,省心很多。
1.4 DMA vs CPU 传输:到底快在哪?
很多人以为 DMA 比 CPU 快。其实不完全对。DMA 的传输速度并不比 CPU 快多少,它真正的优势是不占 CPU 时间。
来看一个对比:
| 对比项 | CPU 轮询传输 | DMA 传输 |
|---|---|---|
| CPU 占用率 | 100%(全程搬运) | 仅配置和完成中断时占用 |
| 传输速度 | 受 CPU 主频和指令周期影响 | 与 CPU 相当,但可并行 |
| 适合场景 | 单次、少量、实时性要求不高的数据 | 批量、连续、高速数据流 |
| 代码复杂度 | 简单,直接读写寄存器 | 需要配置 DMA 通道和中断 |
| 功耗 | CPU 一直跑,功耗高 | CPU 可以休眠,功耗低 |
我举个例子你就明白了。假设你要从 SPI Flash 读 1KB 数据:
- CPU 轮询:CPU 发一个读命令,等 SPI 完成,读一个字节,再发下一个……循环 1024 次。这期间 CPU 啥也干不了。
- DMA 方式:CPU 配置好 DMA,说「从 SPI 搬 1024 字节到内存」,然后就去处理别的任务了。DMA 搬完后触发中断,CPU 再来处理数据。
说白了,DMA 让 CPU 从「亲自搬砖」变成了「指挥搬砖」。效率提升不在速度,而在并行。
注意:DMA 不是万能的。如果每次只传几个字节,配置 DMA 的开销可能比直接 CPU 传输还大。我曾经在一个项目中过度使用 DMA,结果频繁的配置和中断反而拖慢了系统。后来改成:小数据用轮询,大数据用 DMA,效果好了很多。
1.5 DMA 传输的完整流程
下面这张图展示了 DMA 一次典型传输的完整流程。我建议你仔细看看,后面写代码时脑子里要有这个图。
这个流程里有个关键点:DMA 传输是「触发式」的。没有触发信号,DMA 就闲着。触发信号一来,它才动一下。所以配置 DMA 时,触发源的选择特别重要——选错了,数据就丢了。
1.6 本章小结
好,第一节课的内容就这些。我们理清了几个核心概念:
- DMA 是一个独立的数据搬运硬件模块
- 任何 DMA 传输都离不开源地址、目的地址、触发源三要素
- DMA 的优势不是速度,而是让 CPU 从搬运中解放出来
- DMA 适合批量连续传输,小数据用轮询反而更高效