前言很多刚接触 STM32、GD32、NXP、ESP32 或其他 MCU 的同学,第一次看到 DMA 时都会有点懵:
如果只背概念,DMA 很抽象。** 但如果从一个实际问题出发,它就很好理解。** 假设我们要连续采集 ADC,并把采样结果放进数组:
不用 DMA 时,CPU 需要不断做这件事:
也就是 ADC 每转换一次,CPU 就要过来读一次寄存器、写一次内存。** 如果采样频率很高,CPU 会被这种重复搬运拖住。** DMA 的作用就是:
一句话理解:
1. 没有 DMA 时,CPU 在做什么先看一个不用 DMA 的 ADC 采样流程。 伪代码如下:
CPU 做了很多重复工作:
如果采样频率低,这样写没问题。** 但如果 ADC 采样很快,或者系统同时还要跑电机控制、通信协议、UI、算法任务,CPU 就会被数据搬运占用太多时间。** 串口接收也是类似问题。 不用 DMA 时,CPU 可能每收到一个字节就进一次中断:
如果串口数据量大,中断频率就会很高。** CPU 一直进中断,主程序自然就被打断得很碎。** 先看没有 DMA 的情况,CPU 基本是在替外设搬数据。
对比着看,DMA 的价值就更容易理解了:它把“搬数据”这件事从 CPU 手里拿走。 2. DMA 到底是什么DMA 的全称是 ****Direct Memory Access**,直译是“直接内存访问”。** 这个名字容易让人误会,好像 DMA 只是访问内存。** 更准确地说,它是:**
常见传输方向有三种:
DMA 不是凭空工作的。** 它需要 CPU 先告诉它几件事:**
这些就是我们在工程里配置 DMA 的本质。 3. DMA 在系统里的位置先看系统结构图。
从图里可以看到,DMA 不是 CPU 的一条语句,也不是普通函数。** 它是 MCU 内部一个独立的硬件模块,并且和 CPU 一样可以访问总线。** 以 ADC 采样为例:
所以 DMA 的核心不是“让数据消失在后台”,而是把原本 CPU 做的数据搬运交给硬件完成。 4. DMA 一次传输到底发生了什么再看一次完整流程。
还是以 4.1 CPU 配置 DMACPU 先配置:
为什么 ADC 源地址不自增? 因为 ADC 数据永远从同一个寄存器读:
为什么目标地址要自增? 因为每次采样值要依次写入数组:
这就是 DMA 配置里“地址自增”的含义。 4.2 外设产生 DMA 请求DMA 不一定自己主动搬。 对于 ADC、UART、SPI 这类外设,通常是外设事件触发 DMA:
每来一次请求,DMA 就按配置搬一次数据。 4.3 DMA 搬数据并更新计数器DMA 每搬一次,会做几件事:
比如 ADC 采样到数组:
CPU 不需要每次都介入。 4.4 半传输和传输完成如果传输长度是 1024:
这两个事件非常重要。 在 ADC 连续采样里,我们经常用双半区处理:
这样可以实现连续采样,不容易丢数据。 5. DMA 的几个核心配置怎么理解5.1 源地址和目标地址DMA 首先要知道:
例如 ADC 到内存:
UART 发送:
UART 接收:
方向不同,源和目标就不同。 5.2 数据宽度数据宽度决定每次搬多少。 常见有:
例如:
如果宽度配错,轻则数据错位,重则传输异常。 5.3 地址自增地址自增是 DMA 最容易让初学者困惑的地方。 判断方法很简单:
ADC 到数组:
UART 发送数组:
内存到内存:
5.4 传输长度传输长度不是字节数,而是“传输次数”。 如果配置:
表示搬 1024 个半字,也就是 2048 字节。 如果配置:
表示搬 1024 个字节。 这个概念很关键,尤其在调 ADC、SPI、I2S、LCD 时。 5.5 普通模式和循环模式普通模式:
适合:
循环模式:
适合:
循环模式一定要配合好缓冲区处理,否则 CPU 还没处理完,DMA 又把旧数据覆盖了。 6. 一个实际例子:ADC + DMA 连续采样假设我们要采集 1024 个 ADC 数据。 缓冲区:
启动:
当 DMA 写完前 512 个数据时,会进入半传输回调:
当 DMA 写完后 512 个数据时,会进入完成回调:
如果 DMA 是循环模式,流程就是:
这就是很多音频采集、波形采集、电流采样里常见的 ping-pong 思路。 7. 一个实际例子:UART + DMA 接收串口接收如果一个字节进一次中断,数据量大时 CPU 压力会很明显。 DMA 接收可以这样理解:
常见启动方式:
如果接收的是不定长数据,通常会配合空闲中断:
回调:
这样 CPU 不需要每个字节都进中断,而是在收到一段数据后统一处理。 8. DMA 不是完全不占 CPU很多人会说“DMA 不占 CPU”,这句话不够严谨。 更准确的说法是:
CPU 仍然要做:
DMA 省掉的是最重复、最机械的搬运动作。 9. DMA 会不会和 CPU 抢总线会。 DMA 和 CPU 都要访问内存和外设寄存器,本质上都要用总线。** 所以 DMA 并不是魔法,它只是把搬运工作从 CPU 指令流里拿出来,交给独立硬件模块执行。** 在高性能 MCU 上,可能会有:
这些都会影响 DMA 行为。 一般入门阶段先记住:
所以在高速采样、屏幕刷新、音频传输、网络收发等场景里,仍然要注意带宽和缓冲区设计。 10. DMA 常见错误10.1 地址自增配置错ADC 到数组时,如果目标地址没有自增,就会一直写 UART 发送时,如果源地址没有自增,就会一直发送同一个字节。 10.2 数据宽度配置错ADC 是 12 bit 结果,通常用 16 bit 存。** 如果 DMA 配成 8 bit,数据就可能被截断或错位。** 10.3 传输长度理解错传输长度是次数,不一定是字节数。 例如 10.4 缓冲区被覆盖循环模式下,如果 CPU 处理太慢,DMA 会重新写到旧位置。 解决办法:
10.5 Cache 一致性问题在带 D-Cache 的 MCU 上,比如 STM32H7,DMA 和 CPU 看到的数据可能不一致。 典型现象:
这是 Cache 一致性问题,需要根据芯片和工程处理:
这部分是进阶内容,但如果你用的是 H7、MPU、带 Cache 的 Cortex-M7,一定要留意。 11. 怎么判断一个场景该不该用 DMA适合用 DMA 的场景:
典型场景:
简单判断:
12. 总结DMA 可以从三个层次理解。 第一层:
第二层:
第三层:
如果你第一次学 DMA,不要一上来陷入寄存器细节。** 先把这几个问题想清楚:**
能回答这几个问题,DMA 就已经理解了一大半。 最后用一句话收尾:
如果你在调 STM32、RS485、Modbus、RTT、波形采集或者数据可视化,上位机工具 ****多多盒子助手**。 **它把串口、J-Link RTT、Modbus、实时图表、CRC 校验、日志保存和固件升级这些常用能力放在了一起,适合做日常嵌入式调试。 项目地址: |
【福利三:逢7发帖赢大礼】8、基于STM32G070板子的Ymodem串口协议IAP Bootloader开发
【福利三:逢7发帖赢大礼】7、基于STM32G070板子TF卡的FATFS移植
【福利三:逢7发帖赢大礼】6、基于STM32G070板子spiflash的FATFS移植
基于STM32F303双电机FOC驱动: 无感原理图/BOM表/代码等开源...
【福利三:雨露均沾·逢7狂欢】之五:STM32系统时钟PLL故障排除1例
【福利三:逢7发帖赢大礼】5、基于STM32G070板子IRDA红外遥控接收解码
【福利三:逢7发帖赢大礼】4、基于STM32G070板子SPI flash 移植SFUD库
【福利三:逢7发帖赢大礼】基于STM32G070板子的uart shell移植设计
【福利三:逢7发帖赢大礼】3、基于STM32G070板子的YModem串口协议通信
【福利三:逢7发帖赢大礼】2、基于STM32G070板子的OLED移植U8G2库
微信公众号
手机版