引言
在嵌入式开发中,CPU的时间是宝贵资源。DMA技术就像一位不知疲倦的数据搬运工,让CPU从繁琐的数据传输中解放出来,专注于核心逻辑处理。
1. DMA核心概念
1.1 什么是DMA?
DMA(Direct Memory Access)直接存储器存取,是STM32中一个独立于CPU的数据传输模块。它能够在不需要CPU干预的情况下,直接在存储器和外设之间传输数据。
传统方式 vs DMA方式:
- 传统方式:CPU参与每个数据的搬运,占用大量CPU时间
- DMA方式:CPU只负责初始化配置,数据传输由DMA控制器完成
1.2 STM32 DMA资源
STM32F103系列提供两个DMA控制器:
- DMA1:7个通道,支持外设到存储器、存储器到外设的数据传输
- DMA2:5个通道,额外支持存储器到存储器的数据传输
2. 存储器架构基础
2.1 三种主要存储器类型
| 存储器类型 | 特点 | 在STM32中的主要用途 |
|---|---|---|
| Flash | 非易失性,可重复擦写 | 存储程序代码和常量数据 |
| RAM | 高速读写,断电丢失数据 | 存储变量和运行时数据 |
| EEPROM | 非易失性,擦写次数多 | 存储需要修改的用户配置数据 |
2.2 DMA与存储器的关系
DMA作为数据传输的桥梁,可以在这些存储器之间或存储器与外设之间建立高速数据传输通道:
- 外设 → 内存:如ADC采集数据到RAM
- 内存 → 外设:如从RAM发送数据到UART
- 内存 → 内存:如数据块拷贝
3. DMA工作模式
3.1 基本传输模式
// DMA工作模式配置选项
typedef enum {
DMA_MODE_NORMAL, // 正常模式:传输完成即停止
DMA_MODE_CIRCULAR // 循环模式:传输完成自动重启
} DMA_Mode_Typedef;
3.2 触发方式
- 软件触发:通过程序代码启动传输
- 硬件触发:由外设事件自动触发(如ADC转换完成、UART收到数据)
4. DMA配置详解
4.1 基本配置步骤
void DMA_Config(void)
{
// 定义DMA初始化结构体
DMA_InitTypeDef DMA_InitStructure;
// 1. 时钟使能 - DMA属于AHB总线,需要使能对应时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 2. 基本参数配置
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&外设数据寄存器; // 设置外设数据寄存器地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)内存缓冲区; // 设置内存缓冲区地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 设置传输方向:外设为数据源
DMA_InitStructure.DMA_BufferSize = 数据传输数量; // 设置要传输的数据数量
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_Medium; // 通道优先级:中等
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 禁用存储器到存储器模式
// 3. 初始化DMA通道
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
// 4. 使能DMA通道
DMA_Cmd(DMA1_Channel1, ENABLE);
}
4.2 关键参数说明
- 传输方向:决定数据是从外设到内存,还是从内存到外设
- 地址递增:指定传输过程中地址是否自动增加
- 数据宽度:支持字节、半字、全字传输
- 优先级:当多个DMA通道同时请求时的处理顺序
5. 实战应用:ADC多通道采集
5.1 DMA与ADC的协同工作
// 定义ADC数据缓冲区
uint16_t adc_values[ADC_CHANNEL_COUNT]; // 存储ADC转换结果的数组
// ADC多通道DMA采集配置
void ADC_DMA_Config(void)
{
// ADC配置结构体
ADC_InitTypeDef ADC_InitStructure;
// ADC基本配置
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 启用扫描模式(多通道)
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 启用连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 不使用外部触发
ADC_Init(ADC1, &ADC_InitStructure); // 应用ADC配置
// 启用ADC的DMA请求功能
ADC_DMACmd(ADC1, ENABLE);
// DMA配置结构体
DMA_InitTypeDef DMA_InitStructure;
// DMA参数配置
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // ADC数据寄存器地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_values; // 内存缓冲区地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 传输方向:外设到内存
DMA_InitStructure.DMA_BufferSize = ADC_CHANNEL_COUNT; // 缓冲区大小(通道数量)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址固定
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 外设数据:16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 内存数据:16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环模式(自动重复)
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 高优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 非存储器到存储器模式
// 初始化并启用DMA
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
// 启动ADC转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
// DMA传输完成中断服务函数
void DMA1_Channel1_IRQHandler(void)
{
// 检查传输完成标志
if(DMA_GetITStatus(DMA1_IT_TC1)) // TC1表示通道1传输完成
{
// 清除中断标志
DMA_ClearITPendingBit(DMA1_IT_TC1);
// 在这里处理ADC数据
// adc_values数组中已经包含了所有通道的最新数据
// 示例:处理ADC数据
for(int i = 0; i < ADC_CHANNEL_COUNT; i++)
{
// 对每个通道的数据进行处理
process_adc_data(i, adc_values[i]);
}
}
}
6. DMA使用注意事项
6.1 常见问题及解决
- 数据传输不完整
- 检查缓冲器大小设置
- 确认传输模式配置正确
- 数据覆盖问题
- 合理设置循环模式或正常模式
- 及时处理完成中断
- 性能优化
- 根据数据特性选择合适的数据宽度
- 合理设置DMA通道优先级
6.2 最佳实践建议
- 在数据传输前确保DMA通道已正确配置
- 使用DMA完成中断来处理数据,避免轮询
- 对于连续数据流,优先使用循环模式
- 注意内存对齐,提高传输效率
7. 总结
DMA是STM32中提升系统性能的重要技术,通过合理使用DMA可以:
- 释放CPU资源:让CPU专注于算法和逻辑处理
- 提高系统实时性:确保数据及时传输和处理
- 降低系统功耗:CPU可以在DMA工作时进入低功耗模式 掌握DMA技术是嵌入式开发从入门到进阶的重要一步,建议在实际项目中多加练习,逐步深入理解其工作原理和应用技巧。