STM32 进阶封神之路(二十一):DMA 深度解析 —— 从直接内存访问到无 CPU 干预数据传输(底层原理 + 寄存器配置)

STM32 进阶封神之路(二十一):DMA 深度解析 ------ 从直接内存访问到无 CPU 干预数据传输(底层原理 + 寄存器配置)

上一篇我们掌握了 ADC 模拟信号采集的全场景应用,这一篇聚焦 STM32 的 "数据搬运神器"------DMA 直接内存访问。DMA(Direct Memory Access)是 STM32 内置的高速数据传输控制器,能够在不占用 CPU 资源的情况下,实现外设与内存、内存与内存之间的高速数据搬运,广泛应用于 ADC 采集、串口通信、SPI 闪存读写、DAC 输出等高频数据传输场景。

本文基于实战资料,从 DMA 核心价值、硬件架构、传输原理,到通道配置、寄存器解析、初始化流程,手把手带你吃透 DMA 的底层逻辑,为下一篇实战(ADC+DMA 采集、串口 DMA 收发)打下坚实基础,同时覆盖高频面试考点!

一、DMA 核心认知:为什么它是 "CPU 减负神器"?

1. DMA 的核心作用与价值

在 DMA 出现之前,外设与内存的数据传输必须依赖 CPU 干预:CPU 需先从外设读取数据到寄存器,再写入内存(或反之),这个过程中 CPU 无法执行其他任务,导致资源浪费。DMA 的核心价值就是 "解放 CPU":

(1)核心作用
  • 无 CPU 干预数据传输:DMA 独立于 CPU 工作,自动完成外设与内存、内存与内存之间的数据搬运;
  • 高速传输:传输速度仅受总线带宽限制(STM32F103 DMA 最大传输速率可达 72MB/s);
  • 多通道支持:支持多个外设同时传输,通过通道优先级仲裁实现有序传输;
  • 降低功耗:CPU 可在 DMA 传输期间进入睡眠模式,减少系统功耗。
(2)核心应用场景
  • ADC 高速采集:ADC 连续采集数据,DMA 自动将采样值写入内存,CPU 无需等待;
  • 串口大数据收发:串口接收 / 发送大量数据(如文件传输),DMA 替代 CPU 中断处理,避免频繁中断占用资源;
  • SPI 闪存读写:W25Q64 等 SPI 闪存的批量数据读写,DMA 提升传输效率;
  • DAC 波形生成:DMA 循环传输波形数据到 DAC,实现高频信号输出;
  • 内存数据搬运:SRAM 内部、Flash 与 SRAM 之间的大批量数据复制。

2. DMA 与 CPU 中断传输的核心区别(面试高频)

很多新手混淆 DMA 与中断传输,两者虽都能 "并行处理任务",但本质逻辑完全不同:

表格

对比维度 DMA 传输 CPU 中断传输
核心逻辑 硬件直接搬运数据,无 CPU 参与 CPU 响应中断后,手动读写数据
CPU 占用 几乎为 0(仅需初始化配置) 中断响应和数据处理占用 CPU 资源
传输效率 高(硬件级传输,无软件开销) 中(受中断响应时间和软件处理速度限制)
适用场景 大批量、高频数据传输(如 ADC 连续采集、文件传输) 小批量、低频率数据传输(如按键中断、串口单字节接收)
配置复杂度 较高(需配置通道、传输模式、数据长度等) 较低(仅需配置中断和数据处理函数)
功耗 低(CPU 可休眠) 较高(CPU 需频繁响应中断)

示例对比:ADC 采集 1000 个数据

  • 中断传输:ADC 每采集 1 个数据触发 1 次中断,CPU 需响应 1000 次中断,每次中断读取数据并写入内存;
  • DMA 传输:ADC 连续采集 1000 个数据,DMA 自动将所有数据写入内存,CPU 仅需在传输完成后处理一次数据。

3. STM32 DMA 硬件架构深度解析

