1. 什么是DMA?为什么你需要它
如果你曾经遇到过STM32处理大量数据时CPU负载飙升的情况,那么DMA就是你的救星。DMA(Direct Memory Access)直接存储器访问,是一种让外设直接与内存交换数据的技术,完全不需要CPU参与每次数据传输过程。
想象一下这样一个场景:你的STM32正在通过ADC采集音频信号,同时还要处理用户按键输入和更新显示屏内容。如果让CPU来搬运ADC采集的数据,它就得不停地在"读取ADC数据"和"处理其他任务"之间切换。就像让一个厨师同时切菜、炒菜、洗碗还要接待顾客,效率肯定会大打折扣。
而DMA就像请了一个专业的配菜师傅,专门负责把切好的菜(数据)送到厨师(CPU)手边。厨师只需要专注于烹饪(数据处理),不需要分心去做搬运的工作。这样整个系统的效率就大大提高了。
我在实际项目中就吃过这个亏。当时用STM32F103做音频采集,一开始没用DMA,CPU使用率直接飙到80%以上,系统响应慢得让人抓狂。后来启用了DMA,同样的事情CPU使用率不到20%,还能腾出资源做更多事情。
2. STM32的DMA架构深入解析
2.1 DMA控制器的组织结构
STM32的DMA架构设计得很巧妙。以STM32F103系列为例,它有两个DMA控制器:DMA1和DMA2。DMA1有7个通道,DMA2有5个通道,总共12个独立通道。
每个通道都可以独立配置,就像高速公路上的多条车道,每辆车(数据)都可以在自己的车道上畅通无阻地行驶。不同的外设被分配到不同的通道上,比如USART1的发送通常使用DMA1的通道4,ADC1使用DMA1的通道1。
这里有个实用的技巧:在规划项目时,要先根据外设的使用情况合理分配DMA通道。我曾经遇到过通道冲突的问题,两个高优先级的外设分配到了同一个DMA控制器上的通道,导致性能瓶颈。后来重新规划了通道分配,问题就解决了。
2.2 仲裁机制:谁先谁后的智慧
当多个DMA请求同时到来时,仲裁器就要发挥作用了。STM32的DMA仲裁分为两个阶段:
首先是软件优先级,每个通道可以在DMA_CCRx寄存器中设置4个优先级等级:最高、高、中等、低。这就好比医院的急诊科,危重病人(最高优先级)总是先得到救治。
如果两个请求的软件优先级相同,就进入第二阶段——硬件优先级。较低编号的通道享有较高的优先级,比如通道2优先于通道4。
在实际应用中,我通常这样设置优先级:实时性要求高的外设(如ADC、DAC)设为最高优先级,批量数据传输的(如USART、SPI)设为高优先级,内存到内存的传输设为中等优先级。
2.3 数据传输的四种模式
DMA支持四种数据传输模式,覆盖了所有可能的数据流动场景:
内存到内存模式适合大数据块的搬运,比如图像处理中的缓冲区拷贝。内存到外设模式常用于将数据发送到外部设备,比如通过USART发送数据。外设到内存模式用于数据采集,比如ADC采样数据存储到内存。外设到外设模式使用较少,但在某些特定场景下很有用。
我在一次电机控制项目中就用到了内存到外设模式。将预先计算好的PWM波形数据通过DMA直接发送到TIM的CCR寄存器,实现了精确的电机控制,CPU几乎不需要干预。
3. DMA的配置实战详解
3.1 初始化结构体逐个解析
配置DMA时,首先要理解DMA_InitTypeDef这个结构体。每个成员都至关重要:
DMA_PeripheralBaseAddr是外设的基地址,比如USART的数据寄存器地址。DMA_MemoryBaseAddr是内存基地址,通常是数组的首地址。DMA_DIR决定数据传输方向,是从外设到内存还是反过来。
DMA_BufferSize设置要传输的数据量,这个参数很容易出错。我曾经因为把这个值设小了,导致只传输了部分数据,调试了好久才发现问题。
DMA_PeripheralInc和DMA_MemoryInc控制地址是否自增。如果是从外设寄存器读取数据,通常不自增;如果是内存缓冲区,通常要自增。
DMA_PeripheralDataSize和DMA_MemoryDataSize设置数据宽度,可以是8位、16位或32位。这里有个坑:源和目的的数据宽度可以不同,但要注意对齐问题。


721

被折叠的 条评论
为什么被折叠?



