☰
从408真题到嵌入式实战:DMA原理、考点与串口/SPI/ADC应用全解析
2026/9/29 15:47:48 网站建设 项目流程

1. 从一道408真题说起:DMA到底在考什么

如果你正在啃计算机组成原理,尤其是唐朔飞那本教材或者王道复习指导,大概率会在“I/O设备与主机信息传送的控制方式”这一章卡住。程序查询、中断、DMA这三种方式,书上写得都挺清楚,但一到做题就懵——尤其是DMA,概念好像懂了,细节一问就露馅。2024年408第45题就考了DMA相关的知识点,不少人在考场上才发现自己对“周期挪用”“总线占用”“中断与DMA的关系”这些点理解得模棱两可。

这篇文章就是冲着这个痛点来的。我会把DMA方式从底层原理到408考点,再到实际工程中的串口DMA、SPI DMA、ADC多通道DMA采集这些热词背后的技术逻辑,全部串一遍。不管你是考研党还是嵌入式开发者,只要你对“I/O设备与主机之间怎么高效传数据”这件事感兴趣,这篇内容都能让你有收获。

先说清楚DMA是什么。DMA全称Direct Memory Access,直接存储器访问。它的核心思想特别朴素:既然外设和内存之间传数据这件事,CPU只是当个“搬运工”,那为什么不找个专门的硬件来干这个活?这个专门的硬件就是DMA控制器(DMAC)。有了它,CPU只需要在传输开始前告诉DMAC“从哪搬到哪、搬多少、搬完告诉我”,然后就可以去干别的事了。数据搬运的过程完全由DMAC硬件控制,不需要CPU一条一条执行指令。

这跟程序查询和中断方式有本质区别。程序查询是CPU死等,外设没准备好就一直在那转圈;中断方式是外设准备好了打断CPU,CPU放下手头的活去搬一个数据再回来。这两种方式下,数据搬运这个动作始终是CPU在做。而DMA方式下,数据搬运这个动作从CPU身上彻底卸下来了,交给了DMAC。这是理解DMA所有特性的总钥匙。

2. 三种I/O控制方式的本质差异与DMA的定位

2.1 程序查询与中断方式的瓶颈在哪

要理解DMA为什么被发明出来,得先看清楚前两种方式的问题。

程序查询方式最简单,CPU不断读取外设的状态寄存器,检查数据是否准备好。准备好了就读一个数据,然后继续查。这种方式的问题显而易见:CPU的时间几乎全浪费在“查状态”这个动作上。假设外设传输一个字节需要1微秒,CPU查一次状态需要0.1微秒,那CPU有超过90%的时间在做无用功。在早期计算机中,外设速度远低于CPU,这种方式勉强能用,但CPU利用率极低。

中断方式进了一步。外设准备好数据后,主动向CPU发中断请求,CPU响应中断,执行中断服务程序来搬运数据。这样CPU不用一直查状态了,可以在外设准备数据的时候去执行其他程序。但问题在于:每传输一个数据(比如一个字节或一个字),就要经历一次完整的中断响应过程——保存断点、关中断、取中断向量、执行服务程序、恢复断点、开中断。这个过程少说也要几十条指令。如果外设传输速度很快,比如磁盘每微秒就来一个数据,CPU就会陷入“中断风暴”,大部分时间都在处理中断的进出,真正干正事的时间反而很少。

这里有个关键认知:中断方式下,数据搬运本身仍然由CPU执行指令完成。中断只是解决了“什么时候搬”的问题,没有解决“谁来搬”的问题。

2.2 DMA方式的破局思路

DMA的思路就是换人。CPU不搬了,让DMAC来搬。CPU只负责初始化DMAC,告诉它几个关键信息:源地址(外设的数据寄存器地址或内存地址)、目的地址、传输长度、传输方向。然后DMAC自己跟内存和外设打交道,一个数据一个数据地搬。搬完了,DMAC给CPU发一个中断,告诉它“活干完了”。

这个设计带来的变化是革命性的。数据搬运不再消耗CPU的指令周期,CPU可以在这段时间里执行其他程序。对于高速外设(磁盘、网卡、显卡)和大批量数据传输(比如从磁盘读一个文件到内存),DMA的优势极其明显。