STM32F103 系列内置 2 个 DMA 控制器(DMA1、DMA2),其中 DMA1 支持 7 个通道,DMA2 支持 5 个通道,每个通道对应特定的外设请求,硬件架构决定了其传输能力:

(1)核心架构框图

plaintext

复制代码
外设(ADC1/USART1/SPI1等)→ DMA请求 → 通道选择 → 优先级仲裁器 → 数据搬运控制器 → 内存(SRAM/Flash)
                                  ↓
                              控制寄存器(配置传输模式、数据长度等)
(2)关键模块说明
  • DMA 控制器:STM32F103 有 2 个控制器(DMA1、DMA2),DMA2 仅在大容量和中容量芯片中存在(如 STM32F103ZET6),小容量芯片(如 STM32F103C8T6)仅支持 DMA1;
  • 通道(Channel):每个 DMA 控制器包含多个通道,每个通道对应特定的外设请求(如 DMA1_Channel1 对应 ADC1),通道是 DMA 传输的 "专属通道";
  • 优先级仲裁器:当多个通道同时请求传输时,按优先级高低排序(高优先级通道优先传输),支持 4 级优先级(最高、高、中、低);
  • 数据搬运控制器:核心执行单元,负责按配置的传输模式、数据长度、地址增量方式搬运数据;
  • 请求映射:外设需通过特定的 DMA 通道发起传输请求,不可随意选择(如 ADC1 只能通过 DMA1_Channel1 请求)。

4. DMA 核心传输术语(必掌握)

  • 传输方向
    • 外设→内存(Peripheral to Memory):如 ADC 采集数据写入 SRAM;
    • 内存→外设(Memory to Peripheral):如 SRAM 数据通过串口发送;
    • 内存→内存(Memory to Memory):如 SRAM 内部数据复制;
  • 传输模式
    • 正常模式(Normal mode):传输完成后停止 DMA,需手动重启;
    • 循环模式(Circular mode):传输完成后自动重启,持续循环传输(如 ADC 连续采集);
  • 地址增量模式
    • 外设地址增量(Peripheral increment mode):传输时外设地址是否递增(通常关闭,外设寄存器地址固定);
    • 内存地址增量(Memory increment mode):传输时内存地址是否递增(通常开启,连续写入内存);
  • 数据宽度
    • 外设数据宽度(Peripheral data size):8 位、16 位、32 位(需与外设数据格式匹配);
    • 内存数据宽度(Memory data size):8 位、16 位、32 位(需与内存数据类型匹配);
  • 传输计数器:记录待传输的数据个数,传输完成后计数器清零(正常模式)或自动重载(循环模式)。

二、STM32 DMA 通道与外设映射(实战选型关键)

DMA 通道与外设的映射关系是配置的核心,必须严格遵循,否则无法触发 DMA 传输。以下是 STM32F103 DMA1 的常用通道映射表(小容量芯片适用):

表格

DMA 控制器 通道号 对应外设请求 典型应用
DMA1 Channel1 ADC1、TIM2_CH3 ADC1 数据采集
DMA1 Channel2 USART2_TX、TIM1_CH4 USART2 发送数据
DMA1 Channel3 USART2_RX、TIM3_CH1 USART2 接收数据
DMA1 Channel4 USART1_TX、TIM1_CH1 USART1 发送数据
DMA1 Channel5 USART1_RX、TIM1_CH2 USART1 接收数据
DMA1 Channel6 SPI1_TX、TIM3_CH1 SPI1 发送数据
DMA1 Channel7 SPI1_RX、TIM3_CH2 SPI1 接收数据

关键注意

  • 每个通道可对应多个外设请求,但同一时间仅能激活一个请求;
  • 实战选型时,需根据外设选择对应的 DMA 通道(如 ADC1→DMA1_Channel1,USART1_RX→DMA1_Channel5)。

三、DMA 核心寄存器解析(底层配置关键)

