核心内容摘要
网爆黑料
什么是DMA为什么需要它DMADirect Memory Access直接存储器访问是嵌入式系统中一种高效的数据传输机制。
简单来说它就像是一个专门负责搬数据的快递员可以在不打扰CPU的情况下自动完成内存与外设之间的数据搬运。
想象一下这个场景你正在用STM32的串口接收大量传感器数据。
传统方式下每收到一个字节CPU都要停下手中的工作去处理中断就像每收一个快递都要亲自下楼签收一样低效。
而DMA则像雇了个快递柜——数据到达后自动存入指定位置等积累到一定量再通知CPU处理解放了CPU的计算资源。
DMA在STM32中的典型应用场景包括ADC采集数据直接存入内存串口大批量数据收发SPI/I2C与外部设备通信内存到内存的快速拷贝如图像处理以F1系列为例STM32最多有2个DMA控制器DMA2仅大容量型号有DMA1有7个通道DMA2有5个通道。
每个通道可以绑定到特定外设比如DMA1通道4对应USART1_TXDMA1通道5对应ADC1DMA2通道3对应SPI1_RX
STM32 DMA硬件架构解析
1 DMA控制器工作原理DMA控制器的核心是一个多路复用的数据传输引擎。
当外设准备好数据后会通过硬件信号线向DMA控制器发起请求DMA Request。
仲裁器根据优先级决定处理哪个请求然后DMA控制器就会自动执行数据传输。
关键组件解析通道仲裁器处理多个通道的竞争问题软件可设4级优先级很高/高/中/低同优先级时通道号小的优先数据寄存器支持不同位宽转换可处理8/16/32位数据自动处理大小端问题地址发生器支持地址自动递增外设地址通常固定内存地址通常递增
2 寄存器精要掌握这几个核心寄存器就能玩转DMA寄存器功能说明关键位域DMA_ISR中断状态寄存器TCIFx传输完成标志DMA_IFCR中断标志清除寄存器写0清除对应中断标志DMA_CCRx通道配置寄存器最重要数据传输方向、位宽、模式等DMA_CNDTRx数据量寄存器实时显示剩余传输量DMA_CPARx外设地址寄存器如USART1-DRDMA_CMARx内存地址寄存器如SendBuff数组首地址
实战配置串口DMA发送示例
1 CubeMX配置步骤使能USART1的DMA传输功能添加TX方向的DMA通道配置参数Mode: Normal非循环Priority: MediumMemory Increment: EnablePeripheral Increment: DisableData Width: Byte
2 手动编码实现// DMA初始化结构体 DMA_InitTypeDef DMA_InitStruct {0}; //
使能DMA时钟 __HAL_RCC_DMA1_CLK_ENABLE(); //
配置DMA参数 DMA_InitStruct.Direction DMA_MEMORY_TO_PERIPH; // 内存到外设 DMA_InitStruct.PeriphInc DMA_PINC_DISABLE; // 外设地址不递增 DMA_InitStruct.MemInc DMA_MINC_ENABLE; // 内存地址递增 DMA_InitStruct.PeriphDataAlignment DMA_PDATAALIGN_BYTE; DMA_InitStruct.MemDataAlignment DMA_MDATAALIGN_BYTE; DMA_InitStruct.Mode DMA_NORMAL; // 普通模式 DMA_InitStruct.Priority DMA_PRIORITY_MEDIUM; // 中优先级 HAL_DMA_Init(hdma_usart1_tx); //
绑定DMA到串口 __HAL_LINKDMA(huart1, hdmatx, hdma_usart1_tx); //
启动传输 HAL_UART_Transmit_DMA(huart1, (uint8_t*)SendBuff, BUFF_SIZE);
3 调试技巧使用__HAL_DMA_GET_COUNTER()获取剩余传输量检查DMA_FLAG_TCx标志判断传输完成内存地址要对齐特别是32位传输时循环模式记得重设CNDTR值
高级应用技巧
1 双缓冲技术在需要连续传输的场景如音频播放可以使用双缓冲避免数据冲突// 定义双缓冲 uint8_t buffer1[256], buffer2[256]; // 初始化时配置 DMA_InitStruct.Mode DMA_CIRCULAR; // 循环模式 DMA_InitStruct.Memory0BaseAddr (uint32_t)buffer1; DMA_InitStruct.Memory1BaseAddr (uint32_t)buffer2; DMA_InitStruct.MemoryBurst DMA_MBURST_INC4;
2 内存到内存传输某些型号支持内存间DMA传输如STM32F4比CPU拷贝快
倍DMA_InitStruct.Direction DMA_MEMORY_TO_MEMORY; DMA_InitStruct.PeriphInc DMA_PINC_ENABLE; DMA_InitStruct.MemInc DMA_MINC_ENABLE;
3 中断组合使用合理利用半传输中断和完成中断// 在HAL_DMA_Start_IT()后添加 __HAL_DMA_ENABLE_IT(hdma, DMA_IT_HT | DMA_IT_TC); // 中断回调函数 void HAL_DMA_XferHalfCpltCallback(DMA_HandleTypeDef *hdma) { // 处理前半段数据 } void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma) { // 处理后半段数据 }
5.
常见问题排查数据错位检查Memory/Peripheral数据宽度设置确认地址递增配置正确传输不启动确认外设已使能DMA请求检查DMA通道与外设映射关系只传输一次循环模式需设置DMA_CIRCULAR检查CNDTR是否自动重载性能优化使用突发传输Burst Mode合理设置FIFO阈值优先选择支持DMA的外设实际项目中我曾遇到SPI DMA传输偶尔丢失数据的问题。
后来发现是SPI时钟速度过高导致DMA来不及响应通过降低时钟频率并启用DMA FIFO后问题解决。
这也提醒我们DMA性能不仅取决于配置还需要考虑外设特性与总线负载的平衡。