但DMA也不是没有代价。DMAC要访问内存,就要占用总线。而总线是共享资源,CPU也要用。这就引出了DMA最核心的考点之一:DMAC怎么跟CPU抢总线?抢的方式不同,就产生了DMA的三种工作模式。

2.3 三种DMA工作模式的取舍

停止CPU访存模式:DMAC要传数据时,直接向CPU发一个信号,要求CPU暂停使用总线。CPU收到信号后,在当前总线周期结束后让出总线控制权,DMAC开始传输。传完一组数据后,再把总线还给CPU。这种方式的优点是控制简单,DMAC不需要复杂的仲裁逻辑。缺点是CPU在DMA传输期间完全不能访存,如果DMA传输时间很长,CPU就被“冻住”了。适合数据传输率极高的场景,比如显卡刷新。

周期挪用模式:也叫周期窃取。DMAC要传数据时,趁CPU不用总线的间隙“偷”一个总线周期来传一个数据。如果CPU正在用总线,DMAC就等一个周期再试。这种方式下CPU和DMAC交替使用总线,CPU的访存操作会被偶尔延迟,但不会完全停止。缺点是DMAC需要判断当前总线是否空闲,控制逻辑稍复杂。这是实际系统中最常用的方式。

交替访问模式:把总线时间分成固定的时间片,一部分给CPU,一部分给DMAC,两者交替使用,互不干扰。这种方式不需要总线仲裁,但要求CPU和DMAC的访存周期匹配,灵活性差,实际中很少用。

这三种模式的对比是408的高频考点。记住一个口诀:停止CPU最霸道但最简单,周期挪用最灵活但最常用,交替访问最规整但最少见。

3. DMA控制器的内部结构与工作流程拆解

3.1 DMAC里到底有什么

DMAC不是黑盒,它的内部结构直接决定了DMA的工作方式。一个典型的DMAC包含以下关键部件:

地址寄存器:包括源地址寄存器和目的地址寄存器,分别存放数据搬运的起点。每传输一个数据,地址寄存器自动加一(或减一),指向下一个位置。

字计数器:记录还剩多少数据没传。每传一个数据,计数器减一。减到零时,表示传输完成,DMAC发出中断请求。

数据缓冲寄存器:暂存从源端读出的数据,等待写入目的端。这个寄存器的大小通常是一个字或一个字节。

控制与状态寄存器:存放CPU写入的控制信息(传输方向、工作模式、是否允许中断等)和DMAC的状态信息(是否正在传输、是否出错等)。

中断控制逻辑:传输完成后向CPU发中断请求。

总线仲裁逻辑:负责与CPU竞争总线控制权。

这些部件协同工作,构成了DMA传输的硬件基础。408考试中经常考地址寄存器和字计数器的工作原理,比如“传输完成后地址寄存器的值是多少”“字计数器的初值怎么设置”这类问题。

3.2 一次完整的DMA传输经历什么

以“从磁盘读一个数据块到内存”为例,完整流程如下:

  1. CPU执行初始化程序,向DMAC的地址寄存器写入内存起始地址,向字计数器写入数据块长度,向控制寄存器写入传输方向(外设到内存)和启动命令。

  2. DMAC向CPU发出总线请求信号(HRQ)。

  3. CPU在当前总线周期结束后,发出总线响应信号(HLDA),让出总线控制权。

  4. DMAC获得总线控制权后,向外设发出读命令,从外设数据寄存器读出一个数据,存入数据缓冲寄存器。

  5. DMAC向内存发出写命令,将数据缓冲寄存器的内容写入地址寄存器指向的内存单元。

  6. 地址寄存器加一,字计数器减一。

  7. 判断字计数器是否为零。如果不为零,回到步骤4继续传输;如果为零,进入步骤8。

  8. DMAC释放总线控制权,向CPU发出中断请求,表示传输完成。

  9. CPU响应中断,执行中断服务程序,处理传输完成后的收尾工作(比如设置标志位、唤醒等待的进程)。

这个流程中,步骤4到步骤7是DMAC硬件自动完成的,不需要CPU干预。CPU只在步骤1和步骤9参与。这就是DMA高效的根本原因。

注意一个细节:DMAC在传输过程中,每传一个数据都要经历“读-写”两个总线周期。如果是周期挪用模式,这两个周期可能不是连续的,中间可能被CPU的访存操作打断。这一点在分析DMA对CPU性能影响时很重要。

