DMA(Direct Memory Access,直接存储器访问)是一种微控制器中的硬件模块,允许外设直接与存储器(SRAM、Flash 等)之间传输数据,而无需经过 CPU 的参与。DMA 的存在极大提升了数据传输的效率,并降低了 CPU 的负载。
以下是关于 DMA 的理论知识详细讲解:
1. DMA 的基本概念
什么是 DMA?
- DMA 是一种硬件模块,用于在微控制器的外设(如 ADC、DAC、USART、SPI、I2C 等)和存储器之间进行数据传输,而不需要 CPU 直接干预。
- 数据传输由 DMA 控制器完成,CPU 只需配置 DMA 控制器即可。
DMA 的作用
- 减轻 CPU 的负担:DMA 模块可以在后台完成数据传输,CPU 可以专注于其他任务。
- 提升数据传输效率:DMA 可以以硬件级别直接完成数据传输,速度快且延迟低。
- 提供高吞吐量:通过 DMA,可以实现大批量数据的快速传输,例如 ADC 的连续采样、串口数据的高效收发等。
DMA 的数据传输方式
- 外设到存储器:例如 ADC 采样数据传输到内存。
- 存储器到外设:例如从内存发送数据到串口(USART)。
- 存储器到存储器:例如从一个内存区域复制数据到另一个内存区域。
2. DMA 的工作原理
基本工作流程
- CPU 配置 DMA 控制器,定义数据源、目的地址、数据大小、传输方向等。
- 当外设产生事件(如数据准备好)时,触发 DMA 请求。
- DMA 控制器将数据从源地址传输到目的地址,并更新传输计数器。
- 当所有数据传输完成后,DMA 控制器通知 CPU(通过中断或标志)。
关键组件
- 源地址:数据传输的起始地址。
- 目的地址:数据传输的目标地址。
- 传输计数器:记录需要传输的数据量。
- 模式控制器:控制传输方向、数据大小、传输方式等。
- 触发事件:由外设或软件触发 DMA 数据传输。
触发机制
- DMA 的触发事件可以是外设产生的硬件请求(如 ADC 完成采样、中断信号等),也可以是由软件手动触发。
3. DMA 的传输模式
DMA 支持多种传输模式,常见的有以下几种:
普通模式(Normal Mode)
- DMA 传输完成后停止,不会重复传输。
- 适用于一次性传输固定长度的数据。
循环模式(Circular Mode)
- 当传输完成后,自动从起始地址再次开始传输。
- 适用于需要连续传输的数据,例如 ADC 的连续采样。
双缓冲模式(Double Buffer Mode,部分 MCU 支持)
- 配置两个缓冲区,DMA 在传输一个缓冲区的数据时可以准备另一个缓冲区。
- 能有效减少数据丢失,适用于实时性要求较高的场景。
4. DMA 的传输方向
DMA 传输的数据方向可以是以下三种:
外设到存储器
- 例如从 ADC 获取采样数据并存储到内存。
- 使用场景:ADC 连续采样、UART 接收数据。
存储器到外设
- 例如从内存中读取数据并发送到外设(如 USART、SPI)。
- 使用场景:UART 发送数据、DAC 输出波形。
存储器到存储器
- 例如从一个内存区域复制数据到另一个内存区域。
- 使用场景:快速数据复制或内存初始化。
5. DMA 的优点和局限性
5.1 优点
- 减轻 CPU 的负担:DMA 模块在后台运行,CPU 可以处理其他任务。
- 快速传输:DMA 传输速度比 CPU 控制更快,尤其是在大数据量传输时。
- 低延迟:DMA 的硬件传输机制相比软件更高效。
- 支持多种外设和传输模式:如 ADC、USART、SPI 等。
5.2 局限性
- 配置复杂:DMA 的初始化和配置需要注意多个参数。
- 数据传输粒度有限:DMA 不能直接处理复杂的数据结构,只能传输原始字节或简单数组。
- 数据一致性问题:DMA 的传输是异步的,可能需要额外的措施确保数据一致性。
- 需要硬件支持:并非所有微控制器外设都支持 DMA。
6. STM32 DMA 的硬件结构
在 STM32 微控制器中,DMA 控制器通常包括以下硬件模块:
DMA 通道
- 每个 DMA 控制器有多个通道,每个通道可以独立配置和运行。
- 每个外设(如 ADC、USART 等)通常与特定的 DMA 通道绑定。
优先级
- DMA 通道可以配置优先级(高、中、低),当多个通道同时请求时,高优先级的通道会被优先处理。
数据宽度
- 数据传输的宽度可以是字节(8 位)、半字(16 位)或字(32 位)。
中断支持
- DMA 支持传输完成中断、传输错误中断等。
7. DMA 的配置步骤
以 STM32 HAL 库为例,以下是配置 DMA 的基本步骤:
7.1 初始化 DMA
- 使用 CubeMX 或手动配置 DMA 通道。
- 设置 DMA 的数据源、目标地址、数据大小、传输方向等。
7.2 启动 DMA
- 启动外设(例如 ADC、USART)。
- 启动 DMA 传输。
7.3 数据传输
DMA 会根据配置自动完成数据传输。
7.4 中断处理(可选)
如果启用了中断,可以在中断回调函数中处理传输完成事件。
8. DMA 的实际应用场景
ADC 数据采集
- 使用 DMA 将 ADC 的采样数据连续存储到内存中,适合高频信号采样。
USART 数据收发
- 使用 DMA 实现串口数据的高效发送和接收。
SPI 数据传输
- 使用 DMA 快速传输大批量数据到 SPI 外设,适合屏幕显示、数据传输等。
DAC 波形输出
- 使用 DMA 将波形数据从内存传输到 DAC,生成实时波形。
存储器数据复制
- 使用 DMA 在存储器之间快速复制数据,例如初始化大数组。
9. DMA 的简单代码示例
以下是一个使用 DMA 配合 ADC 的简单代码示例:
c
#include "stm32f4xx_hal.h"
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
uint32_t adc_buffer[100]; // 存储 ADC 数据
void ADC_DMA_Init(void)
{
// 初始化 ADC
hadc1.Instance = ADC1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
HAL_ADC_Init(&hadc1);
// 配置 DMA
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR; // 循环模式
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_adc1);
// 将 DMA 与 ADC 关联
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
// 启动 ADC 和 DMA
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, 100);
}
10. 总结
DMA 是嵌入式系统中非常重要的硬件模块,它通过硬件级别的直接数据传输,显著提高了数据传输效率并降低了 CPU 的负担。通过合理使用 DMA,可以实现高效的数据采集、处理和传输,是嵌入式开发中不可或缺的技术之一。