参考资料:
1、正点原子探索者STM32f407开发板-《STM32f407开发指南-库函数版本》-第28章DMA实验;
2、STM32F4xx 官方参考资料《STM32F4xx中文参考手册》-第9章-DMA控制器。
目录
概念认知:
DMA是什么?
通道选择:
DMA请求映射:
DMA事务:
三种传输方式原理:
DMA_SxCR寄存器(x=0~7):
其他
编辑数据宽度、封装/解封、字节序
双缓冲区模式
DMA 中断
流配置过程(重要):
DMA配置参数
重要函数:
DMA_Init();
DMA配置程序过程(串口发送DMA)
全称Direct Memory Access,即直接存储器访问。
正常情况下,数据如果想要传输,得由CPU先读取目标数据,再传向目标地址。有了DMA后,传输的事就是DMA做的了,CPU只要初始化DMA就可以了。
DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
作用:为CPU减负。
STM32F4最多有2个DMA控制器,2个DMA控制器总共有16个数据流(每个控制器8个),8个数据流中每一个都连接到专用硬件DMA通道(请求)。每个DMA控制器都用于管理一个或者多个外设的存储器访问请求。每个数据流总共可以有多达8个通道(或请求)(理论上一共就是16x8=128个通道,实际上每个通道都固定了对应的映射,所以并没有128个),每个通道都有一个仲裁器,用于处理DMA请求间的优先级。
注:每个数据流只能同时接一个通道!
DMA传输,由源地址->目标地址,源地址和目标地址都可以是外设、存储器的地址。
特别的,当存储器到存储器时候,仅限于DMA2控制器;一般没有外设到外设。
可设置独立的源和目标传输宽度(字节(8位)、半字(16位)、字(32位)):源和目标的数据宽度不相等时,DMA 自动封装/解封必要的传输数据来优化带宽。这个特性仅在 FIFO 模式下可用。如:A设置8位传输出去,B设置32位接收,FIFO会等A传出4次,共32位数据,然后封装成B的一次“字”的接收。
一般通过DMA_SxCR寄存器和DMA_SxM0AR寄存器来控制源到目标的传输类型:
关于“指针递增”,“循环模式”之类的,暂时用不到,不多赘述。
可以看到由DMA_SxNDTR来决定数据宽度,PSIZE就是外设,MSIZE就是储存器。
也就是说,在开启双缓冲区模式的情况下,DMA_SxM0AR和DMA_SxM1AR才会同时有用,一般情况下只有DMA_SxM0AR有效,这些寄存器装的都是地址。
简单来说,开启时,一个事务结束后,源地址/目标地址进行切换,所以叫双缓冲,如果是x缓冲,就是x个源地址/目标地址在事务结束后进行切换。
①通道
②优先级
③数据传输方向
④存储器/外设 数据宽度
⑤存储器/外设 地址是否增量
⑥循环模式
⑦数据传输量
函数在stm32f4xx_dma.c / stm32f4xx_dma.h中
常用的DMA库函数
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);
void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState);
void DMA_SetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx, uint16_t Counter);
uint16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx);
void DMA_DoubleBufferModeConfig(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t Memory1BaseAddr,uint32_t DMA_CurrentMemory);
void DMA_DoubleBufferModeCmd();
void DMA_MemoryTargetConfig(DMA_Stream_TypeDef* DMAy_Streamx,
uint32_t MemoryBaseAddr, uint32_t DMA_MemoryTarget);
uint32_t DMA_GetCurrentMemoryTarget(DMA_Stream_TypeDef* DMAy_Streamx);
FunctionalState DMA_GetCmdStatus(DMA_Stream_TypeDef* DMAy_Streamx);
uint32_t DMA_GetFIFOStatus(DMA_Stream_TypeDef* DMAy_Streamx);
FlagStatus DMA_GetFlagStatus(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t DMA_FLAG);
void DMA_ClearFlag(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t DMA_FLAG);
void DMA_ITConfig(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t DMA_IT, FunctionalState NewState);
ITStatus DMA_GetITStatus(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t DMA_IT);
void DMA_ClearITPendingBit(DMA_Stream_TypeDef* DMAy_Streamx, uint32_t DMA_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);
一般在相应的外设那里有对应的DMA使能函数,如USART:
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);
typedef struct
{
uint32_t DMA_Channel;
uint32_t DMA_PeripheralBaseAddr;
uint32_t DMA_Memory0BaseAddr;
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_FIFOMode;
uint32_t DMA_FIFOThreshold;
uint32_t DMA_MemoryBurst;
uint32_t DMA_PeripheralBurst;
}DMA_InitTypeDef;
① 使能DMA时钟
RCC_AHB1PeriphClockCmd();
② 初始化DMA通道参数
DMA_Init();
③使能串口DMA发送,串口DMA使能函数:
USART_DMACmd();
④查询DMA的EN位,确保数据流就绪,可以配置
DMA_GetCmdStatus();
⑤设置通道当前剩余数据量
DMA_SetCurrDataCounter();
⑥使能DMA1通道,启动传输。
DMA_Cmd();
⑤查询DMA传输状态
DMA_GetFlagStatus();
⑥获取/设置通道当前剩余数据量:
DMA_GetCurrDataCounter();
Fin.