第1章 DMA传输概述

大家好,我是老李,搞嵌入式系统十几年了。今天咱们开篇聊聊DMA——直接存储器访问。这玩意儿在嵌入式系统里几乎无处不在,但很多人对它又爱又恨。爱的是它能解放CPU,恨的是调试起来容易让人抓狂。尤其是缓存一致性问题,一不留神就掉坑里。

这一章我们先把基础打牢。搞清楚DMA是什么、DMA跟CPU怎么配合工作。后面那些调试技巧才能用得上。

什么是DMA?

DMA全称Direct Memory Access,直接存储器访问。说白了,就是一块硬件,能在不占用CPU的情况下,自己把数据从A搬到B。

我记得刚入行那会儿,第一块板子上跑数据采集,CPU每隔200微秒就得去读一次ADC结果,再写到内存。程序一跑,主循环卡得要死。后来老工程师甩给我一句话:“用DMA啊,CPU只负责点火,搬砖交给DMA去做。”换完之后,CPU占用率直接降了90%。

DMA传输的核心流程其实很简单:

  • CPU配置好源地址、目的地址、传输长度
  • 启动DMA,然后CPU该干嘛干嘛
  • DMA自己走总线,在源和目的之间搬数据
  • 搬完触发中断,通知CPU“活干完了”

你想想看,如果CPU每搬一个字节都要取指令、执行指令,那效率多低。DMA是硬连线逻辑,一次传输只需要占用总线几个时钟周期。尤其在大量数据重复搬运的场景下,DMA简直是神器。

DMA与CPU的协同工作

很多人以为DMA和CPU是抢关系的。其实不然。DMA和CPU共用系统总线,但有一套仲裁机制。谁有更高优先级,谁先用总线。常用的策略有:

  • 轮询仲裁:CPU和DMA轮流用总线,公平但不高效
  • 优先级仲裁:通常CPU优先级高,DMA发请求时,CPU会在空闲周期放行
  • 突发传输:DMA连续搬多个字再释放总线,减少切换开销

我个人习惯在工程设计里,把DMA优先级设得比CPU高,但只在传输期间。这样CPU做计算时,DMA搬运数据,互不干扰。但要注意:如果DMA长时间霸占总线,CPU中断响应会被延迟。所以很多芯片支持DMA在每次传输后自动让出总线。

下面是DMA与CPU协同工作的一个典型流程:

  1. CPU配置DMA:写源地址寄存器、目的地址寄存器、传输计数值
  2. CPU启动DMA:置位使能位,DMA开始工作
  3. DMA请求总线:DMA控制器向总线仲裁器发请求
  4. 总线仲裁授予:CPU当前指令执行完,释放总线,仲裁器把总线让给DMA
  5. DMA执行传输:从源地址读数据,写到目的地址,每次传输后地址加1,计数减1
  6. 传输完成:计数到0,DMA发中断给CPU
  7. CPU响应中断:进入中断服务程序,处理传输完的数据

这个流程里有个隐藏的坑——缓存一致性。后面我们会专门讲,这里先提一嘴:如果CPU先写了内存的数据,然后让DMA把这个数据搬到外设。但CPU写的数据还在Cache里,没回写到内存。DMA直接从内存读,读到的是旧数据。这就是经典的一致性问题。

我曾经**遇到过这种bug:** 在摄像头图像采集项目里,DMA把数据搬到了内存,CPU往这个内存写描述符,结果图像花屏。查了三天,最后发现是DMA读的时候Cache里的数据没刷出去。加了一行flush操作,问题解决。

CPU搬运 vs DMA搬运

为了更直观对比,我整理了一个常用表格:

对比维度 CPU搬运 DMA搬运
数据传输方式 逐字读-写-循环 硬件自动批量搬移
CPU利用率 传输期间CPU被占用 传输期间CPU可执行其他任务
传输速度 受CPU频率和指令流水线限制 纯总线速度,通常比CPU快2~3倍
适用场景 小数据量、非实时或低频率操作 大数据量、周期性传输(如ADC采样、显示刷新)
代码复杂度 简单循环 需要配置寄存器、处理中断
缓存问题 CPU直接操作,天然一致 容易产生缓存一致性问题

选哪个,看你的场景。我个人习惯:单次传输少于50字节且不频繁,直接用CPU搬。超过256字节且需要周期性执行,必上DMA。

图形化理解DMA工作流程

下面这张图总结了DMA从配置到完成的完整路径。你一眼就能看明白各个角色怎么配合。

DMA传输协同工作流程图 1. CPU配置DMA 源/目的地址 + 长度 + 控制 2. DMA请求总线 DMA控制器向仲裁器发请求 3. 总线仲裁授予 CPU执行完当前指令后让出总线 4. DMA传输数据 源→目的,地址自增,计数递减 5. 传输完成中断 DMA计数到0,触发CPU中断 关键配置项 源地址 目的地址 传输长度 循环/单次模式 常见传输模式 内存→内存 外围→内存 内存→外围 外围→外围 注意:DMA传输期间 CPU仍可执行指令(无总线冲突时)

小技巧: 配置DMA时,建议把传输长度寄存器先清零再赋值。我遇到过芯片复位后该寄存器保留随机值的情况,直接写低16位会出错。养成好习惯,先清零后赋值,少踩坑。

小结

这一章我们捋清了DMA的基本概念和与CPU的协同机制。DMA本质上是个“硬件搬运工”,CPU只管交任务、收结果。两者通过总线仲裁和中断机制配合工作。

重点记住三点:

  • DMA传输不占CPU时间,但会占总线带宽
  • DMA配置必须保证源/目的地址和长度准确
  • 缓存一致性是调试中最容易忽略的雷——CPU写内存后,DMA读之前,一定要clean Cache;DMA写内存后,CPU读之前,一定要invalid Cache

后面的章节,我会结合真实调试案例,详细拆解缓存一致性问题怎么定位、怎么修复。咱们慢慢聊。

一句话总结: DMA让CPU从重复搬砖中解放出来,但解放的前提是——你得跟缓存搞好关系。