ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DMA方式深度拆解:从周期窃取到STM32串口DMA实战

DMA方式深度拆解:从周期窃取到STM32串口DMA实战 考研计算机组成原理这部分最容易让408考生“背了半天但不会算”的就是I/O设备与主机之间的信息传送控制方式。程序查询、程序中断、DMA、通道这四类控制方式很多同学背得滚瓜烂熟可一到周期窃取的计算题、DMA与中断的辨析选择题照样出错。这很正常因为DMA方式牵扯硬件结构、总线控制、Cache一致性一堆东西光靠记忆而不理解它的行为逻辑题目稍微变个问法就懵了。今天这篇就专门把DMA方式从头到尾拆一遍从它出现的动机、硬件组成、传送流程到408真题里常考的计算套路最后再聊聊我实际在嵌入式项目里用DMA踩过的坑。不管你是刷王道的考研党还是读完唐朔飞教材想补强这块的学生这篇文章都能帮上忙。适合谁看备考408计算机学科基础综合的考生尤其I/O这一章容易被轻视、平时靠硬背的同学学完计算机组成原理想串一遍体系的人另外搞单片机和嵌入式的老哥们也可以看看DMA在真实硬件上的玩法跟书本知识是能对上的。1. 为什么需要DMAI/O控制方式的演进逻辑1.1 程序查询方式的瓶颈程序查询方式也叫程序轮询它的思想很原始CPU不断循环检查I/O设备的状态寄存器判断设备是否准备好准备好了就进行一次数据传送。缺点一目了然CPU把大量时间耗在“等”上。一个高速外设每秒可能产生几百万次数据CPU为了等一次数据可能要在状态检测上花费成百上千个时钟周期。假如设备传送一个字节需要10us而CPU检测一次状态只要0.1us那CPU在每次传送中大约有99%的时间都在空转。对于打印机这种慢速设备还能忍对于磁盘、高速网卡、高分辨率显示器这种高速设备程序查询基本是不可行的。这也是为什么教材里讲I/O控制方式时总是把程序查询排在第一位——它的逻辑最简单也最能让你体会到“CPU亲自下场搬砖”有多浪费。很多同学觉得这节简单但我建议你多想想CPU主频越来越高外设却五花八门、速度差异巨大如果每次搬几个字节都要CPU亲力亲为系统的整体吞吐量会被严重拖累。理解了这一点你才能真正明白后面几种方式的设计动机。1.2 程序中断方式的改进与不足程序中断方式就是对程序查询的改进CPU不需要反复问“你好了没”设备准备好了以后主动给CPU发一个中断请求信号CPU停下目前的工作转入中断服务程序搬运数据搬完再返回原工作。这套机制已经好很多了CPU至少不用空等可以去做别的事。但它的关键问题在于一次中断只适合传送一个字节或一个字。原因很简单中断处理有开销——保护现场、恢复现场、跳转、返回这些操作本身就要消耗不少CPU周期。要是设备每来一个字节就触发一次中断CPU整天就在“保护现场-搬数据-恢复现场”里转圈。而高速外设往往是块设备磁盘一读就是几百字节甚至几KB中断方式根本喂不饱它。所以本质上程序查询和程序中断有个共同的毛病数据传送的每一步都要经过CPU。要么CPU主动搬运要么CPU被中断去搬运。那能不能让设备自己把一大块数据直接送进内存CPU只在开头和结尾插一句嘴这就是DMA方式出现的动机。控制方式数据传送单位CPU参与程度主要优点主要缺点程序查询字节/字全程参与控制简单CPU极度浪费程序中断字节/字每次传送都要CPU执行中断服务程序相比轮询提高CPU利用率频繁中断开销大DMA数据块启动和收尾时介入传送过程不参与传送速度快适合高速块设备硬件成本高2. DMA方式的核心原理与硬件结构2.1 DMA传送的三个阶段DMA全称Direct Memory Access直接存储器存取。它做的事情可以概括成一句话外设和内存之间直接传数据CPU只负责发起和收尾中间一大段数据搬运完全由DMA控制器DMAC接管。我习惯把整个DMA传送过程分成三个阶段理解这也是考试里经常让你“说明过程”的得分点。第一阶段叫预处理CPU给DMA控制器“交代工作”。具体包括设置传送方向内存→外设还是外设→内存设置主存缓冲区首地址设置传送的数据块长度并向DMA控制器发出“启动”命令。这些参数都被写入DMA控制器的寄存器里。这个阶段CPU是主动的它干完这些事后就继续执行自己的程序了。第二阶段是数据传送这是DMA真正干活的阶段。DMA控制器向总线的仲裁机构发出总线请求获得总线使用权之后由它控制外设和内存之间的数据交换。数据流不经过CPU内部的寄存器所以CPU可以在传送过程中继续执行不访存的指令或者虽然访存但被DMA优先。传送过程中每传完一个字节/字DMA控制器内部的字数计数器减1主存地址寄存器加1直到计数器归零表示一块数据传送完毕。第三阶段是后处理。当字数计数器等于0DMA控制器会向CPU发出一个中断请求告诉CPU“我这块搬完了”。CPU响应中断后去执行中断服务程序做后续的校验、判断是否正确、决定是否继续传送下一块等工作。注意这个中断不是普通的数据传送中断而是“DMA传送完成”的通告中断一次中断对应一整块数据不是每个字节都来一次。三个阶段只要记清CPU先配置然后撒手不管最后来收尸。很多题目专门挖坑说“DMA传送过程中CPU完全不参与”这句话如果理解成CPU连总线、连初始化都不管那就错了。准确的表述是数据传送过程中CPU不参与数据的具体搬运。2.2 DMA控制器的硬件组成想要把DMA过程讲明白必须认识DMA控制器里的几个关键部件。看到这里别绕记住它们是干什么的后面计算题和学习嵌入式DMA都会用到。主存地址寄存器MAR用来存放当前要访问的主存地址。传送开始前由CPU写入首地址每传送一个数据该寄存器内容自动加1指向下一个主存单元。它决定了数据块应该放在内存哪里。传送字数计数器WC记录还剩下多少个数据没传完。初始值由CPU写入待传送的数据总量每传送一个数据自动减1减到0时发出“计数为零”信号表明这块数据已经处理完。它决定了这个块有多大。数据缓冲寄存器DBR用来暂存外设与主存之间的数据。以“外设→内存”为例外设把一个字送到DBR里然后DMA控制器再把DBR内容写进内存。别看它只是个暂存器它让外设和内存之间的速度和宽度差异得到缓冲。设备地址寄存器DAR或者叫I/O控制逻辑用来存放I/O设备的设备编码指明这次跟哪个设备进行数据交换。同时里面还包含传送方向的控制逻辑是“读外设写内存”还是“读内存写外设”。除了这些整个DMA控制器还包含DMA控制逻辑负责管理DMA请求、总线请求、总线应答等信号和中断机构用于后处理阶段通知CPU。硬件结构这块408很少让你画完整框图但非常喜欢考“CPU初始化DMA控制器时需要对哪些寄存器赋值”或者“DMA传送过程中哪个寄存器的值在变化”。记住CPU初始化的是主存首地址、传送字数、设备地址、传送方向DMA每传一个字主存地址寄存器自动1字数计数器自动-1。就这两句话基本能应付大部分小题。2.3 DMA与中断方式的本质区别DMA和程序中断经常被放在一起考很多同学背完二者的定义就以为懂了其实它们有本质区别。第一响应时机不同。程序中断方式是在一条指令执行结束后响应CPU要停下来去执行中断服务程序DMA是总线空闲或CPU访存间隙时响应CPU不一定停止执行程序只是让出总线或者让出一个存取周期带宽。第二传送单位不同。程序中断以字节或字为单位来一个数据中断一次DMA以数据块为单位一整块数据传完才中断一次。第三CPU的角色不同。程序中断方式里CPU是数据的搬运工数据要从外设读到CPU寄存器再从CPU寄存器写到内存全程经过CPUDMA方式里DMA控制器才是搬运工数据不经过CPU内部寄存器CPU只负责开局和收尾。第四CPU响应DMA请求的时机可以更细。中断方式一般要等当前指令执行完因为要保存完整的程序断点DMA请求不一样它通常只要求CPU让出一个总线周期或存取周期CPU可以在指令执行过程中的某个总线周期放弃总线控制权。这也是“周期窃取”能做到那么细的原因。注意408真题经常这么出——“DMA方式与程序中断方式的区别不包括哪一项”。四项里经常混入“DMA传送过程中CPU不能执行程序”这种话那是一定要排除的错误项。DMA传送过程中CPU大部分时间是可以继续干活的Swiss-DMA就是靠这个才把CPU利用率拉上去的。3. DMA的数据传送过程与总线控制3.1 DMA占用总线的三种方式DMA控制器要和外设、内存交换数据必然要占用总线。总线资源的争夺主流的方案有三类这是408一节的必考选择题素材。停止CPU访存方式。DMA控制器请求总线后CPU把自己的总线使用权完全让出在一个相对较长的时间段内DMA独占总线成批传送数据传完再把总线还给CPU。这种方式控制简单适合高速外设传送整块数据。缺点很直接DMA工作期间CPU基本无法访存如果程序里有大量访存操作CPU会处于停滞状态等于DMA期间CPU被“绑住”了。交替分时访问方式。CPU和DMA交替使用总线DMA在固定的半个系统周期工作CPU在另外半个周期工作。这种方式对CPU最友好因为每个机器周期里CPU总有一部分时间能访问内存但它要求总线控制器的时序设计更复杂而且DMA的传送速率被限制在总线带宽的一半以内适合不需要DMA连续占满总线的场景。周期挪用周期窃取方式。这是我的重点因为408最常考它。DMA每传送一个字节/字就向CPU“借”一个存取周期在这个周期内把数据传到内存或从内存读出然后立刻把总线还给CPU。CPU忙碌时DMA请求会被暂时挂起但不会像停止方式那样让CPU完全停下来CPU也会在某个总线周期间隙接受DMA的请求。简单说DMA用“小步快跑”的方式把一个完整数据块的传送拆成许多次周期挪用。3.2 周期窃取的“代价”到底怎么算很多同学把周期窃取理解成“DMA没有花CPU的时间”这是不对的。DMA请求一个存取周期后CPU虽然不用亲自搬数据但那个存取周期里CPU自己没法访存了如果CPU本来要用这个周期读指令或读写数据就得等一等。所以周期窃取照样会“占用CPU时间”只是占用的粒度很细。实际解题时核心就是算清楚在一段时间内DMA一共挪用了多少个存取周期占这段时间的比例是多少。我拿一个经典例题来演示。假设某设备每32us产生一次DMA请求每次请求DMA传送一个4字节的数据块。主存存取周期为1usCPU其他时间可以正常工作。问在一秒内周期窃取对CPU的影响大约是多少先算一秒内DMA请求次数1秒 / 32us 1000000us / 32us 31250次。每次请求挪用一个存取周期也就是1us。所以一秒内DMA占用总线的时间是31250 us 31.25ms。占CPU可用时间的比例就是 31.25ms / 1000ms 3.125%。也就是说即使DMA搬运了125KB的数据CPU实际被“偷走”的时间也只有约3.1%剩下的时间照样可以跑程序。这就是周期窃取的核心优势——用很小的代价换取大数据块的搬运。考试里常见的变形有两种。一种是告诉你设备传输率比如“某磁盘转速7200转/分每道32个扇区每扇区512字节平均寻道时间多少”让你先算出传输率再算DMA请求频率和挪用的存取周期比例。另一种是反过来给了DMA占用CPU的比例上限计算缓存区应设置多大、DMA每次应传送多少个字。解题时千万抓住一个等式DMA挪用时间 请求次数 × 每次占用的存取周期数。别把“每次传送4字节”和“每传送一个字节挪用一个周期”搞混题目如果说数据总线宽度是16位、每次DMA请求传送一个字那一次请求就只挪用一个存取周期如果说32位那一次请求可能对应两个存取周期。题目怎么给你就怎么拆。提示做周期窃取计算题先统一单位。us、ms、s混在一起算错的人特别多。我自己的习惯是全部化成ns或us列完等式再换算成百分比。稳一点不容易翻车。4. 408考试中的DMA考点归纳与解题思路4.1 高频选择题考点DMA这一节在408真题里出现的频率不低但集中在几个固定的知识点上。我整理了一份清单复习时照着过一遍就好。第一个考点区分DMA与中断方式。常见问法是“以下关于DMA和中断方式的说法正确的是”。正确选项经常是“DMA一次传送一整块数据中断一次传送一个字节”错误选项经常是“DMA和中断一样都由CPU执行服务程序完成数据传送”。第二个考点DMA传送前后CPU做的事情。记住“预处理和后处理才需要CPU传送过程不需要”。所以“传送过程中CPU需要对数据做加工”是错误的。第三个考点DMA请求和中断请求的优先级。系统里同时存在DMA请求和中断请求时DMA请求优先因为DMA通常要求实时性更高的数据传送而且它一次只挪用很短的总线时间不会把系统拖太久。中断请求一般要等当前指令执行完后响应DMA请求可能在指令执行过程中就被响应。第四个考点周期窃取和停止CPU访存的选择。题干给一个高速设备问更适合哪种DMA传送方式一般选周期挪用因为它兼顾了CPU利用率和设备传输率。第五个考点Cache与DMA的一致性。这个在组成原理Cache章节和DMA章节接缝处出现问的往往是“如果内存中某块数据被DMA更新但Cache里还留着旧值系统如何解决”。常见的思路包括让DMA只访问内存、DMA更新后把Cache中对应行置为无效、或者采用某种一致性协议。选择题里能认出“置无效”是合法做法就够了。4.2 综合题与计算题套路408大题的I/O部分DMA计算是最容易得分的题因为它套路固定。我来梳理一套通用的解题流程。第一步从题干中提取设备参数。看清楚设备传输率是多少字节/秒或者设备多久发一次DMA请求、每次请求传多少字节/字。把单位统一成字节每秒或者us一次。第二步计算DMA请求频率。如果给了“每T us请求一次”那一秒请求次数 1000000 / T。如果给了设备传输率R B/s和每次请求传送B字节那一秒请求次数 R / B。第三步计算每次请求挪用的存取周期数。注意题干给定的数据总线宽度。如果每次传送一个字节每次请求可能占用一个存取周期如果传送32位但内存存取周期对应的数据宽度是16位可能一次请求占两个周期。这部分要看清楚“一次DMA请求”和“一个存取周期”之间的关系。第四步算总挪用时间。公式是总挪用时间 每秒请求次数 × 每次占用存取周期数 × 存取周期。然后算出占CPU总时间的比例或者算CPU剩余时间。第五步别忘了判断系统的可行性。有些题会问你“该设备能否正常工作”这时候算出每秒总传送时间后要与设备要求的传输时间做对比。如果设备的平均传输周期大于DMA每秒钟能挪用的最大周期数量那系统就喂不饱设备要考虑改成停止CPU访存或提高总线宽度。我把常考的公式列成一张速查表需要求的量公式每秒DMA请求次数每秒数据量 / 每次DMA传送的数据量DMA挪用总时间每秒请求次数 × 每次挪用存取周期数 × 存取周期CPU被DMA占用比例DMA挪用总时间 / 1秒设备传输周期每次请求间隔 单次数据量 / 设备传输速率数据块传送总时间块大小 / 设备传输速率 预处理和后处理时间注意预处理和后处理的耗时一般不会在计算题中给但偶有分析题问你“DMA方式总体效率为什么不等于传送时间/总时间”答案就是除了传送还有初始化、中断检测这些开销。5. 从408概念到嵌入式DMA实践5.1 为什么嵌入式里到处是DMA很多人学组成原理觉得DMA离自己很远其实嵌入式开发里DMA几乎是标配。STM32这类MCU上串口、SPI、ADC、定时器、存储器之间为了不占用CPU都会用DMA搬运数据。串口一秒钟接收几万个字节如果全靠中断CPU每一两分钟就把大部分时间花在进中断上了。开了DMA之后串口收到一帧数据直接进内存收满了再告诉你一次CPU只在最后处理整包数据跟组成原理里的DMA后处理一个道理。热词里那么多“AT32串口DMA”、“GD32 DMA”、“STM32F407 ADC DMA”说明在实际项目中遇到的问题本质上还是不理解DMA的时序和缓冲管理。我举两个最常见的例子。第一个例子是串口DMA发送。你在CubeMX里把USART1的TX配置成DMA请求然后调用HAL_UART_Transmit_DMA。调用返回后数据并不是立刻发完而是由DMA逐个字节往串口数据寄存器里塞。如果你紧接着改动了发送缓冲区里的数据DMA很可能把修改后的数据也发出去这就是典型的“DMA还在读你已经改了”。正确做法是等DMA传输完成中断回调里再释放缓冲区。第二个例子是ADC连续采样 DMA循环模式。ADC每次转换完触发DMA搬运DMA把结果按顺序放进一个环形缓冲区CPU只有在缓冲区半满或全满时才去读取。这样做的好处是高频率采样时CPU不用每次转换都进中断只要定期把缓冲区里的数据取走计算就行。缺点也很明显如果CPU取数据的速度跟不上DMA写缓冲区的速度新数据会把旧数据覆盖导致数据错位或丢失。这些实际场景里的问题本质上都是对“DMA传送过程中CPU不参与具体搬运”这一句话的朴素应用。你只要想清楚数据在谁手里、什么时候更新、什么时候读完就不会踩大坑。5.2 STM32串口DMA发送的关键配置嵌入式DMA的配置基本是照着寄存器或CubeMX填参数。我不打算贴一大堆寄存器代码只把核心环节说清楚。第一步选择DMA通道串口的TX找对应的DMA请求映射比如STM32F1上USART1_TX映射到DMA1通道4。第二步设置数据方向。从内存到外设还是从外设到内存对应到DMA_CCR寄存器的DIR位。第三步设置缓冲大小和地址。内存地址是你要发送的数组首地址外设地址是USART_DR寄存器地址。传送长度就是数组长度。第四步启动DMA并尽量使能传输完成中断否则你不知道什么时候能安全修改发送缓冲区。实际调试时我自己比较推荐用这个最小流程验证uint8_t txBuf[] hello dma; HAL_UART_Transmit_DMA(huart1, txBuf, sizeof(txBuf));在发送完成回调里加一个标志位。如果标志位没置位就不允许更新txBuf。这套逻辑跟我前面讲的后处理阶段完全对应——CPU在DMA传送前配置地址、长度传送中干别的传送完成后再收到中断。只是嵌入式里把这个过程变成了函数调用和回调。6. 实操心得DMA调试中的常见问题与避坑6.1 DMA传输未完成就读取数据这个坑实在太常见了。你用DMA接收串口数据主循环里立刻判断缓冲区有没有新数据结果发现收到的是一堆残缺帧。原因很简单DMA传输是异步的你读的时候可能只写了一半。排查思路看传输完成标志或者中断。如果不想用中断可以轮询DMA的状态寄存器但记住轮询只能读取“是否完成”不要用等待方式来倒逼DMA提速那样还不如不用DMA直接把CPU拖死。6.2 缓冲区大小与传送长度不匹配DMA控制器里的字数计数器决定了它搬运多少个字节/字。如果程序里写的传送长度比缓冲区大DMA会越界写内存造成不可预知的程序崩溃。反过来如果长度比缓冲区小数据可能只传了一半。这个问题映射回书本就是预处理时CPU要设置传送字数计数器。实际项目中我见过有人把数组声明成局部变量然后把地址传给DMA等到DMA慢慢搬运时局部变量所在的栈空间已经被释放或复用了。DMA这时访问的是野指针程序直接跑飞。所以DMA用的缓冲区要么是全局变量要么是经过内存管理分配的、生命周期确保足够长的内存块。6.3 DMA优先级与中断优先级设置DMA也有多个通道比如STM32的DMA1和DMA2。两个DMA通道同时请求总线时优先级高的先执行。如果ADC的DMA和串口DMA都开了优先级设置不当就可能出现高频采样丢数据或者串口数据断流。考试时你不需要知道具体配置值但你要理解它的思想DMA请求之间、DMA请求与CPU访存之间都有仲裁机制优先级高不代表无限抢占只是让高实时性的设备先拿到总线。408选择题里偶尔出现“DMA请求的优先级高于普通中断请求”这背后的原因就是DMA所服务的高速设备如果等待太久会丢失数据。6.4 DMA与Cache一致性的坑如果系统带CacheDMA直接访问内存时Cache里可能还留着旧值。比如CPU先读到某地址的内容该地址随后被DMA写入新数据但Cache没更新CPU再去读时拿到的还是旧数据。这就是一致性问题。实际项目的通用解法几种一是把DMA缓冲区设置成“不可缓存”的区域二是在DMA传输完成后对相关Cache行执行清理/无效操作三是从设计上避免同一块数据同时被Cache和DMA操作。408考试不要求你写代码但选择题里见到“使Cache行无效”作为解决方法要知道它是正确的。6.5 常见问题速查表遇到的问题可能的原因排查与解决方法DMA收的数据是残缺的主循环读取时机太早用传输完成中断/标志位等待DMA传送结束程序莫名崩溃DMA长度超过缓冲区核对缓冲区和传送长度禁止越界串口DMA发送只发一次发送完成标志没清除在完成回调里检查状态重新启动DMA高频采样数据错位ADC DMA优先级低于其他DMA调整DMA通道优先级CPU访问的数据没更新Cache一致性问题对DMA相关Cache行做无效/清理操作我在实际项目里最大的体会是DMA并不是什么高高在上的“黑科技”它就是外设和内存之间的一条高速专线。你只要想明白三个阶段CPU配置、DMA自行传送、中断收尾再记住它偷用的是总线存取周期而不是“让CPU停止运转”408这部分就不会再成为丢分点。学完教材上的概念再到单片机上调一次DMA发送你会发现那些寄存器、中断、缓冲区的配置居然是组成原理课本里考点的一比一还原。这也是为什么我建议考研的同学别只刷题有条件的话找一个带DMA的MCU开发板实际跑一遍哪怕只是点亮一个用DMA驱动的LED流水灯你对“周期窃取”和“传送完成中断”的理解都会上一个台阶。
返回列表