STM32 DMA 三种传输模式深度解析Flash到SRAM的32字数据拷贝实战指南1. DMA技术核心原理与STM32实现架构直接内存访问(DMA)是现代微控制器系统中提升数据吞吐效率的关键技术。STM32系列芯片内置的DMA控制器通过专用总线矩阵实现了外设与存储器间的高速数据搬运将CPU从繁重的数据搬运任务中解放出来。DMA工作流程的核心机制总线仲裁DMA控制器通过AHB总线矩阵与CPU共享系统总线带宽采用优先级仲裁机制解决访问冲突数据传输单元每个DMA通道包含独立的源/目标地址寄存器、数据计数器和控制逻辑触发方式支持硬件触发外设请求和软件触发两种启动模式传输完成通知通过状态标志位或中断信号向CPU报告操作状态在STM32F10x系列中DMA控制器的主要特性参数如下表所示特性DMA1DMA2可用通道数75外设请求映射固定可配置存储器到存储器模式支持支持优先级管理4级可编程4级可编程数据宽度8/16/32位8/16/32位关键提示当启用存储器到存储器模式时DMA_CCR寄存器的MEM2MEM位必须置1此时传输会立即开始而不需要外设请求信号。2. 存储器到存储器模式实战Flash到SRAM数据迁移存储器到存储器模式是DMA最基础的应用场景适合需要高速数据搬移且不涉及外设交互的情况。下面我们通过完整的工程示例展示如何将常量数据从Flash迁移到SRAM。2.1 硬件连接与工程配置本实验无需额外硬件连接主要涉及以下软件配置步骤启用DMA时钟位于AHB总线RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);定义源数据和目标缓冲区// Flash中的常量数据4字节对齐 const uint32_t SRC_Const_Buffer[32] __attribute__((aligned(4))) { 0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F10, // ... 其他数据 }; // SRAM中的目标缓冲区 uint32_t DST_Buffer[32] __attribute__((aligned(4)));DMA通道参数配置结构体初始化DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)SRC_Const_Buffer; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)DST_Buffer; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; // 传输方向 DMA_InitStructure.DMA_BufferSize 32; // 传输字数 DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Enable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Word; DMA_InitStructure.DMA_Mode DMA_Mode_Normal; // 单次传输模式 DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Enable; // 关键配置位2.2 传输过程监控与验证为确保数据传输的准确性我们需要实现数据校验机制uint8_t BufferCompare(const uint32_t* pBuf1, const uint32_t* pBuf2, uint32_t len) { while(len--) { if(*pBuf1 ! *pBuf2) return 0; } return 1; } // 在主函数中调用验证 if(BufferCompare(SRC_Const_Buffer, DST_Buffer, 32)) { LED_GREEN_ON(); // 验证成功 } else { LED_RED_ON(); // 验证失败 }常见问题排查表现象可能原因解决方案传输数据错位地址未对齐确保源/目标地址按数据宽度对齐部分数据丢失缓冲区大小设置错误检查DMA_BufferSize与实际数据量匹配传输未启动MEM2MEM位未使能确认DMA_CCR的MEM2MEM位置1数据校验失败缓存一致性未处理必要时调用SCB_CleanDCache()3. 存储器到外设模式实战USART高速数据发送存储器到外设模式是DMA最典型的应用场景之一特别适合需要持续数据流输出的外设如USART、SPI等。下面以USART1为例展示实现方法。3.1 外设与DMA协同配置USART基础配置115200bps, 8N1USART_InitTypeDef USART_InitStruct; USART_InitStruct.USART_BaudRate 115200; USART_InitStruct.USART_WordLength USART_WordLength_8b; USART_InitStruct.USART_StopBits USART_StopBits_1; USART_InitStruct.USART_Parity USART_Parity_No; USART_InitStruct.USART_Mode USART_Mode_Tx; USART_Init(USART1, USART_InitStruct); USART_Cmd(USART1, ENABLE);DMA通道特殊配置DMA_InitTypeDef DMA_InitStruct; DMA_InitStruct.DMA_PeripheralBaseAddr (uint32_t)USART1-DR; DMA_InitStruct.DMA_MemoryBaseAddr (uint32_t)SendBuffer; DMA_InitStruct.DMA_DIR DMA_DIR_PeripheralDST; // 存储器到外设 DMA_InitStruct.DMA_BufferSize 256; DMA_InitStruct.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode DMA_Mode_Normal; DMA_InitStruct.DMA_Priority DMA_Priority_Medium; DMA_InitStruct.DMA_M2M DMA_M2M_Disable;启用USART的DMA发送请求USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);3.2 循环模式与中断管理对于持续数据流应用建议采用循环模式并配合传输完成中断// 启用DMA传输完成中断 DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); // NVIC配置 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel DMA1_Channel4_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority 1; NVIC_InitStruct.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStruct); // 中断服务例程 void DMA1_Channel4_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC4)) { DMA_ClearITPendingBit(DMA1_IT_TC4); // 处理传输完成事件 } }重要提示在循环模式下无需在每次传输后重新配置DMA控制器会自动重置计数器并开始新一轮传输。但需注意缓冲区数据更新时的同步问题。4. 外设到存储器模式实战ADC高速数据采集外设到存储器模式常见于数据采集系统如ADC连续采样、I2S音频输入等场景。本节以ADC1连续采样为例展示DMA如何实现高效数据搬运。4.1 ADC与DMA联动配置ADC常规配置12位分辨率扫描模式ADC_InitTypeDef ADC_InitStruct; ADC_InitStruct.ADC_Mode ADC_Mode_Independent; ADC_InitStruct.ADC_ScanConvMode ENABLE; ADC_InitStruct.ADC_ContinuousConvMode ENABLE; ADC_InitStruct.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStruct.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStruct.ADC_NbrOfChannel 4; ADC_Init(ADC1, ADC_InitStruct);DMA特殊配置要点DMA_InitTypeDef DMA_InitStruct; DMA_InitStruct.DMA_PeripheralBaseAddr (uint32_t)ADC1-DR; DMA_InitStruct.DMA_MemoryBaseAddr (uint32_t)ADCBuffer; DMA_InitStruct.DMA_DIR DMA_DIR_PeripheralSRC; // 外设为数据源 DMA_InitStruct.DMA_BufferSize 1024; // 采样点数 DMA_InitStruct.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; DMA_InitStruct.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord; DMA_InitStruct.DMA_Mode DMA_Mode_Circular; // 循环缓冲启用ADC的DMA功能ADC_DMACmd(ADC1, ENABLE);4.2 双缓冲技术与数据处理对于实时性要求高的应用可采用双缓冲技术避免数据竞争// 双缓冲定义 uint16_t ADCBuffer[2][256]; volatile uint8_t CurrentBuffer 0; // DMA中断服务例程 void DMA1_Channel1_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_HT1)) { // 半传输完成缓冲区前半满 ProcessBuffer(ADCBuffer[CurrentBuffer^1], 128); } if(DMA_GetITStatus(DMA1_IT_TC1)) { // 传输完成缓冲区后半满 ProcessBuffer(ADCBuffer[CurrentBuffer], 128); CurrentBuffer ^ 1; // 切换缓冲区 } DMA_ClearITPendingBit(DMA1_IT_HT1 | DMA1_IT_TC1); }三种传输模式关键对比特性存储器到存储器存储器到外设外设到存储器触发方式立即/软件触发外设硬件触发外设硬件触发典型应用内存数据搬移USART发送ADC采集地址递增通常双方都递增存储器递增存储器递增循环模式较少使用常用常用数据宽度必须一致可不同需外设支持可不同需外设支持在实际项目中我曾遇到ADC采样数据错位的问题最终发现是DMA配置中数据宽度16位与ADC数据寄存器宽度12位右对齐不匹配导致。通过统一设置为半字16位传输并适当处理数据问题得以解决。这提醒我们外设数据寄存器特性必须与DMA配置严格匹配。