DMA 的配置本质是操作控制寄存器,以下是初始化和传输过程中涉及的核心寄存器,结合配置流程解析:

1. DMA 通道配置寄存器(DMA_CCRx)

  • 地址:DMA1_Channel1 的 CCR 寄存器地址为 0x40020000,通道 2~7 依次递增 0x14;
  • 核心作用:配置 DMA 通道的传输模式、地址增量、数据宽度、优先级等;
  • 关键位说明:
    • EN(bit0):通道使能位(1 = 使能,0 = 禁用);
    • CIRC(bit5):循环模式使能位(1 = 循环,0 = 正常);
    • PINC(bit6):外设地址增量使能(1 = 使能,0 = 禁用,通常为 0);
    • MINC(bit7):内存地址增量使能(1 = 使能,0 = 禁用,通常为 1);
    • PSIZE(bit8~9):外设数据宽度(00=8 位,01=16 位,10=32 位);
    • MSIZE(bit10~11):内存数据宽度(00=8 位,01=16 位,10=32 位);
    • PL(bit12~13):通道优先级(00 = 低,01 = 中,10 = 高,11 = 最高);
    • DIR(bit14):传输方向(0 = 外设→内存,1 = 内存→外设);
    • MEM2MEM(bit14):内存到内存模式(仅当 DIR=1 时,该位 = 1 表示内存→内存)。

2. DMA 通道传输计数器寄存器(DMA_CNDTRx)

  • 地址:DMA1_Channel1 的 CNDTR 寄存器地址为 0x40020004;
  • 核心作用:存储待传输的数据个数(0~65535);
  • 关键说明:
    • 传输过程中,计数器自动递减,每次传输 1 个数据,计数器减 1;
    • 传输完成后,计数器清零(正常模式)或自动重载初始值(循环模式);
    • 可通过读取该寄存器获取剩余传输数据个数。

3. DMA 通道外设地址寄存器(DMA_CPARx)

  • 地址:DMA1_Channel1 的 CPAR 寄存器地址为 0x40020008;
  • 核心作用:存储外设数据寄存器的地址(如 ADC1_DR 的地址 = 0x40012440);
  • 关键说明:外设地址通常固定,传输过程中不会变化(PINC=0)。

4. DMA 通道内存地址寄存器(DMA_CMARx)

  • 地址:DMA1_Channel1 的 CMAR 寄存器地址为 0x4002000C;
  • 核心作用:存储内存缓冲区的起始地址(如 SRAM 中数组的首地址);
  • 关键说明:若开启内存地址增量(MINC=1),传输过程中内存地址自动递增(递增步长 = 数据宽度,如 16 位数据递增 2 字节)。

5. DMA 中断状态寄存器(DMA_ISR)

  • 地址:0x40020000 + 0x00000078(DMA1_ISR);
  • 核心作用:存储所有通道的中断状态标志;
  • 关键位说明:
    • TCIFx(bit1、bit5、bit9 等):通道 x 传输完成中断标志(1 = 传输完成);
    • HTIFx(bit2、bit6、bit10 等):通道 x 半传输中断标志(1 = 传输完成一半);
    • TEIFx(bit3、bit7、bit11 等):通道 x 传输错误中断标志(1 = 传输错误)。

四、DMA 初始化核心流程(必掌握!)

DMA 的初始化流程严格遵循 "使能时钟→配置通道参数→设置传输计数器→使能通道" 的顺序,以下以 "ADC1→DMA1_Channel1→内存" 传输为例,解析完整流程:

1. 步骤 1:使能 DMA 控制器时钟

DMA 控制器属于 AHB 总线外设,需通过 RCC 寄存器使能时钟:

c

运行

复制代码
// 使能DMA1时钟(DMA1挂载AHB总线)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

底层寄存器操作RCC->AHBENR |= (1<<0)(DMA1 时钟使能位为 bit0)。

