二十四、STM32的DMA

前言:现代 MCU 经常需要处理大量外设数据(ADC、UART、SPI、I²C 等),如果把这些数据搬运完全交给 CPU,会占用大量 CPU 周期,降低系统响应能力。**DMA(Direct Memory Access,直接内存访问)**就是解决这个问题的利器:它可以在不占用 CPU 的情况下,把外设数据自动搬运到内存(或把内存数据搬到外设),显著降低 CPU 负担、提升实时性与吞吐量。

目录

[一、DMA 是什么?为什么要用?](#一、DMA 是什么?为什么要用?)

[二、DMA 的总体架构](#二、DMA 的总体架构)

[三、DMA 的典型传输方向与使用场景](#三、DMA 的典型传输方向与使用场景)

[四、DMA 的关键配置项与概念](#四、DMA 的关键配置项与概念)

[五、DMA 的工作时序与中断](#五、DMA 的工作时序与中断)

六、常见误区与调试要点

七、性能注意事项与优化建议

八、ADC+DMA


一、DMA 是什么?为什么要用?

DMA 的本质:一个硬件控制器,负责在外设寄存器、内存和外设之间直接搬运数据,搬运过程中 CPU 不参与数据逐字节拷贝(但仍需做配置和处理完成后的逻辑)。

主要优点

  • 降低 CPU 占用:尤其适合大量或高速数据采集(如音频、传感器阵列、图像流等)。

  • 更稳定的实时性:减少中断频率或避免中断上下文开销。

  • 更高吞吐量:硬件搬运比软件循环拷贝更快、更节省总线带宽。

适用场景举例:ADC 多通道高速采样、UART 大量数据接收、SPI 快速数据传输、内存到内存的批量拷贝、PWM 波形输出缓冲等。

二、DMA 的总体架构

  • STM32F1 系列通常有 DMA1(个别器件还有 DMA2)。

  • DMA 被划分为 通道(Channel),每个通道映射到一个或多个外设事件(例如 ADC、USART、SPI 等)。通道数量有限(例如 DMA1 通常有 7 个通道)。

  • 每个通道有自己的配置寄存器,控制源/目的地址、方向、数据宽度、优先级、传输长度、模式(循环/普通)等。

  • DMA 与外设通过**请求映射(request mapping)**关联:某个外设触发数据准备(如 ADC EOC),会产生 DMA 请求到对应通道。

三、DMA 的典型传输方向与使用场景

  • 外围设备 → 内存(Peripheral-to-Memory)

    场景:ADC 将转换结果推送到内存、USART RX 接收数据写内存。是最常见的用于数据采集的方向。

  • 内存 → 外围设备(Memory-to-Peripheral)

    场景:从一个内存缓冲区把数据写到 DAC、UART TX、SPI TX(比如发送音频或图像数据)。

  • 内存 → 内存(Memory-to-Memory)

    场景:大块内存数据搬运(在 F1 上支持有限,注意映射与配置)。用于缓冲区复制、DMA 驱动的块传输等。

  • 双向 / 循环模式 (实际是模式选择)

    循环(Circular)适合连续流数据采集;普通(Normal)适合一次性传输。

四、DMA 的关键配置项与概念

  • 源地址(Peripheral address)与目的地址(Memory address)

    DMA 在搬运时会从源地址读取数据并写到目的地址;比如 ADC->内存: 源=ADC_data_register,目的=缓冲区首地址。

  • 传输方向(Direction):见上一节。

  • 传输长度(Number of Data):要搬运的数据项个数(不是字节数,依赖数据宽度)。

  • 数据宽度(Peripheral & Memory Data Size):常见为 8-bit、16-bit、32-bit。ADC(12-bit)一般使用 16-bit 宽度(half-word),UART 通常用 8-bit。数据宽度必须匹配硬件与缓冲格式。

  • 地址递增(Peripheral increment / Memory increment):是否在每次传输后自动增加地址。外设寄存器通常禁用地址递增(固定地址),内存缓冲通常启用递增。

  • 传输模式(Normal / Circular)

    • Normal:完成指定数量传输后停止并触发完成中断。

    • Circular:完成后自动重新开始(围绕缓冲区),适合连续采样。

  • 优先级(Priority):在多通道竞争 DMA 总线时决定抢占顺序(高优先级通道更早获得总线)。

  • 中断/标志:半传中断(Half Transfer)、传输完成中断(Transfer Complete)、传输错误中断。半传在循环模式下非常有用------可以实现"前半缓冲处理同时后半缓冲接收"的方案。

五、DMA 的工作时序与中断

  • 启动步骤(概念层面):

    1. 配置 DMA(源/目地址、长度、宽度、递增、模式、优先级)。

    2. 配置外设(使能外设 DMA 请求,比如 ADC_DMACmd)。

    3. 使能 DMA 通道与相关中断(如果需要)。

    4. 启动外设触发(ADC 软件触发或定时器触发),数据开始流入缓冲区。

  • 半传中断(HT)与传输完成中断(TC)

    在循环模式下,半传中断可以触发处理已填满的缓冲区一半数据,而 DMA 继续写入另一半;传输完成中断则表示一轮缓冲写满。利用这两个事件可以实现无缝"生产者-消费者"模式。

  • 错误处理:DMA 也可能产生传输错误(比如总线错误),要有相应的检测与恢复策略(重启 DMA、报警等)。

六、常见误区与调试要点

  • 误区:DMA 一开就万事大吉

    DMA 确实减轻 CPU,但错误配置(地址错误、长度错误、宽度不匹配)会导致数据错位、溢出或硬件挂起。调试时重点检查外设数据寄存器地址、数据宽度和内存缓冲区大小。

  • 误区:半传中断和全传中断二者可互相替代

    半传中断用于"流式处理"更低延迟;若只用全传中断,会在缓冲写满后才处理,可能加大延迟。

  • 调试技巧

    • 先用短缓冲进行功能验证(例如 4 个采样点);

    • 打开半传/全传中断并在 ISR 中简单翻转 IO(示波器观察)确认触发节奏;

    • 检查 DMA 优先级与竞争关系;

    • 确认内存对齐(16-bit/32-bit)与 CPU 访问不会引起未对齐故障。

  • 跨域缓存问题(高级):F1 的 Cortex-M3 没有数据缓存问题,然而在更高端系列(如 Cortex-M7)需要考虑 D-cache 与 DMA 的一致性:使用缓存刷新/失效或使用不可缓存内存区。

七、性能注意事项与优化建议

  • 选择合适的数据宽度:尽量让外设与内存宽度匹配,减少额外的对齐/移动操作(例如 ADC 用 16-bit 存储)。

  • 优先级与通道分配:把关键通道设高优先级;避免多个高频外设共享同一 DMA 通道。

  • 中断开销最小化:把处理尽量放到任务或主循环,不在 ISR 中做大量计算;使用半传中断只做标记并在主循环处理。

  • 使用循环模式处理持续数据流:比如 ADC 连续采样或串口持续接收,循环模式可以避免频繁重启 DMA。

  • 考虑缓冲大小权衡:小缓冲降低延迟但增加中断频率;大缓冲降低中断负担但增加响应延迟。

八、ADC+DMA

  • 当需要实时采集多通道数据(如传感器阵列、音频采样、振动分析等)时,CPU 去轮询或处理中断会消耗大量计算资源,甚至跟不上数据速率。

  • ADC + DMA 可以实现:ADC 将转换结果自动写入内存缓冲区,CPU 仅在缓冲半满或满时处理数据,极大提高效率与实时性。

配置流程:

  • 设计缓冲区 :为采样结果准备好连续的内存数组(例如 uint16_t buffer[NUM_SAMPLES];)。通常选择 16-bit 单元来保持 ADC 的 12-bit 数据。

  • 配置 ADC

    • 打开 ADC 时钟,设置采样时间、分辨率(12-bit)、触发模式(软件或定时器触发)、扫描模式(若为多通道自动扫描)。

    • 对于多通道自动采样,启用扫描模式并在序列寄存器中设置通道顺序。

  • 配置 DMA(外设 → 内存):

    • 源地址:ADC 数据寄存器地址(固定);目的地址:缓冲区首地址;

    • 数据宽度:一般选择 16-bit(half-word);

    • 内存地址递增:启用;外设地址递增:禁用;

    • 模式:如果是持续采样(例如定时器触发连续采样),选择 Circular(循环) 模式;若是一次性采集选择 Normal

    • 启用半传/全传中断(根据需要)。

  • 把 ADC 的 DMA 请求打开(告诉 ADC 在 EOC 时发出 DMA 请求,让 DMA 自动搬运数据)。

  • 启动 ADC(与触发器)与 DMA 通道:在正确的时序下启动 DMA,然后启动 ADC & 触发(如果用定时器触发,启动定时器)。

  • 在中断/任务中处理数据

    • 如果使用循环模式并启用了半传/全传中断:在半传中断中处理缓冲区前半,在全传中断中处理后半;处理完后继续让 DMA 循环写入。

    • 如果使用普通模式:在传输完成中断中处理整个缓冲区并可重新配置/重启 DMA。

下一章节将详细讲解代码部分。

相关推荐
蓝斯49712 小时前
从.到实现一个Windows上的虚拟hid键盘设备
windows·单片机·计算机外设
熙芯XiChip13 小时前
硬实时与软实时RTOS核心区别
嵌入式硬件
伟大的前程13 小时前
unsigned int 定义的变量 在 电脑的 64 位系统中 和 32 位 cortex-m0 和 8 位的 51 单片机
单片机·嵌入式硬件
小渔村的拉线工13 小时前
1.HPM6E80 解析芯片整体工作原理和存储架构
单片机·嵌入式硬件·mcu·架构·hpm6e80
熙芯XiChip14 小时前
CAN与CANopen的本质区别
单片机
lilihewo15 小时前
Altium Designer 封装库的创建(分享pcb 3D封装库)
嵌入式硬件·51单片机
神罚天下ET16 小时前
典型arm位单片机启动流程(从上电到main.c)
c语言·arm开发·单片机
碧海银沙音频科技研究院16 小时前
蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法
嵌入式硬件·算法
Zevalin爱灰灰17 小时前
模拟信号链电路——基于TL431的电压基准电路
单片机·嵌入式硬件
csdn杰哥18 小时前
瑞萨单片机开发教程[二十七] DTC 外部中断触发传输实验
单片机·嵌入式硬件·mongodb·瑞萨·ra6m5