DMA:全称Direct Memory Access(直接存储器访问),把一个地址空间的值“复制”到另一个地址空间,使用DMA传输方式无需CPU直接控制传输,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
作用:为CPU减负。
硬件方面,STM32最多有2个DMA控制器(DMA2仅存在大容量产品中),DMA1有7个通道,DMA2有5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求(由硬件决定),附图DMA各通道限定可处理请求:
还有一个“仲裁器”来协调各个DMA请求的优先级(可以通过软件编程设置,共有四级:很高、高、中等和低),相同优先级的DMA请求,低编号通道优先于高编号通道传输。
大致流程(以外设->存储器为例):
外设(APB1、2)发送DMA请求–》经仲裁器处理–》发送到对应通道–》经DMA总线–》对存储器进行访问
1.每个通道都直接连接专用的硬件DMA请求,反映在各个通道可处理的DMA请求的类型不同,都支持软件触发,这些通过软件来配置。
2.在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如在相等优先权时由硬件决定(请求0优先于请求1,前面有提到,依此类推) 。
3.存储器(闪存、SRAM、外设的SRAM)、外设(APB1 APB2和AHB)均可作为访问的源和目标
4.独立的源和目标数据区的传输宽度(字节(8位)、半字(16位)、全字(32位))
5.可编程的数据传输数目:0~65536
8.传输方向:外设和存储器之间的传输,存储器和外设之间的传输 ,存储器和存储器之间的传输
9.指针增量模式:即每传输完一个数据,地址自增
====================================================================
归纳(每次DMA传送有以下4个操作):
1.将外设基地址写入CPARx
2.将存储器基地址写入CMARx
3.将要传输的数据数目写入CNDTRx,每传输一个数据,该寄存器自减1
4.设置DIR位,即确定数据传输方向
====================================================================
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);
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);
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; //外设基地址
uint32_t DMA_MemoryBaseAddr; //存储器基地址
uint32_t DMA_DIR; //数据传输方向
uint32_t DMA_BufferSize; //通道传输数据量
uint32_t DMA_PeripheralInc;//外设增量模式
uint32_t DMA_MemoryInc; //存储器增量模式
uint32_t DMA_PeripheralDataSize; //外设数据宽度
uint32_t DMA_MemoryDataSize; //存储器数据宽度
uint32_t DMA_Mode; //模式:是否循环
uint32_t DMA_Priority; //优先级
uint32_t DMA_M2M; //是否存储器到存储器方式
}DMA_InitTypeDef;
① 使能DMA时钟
RCC_AHBPeriphClockCmd();
② 初始化DMA通道参数
DMA_Init();
③使能串口DMA发送,串口DMA使能函数:
USART_DMACmd();
④使能DMA1通道,启动传输。
DMA_Cmd();
⑤查询DMA传输状态
DMA_GetFlagStatus();
⑥获取/设置通道当前剩余数据量:
DMA_GetCurrDataCounter();
DMA_SetCurrDataCounter();