DMA就是将一个地址空间复制到另外一个地址空间。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备直接传送数据,使CPU的效率大大的提高了。
stm一般有两个DMA控制器,DMA1有7通道。DMA2有5个通道。
STM32 的 DMA 有以下一些特性:
●每个通道都直接连接专用的硬件 DMA 请求,每个通道都同样支持软件触发。
这些功能通过软件来配置。
●在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),
假如在相等优先权时由硬件决定(请求 0 优先于请求 1,依此类推) 。
●独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。
源和目标地址必须按数据传输宽度对齐。
●支持循环的缓冲器管理
●每个通道都有 3 个事件标志(DMA 半传输, DMA 传输完成和 DMA 传输出错),
这3个事件标志逻辑或成为一个单独的中断请求。
●存储器和存储器间的传输
●外设和存储器,存储器和外设的传输
●闪存、 SRAM、外设的 SRAM、 APB1 APB2 和 AHB 外设均可作为访问的源和目标。
●可编程的数据传输数目:最大为 65536
我们可以通过DMA中断状态寄存器(DMA_ISR)来获取当前DMA传输的状态。通过
DMA中断标志清除寄存器(CMA_IFCR)清除中断状态寄存器对应的位。
1)使能 DMA 时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟2)初始化 DMA 通道 4 参数
1 DMA_InitTypeDef DMA_InitStructure; 2 DMA_InitStructure.DMA_PeripheralBaseAddr = &USART1->DR; //DMA 外设 ADC 基地址 3 DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA 内存基地址 4 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //从内存读取发送到外设 5 DMA_InitStructure.DMA_BufferSize = 64; //DMA 通道的 DMA 缓存的大小 6 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变 7 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增 8 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //8 位 9 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8 位10 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式11 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA 通道 x 拥有中优先级12 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输13 DMA_Init(DMA_CHx, &DMA_InitStructure); //根据指定的参数初始化
3) 使能串口 DMA 发送
进行 DMA 配置之后,我们就要开启串口的 DMA 发送功能,使用的函数是:USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);如果是要使能串口 DMA 接受,那么第二个参数修改为 USART_DMAReq_Rx 即可。
4) 使能 DMA1 通道 4,启动传输。
使能串口 DMA 发送之后,我们接着就要使能 DMA 传输通道:DMA_Cmd(DMA_CHx, ENABLE);
5) 查询 DMA 传输状态在 DMA 传输过程中,我们要查询 DMA 传输通道的状态,使用的函数是:FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)比如我们要查询 DMA 通道 4 传输是否完成,方法是:DMA_GetFlagStatus(DMA2_FLAG_TC4);这里还有一个比较重要的函数就是获取当前剩余数据量大小的函数:uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)比如我们要获取 DMA 通道 4 还有多少个数据没有传输,方法是:DMA_GetCurrDataCounter(DMA1_Channel4);
1 void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr) 2 { 3 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输 4 5 DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值 6 7 DMA1_MEM_LEN=cndtr; 8 DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设基地址 9 DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA内存基地址10 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设11 DMA_InitStructure.DMA_BufferSize = cndtr; //DMA通道的DMA缓存的大小12 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变13 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增14 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位15 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位16 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常模式17 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 18 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输19 DMA_Init(DMA_CHx, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器20 21 } 22 //开启一次DMA传输23 void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)24 { 25 DMA_Cmd(DMA_CHx, DISABLE ); //关闭USART1 TX DMA1 所指示的通道 26 DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小27 DMA_Cmd(DMA_CHx, ENABLE); //使能USART1 TX DMA1 所指示的通道 28 }
知识来源:STM32F1 开发指南