3.3 DMA与中断的关系:不是替代而是互补

很多人初学时会困惑:有了DMA,还需要中断吗?答案是需要的,而且DMA离不开中断。

DMA解决的是“数据搬运”的效率问题,中断解决的是“事件通知”的问题。DMA传输完成后,怎么告诉CPU“活干完了”?就是通过中断。所以DMA和中断是配合关系,不是替代关系。

在408考试中,经常考的一个点是:DMA请求和中断请求的优先级谁高?答案是DMA请求优先级更高。原因很简单:DMA传输是硬件级别的数据搬运,如果被中断打断,可能导致数据丢失或外设缓冲区溢出。而中断是CPU级别的程序切换,晚一点响应通常不会造成严重后果。所以在总线仲裁中,DMAC的请求总是优先于CPU的访存请求。

另一个常考点是:DMA传输过程中,CPU能不能响应中断?这取决于DMA的工作模式。如果是停止CPU访存模式,CPU被暂停了,自然不能响应中断。如果是周期挪用模式,CPU只是在总线周期级别被偶尔延迟,中断响应不受影响。

4. 408考点精讲与典型题型拆解

4.1 地址寄存器和字计数器的计算题

这是408中最常见的DMA计算题类型。题目通常给出内存起始地址、传输数据量、每次传输的宽度,要求计算传输完成后地址寄存器的值或字计数器的初值。

举个例子:内存起始地址为2000H,传输1024个字节,每次传输一个字节。地址寄存器初值为2000H,每传一个字节加一,传输完成后地址寄存器的值为2000H + 1024 = 2400H。字计数器初值为1024,传输完成后减为0。

如果每次传输一个字(假设字长为16位,即2字节),那么传输1024个字节需要传输512次。地址寄存器每次加2,传输完成后地址寄存器的值为2000H + 1024 = 2400H(地址增量仍然是总字节数)。字计数器初值为512,传输完成后为0。

这里容易出错的地方是:地址寄存器的增量取决于每次传输的宽度,而字计数器的初值取决于传输次数。两者不能混淆。

4.2 DMA对CPU性能影响的分析题

这类题目通常给出CPU的时钟频率、总线周期、DMA传输速率等参数,要求计算DMA传输占用多少CPU时间,或者CPU性能下降多少。

核心思路是:在周期挪用模式下,DMAC每传一个数据需要占用一个或两个总线周期。如果CPU的总线周期为T,DMA传输N个数据,那么被DMA占用的总线时间为N×T(或2N×T)。这段时间内CPU无法访存,相当于CPU的有效访存时间减少了。

如果题目给出CPU在执行某程序时需要访存M次,每次访存需要一个总线周期,那么在没有DMA时,程序执行时间为M×T。有DMA时,如果DMA占用了K个总线周期,程序执行时间变为(M+K)×T。性能下降比例为K/(M+K)。

这类题目的关键是搞清楚“DMA占用了多少个总线周期”以及“CPU原本需要多少个总线周期”。

4.3 DMA与中断的优先级判断题

题目通常描述一个场景:CPU正在执行中断服务程序,此时DMAC发出总线请求,问CPU会怎么处理。

答案:DMAC的总线请求优先级高于CPU的当前访存操作。CPU会在当前总线周期结束后让出总线,DMAC完成一个数据的传输后归还总线,CPU继续执行中断服务程序。如果DMAC请求的是停止CPU访存模式,则CPU会暂停执行,直到DMA传输完成。

这里要注意区分“总线请求”和“中断请求”。总线请求是DMAC向CPU要总线控制权,中断请求是外设向CPU要程序执行权。两者的优先级和处理机制不同。

4.4 三种I/O方式对比的简答题

这是408中常见的简答题类型,要求比较程序查询、中断、DMA三种方式的优缺点和适用场景。

答题要点:程序查询方式硬件简单但CPU利用率极低,适合外设速度极慢且CPU空闲的场景;中断方式CPU利用率提高但每次传输都要CPU介入,适合外设速度较慢、数据传输量不大的场景;DMA方式CPU利用率最高但硬件复杂,适合高速外设和大批量数据传输的场景。

答题时要注意用“数据搬运由谁执行”这条主线来组织答案,这样逻辑清晰,不容易漏点。

