STM32 DMA通信详解
DMA(Direct Memory Access,直接内存访问)是STM32微控制器中一种重要的数据传输机制,它允许外设与内存之间或内存与内存之间直接传输数据,而无需CPU的干预。这种机制可以显著提高系统性能,特别是在需要高速数据传输或大量数据处理的场景中。
一、DMA基础概念
1. DMA工作原理
DMA控制器是一种专门设计用于在系统内不同组件之间高效传输数据的硬件模块。其核心工作原理是:
-
初始化阶段:CPU配置DMA控制器,设置源地址、目标地址、传输长度等参数
-
传输阶段:当外设或软件触发DMA请求时,DMA控制器接管总线控制权
-
数据搬运:DMA控制器直接在源地址和目标地址之间搬运数据
-
完成通知:传输完成后,DMA控制器可以产生中断通知CPU1
2. STM32 DMA特性
STM32系列MCU的DMA控制器具有以下特点:
-
多通道设计:如STM32F1系列有2个DMA控制器(DMA1和DMA2),DMA1有7个通道,DMA2有5个通道1
-
优先级管理:每个通道有可编程优先级,通过仲裁器处理多个同时请求
-
多种传输模式:支持单次传输(Normal)和循环传输(Circular)
-
灵活的数据宽度:支持字节(8位)、半字(16位)和字(32位)传输7
二、DMA配置与使用
1. 使用STM32CubeMX配置DMA
STM32CubeMX工具可以简化DMA的配置过程:
-
启用DMA控制器:在"System Core"→"DMA"中启用所需的DMA控制器
-
配置DMA通道:
-
选择请求源(如USART1_TX/USART1_RX)
-
设置优先级(低/中/高/非常高)
-
选择传输模式(Normal或Circular)
-
配置地址递增(外设地址通常不递增,内存地址通常递增)
-
设置数据宽度(通常与相关外设匹配)37
-
-
配置相关外设:如USART、SPI等,并启用其DMA功能1
2. DMA编程接口
HAL库提供了简洁的DMA API:
// 启动DMA传输
HAL_UART_Transmit_DMA(&huart1, txBuffer, length);
HAL_UART_Receive_DMA(&huart1, rxBuffer, length);
// DMA传输完成回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
三、常见DMA应用场景
1. 串口DMA通信
串口使用DMA可以显著降低CPU负载,特别适合高速或大数据量通信:
-
发送数据:将数据从内存传输到USART_TDR寄存器
-
接收数据:将数据从USART_RDR寄存器传输到内存
-
不定长数据接收:结合IDLE中断实现不定长数据接收5
示例代码:
// 启用IDLE中断
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
// IDLE中断处理
void USART1_IRQHandler(void) {
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_DMAStop(&huart1);
uint16_t len = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx);
// 处理接收到的数据...
HAL_UART_Receive_DMA(&huart1, rxBuffer, BUFFER_SIZE);
}
HAL_UART_IRQHandler(&huart1);
}
2. SPI DMA通信
SPI接口使用DMA可以高效传输大量数据,特别适合与ADC、DAC、存储器等外设通信:
-
配置SPI DMA:
c
// SPI TX DMA配置 hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_spi1_tx.Init.PeriphInc = DMA_PERRIPH_INC_DISABLE; hdma_spi1_tx.Init.MemInc = DMA_MEM_INC_ENABLE; // ...其他配置
-
启动SPI DMA传输:
HAL_SPI_Transmit_DMA(&hspi1, txData, length); HAL_SPI_Receive_DMA(&hspi1, rxData, length); // 或同时收发 HAL_SPI_TransmitReceive_DMA(&hspi1, txData, rxData, length);
3. 内存到内存DMA传输
DMA也可以用于高效的内存拷贝操作:
// 配置内存到内存DMA
hdma_memtomem.Init.Direction = DMA_MEMORY_TO_MEMORY;
// ...其他配置
// 启动传输
HAL_DMA_Start(&hdma_memtomem, (uint32_t)src, (uint32_t)dest, length);
四、DMA优化技巧
-
双缓冲技术:使用两个缓冲区交替工作,提高吞吐量
-
数据对齐:确保DMA缓冲区地址与数据宽度对齐
-
缓存一致性:在Cortex-M7等有缓存的核心上,注意维护缓存一致性
-
合理设置优先级:根据实时性要求为不同DMA通道设置适当优先级
-
使用循环模式:对于连续数据流,使用循环模式避免频繁重新配置7
五、常见问题与调试
-
DMA传输不启动:
-
检查DMA和外设时钟是否使能
-
验证DMA配置参数是否正确
-
确保DMA请求源与通道映射正确2
-
-
数据损坏或丢失:
-
检查缓冲区大小是否足够
-
验证地址递增设置是否正确
-
确保在传输完成前不访问缓冲区9
-
-
性能优化:
-
使用DMA突发传输
-
合理设置FIFO阈值
-
考虑使用LL库以获得更精细的控制4
-
六、高级应用
1. DMAMUX (DMA请求复用器)
新型STM32系列(如STM32G0/G4/H7)引入了DMAMUX模块,它提供了:
-
更灵活的DMA请求映射
-
更多触发源选择(如GPIO中断、定时器等)
-
可配置的请求发生器4
配置示例:
// 使能DMAMUX请求发生器
HAL_DMAEx_EnableMuxRequestGenerator(&hdma_muxgen);
// 配置GPIO外部中断触发DMA
HAL_DMAEx_ConfigMuxRequestGenerator(&hdma_muxgen, &muxgen_config);
2. 分散/聚集DMA
某些高级STM32支持分散/聚集DMA,允许非连续内存区域的传输:
// 配置链表描述符
DMA_LinkNodeTypeDef node;
node.Init.Request = DMA_REQUEST_MEM2MEM;
node.Init.BlkHWRequest = DMA_HWREQUEST_SINGLEBURST;
// ...其他配置
HAL_DMAEx_List_BuildNode(&node, &node_config);
// 创建链表
HAL_DMAEx_List_InsertNode(&list, &node);
总结
STM32的DMA功能为高效数据搬运提供了强大支持,合理使用DMA可以:
-
显著降低CPU负载,提高系统整体性能
-
实现更高带宽的数据传输
-
改善实时性,使CPU能更专注于关键任务
-
降低功耗,特别适合电池供电设备
通过STM32CubeMX工具和HAL库,开发者可以快速实现各种DMA应用场景,从简单的内存拷贝到复杂的外设数据流处理。