DMA简介
DMA(Direct Memory Acess,直接存储区访问)
可以实现外设寄存器于存储器之间以及存储器与存储器之间的数据高速传输。DMA传输实现高速数据移动过程无需任何CPU操作控制。DMA控制器是独立于Cortex-M4内核的。
此处的外设一般指外设的数据寄存器,如ADC、SPI、I2C、等外设的数据寄存器。存储器一般是指SRAM、外部寄存器、片内Flash等
DMA功能框图
外设通道选择
STM32F4有两个DMA控制器,每个DMA控制器有8个数据流,每个数据流对应8个数据端口。
数据流包含要传输数据的源地址、目标地址、数据等信息。
外设通道主要解决的是决定哪一个外设作为数据流的源地址以及目标地址。
每一个外设请求都占用一个数据通道,相同数据请求可以占用不同数据通道。
仲裁器
如果要同时使用同一个DMA控制器的多个外设请求,需要决定哪一个数据流优先传输。
仲裁器管理数据流分为两个阶段。第一阶段属于软件阶段,主要是寄存器里面的配置,DMA_SxCR的PL[1:0]位。
第二阶段属于硬件阶段。如果有两个及以上的数据流配置的优先级一样,那么它们的优先级取决于编号,编号越低,优先级越高。如数据流2高于数据流3。
FIFO
每个数据流都独立拥有四级32位FIFO(先进先出存储器缓冲区)。
DMA传输具有直接模式以及FIFO模式,直接模式在每个外设请求DMA传输时会立即启动传输。FIFO用于在源数据传输到目标地址之前临时存放这些数据。
FIFO对于要求源地址和目标地址数据宽度不同时非常有用。
存储器以及外设端口
DMA1不能实现存储器到存储器传输。
DMA数据配置
DMA传输模式
DMA2支持全部三种传输模式,DMA1只有外设到存储器以及存储器到外设两种模式。
模式选择可以通过DMA_SxCR的DIR[1:0]位控制,进而将DMA_SxCR寄存器的EN位置1就可以使能DMA传输。
在DMA_SxCR寄存器的PSIZE[1:0]和MSIZE[1:0]分别指定外设和存储器数据宽度大小。可以指定为字节(8位)、半字(16位)、字(32位)。直接模式要求外设和存储器宽度一致,这种模式下,DMA数据流直接使用PSIZE,MSIZE不使用。
源地址和目标地址
DMA_SxPAR、DMA_SxM0AR、DMA_SxM1AR(x为0~7)都是用于存储地址的,都是32位数据有效。第一个用于存储外设地址,后面两个用来存放存储器地址。注意,DMA_SxM1AR只用于双缓冲模式。
模式 | DMA_SxCR的DIR[1:0]配置 | 源地址 | 目标地址 |
---|---|---|---|
外设到存储器 | 00 | DMA_SxPAR | DMA_SxM0AR |
存储器到存储器 | 10 | DMA_SxPAR | DMA_SxM0AR |
存储器到外设 | 01 | DMA_SxM0AR | DMA_SxPAR |
流控制器
流控制器主要涉及控制DMA传输停止的问题。
如果我们知道传输数据的数目,那么我们在传输之前可以设置DMA_SxNDTR寄存器为传输数目值,DMA控制器在传输完这么多数据数目后就可以控制DMA停止传输。
DMA数据流x数据项数DMA_SxNDTR(x为0~7)寄存器用来记录当前仍需要传呼数目。16位数据有效,最大值65535,编程时一般明确指定一个传输数量,完成一次传输后DMA_SxNDTR就会自减,到零时表明传输完成。
如果在传输之前无法得知数据数目,DMA就无法自动控制传输停止了,此时需要硬件通信向DMA控制器发送停止信号,只有SDIO有这个功能。
循环模式
一次模式就是传输一次就停止传输,下一次传输需要手动控制。
循环模式在传输一次后会自动按照相同配置重新传输,一直往复知道被控制停止或传输发生错误。
通过DMA_SxCR寄存器的CIRC可以使能循环模式。
传输类型
突发传输就是在传输阶段吧速度瞬时提升,实现高速传输,需要占用AHB总线
单次传输必须通过AHB的总线仲裁多次控制才传输完成
直接模式
默认情况下,DMA工作在直接模式,不使能FIFO阈值级别。
直接模式要求源地址和目标地址的数据宽度必须一致。所以只有PSIZE控制,MSIZE被忽略。
直接模式不能用于存储器到存储器传输。
直接模式下,如果DMA配置为存储器到外设传输那DMA会将一个数据存放在FIFO内,如果外设启动DMA传输请求就可以马上将数据传输过去。
双缓冲模式
DMA中断
每个DMA数据流可以在一下事件时产生中断。
达到半传输、传输完成、传输错误、FIFO错误、直接模式错误
DMA结构体
typedef struct
{
uint32_t DMA_Channel; //通道选择
uint32_t DMA_PeripheralBaseAddr; //外设地址 一般设置外设的数据寄存器地址
// 如ADC3的DR ((uint32_t) ADC3+0X4c)
uint32_t DMA_Memory0BaseAddr; //存储器0地址
uint32_t DMA_DIR; //传输方向
uint32_t FMA_BufferSize; //数据数目 初始化DMA_SxNDTR寄存器的值
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; //FIFO模式
uint32_t DMA_FIFOThreshold; //FIFO阈值
uint32_t DMA_MemoryBurst; //存储器突发传输
uint32_t DMA_PeripheralBurst; //外设突发传输
}DMA_InitTypeDef;
DMA初始化配置大体流程
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(xx,ENABLE);//开启DMA时钟
DMA_DeInit(XXX,);//复位初始化DMA数据流
while(DMA_GetCmdStatus(XXX)!=DISABLE);//确保DMA数据流复位完成
···
···配置DMA结构体···
···
DMA_Init(XXX,&DMA_InitStructure);//配置数据流
DMA_Cmd(XXX,ENABLE);//使能DMA
while(DMA_GetCmdStatus(XXX)!=ENABLE);//确保DMA数据流有效