5. 从408到工程实践:串口DMA、SPI DMA与ADC多通道采集

5.1 为什么嵌入式开发中DMA如此重要

学完408的DMA理论,很多人会觉得“这不就是个考试知识点吗”。但如果你做过STM32、GD32、HC32F460或者MSPM0G3507的嵌入式开发,就会发现DMA是绕不开的核心技术。

以串口通信为例。假设你用STM32的UART接收GPS模块的数据,波特率115200,每秒大约传输11520个字节。如果用中断方式接收,每收到一个字节就进一次中断,CPU每秒要处理11520次中断。每次中断进出加上数据处理,少说也要几十个时钟周期。在72MHz的主频下,这已经占用了可观的CPU时间。如果还有其他任务要处理,系统响应就会变慢。

用DMA就不一样了。你只需要配置好DMA通道,告诉它“从UART数据寄存器搬到内存缓冲区,搬100个字节”,然后就可以去处理其他任务。DMA搬完100个字节后发一个中断,CPU只需要处理一次中断。中断次数从11520次降到115次,CPU负担大幅降低。

这就是DMA在嵌入式中的核心价值:把CPU从繁琐的数据搬运中解放出来。

5.2 串口DMA的配置要点与避坑经验

以STM32的UART DMA接收为例,配置流程大致如下:

  1. 使能UART和DMA的时钟。
  2. 配置UART的波特率、数据位、停止位、校验位等参数。
  3. 配置DMA通道:设置外设地址为UART数据寄存器地址,内存地址为接收缓冲区地址,传输方向为外设到内存,传输长度为缓冲区大小,使能传输完成中断。
  4. 使能UART的DMA接收请求。
  5. 使能DMA通道。
  6. 在DMA传输完成中断服务程序中处理接收到的数据。

这里有几个容易踩的坑:

坑一:DMA传输完成中断只触发一次。如果你配置的传输长度是100,那么DMA搬完100个字节后才触发一次中断。如果你希望每收到一批数据就处理一次,需要在中断服务程序中重新设置DMA传输长度并重新使能DMA。或者使用“空闲中断+DMA”的方式:DMA一直处于接收状态,当串口总线空闲时触发空闲中断,在空闲中断中读取DMA已经搬运的字节数并处理数据。

坑二:内存地址对齐问题。某些DMA控制器要求外设地址和内存地址的对齐方式匹配。比如外设是字节宽度的,内存地址也应该是字节对齐的。如果内存地址是半字对齐的,可能导致数据传输错误。

坑三:DMA和CPU同时访问同一块内存。如果DMA正在往缓冲区写数据,CPU同时去读这个缓冲区,可能读到不完整的数据。解决办法是使用双缓冲区:DMA写缓冲区A时,CPU读缓冲区B;DMA写缓冲区B时,CPU读缓冲区A。

实测下来,串口DMA配合空闲中断是最稳定的方案。空闲中断的触发条件是串口总线在一个字节传输时间后没有新数据,这个时间通常很短,不会造成明显延迟。

5.3 SPI DMA与ADC多通道采集的DMA应用

SPI DMA的配置跟串口DMA类似,区别在于SPI是全双工同步通信,DMA需要同时处理发送和接收。通常配置两个DMA通道:一个用于发送(内存到SPI数据寄存器),一个用于接收(SPI数据寄存器到内存)。发送和接收的传输长度相同,传输完成后两个通道都触发中断。

ADC多通道采集是DMA的另一个经典应用场景。假设你要采集8个模拟通道的电压值,用传统方式需要依次切换通道、启动转换、等待转换完成、读取结果,每个通道都要CPU介入。用DMA就不一样了:配置ADC为扫描模式,使能DMA请求,DMA会自动把每个通道的转换结果搬运到内存数组中。CPU只需要在DMA传输完成后一次性读取8个通道的数据。

这里的关键配置是:ADC的DMA请求使能、DMA传输长度等于通道数、DMA外设地址为ADC数据寄存器地址、DMA内存地址为数组首地址。传输完成后,数组中就按通道顺序存放了8个转换结果。

5.4 DMA测速与性能评估

在实际工程中,有时候需要评估DMA的传输性能。比如用DMA测速软件测试内存到内存的DMA传输速率,或者测试外设到内存的DMA吞吐量。

