一、DMA简介 

DMA 全称Direct Memory Access,即直接存储器访问

DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。

DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。

作用:为CPU减负。

STM32最多有2个DMA控制器(DMA2仅存在大容量产品中),DMA1有7个通道。DMA2有5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁起来协调各个DMA请求的优先权。

二、DMA框图 

三、 STM32的DMA有以下一些特性:

①每个通道都直接连接专用的硬件DMA请求,都支持软件触发,这些通过 软件来配置 。
②在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如在相等优先权时由硬件决定(请求0优先于请求1,依此类推) 。
③ 独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。

④ 支持循环的缓冲器管理

⑤ 每个通道都有3个事件标志(DMA 半传输,DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。

⑥ 外设和存储器,存储器和外设的传输 ,存储器和存储器间的传输

⑦ 闪存、SRAM、外设的SRAM、APB1 APB2和AHB外设均可作为访问的源和目标。

⑧ 可编程的数据传输数目:最大为65536

四、DAM寄存器介绍

 

 

 

五、 DMA配置参数

①通道
②优先级
③数据传输方向
④存储器/外设 数据宽度
⑤存储器/外设 地址是否增量
⑥循环模式
⑦数据传输量

六、常用的DMA库函数 

void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx,

                                                          DMA_InitTypeDef* DMA_InitStruct);

void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx,

                                                           FunctionalState NewState);

void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx,

                                   uint32_t DMA_IT, FunctionalState NewState);

void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx,

                                    uint16_t DataNumber);

uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);

void DMA_ClearFlag(uint32_t DMAy_FLAG);

ITStatus DMA_GetITStatus(uint32_t DMAy_IT);

void DMA_ClearITPendingBit(uint32_t DMAy_IT);

常用的外设DMA使能库函数:

void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);

void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);

void DAC_DMACmd(uint32_t DAC_Channel, FunctionalState NewState);

void I2C_DMACmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

void SDIO_DMACmd(FunctionalState NewState);

void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);

void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase, uint16_t TIM_DMABurstLength)

void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource, FunctionalState NewState);

七、 DMA配置程序过程

八、代码区

以ADC的DAM为例

adc.c
 
__IO uint16_t ADC_ConvertedValue;
 
void DMA_Configure(void)
{	 
    DMA_InitTypeDef DMA_InitStructure;
	
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);// 打开DMA时钟
	
    DMA_DeInit(DMA1_Channel1);   		// 复位DMA控制器 
 
    // 配置 DMA 初始化结构体
	// 外设基址为:ADC 数据寄存器地址
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32) ( & ( ADCx->DR ) );
	
	// 存储器地址,实际上就是一个内部SRAM的变量
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;
	
	// 数据源来自外设
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	
	// 缓冲区大小为1,缓冲区的大小应该等于存储器的大小(即多少个通道就写多少 )
	DMA_InitStructure.DMA_BufferSize = 1;
	
	// 外设寄存器只有一个,地址不用递增
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
 
	// 存储器地址固定
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; 
	
	// 外设数据大小为半字,即两个字节
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	
	// 存储器数据大小也为半字,跟外设数据大小相同
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	
	// 循环传输模式  选择循环模式,有数据就一直会传输 
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	
 
	// DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	
	// 禁止存储器到存储器模式,因为是从外设到存储器
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	
	// 初始化DMA
	DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
	
	// 使能 DMA 通道
	DMA_Cmd(ADC_DMA_CHANNEL , ENABLE);
}
 
void adc_init(void)
{
    ....
    // 使能ADC DMA 请求
	ADC_DMACmd(ADCx, ENABLE);
    ....
}
 
/**
  * @brief  ADC初始化
  * @param  无
  * @retval 无
  */
void ADCx_Init(void)
{
	ADCx_GPIO_Config();
	ADCx_Mode_Config();
}