ARTICLE DETAIL

资讯详情

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

DMA原理与408考点解析:从总线控制权到STM32实战

DMA原理与408考点解析:从总线控制权到STM32实战 1. 从一道408真题说起DMA到底在考什么如果你正在啃计算机组成原理尤其是唐朔飞那本教材或者王道复习指导翻到I/O那一章的时候大概率会被一堆控制方式搞得头大——程序查询、程序中断、DMA、通道名字听着都差不多但考试偏偏就爱在这几个地方挖坑。2024年408第45题考的就是DMA具体考了什么考的是DMA方式和中断方式在数据传送过程中的本质区别以及DMA控制器的基本组成和工作流程。这道题其实暴露了一个很典型的问题很多人学DMA的时候只记住了“不需要CPU干预”这句话但真要问DMA控制器里面有哪些寄存器、DMA请求什么时候发、总线归谁管、传送完了怎么通知CPU就答不上来了。而这恰恰是408喜欢考的地方——它不考你背概念它考你理解整个数据通路和控制流程。所以这篇文章我打算把DMA方式从头到尾拆一遍。不是那种教科书式的罗列而是按照一个从业者的思路把DMA为什么会出现、它怎么工作的、和中断方式到底差在哪里、考试会怎么考、实际芯片里DMA又是怎么实现的全部串起来讲清楚。不管你是正在准备408考研还是做嵌入式开发时经常用STM32的DMA但没深究过原理这篇文章应该都能帮你把这块知识补完整。先给一个最直观的结论DMA的本质是让数据在内存和外设之间直接搬不经过CPU的寄存器。CPU只负责发起和收尾中间搬砖的活交给DMA控制器。这个“搬砖”的过程就是408考试里反复出现的核心考点。2. DMA方式的核心设计思路拆解2.1 为什么需要DMA从程序查询到中断的演进逻辑要理解DMA得先理解它前面那两种方式为什么不够用。程序查询方式最简单CPU不断轮询外设状态寄存器外设准备好了就传一个数据。问题在于CPU完全被绑死了传一个字节就要执行一堆指令效率极低。你想想一个硬盘读1MB数据如果每个字节都靠CPU轮询来搬那CPU啥也别干了。程序中断方式进了一步外设准备好之后主动发中断告诉CPUCPU暂停当前程序去处理数据传送。这比轮询好多了CPU不用一直等着。但问题依然存在每次传送一个数据或者一小批数据都要打断CPU保存现场、执行中断服务程序、恢复现场。对于高速外设比如磁盘、网卡数据来得太快中断太频繁CPU大部分时间都在处理中断真正干活的 time 被切得稀碎。这里有个关键的数字对比假设磁盘传输速率是40MB/s每传一个字节中断一次那每秒就是4000万次中断。CPU根本扛不住。即使每次传一个扇区512字节每秒也有将近8万次中断每次中断开销假设100个时钟周期那光中断处理就吃掉800万个时钟周期。对于1GHz的CPU来说这已经占了将近1%的时间而且这还没算上中断响应延迟和总线争用的开销。所以DMA的思路就很自然了既然CPU只是搬砖的那为什么不找个专门的搬砖工DMA控制器就是这个搬砖工。CPU只需要告诉DMA控制器从哪个外设读、写到内存哪个位置、传多少字节、传完了告诉我一声。然后CPU就可以去干别的事了DMA控制器自己完成所有的数据搬运。2.2 DMA控制器的基本组成与寄存器配置DMA控制器不是随便就能搬数据的它需要知道几件事数据从哪来、到哪去、搬多少、搬完了怎么办。对应到硬件上就是几个核心寄存器。寄存器名称功能说明408考试常见考法内存地址寄存器存放数据在内存中的起始地址每传一个数据自动加1或减1考地址递增/递减模式以及什么时候需要重置传送长度计数器记录剩余需要传送的字节数或字数每传一个减1减到0表示完成考计数器的初值设置和溢出判断设备地址寄存器存放外设的地址或端口号用于选中目标外设考DMA控制器的设备寻址方式控制与状态寄存器存放传送方向、传送模式、中断使能等控制位以及完成、错误等状态位考控制位的含义和状态位的读取这几个寄存器是DMA控制器的核心。408考试里经常考的是DMA控制器在传送前需要CPU对它进行初始化也就是把这些寄存器都设置好。初始化完成后CPU启动DMA然后DMA控制器就开始工作。这里有个容易混淆的点DMA控制器里的内存地址寄存器和CPU里的MAR不是一回事。MAR是CPU用来访问内存的DMA控制器里的地址寄存器是DMA自己用来访问内存的。当DMA控制器获得总线控制权后它会把自己的地址寄存器内容放到地址总线上把数据放到数据总线上完成一次内存访问。这个过程完全不经过CPU的寄存器。2.3 DMA与中断的本质区别总线控制权的转移很多人学DMA的时候最大的困惑就是DMA和中断到底差在哪不都是外设通知CPU吗区别在于总线控制权。中断方式下外设发中断请求CPU响应中断后CPU仍然掌握总线控制权。数据传送是通过CPU执行指令完成的比如CPU执行一条“从外设端口读数据到累加器”的指令然后再执行一条“把累加器数据写到内存”的指令。数据经过了CPU的寄存器总线始终由CPU控制。DMA方式下外设发DMA请求DMA控制器向CPU申请总线控制权。CPU在当前总线周期结束后把总线让给DMA控制器。DMA控制器获得总线控制权后直接控制总线完成内存和外设之间的数据传送。数据不经过CPU寄存器总线由DMA控制器控制。这个区别是408考试的核心考点。2024年第45题就考了这个DMA方式下数据传送不需要CPU干预但DMA控制器需要向CPU申请总线控制权。CPU让出总线后DMA控制器完成一个数据传送然后可能继续申请下一个也可能释放总线。还有一个细节DMA请求和中断请求是两种不同的请求。DMA请求是请求总线控制权中断请求是请求CPU执行中断服务程序。DMA请求的优先级通常高于中断请求因为DMA传送不能等太久否则数据会丢失。3. DMA的三种传送方式与总线仲裁机制3.1 停止CPU访存、周期挪用与交替访存DMA控制器获得总线控制权后怎么和CPU共享内存总线这里有三种方式408考试经常考它们的区别和适用场景。停止CPU访存DMA控制器申请总线时CPU完全停止访问内存把总线让给DMA。DMA传送完一批数据后再把总线还给CPU。这种方式最简单DMA传送效率最高但CPU在DMA传送期间完全不能访存对于高速外设大批量传送来说CPU会被阻塞很久。适合外设传送速率极高、数据量很大的场景。周期挪用DMA控制器每次只申请一个总线周期传送一个数据然后释放总线。CPU和DMA交替使用总线。这种方式对CPU影响小但DMA传送效率低因为每次都要申请和释放总线。适合外设速率不太高、数据量不大的场景。交替访存把总线时间分成两个时间段一个给CPU一个给DMA两者交替使用。这种方式不需要总线申请和释放效率高但硬件实现复杂。适合CPU和DMA都需要频繁访存的场景。这三种方式的核心区别在于总线控制权的分配粒度。停止CPU访存是整批分配周期挪用是单个周期分配交替访存是固定时间片分配。408考试里经常给一个场景让你选合适的方式或者问某种方式下CPU和DMA的访存冲突怎么解决。3.2 总线仲裁DMA如何从CPU手里拿到控制权DMA控制器要获得总线控制权需要经过总线仲裁。总线仲裁有两种基本方式集中式仲裁和分布式仲裁。集中式仲裁里有一个专门的仲裁器通常是CPU或者独立的仲裁电路所有设备都向它发请求它决定谁获得总线。集中式仲裁又分三种链式查询、计数器定时查询、独立请求。链式查询最简单所有设备串成一条链仲裁器发一个允许信号信号沿着链传下去第一个发出请求的设备截获信号并获得总线。优点是硬件简单缺点是优先级固定离仲裁器近的设备优先级高远的优先级低而且一个设备故障可能影响后面的设备。计数器定时查询是仲裁器有一个计数器轮询各个设备。优点是优先级可以灵活设置缺点是轮询有延迟。独立请求是每个设备都有一根独立的请求线和允许线仲裁器根据优先级决定谁获得总线。优点是响应快、优先级灵活缺点是线太多。DMA控制器的总线请求通常用独立请求方式因为DMA请求需要快速响应。408考试里经常考这几种仲裁方式的优缺点和适用场景。3.3 DMA传送的完整流程从初始化到完成中断把前面这些串起来DMA传送的完整流程是这样的CPU初始化DMA控制器设置内存地址寄存器、传送长度计数器、设备地址寄存器、控制寄存器。这一步是CPU用指令完成的比如往DMA控制器的端口写数据。CPU启动DMA往控制寄存器写启动位DMA控制器开始工作。外设发DMA请求外设准备好数据后向DMA控制器发DMA请求信号。DMA控制器向CPU发总线请求DMA控制器收到外设请求后向CPU发总线请求信号HOLD。CPU响应总线请求CPU在当前总线周期结束后发总线响应信号HLDA让出总线控制权。DMA控制器控制总线传送数据DMA控制器把内存地址寄存器内容放到地址总线把设备地址放到另一组地址线或使用I/O指令完成一个数据的传送。然后内存地址寄存器加1传送长度计数器减1。判断是否传送完成如果传送长度计数器不为0继续第3步如果为0DMA控制器向CPU发中断请求通知传送完成。CPU响应中断处理善后CPU执行中断服务程序检查DMA状态处理数据然后继续原来的程序。这个流程里第4步到第6步是DMA的核心也是408考试最爱考的地方。特别是总线请求和响应的时序以及DMA控制器在传送过程中对地址和计数器的操作。4. 408考试中DMA的常见考法与解题套路4.1 2024年第45题拆解DMA与中断的对比分析2024年408第45题考的是DMA方式和中断方式的对比。题目大意是比较DMA方式和中断方式在数据传送过程中的区别包括数据传送的控制者、总线控制权的归属、CPU的参与程度等。这道题的解题关键是抓住几个核心区别对比维度中断方式DMA方式数据传送控制者CPUDMA控制器总线控制权CPU始终掌握DMA控制器在传送期间掌握数据经过的路径外设→CPU寄存器→内存外设→内存不经过CPUCPU参与程度每次传送都要执行中断服务程序只在初始化和结束时参与适用场景低速外设、数据量小高速外设、数据量大请求类型中断请求DMA请求总线请求这道题还考了一个细节DMA方式下DMA控制器向CPU发的是总线请求而不是中断请求。总线请求的优先级高于中断请求。CPU响应总线请求后让出总线控制权但不保存现场因为DMA传送不改变CPU的程序执行状态。4.2 王道和唐朔飞教材里的典型例题王道复习指导里有一道经典例题一个DMA控制器采用周期挪用方式外设传输速率为1MB/sCPU主频为500MHz每个总线周期为0.1微秒。问DMA传送对CPU的影响有多大。解题思路外设每秒传1MB每个字节需要一次DMA传送也就是每秒100万次DMA请求。每次DMA传送占用一个总线周期即0.1微秒。所以DMA每秒占用总线时间为100万×0.1微秒0.1秒也就是10%的时间。CPU可用总线时间为90%。这个计算说明周期挪用方式下DMA对CPU的影响取决于外设速率和总线周期。唐朔飞教材里有一道题考DMA控制器的组成DMA控制器包括哪些寄存器各自的作用是什么。答案就是前面表格里列的那几个内存地址寄存器、传送长度计数器、设备地址寄存器、控制与状态寄存器。这道题还考了DMA控制器的两种工作状态被动状态CPU对它进行初始化和主动状态它控制总线进行传送。4.3 常见陷阱DMA请求与中断请求的优先级408考试里有一个反复出现的陷阱DMA请求的优先级高于中断请求。为什么因为DMA传送的数据通常来自高速外设如果DMA请求被中断请求阻塞数据可能会丢失。而中断请求可以稍微延迟因为中断服务程序可以稍后执行。但这里有个细节DMA请求和中断请求的优先级比较是在同一时刻两者同时发生时才有意义。如果CPU正在执行中断服务程序此时DMA请求来了CPU会暂停中断服务程序让出总线给DMADMA传送完后再继续执行中断服务程序。这个过程叫中断嵌套但DMA不是中断它是总线控制权的转移。还有一个陷阱DMA传送结束后DMA控制器向CPU发中断请求通知传送完成。这个中断请求的优先级和普通中断请求是一样的CPU按正常中断流程处理。所以DMA方式下CPU仍然需要处理中断只是中断次数大大减少了——从每次传送一个数据中断一次变成每传送一批数据中断一次。5. 从408到实战DMA在嵌入式芯片中的实现5.1 STM32的DMA控制器架构学408的时候可能觉得DMA很抽象但如果你用过STM32就会发现DMA其实很具体。STM32的DMA控制器有多个通道每个通道可以独立配置。以STM32F4为例有两个DMA控制器每个有8个流Stream每个流有8个通道Channel总共可以支持16个流同时工作。STM32的DMA控制器里和408教材对应的寄存器有外设地址寄存器DMA_SxPAR对应408里的设备地址寄存器内存地址寄存器DMA_SxM0AR对应408里的内存地址寄存器数据项数寄存器DMA_SxNDTR对应408里的传送长度计数器控制寄存器DMA_SxCR对应408里的控制与状态寄存器配置DMA的流程和408里讲的完全一致先设置外设地址和内存地址再设置传送长度然后配置控制寄存器传送方向、数据宽度、优先级、中断使能等最后使能DMA流。使能之后DMA控制器就开始工作外设每发一次DMA请求DMA就传送一个数据地址自动递增计数器自动递减减到0时触发中断。5.2 串口DMA发送的配置实例以STM32的串口DMA发送为例配置步骤如下// 1. 使能DMA时钟和串口时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // 2. 配置DMA流 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_Channel DMA_Channel_4; // 串口2发送对应DMA1 Stream6 Channel4 DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)USART2-DR; // 外设地址串口数据寄存器 DMA_InitStructure.DMA_Memory0BaseAddr (uint32_t)txBuffer; // 内存地址发送缓冲区 DMA_InitStructure.DMA_DIR DMA_DIR_MemoryToPeripheral; // 传送方向内存到外设 DMA_InitStructure.DMA_BufferSize txLength; // 传送长度 DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; // 外设地址不递增 DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; // 内存地址递增 DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; // 外设数据宽度字节 DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; // 内存数据宽度字节 DMA_InitStructure.DMA_Mode DMA_Mode_Normal; // 普通模式传完一次就停 DMA_InitStructure.DMA_Priority DMA_Priority_High; // 高优先级 DMA_InitStructure.DMA_FIFOMode DMA_FIFOMode_Disable; // 不使用FIFO DMA_Init(DMA1_Stream6, DMA_InitStructure); // 3. 使能DMA流 DMA_Cmd(DMA1_Stream6, ENABLE); // 4. 使能串口的DMA发送请求 USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE); // 5. 启动串口发送DMA会自动搬运数据 USART_Cmd(USART2, ENABLE);这段代码里DMA_PeripheralBaseAddr对应408里的设备地址寄存器DMA_Memory0BaseAddr对应内存地址寄存器DMA_BufferSize对应传送长度计数器DMA_DIR、DMA_PeripheralInc、DMA_MemoryInc等对应控制寄存器里的控制位。配置完成后CPU只需要执行DMA_Cmd使能DMA然后就可以去干别的事了。串口每发送完一个字节就会向DMA控制器发一次DMA请求DMA控制器自动从内存取下一个字节送到串口数据寄存器直到所有数据发送完毕触发DMA传输完成中断。5.3 DMA传输完成中断的处理DMA传输完成后DMA控制器会触发中断。在STM32里需要配置NVIC并使能DMA流的中断// 使能DMA传输完成中断 DMA_ITConfig(DMA1_Stream6, DMA_IT_TC, ENABLE); // 配置NVIC NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel DMA1_Stream6_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); // 中断服务程序 void DMA1_Stream6_IRQHandler(void) { if (DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6)) { DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6); // 处理传输完成后的逻辑比如置标志位、启动下一次传输等 dma_tx_complete 1; } }这个中断服务程序对应408里讲的“DMA传送结束后向CPU发中断请求”。CPU响应中断后执行这个服务程序处理善后工作。注意这个中断和普通中断一样需要保存现场、恢复现场但中断频率比程序中断方式低得多——只有整批数据传完了才中断一次。6. DMA实操中的常见问题与排查技巧6.1 DMA传输不启动或传输不完整这是实际开发中最常见的问题。DMA配置好了使能了但数据就是不动或者只传了一部分。排查思路如下检查时钟使能DMA控制器的时钟和外设的时钟都要使能。STM32里DMA1挂在AHB1总线上DMA2也挂在AHB1上串口挂在APB1或APB2上。如果时钟没使能DMA控制器根本不工作。检查DMA请求映射不同外设对应不同的DMA通道或流。比如STM32F4里USART2_TX对应DMA1 Stream6 Channel4如果选错了流或通道DMA请求传不到DMA控制器。这个映射关系在参考手册的DMA请求映射表里有选错了就是白忙活。检查传送长度DMA_BufferSize设置的是传送的数据项数不是字节数。如果数据宽度是半字16位传送10个半字DMA_BufferSize应该设为10而不是20。设错了会导致传送不完整或越界。检查外设的DMA使能位以串口为例光配置DMA不够还要调用USART_DMACmd使能串口的DMA发送或接收请求。这个位不使能串口不会向DMA控制器发DMA请求。检查内存地址和外设地址内存地址要指向有效的缓冲区外设地址要指向外设的数据寄存器。地址错了DMA搬的数据就是错的或者根本搬不动。6.2 DMA与CPU访存冲突导致数据错误DMA和CPU同时访问内存时如果发生冲突可能导致数据错误。比如CPU正在写一个变量DMA同时往这个变量的地址写数据结果就不可预测了。避免冲突的方法使用双缓冲区DMA往缓冲区A写的时候CPU处理缓冲区BDMA写完了切换缓冲区。这样CPU和DMA不会同时访问同一块内存。使用内存屏障在DMA传输前后插入内存屏障指令确保CPU的写操作对DMA可见DMA的写操作对CPU可见。合理设置DMA优先级如果DMA和CPU都要频繁访存可以调整DMA优先级或者使用交替访存方式。在STM32里DMA和CPU访存冲突由总线矩阵处理。如果DMA和CPU同时访问同一个从设备比如SRAM总线矩阵会仲裁通常DMA优先级高于CPU因为DMA传送不能等。但这可能导致CPU访存延迟影响实时性。6.3 DMA传输完成中断不触发DMA传输完成了但中断没触发。排查思路检查中断使能位DMA_ITConfig要调用使能传输完成中断。这个位不使能DMA传完了也不会触发中断。检查NVIC配置NVIC的IRQ通道要使能优先级要设置。如果NVIC没配置中断信号传不到CPU。检查中断标志清除在中断服务程序里要清除中断标志位。如果不清除中断会一直触发或者下次中断无法触发。检查DMA模式如果是循环模式CircularDMA传完一轮后会自动重装计数器继续传送不会触发传输完成中断或者触发的是半传输中断。只有普通模式Normal才会在传完后触发传输完成中断。检查DMA流是否被禁用如果DMA流在传输过程中被禁用传输会停止中断也不会触发。确保DMA流在传输期间保持使能。6.4 常见问题速查表问题现象可能原因排查方法DMA完全不工作时钟未使能检查RCC配置使能DMA和外设时钟DMA传输不完整传送长度设置错误检查DMA_BufferSize确认数据宽度和项数DMA传输方向错误DMA_DIR设置错误检查是MemoryToPeripheral还是PeripheralToMemoryDMA请求不触发DMA请求映射错误查参考手册的DMA请求映射表确认流和通道数据错位地址递增设置错误检查DMA_PeripheralInc和DMA_MemoryInc中断不触发中断使能或NVIC未配置检查DMA_ITConfig和NVIC_Init数据被覆盖CPU和DMA访存冲突使用双缓冲区或调整优先级传输速率不达标总线仲裁开销大考虑使用突发传输或提高DMA优先级7. 从考试到实战的知识迁移学408的DMA很多人觉得就是为了考试考完就忘了。但实际上DMA的原理在嵌入式开发里天天用。你配置STM32的DMA本质上就是在做408里讲的“CPU初始化DMA控制器”这一步。你设置DMA_BufferSize就是在设置传送长度计数器。你使能DMA流就是在启动DMA。你处理DMA传输完成中断就是在处理“DMA传送结束后向CPU发中断请求”这一步。反过来如果你在实际开发中用过DMA再回头看408的教材会发现那些抽象的概念都变得具体了。比如“周期挪用”在STM32里就是DMA每次传一个数据就释放总线CPU和DMA交替访存。“停止CPU访存”在STM32里就是DMA突发传输一次占用总线传多个数据。所以我的建议是学408的时候不要只背概念试着把每个概念对应到一个实际场景。DMA控制器有哪些寄存器想想STM32的DMA配置结构体里有哪些字段。DMA传送有哪几种方式想想STM32的DMA模式有哪几种。DMA和中断有什么区别想想串口用中断发送和用DMA发送有什么不同。这种对应关系建立起来之后408的DMA就不再是死记硬背的知识点而是你真正理解的东西。考试的时候不管题目怎么变你都能从原理出发推导出答案。实际开发的时候不管用什么芯片你都能快速上手配置DMA。最后说一个我自己的体会DMA最核心的思想就是让专业的硬件干专业的事。CPU擅长逻辑控制和运算不擅长大量数据搬运。DMA控制器就是为数据搬运而生的。理解了这一点你就理解了DMA存在的意义也理解了为什么408要考它——因为它体现了一种重要的计算机设计思想专用硬件加速。这个思想在计算机组成原理里反复出现从Cache到DMA到通道到GPU都是这个思想的体现。把DMA放在这个大的框架里理解你的知识就不是孤立的点而是一张网。
返回列表