测速的基本方法是:配置DMA传输一大块数据(比如1MB),在传输前后读取系统时钟计数器,计算传输时间,然后算出传输速率。需要注意的是,DMA传输速率受限于总线带宽、内存访问速度和外设速度。如果DMA传输速率远低于理论值,可能是总线仲裁配置不合理,或者DMA通道优先级设置不当。

在STM32中,DMA通道有优先级设置。如果多个DMA通道同时请求,优先级高的通道先获得总线。如果某个通道的优先级设置过低,可能导致数据传输延迟增大。

6. 常见问题与排查技巧实录

6.1 DMA传输不触发中断怎么办

这是嵌入式开发中最常见的问题之一。排查思路如下:

首先检查DMA传输完成中断是否使能。在STM32中,需要同时使能DMA通道的传输完成中断和NVIC中的对应中断向量。两者缺一不可。

其次检查DMA传输长度是否设置正确。如果传输长度设置为0,DMA不会启动,自然也不会触发中断。

然后检查DMA通道是否使能。有些开发者配置完DMA参数后忘记使能DMA通道,导致DMA根本不工作。

最后检查中断服务程序中是否清除了中断标志位。如果标志位没有清除,中断会反复触发或者不再触发。

6.2 DMA传输数据错位或丢失

数据错位通常跟地址对齐有关。检查外设地址和内存地址的宽度是否匹配。比如UART数据寄存器是8位的,内存缓冲区也应该是8位宽度的。如果内存缓冲区定义为16位数组,DMA搬运时可能按16位搬运,导致数据错位。

数据丢失通常跟缓冲区溢出有关。如果DMA传输速率高于CPU处理速率,缓冲区可能被覆盖。解决办法是增大缓冲区或者使用双缓冲区。

6.3 DMA与CPU访问冲突导致系统异常

在某些架构中,DMA和CPU同时访问同一块内存可能导致总线冲突,严重时触发硬件异常。比如在Cortex-M系列中,如果DMA正在写Flash,CPU同时从Flash取指令,可能导致总线错误。

解决办法是避免DMA和CPU同时访问同一存储区域。如果无法避免,可以使用内存屏障指令或者调整DMA传输时机。

6.4 408考试中的DMA常见失分点

在408考试中,DMA相关的失分点主要集中在以下几个方面:

一是混淆DMA请求和中断请求的优先级。记住DMA请求优先级更高。

二是搞错地址寄存器和字计数器的变化规律。地址寄存器每次增加传输宽度,字计数器每次减一。

三是忽略DMA传输对CPU性能的影响。在周期挪用模式下,CPU的访存操作会被延迟,但不会停止。

四是忘记DMA传输完成后需要中断来通知CPU。DMA和中断是配合关系。

常见问题排查方向解决方法
DMA不触发中断中断使能、传输长度、通道使能、标志清除逐项检查配置寄存器
数据错位地址对齐、数据宽度确保外设和内存宽度匹配
数据丢失缓冲区大小、传输速率增大缓冲区或使用双缓冲
系统异常总线冲突、存储区域重叠避免DMA和CPU同时访问同一区域
408失分优先级、寄存器变化、性能影响用“谁搬数据”主线理解

7. 我个人在实际操作中的体会

DMA这个东西,408考试里考的是原理和计算,嵌入式开发里用的是配置和调试。两者看似不同,但底层逻辑完全一致。如果你在408中真正理解了“数据搬运由谁执行”这条主线,到了嵌入式开发中配置DMA时,就会知道每个配置项对应的是什么硬件行为。

比如配置DMA传输长度时,你脑子里应该浮现出字计数器的减一操作;配置DMA优先级时,你应该想到总线仲裁的逻辑;使用双缓冲区时,你应该理解这是为了避免DMA和CPU的访存冲突。这种从原理到实践的贯通,才是学习DMA最大的收获。

最后分享一个小技巧:在调试DMA相关问题时,先用最简单的内存到内存DMA传输验证配置是否正确,然后再逐步加入外设。这样可以快速定位问题是出在DMA配置本身还是外设配置上。另外,养成在DMA传输完成后翻转一个GPIO引脚的习惯,用示波器观察引脚波形,可以直观地看到DMA传输的时序和耗时,比单纯看代码调试高效得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询