2. 步骤 2:配置 DMA 通道参数(DMA_CCRx)

通过DMA_InitTypeDef结构体配置通道核心参数,对应寄存器位:

c

运行

复制代码
DMA_InitTypeDef DMA_InitStruct;

// 1. 选择DMA通道(ADC1→DMA1_Channel1)
DMA_InitStruct.DMA_Channel = DMA_Channel_1;

// 2. 配置传输方向(外设→内存)
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // SRC=源,外设为数据来源

// 3. 配置内存地址(SRAM中存储ADC数据的数组首地址)
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)adc_data_buf; // adc_data_buf为uint16_t数组

// 4. 配置外设地址(ADC1数据寄存器地址)
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;

// 5. 内存地址增量使能(开启,连续写入内存)
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;

// 6. 外设地址增量使能(关闭,ADC_DR地址固定)
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

// 7. 内存数据宽度(16位,与ADC采样值宽度匹配)
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

// 8. 外设数据宽度(16位,ADC_DR为16位寄存器)
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

// 9. 传输模式(循环模式,持续采集)
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;

// 10. 通道优先级(中优先级)
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;

// 11. 内存到内存模式(关闭,仅外设→内存)
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;

// 12. 写入配置参数到DMA通道
DMA_Init(DMA1_Channel1, &DMA_InitStruct);

3. 步骤 3:设置传输计数器(DMA_CNDTRx)

配置待传输的数据个数,若为循环模式,计数器会自动重载:

c

运行

复制代码
// 配置传输数据个数(1000个数据,adc_data_buf数组长度≥1000)
DMA_SetCurrDataCounter(DMA1_Channel1, 1000);

底层寄存器操作DMA1_Channel1->CNDTR = 1000

4. 步骤 4:使能 DMA 通道

c

运行

复制代码
// 使能DMA1_Channel1
DMA_Cmd(DMA1_Channel1, ENABLE);

底层寄存器操作DMA1_Channel1->CCR |= (1<<0)(EN 位 = 1)。

5. 步骤 5:使能外设 DMA 请求(如 ADC1 DMA 请求)

DMA 传输需由外设发起请求(如 ADC 转换完成后请求 DMA 搬运数据),需在了你那个外设中使能 DMA 请求:

c

运行

复制代码
// 使能ADC1的DMA请求(ADC_CR2的DMA位=1)
ADC_DMACmd(ADC1, ENABLE);

底层寄存器操作ADC1->CR2 |= (1<<8)(ADC_CR2 的 DMA 位为 bit8)。

6. 步骤 6:(可选)配置 DMA 中断

若需在传输完成、半传输或传输错误时触发中断,需配置 NVIC 和 DMA 中断使能:

c

运行

复制代码
// 使能DMA1_Channel1传输完成中断
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

// 配置NVIC中断优先级
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);

五、DMA 相关面试高频题(附标准答案)

1. 问题 1:STM32 DMA 的核心作用是什么?与 CPU 中断传输相比有哪些优势?

标准答案:
  • 核心作用:在不占用 CPU 资源的情况下,实现外设与内存、内存与内存之间的高速数据传输;
  • 优势:
    1. 解放 CPU:传输过程无需 CPU 干预,CPU 可执行其他任务或进入睡眠模式;
    2. 传输效率高:硬件级传输,无软件开销,速度仅受总线带宽限制;
    3. 降低功耗:CPU 无需频繁响应中断,减少功耗;
    4. 适配大批量数据:适合 ADC 连续采集、串口文件传输等高频、大批量数据场景。

2. 问题 2:STM32 DMA 支持哪些传输方向?各自的典型应用是什么?

标准答案:
  • 支持三种传输方向:
    1. 外设→内存(Peripheral to Memory):典型应用是 ADC 采集数据写入 SRAM、串口接收大数据存储到内存;
    2. 内存→外设(Memory to Peripheral):典型应用是 SRAM 数据通过串口发送、DMA 驱动 DAC 输出波形;
    3. 内存→内存(Memory to Memory):典型应用是 SRAM 内部大批量数据复制、Flash 数据搬运到 SRAM。

