DMA这三个字母,但凡翻过几页计算机组成原理的人都不陌生。但我在带考研学生和做嵌入式开发的两拨人之间来回切换时发现一个很有意思的现象:考研的同学能把DMA的定义背得滚瓜烂熟,什么"直接存储器存取""不需要CPU干预""在主存和外设之间开辟直接数据通路",但一问到"为什么DMA能绕过CPU""周期挪用到底挪的是哪个周期""DMA和中断到底怎么配合",就开始支支吾吾;而做嵌入式的工程师天天配DMA通道,却未必说得清楚408考纲里DMA控制器的内部结构和工作阶段划分。这篇东西就是想把这两条线打通——既把408考试里DMA的考点逻辑讲透,也把实际配置DMA时那些文档里不会写的坑说清楚。不管你是正在啃唐朔飞还是王道,还是在用STM32、GD32、AT32这些片子做串口DMA收发,下面这些内容应该都能对上你的需求。
1. 为什么DMA不是"更快的程序查询",而是I/O控制方式的一次范式转换
1.1 从程序查询到中断再到DMA:CPU被"绑住"的程度在变
要理解DMA,得先把前面两种方式的问题说清楚。程序查询方式下,CPU发出I/O指令后就不停地读设备状态寄存器,设备没准备好就继续读,准备好了就传一个数据,然后接着读下一个。这种方式的问题不是"慢"这么简单,而是CPU在等待期间完全被绑死,什么也干不了。你可以想象成你去银行办业务,柜台说"系统在加载,你站这儿等着",你就只能站着,不能去填别的表。
中断方式改善了一点:CPU发出I/O指令后可以去执行别的程序,设备准备好数据后通过中断线通知CPU,CPU暂停当前程序,转去执行中断服务程序完成一次数据传送,然后返回。但注意,每次传送一个数据(或者一小批数据)都要经历一次完整的中断响应过程——保存断点、关中断、取中断向量、执行服务程序、恢复现场、开中断。对于高速设备(比如磁盘、网卡)来说,数据像流水一样来,如果每个字节或每个字都中断一次,CPU光处理中断上下文就忙不过来了。
DMA的思路完全不同:它不是在"CPU怎么参与传送"上做优化,而是直接引入一个专门的硬件控制器(DMA控制器,DMAC),让这个控制器来接管数据传送这件事。CPU只需要在传送开始前告诉DMAC"从哪传到哪、传多少、传完了告诉我",然后就可以去干别的事了。整个数据传送过程由DMAC硬件自主完成,CPU完全不介入。
这里有一个关键认知:DMA不是"更快的程序查询",也不是"更高效的中断",它是I/O控制方式的一次范式转换——从"CPU主导数据传送"变成"专用硬件主导数据传送"。这个转换的意义在于,CPU终于从数据搬运工的角色中解放出来,可以专注于计算和控制。
1.2 DMA方式的核心特征:三条总线权的转移
DMA之所以能绕过CPU,本质上是它拿到了总线控制权。在正常的冯诺依曼结构中,CPU是总线的主控者,所有对主存的访问都要经过CPU(或者至少由CPU发起)。但DMA方式下,DMAC可以向CPU发出总线请求(HOLD),CPU在当前总线周期结束后响应,发出总线允许(HLDA),把总线控制权交给DMAC。DMAC拿到总线权后,就可以像CPU一样直接对主存进行读写操作。
这个过程涉及三条总线权的转移:地址总线、数据总线、控制总线。DMAC需要自己产生地址信号(告诉主存读写哪个单元)、自己产生读写控制信号(告诉主存是读还是写)、自己处理数据传送。这就是为什么DMAC内部必须有地址寄存器、字计数器、状态寄存器等部件。
考试里经常考的一个点是:DMA传送过程中,CPU是否完全停止工作?答案是:CPU暂时放弃总线控制权,但CPU内部的操作(比如指令译码、算术运算)可能还在继续,只是无法访问主存。如果CPU当前执行的指令不需要访问主存(比如寄存器间的运算),那它甚至可以继续执行。但一旦需要访存,就必须等待DMA释放总线。
1.3 DMA与中断的本质区别:谁在"搬数据"
很多人把DMA和中断混在一起理解,觉得DMA就是"中断的升级版"。这个理解是错的。中断方式下,数据传送是由CPU执行中断服务程序完成的——CPU亲自把数据从I/O接口读到寄存器,再写到主存。DMA方式下,数据传送是由DMAC硬件完成的——CPU根本不碰数据,数据直接在DMAC的控制下从I/O设备流向主存(或反向)。
打个比方:中断方式像是你请了一个助理,但每次有快递来,助理都要跑过来问你"这个放哪",你告诉他之后他再放。DMA方式像是你给助理一把仓库钥匙和一张清单,告诉他"按清单上的地址放,放完了跟我说一声",然后你就可以去开会了。
这个区别决定了DMA的适用场景:高速、大批量的数据传送。比如磁盘读写、网络数据包收发、图像采集等。对于低速设备(比如键盘),用DMA反而浪费——因为DMAC的初始化和总线权切换也是有开销的。
2. DMA控制器的内部结构:考试画图题和实际芯片的对应关系
2.1 408考纲要求的DMAC组成部件
唐朔飞教材里给出的DMAC结构图是考试的重点。一个典型的DMAC包含以下部件:
- 主存地址寄存器(AR):存放要访问的主存地址,每传送一个数据后自动加1(或减1)。
- 字计数器(WC):存放剩余要传送的字数,每传送一个数据后自动减1,减到0时发出结束信号。
- 数据缓冲寄存器(DR):暂存传送中的数据。
- DMA请求触发器:接收I/O设备的DMA请求信号。
- 控制/状态逻辑:产生DMA请求信号(向CPU)、接收DMA响应信号、控制传送过程。
- 中断机构:当字计数器减到0时,向CPU发出中断请求,通知传送完成。
这些部件在考试里经常以框图形式出现,要求你标注各部件名称并说明其作用。我建议的记忆方式是:地址、计数、缓冲、请求、控制、中断——六个关键词对应六个部件。
2.2 实际芯片里的DMA控制器:以STM32为例
如果你用过STM32的DMA,你会发现实际芯片里的DMA控制器比教材里的框图复杂得多,但核心逻辑是一致的。STM32的DMA控制器有多个通道(比如STM32F103有7个通道),每个通道有:
- 外设地址寄存器(CPAR):对应教材里的I/O设备地址。
- 存储器地址寄存器(CMAR):对应教材里的主存地址寄存器。
- 数据数量寄存器(CNDTR):对应教材里的字计数器。
- 控制寄存器(CCR):配置传送方向、数据宽度、循环模式、优先级等。
你看,教材里的AR和WC在实际芯片里就是CMAR和CNDTR,只是名字不同。教材里的"数据缓冲寄存器"在实际芯片里可能不是一个独立的寄存器,而是集成在数据通路里。教材里的"中断机构"在实际芯片里对应的是DMA传输完成中断、半传输中断、传输错误中断等。
我在带学生做STM32串口DMA实验时,经常让他们先把教材里的DMAC框图画一遍,然后在STM32参考手册里找到对应的寄存器,一一对应。这样做的好处是,考试的时候看到框图题不会慌,做实验的时候也知道每个寄存器在干什么。
2.3 地址寄存器和字计数器的自动修改逻辑
考试里经常考的一个细节是:DMA传送过程中,主存地址寄存器和字计数器是如何变化的?答案是:每传送一个数据,地址寄存器加1(或减1,取决于传送方向),字计数器减1。当字计数器减到0时,传送结束,DMAC向CPU发出中断请求。
这个"自动加1/减1"是硬件实现的,不需要CPU干预。在实际芯片里,这个功能由DMA通道的硬件逻辑自动完成。你在配置STM32的DMA时,只需要设置起始地址和传送数量,剩下的地址递增和计数递减都是硬件自动做的。
但这里有一个坑:不是所有DMA通道都支持地址递增。比如STM32的DMA在外设端通常不支持地址递增(因为外设寄存器地址固定),但在存储器端支持递增。如果你配置错了,数据就会全部传到同一个地址,覆盖掉之前的数据。这个坑我在实际项目中踩过——当时用DMA采集ADC多通道数据,忘了配置存储器地址递增,结果所有通道的数据都堆在同一个数组元素里,查了半天才发现是DMA配置的问题。
3. DMA传送的三种方式:停止CPU访存、周期挪用、交替访存
3.1 停止CPU访存方式:简单但浪费
这种方式下,DMAC向CPU发出总线请求后,CPU放弃总线控制权,直到DMA传送全部完成才恢复。也就是说,在整个DMA传送期间,CPU完全不能访存。
这种方式的优点是控制简单,DMAC不需要复杂的时序逻辑。缺点是CPU在DMA传送期间无法访存,如果DMA传送时间很长(比如传送一个大文件),CPU的效率会大幅下降。
考试里经常问:这种方式适合什么场景?答案是:适合DMA传送时间短、CPU在此期间不需要访存的场景。比如传送一个数据块,传送时间很短,CPU等一下也无所谓。
3.2 周期挪用方式:DMA和CPU交替使用总线
这种方式下,DMAC不是一直占用总线,而是在需要传送数据时才向CPU发出总线请求,挪用一两个总线周期来传送数据,传送完成后立即释放总线。CPU在DMAC不占用总线的周期里可以正常访存。
这种方式的关键是"周期挪用"——DMAC挪用的是CPU的总线周期。具体来说,当I/O设备准备好一个数据后,DMAC向CPU发出总线请求,CPU在当前总线周期结束后响应,DMAC占用一个总线周期完成数据传送,然后释放总线。下一个数据准备好后,再重复这个过程。
这种方式的优点是CPU和DMA可以交替使用总线,CPU的效率比停止CPU访存方式高。缺点是DMAC需要更复杂的时序控制逻辑,而且每次传送都要请求总线,有一定的开销。
考试里经常考的一个计算题是:假设CPU的工作周期为T,DMA传送一个数据需要占用一个总线周期,I/O设备准备好一个数据的时间为t,问DMA传送对CPU效率的影响。这类题目的关键是搞清楚DMA占用总线的频率和每次占用的时间。
3.3 交替访存方式:把总线周期分给CPU和DMA
这种方式下,总线周期被分成两个时间片,一个给CPU,一个给DMA。CPU和DMA交替使用总线,不需要总线请求和响应过程。
这种方式的优点是效率高,因为不需要总线权的切换开销。缺点是需要更复杂的时序控制,而且对CPU和DMA的时序配合要求很高。这种方式在实际芯片里不太常见,但在考试里是一个重要的知识点。
3.4 三种方式的对比与选择
| 方式 | 总线权切换 | CPU效率 | 控制复杂度 | 适用场景 |
|---|---|---|---|---|
| 停止CPU访存 | 有 | 低 | 简单 | DMA传送时间短 |
| 周期挪用 | 有 | 中 | 中等 | 高速设备、大批量传送 |
| 交替访存 | 无 | 高 | 复杂 | 对效率要求极高的场景 |
在实际芯片里,STM32的DMA采用的是周期挪用方式——DMA在需要传送数据时占用总线,传送完成后释放。你在配置STM32的DMA时,可以设置通道优先级,高优先级的通道可以优先占用总线。这个优先级机制在多个DMA通道同时工作时非常重要。
4. DMA传送的完整过程:从请求到中断的五个阶段
4.1 第一阶段:CPU初始化DMAC
DMA传送开始前,CPU需要做以下初始化工作:
- 设置主存地址寄存器(AR)的初值——告诉DMAC数据要传到主存的哪个位置。
- 设置字计数器(WC)的初值——告诉DMAC要传送多少个数据。
- 设置传送方向——是读(从主存到I/O设备)还是写(从I/O设备到主存)。
- 启动I/O设备——让设备开始准备数据。
- 如果DMAC有多个通道,还需要设置通道优先级。
这些初始化工作是通过CPU执行I/O指令完成的。在实际芯片里,就是配置DMA的各个寄存器。比如在STM32里,你需要配置CPAR、CMAR、CNDTR、CCR等寄存器。
这里有一个考试常考的细节:DMA传送前,CPU需要执行I/O指令来初始化DMAC,但DMA传送过程中,CPU不再执行I/O指令。这个区别是DMA和程序查询、中断方式的重要区别。
4.2 第二阶段:I/O设备发出DMA请求
I/O设备准备好一个数据后,向DMAC发出DMA请求信号。这个信号告诉DMAC"我有一个数据要传送了"。
在实际芯片里,这个请求信号可能是外设的DMA请求线(比如STM32的ADC、USART、SPI等外设都有DMA请求线)。当外设的数据寄存器准备好后,硬件自动拉高DMA请求线,通知DMAC。
4.3 第三阶段:DMAC向CPU发出总线请求
DMAC收到I/O设备的DMA请求后,向CPU发出总线请求信号(HOLD)。这个信号告诉CPU"我要用总线了,请你让一下"。
CPU收到总线请求后,会在当前总线周期结束后响应,发出总线允许信号(HLDA),把总线控制权交给DMAC。注意,CPU不是立即响应,而是等当前总线周期结束后才响应。这个"当前总线周期结束"是一个关键的时间点——如果CPU正在执行一条需要多个总线周期的指令,DMAC需要等到这条指令的所有总线周期都结束后才能拿到总线权。
4.4 第四阶段:DMAC控制数据传送
DMAC拿到总线权后,开始控制数据传送。具体过程是:
- DMAC把主存地址寄存器(AR)的内容送到地址总线上。
- DMAC向主存和I/O设备发出读写控制信号。
- 数据通过数据总线在主存和I/O设备之间传送。
- 每传送一个数据,AR加1(或减1),WC减1。
- 如果WC不为0,继续传送下一个数据;如果WC为0,传送结束。
这个过程中,DMAC需要自己产生地址信号和读写控制信号,所以DMAC必须有时序控制逻辑。在实际芯片里,这个逻辑由DMA通道的硬件自动完成,你只需要配置好寄存器就行。
4.5 第五阶段:传送结束,DMAC发出中断请求
当字计数器(WC)减到0时,DMAC知道所有数据都传送完了。它会释放总线控制权(撤销HOLD信号),然后向CPU发出中断请求,通知CPU"DMA传送完成了"。
CPU收到中断请求后,执行中断服务程序,做后续处理(比如处理数据、启动下一次传送等)。这个中断是DMA传送的"收尾"——虽然DMA传送过程中CPU不参与,但传送完成后CPU需要知道传送结果。
考试里经常考的一个问题是:DMA传送结束后,CPU是如何知道的?答案是通过中断。DMAC在传送结束后向CPU发出中断请求,CPU执行中断服务程序来处理后续工作。这个中断和普通的中断没有本质区别,只是触发源是DMAC而不是I/O设备。
5. DMA与中断的配合:为什么DMA传送完成后还需要中断
5.1 DMA不处理异常,中断负责"善后"
DMA传送过程中,DMAC只负责搬数据,不负责判断数据是否正确、是否需要重传、是否需要通知上层应用。这些"善后"工作是由CPU通过中断服务程序完成的。
比如,在网络数据包接收中,DMA把数据包从网卡搬到主存后,发出中断通知CPU。CPU在中断服务程序里检查数据包的校验和、解析协议头、通知上层应用。如果没有中断,CPU就不知道数据包已经接收完了。
5.2 中断服务程序里通常做什么
在DMA传送完成的中断服务程序里,CPU通常做以下工作:
- 检查DMA传送是否成功(有没有错误标志)。
- 处理接收到的数据(比如解析、校验、转发)。
- 准备下一次DMA传送(重新设置AR和WC)。
- 唤醒等待数据的进程或线程。
在实际的嵌入式开发中,DMA传输完成中断的服务程序通常很短——只做标志位的设置和数据的初步处理,复杂的处理放在主循环里做。这是因为中断服务程序应该尽量短,避免影响其他中断的响应。
5.3 半传输中断和循环模式:实际开发中的常用技巧
在实际芯片里,DMA通常还支持半传输中断和循环模式。半传输中断是指当传送了一半数据时触发中断,这样CPU可以在DMA还在传送后半部分数据时就开始处理前半部分数据,实现"乒乓缓冲"。
循环模式是指DMA传送完成后自动重新开始,不需要CPU重新配置。这在连续数据采集(比如ADC连续采样、串口连续接收)中非常有用。
我在做串口DMA接收时,通常会用循环模式加半传输中断和传输完成中断。这样串口数据可以连续接收,CPU在半传输中断里处理前半缓冲区的数据,在传输完成中断里处理后半缓冲区的数据。这种方式可以避免数据丢失,而且CPU的负担很小。
6. 从408考题到实际配置:DMA知识点在考试和开发中的不同侧重
6.1 408考试里DMA的常见考法
408考试里,DMA的考点主要集中在以下几个方面:
- DMA的基本概念和特点(选择题)。
- DMAC的组成部件和作用(简答题或框图题)。
- DMA传送的三种方式及其区别(选择题或简答题)。
- DMA传送的过程(简答题)。
- DMA与中断的区别和配合(综合题)。
- 计算题:DMA传送对CPU效率的影响。
其中,计算题是很多人的难点。比如:假设CPU的工作周期为100ns,DMA传送一个数据需要占用一个总线周期(100ns),I/O设备准备好一个数据的时间为1μs,问DMA传送对CPU效率的影响。这类题目的关键是算出DMA占用总线的频率和每次占用的时间,然后算出CPU可用时间的比例。
6.2 实际开发中DMA配置的关键参数
在实际开发中,配置DMA时需要注意以下参数:
- 传送方向:外设到存储器、存储器到外设、存储器到存储器。
- 数据宽度:字节、半字、字。外设和存储器的数据宽度要匹配,否则会出现数据错位。
- 地址递增:外设端通常不递增,存储器端通常递增。
- 循环模式:连续传送时使用。
- 优先级:多个通道同时工作时的仲裁。
- 中断使能:传输完成、半传输、传输错误。
这些参数在STM32的DMA配置里都有对应的寄存器位。配置错了,DMA要么不工作,要么工作不正常。
6.3 一个真实的踩坑案例:串口DMA接收数据错位
我曾经在一个项目里用STM32的串口DMA接收数据,配置的是字节宽度、存储器地址递增、循环模式。但接收到的数据总是错位——第一个字节跑到第二个位置,第二个字节跑到第三个位置。查了半天,发现是串口的数据寄存器配置成了半字宽度,而DMA配置的是字节宽度。串口每次发送一个半字(两个字节),但DMA只搬一个字节,导致数据错位。
这个坑的教训是:DMA的数据宽度必须和外设的数据宽度匹配。串口的数据寄存器是8位的,DMA就应该配置成字节宽度;如果串口配置成了9位数据(带校验位),DMA可能需要配置成半字宽度。这个细节在参考手册里不会特别强调,但实际开发中很容易踩坑。
7. DMA传送效率的量化分析:考试计算题和实际性能评估
7.1 考试里的DMA效率计算
考试里经常考的一类题目是:假设CPU的工作周期为T,DMA传送一个数据需要占用一个总线周期,I/O设备准备好一个数据的时间为t,求DMA传送对CPU效率的影响。
这类题目的解题思路是:
- 算出DMA传送一个数据的频率:每t时间传送一个数据。
- 算出DMA每次占用总线的时间:一个总线周期T。
- 算出DMA占用总线的比例:T/t。
- CPU可用时间的比例:1 - T/t。
比如,T=100ns,t=1μs,则DMA占用总线的比例为100ns/1μs=10%,CPU可用时间的比例为90%。
如果考虑DMA传送的总数据量,还需要算出总传送时间和CPU可用时间。比如传送1000个数据,总传送时间为1000×1μs=1ms,DMA占用总线的时间为1000×100ns=100μs,CPU可用时间为900μs。
7.2 实际开发中的DMA性能评估
在实际开发中,评估DMA性能时需要考虑以下因素:
- 总线带宽:DMA传送占用总线带宽,如果总线带宽不足,DMA传送会变慢。
- 总线仲裁开销:每次DMA请求和响应都有开销,如果传送的数据量很小,开销占比会很高。
- CPU访存冲突:DMA传送时CPU如果也需要访存,会产生冲突,降低CPU效率。
- 外设速度:DMA传送速度受外设速度限制,如果外设速度很慢,DMA的效率优势不明显。
在实际项目中,我通常会用示波器或者逻辑分析仪测量DMA传送的实际时间,然后和理论计算值对比。如果实际时间远大于理论值,说明有额外的开销(比如总线仲裁、中断处理等)。
7.3 DMA测速软件和实际测试方法
热词里提到了"dma测速软件",这在实际开发中确实是一个需求。测试DMA性能的方法通常有:
- GPIO翻转法:在DMA传送开始和结束时翻转GPIO,用示波器测量时间。
- 定时器计数法:用定时器记录DMA传送的周期数,算出传送时间。
- 内存对比法:DMA传送完成后,对比源数据和目标数据,确认传送正确性。
- 吞吐量测试:连续传送大量数据,测量单位时间内的传送量。
在STM32上,我通常用定时器来测量DMA传送时间。具体做法是:在DMA传送开始前启动定时器,在DMA传输完成中断里停止定时器,读取定时器的计数值,算出传送时间。这种方法精度高,而且不需要额外的硬件。
8. 不同芯片平台的DMA实现差异:STM32、GD32、AT32的对比
8.1 STM32的DMA架构
STM32的DMA控制器通常有多个通道(比如STM32F103有7个通道,STM32F4有16个流),每个通道可以独立配置。STM32的DMA支持外设到存储器、存储器到外设、存储器到存储器三种传送方向,支持字节、半字、字三种数据宽度,支持循环模式和普通模式。
STM32的DMA请求映射是固定的——每个通道对应特定的外设请求。比如STM32F103的DMA1通道4对应USART1_TX,通道5对应USART1_RX。你在配置DMA时,需要查参考手册的DMA请求映射表,确认哪个通道对应哪个外设。
8.2 GD32的DMA架构
GD32的DMA架构和STM32非常相似,很多寄存器都是兼容的。但GD32的DMA在某些细节上有差异,比如中断标志位的清除方式、通道优先级的仲裁方式等。如果你从STM32迁移到GD32,大部分DMA代码可以直接用,但需要注意这些细节差异。
8.3 AT32的DMA架构
AT32的DMA架构也和STM32相似,但在DMA请求映射上有所不同。AT32的DMA通道可以灵活映射到不同的外设,不像STM32那样固定。这个灵活性在某些应用场景下很有用,但也增加了配置的复杂度。
我在实际项目中用过STM32、GD32和AT32的DMA,感受是:STM32的DMA配置最成熟,文档最全;GD32的DMA和STM32最接近,迁移成本最低;AT32的DMA最灵活,但需要仔细阅读参考手册。不管用哪个平台,核心概念都是一样的——地址、计数、方向、宽度、模式、优先级。
8.4 跨平台DMA配置的通用思路
不管你用哪个平台的DMA,配置的思路都是一样的:
- 确定传送方向(外设到存储器、存储器到外设、存储器到存储器)。
- 确定数据宽度(字节、半字、字)。
- 配置源地址和目标地址。
- 配置传送数量。
- 配置地址递增模式。
- 配置循环模式或普通模式。
- 配置优先级。
- 使能中断(如果需要)。
- 使能DMA通道。
这个思路在STM32、GD32、AT32上都适用,只是寄存器的名字和位定义不同。
9. DMA在实际项目中的典型应用场景
9.1 串口DMA收发:最常用的场景
串口DMA收发是DMA最常用的场景之一。用DMA接收串口数据时,CPU不需要每个字节都中断一次,而是等一整批数据接收完成后才中断一次。这大大降低了CPU的负担,特别是在高波特率下。
配置串口DMA接收的步骤是:
- 配置串口的波特率、数据位、停止位、校验位。
- 配置DMA通道:源地址为串口数据寄存器,目标地址为接收缓冲区,数据宽度为字节,地址递增(存储器端),循环模式。
- 使能串口的DMA接收请求。
- 使能DMA通道。
- 在DMA传输完成中断里处理数据。
这里有一个坑:串口DMA接收时,如果数据量不确定(比如接收不定长数据),需要用空闲中断来判断一帧数据是否接收完成。STM32的串口有空闲中断(IDLE),当串口总线空闲时触发中断,可以在中断里计算接收到的数据长度。
9.2 ADC多通道采集DMA:提高采样效率
ADC多通道采集时,用DMA可以把转换结果自动搬到内存数组里,不需要CPU每次转换完成后都去读ADC数据寄存器。配置ADC的DMA时,需要设置ADC为扫描模式、连续转换模式,DMA为循环模式,存储器地址递增。
这个场景我在实际项目中用过很多次。比如用STM32的ADC采集8个通道的模拟量,配置DMA循环模式,ADC连续转换,DMA自动把8个通道的数据搬到数组里。CPU只需要定期读取数组就行,完全不需要干预ADC的转换过程。
9.3 SPI DMA传输:高速数据交换
SPI的速率通常比串口高很多,用DMA传输可以充分发挥SPI的速率优势。配置SPI DMA时,需要注意SPI的数据宽度(8位或16位)和DMA的数据宽度匹配。
9.4 存储器到存储器DMA:数据搬运
存储器到存储器的DMA传送在数据搬运场景中很有用。比如把一块数据从Flash搬到RAM,或者把一块数据从RAM的一个区域搬到另一个区域。这种传送不需要外设参与,DMAC直接控制主存的读写。
需要注意的是,不是所有DMA控制器都支持存储器到存储器传送。STM32的DMA1不支持存储器到存储器,DMA2支持。在配置前需要确认芯片的DMA是否支持这个功能。
10. 复习DMA知识点的几个实用建议
10.1 用"数据流向图"串联所有知识点
DMA的知识点比较散——有DMAC的结构、有传送方式、有传送过程、有和中断的配合。我建议用一张"数据流向图"把这些知识点串起来:从I/O设备发出DMA请求,到DMAC向CPU请求总线,到DMAC控制数据传送,到传送完成后发出中断。这张图可以把大部分知识点都涵盖进去。
10.2 把教材框图和实际芯片寄存器对应起来
如果你既考研又做嵌入式,最好的复习方式是把教材里的DMAC框图和实际芯片的DMA寄存器对应起来。教材里的AR对应CMAR,WC对应CNDTR,DR对应数据通路,中断机构对应DMA中断。这样既加深了对教材的理解,又提高了实际开发能力。
10.3 计算题要多练,搞清楚每个时间参数的含义
DMA的计算题不难,但容易搞混时间参数。关键是要搞清楚:CPU的工作周期、总线周期、DMA传送一个数据的时间、I/O设备准备好一个数据的时间,这几个参数之间的关系。多练几道题,把每个参数的含义搞清楚,考试时就不会慌。
10.4 实际配置DMA时先画时序图
在实际配置DMA时,我建议先画一张时序图——标出DMA请求、总线请求、总线响应、数据传送、中断请求的时间关系。这张图可以帮助你理解DMA的工作过程,也可以帮助你在调试时定位问题。比如如果DMA不工作,你可以检查是DMA请求没发出,还是总线请求没响应,还是数据传送出错。
10.5 注意DMA和Cache的一致性问题
在高性能处理器(比如ARM Cortex-A系列)中,DMA和Cache的一致性是一个重要问题。DMA直接访问主存,不经过Cache,如果Cache里有对应的数据,就会出现数据不一致。解决方法是使用一致性内存或者手动刷新Cache。这个问题在408考试里不考,但在实际开发中很重要。
我在实际项目中遇到过这个问题:用DMA接收网卡数据后,CPU读到的还是Cache里的旧数据。后来在DMA传送前刷新了Cache,问题就解决了。这个坑在嵌入式Linux开发中很常见,做裸机开发时如果用了Cache,也需要注意。
10.6 多通道DMA的优先级仲裁
当多个DMA通道同时请求时,DMAC需要仲裁哪个通道先传送。STM32的DMA优先级分为四级:最高、高、中、低。如果两个通道的优先级相同,则通道号小的优先。在实际项目中,高速外设(比如SPI、ADC)应该配置高优先级,低速外设(比如串口)可以配置低优先级。
这个优先级仲裁机制在考试里不考,但在实际开发中很重要。如果优先级配置不当,高速外设的数据可能会丢失。
10.7 DMA传输错误的中断处理
DMA传输过程中可能会出现错误,比如总线错误、地址错误等。STM32的DMA有传输错误中断,可以在中断里处理错误。在实际项目中,我通常会在DMA传输错误中断里记录错误标志,然后重新初始化DMA通道。这个错误处理机制在考试里不考,但在实际开发中是必须的。
10.8 从408考题看DMA的考试重点
最后说一下408考题里DMA的考试重点。根据我对历年真题的分析,DMA的考点主要集中在:
- DMA的基本概念(选择题,1-2分)。
- DMAC的组成(简答题,2-3分)。
- DMA传送方式(选择题或简答题,2-3分)。
- DMA传送过程(简答题,3-4分)。
- DMA与中断的区别(综合题,3-4分)。
其中,DMA传送过程和DMA与中断的区别是高频考点,几乎每年都考。计算题出现的频率较低,但一旦出现就是大题。
我在复习DMA时,会把教材里的DMA章节反复看三遍:第一遍理解概念,第二遍记忆细节,第三遍做课后题。唐朔飞的课后题里有很多DMA的题目,做完之后对知识点的理解会深很多。
10.9 用实际项目经验反哺考试理解
如果你既做嵌入式开发又考研,你会发现实际项目经验对考试理解有很大帮助。比如你实际配置过STM32的DMA,再来看教材里的DMAC框图,就会觉得非常直观——AR就是CMAR,WC就是CNDTR,中断机构就是DMA中断。这种"理论联系实际"的学习方式,比死记硬背效率高得多。
10.10 注意DMA在不同教材里的表述差异
不同教材对DMA的表述可能有差异。比如唐朔飞教材里把DMA传送方式分为三种:停止CPU访存、周期挪用、交替访存。而有些教材可能分为两种或四种。考试时以目标院校的指定教材为准。如果你不确定用哪本教材,可以查目标院校的考试大纲。
我在带学生复习时,会让他们先确认目标院校的指定教材,然后按照教材的表述来复习。不同教材的表述差异不大,但细节上可能有出入,考试时以指定教材为准最稳妥。