3. 问题 3:STM32 DMA 的通道与外设的映射关系是什么?如何选择 DMA 通道?

标准答案:
  • 映射关系:每个 DMA 通道对应特定的外设请求(如 DMA1_Channel1 对应 ADC1,DMA1_Channel5 对应 USART1_RX),由 STM32 硬件设计固定,不可随意更改;
  • 选择方法:
    1. 确定待传输的外设(如 ADC1、USART1);
    2. 查阅 STM32 参考手册的 "DMA 通道与外设请求映射表";
    3. 选择该外设对应的 DMA 通道(如 ADC1→DMA1_Channel1);
    4. 确保同一时间仅一个外设使用该通道。

4. 问题 4:DMA 的循环模式和正常模式有什么区别?各自适用场景是什么?

标准答案:
  • 循环模式(Circular mode):传输完成后,DMA 传输计数器自动重载初始值,继续循环传输,无需手动重启;适用于连续、周期性数据传输(如 ADC 连续采集、DAC 循环输出波形);
  • 正常模式(Normal mode):传输完成后,DMA 通道自动关闭,传输计数器清零,需手动调用DMA_SetCurrDataCounterDMA_Cmd重启传输;适用于一次性数据传输(如单次文件发送、内存数据复制)。

六、总结:DMA 底层原理核心要点与实战铺垫

1. 核心要点回顾

  • DMA 本质:无 CPU 干预的硬件数据搬运控制器,核心价值是 "解放 CPU + 高速传输";
  • 核心参数:传输方向、通道选择、地址增量、数据宽度、传输模式、优先级,决定传输逻辑;
  • 初始化流程:使能 DMA 时钟→配置通道参数→设置传输计数器→使能通道→使能外设 DMA 请求;
  • 关键映射:外设与 DMA 通道的映射关系固定,需按手册选型;
  • 应用场景:大批量、高频数据传输,如 ADC 采集、串口大数据收发、SPI 闪存读写。

2. 下一篇实战铺垫

掌握底层原理后,下一篇我们将聚焦 DMA 实战开发,覆盖:

  • ADC+DMA 采集:ADC1 连续采集光敏电阻数据,DMA 自动写入内存,实现无 CPU 干预采集;
  • 串口 DMA 收发:USART1 通过 DMA 接收大数据(如字符串),DMA 自动存储到缓冲区;
  • 内存到内存传输:实现 SRAM 数组数据复制,验证 DMA 传输效率;
  • 中断处理:配置 DMA 传输完成中断,处理采集或传输后的数据。
相关推荐
LCG元1 小时前
STM32实战:基于LVGL的嵌入式GUI界面开发(智能手表UI)
stm32·智能手表
DLGXY1 小时前
STM32(二十八)——FLASH闪存
stm32·单片机·嵌入式硬件
9稳1 小时前
基于plc的自动化立体仓库控制系统设计
开发语言·网络·数据库·嵌入式硬件·plc
ToneChip1 小时前
USBi 调试 与 MCU 运行 共存方案总结
单片机·嵌入式硬件·音频
Hello World . .2 小时前
51单片机基础
单片机·嵌入式硬件·51单片机
’长谷深风‘2 小时前
51单片机入门(3:串口通信)
单片机·嵌入式硬件·51单片机·串口·串口通信
9稳2 小时前
基于智能巡检机器人与PLC系统联动控制设计
开发语言·网络·数据库·嵌入式硬件·plc
BackCatK Chen2 小时前
STM32U3B5/3C5深度解析:HSP加速器赋能边缘AI与DSP,超低功耗新标杆
人工智能·stm32·嵌入式硬件
_Ningye10 小时前
STM32 — 2.2 新建工程
stm32·单片机·嵌入式硬件