DMA(Direct Memory Access,直接存储区访问)为实现数据高速在外设寄存器与存储器之间或者存储器与存储器之间传输提供了高效的方法。
DMA1:P->M,M->P
DMA2:P->M,M->P,M->M
流:是数据传输的一条链路,每个DMA控制器有8条独立的数据流,每次传输的数据量最大为65535,如果数据的单位为字的话,那一次可以传输256KB。
通道:每个数据流有8个通道选择,每个通道对应不同的DMA请求。
多个DMA同时请求,怎么办?
1、软件阶段,DMA_SxCR:PL
2、硬件阶段,数据流编号小的优先级大
同一个数据流只能使用一个通道,同一个DMA控制器可以使用多个数据流。
typedef struct {
uint32_t DMA_Channel; 通道选择
uint32_t DMA_PeripheralBaseAddr; 外设地址
uint32_t DMA_Memory0BaseAddr; 存储器 0 地址
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; FIFO 模式
uint32_t DMA_FIFOThreshold; FIFO 阈值
uint32_t DMA_MemoryBurst; 存储器突发传输
uint32_t DMA_PeripheralBurst; 外设突发传输
} DMA_InitTypeDef;
DMA_Channel: DMA 请求通道选择,可选通道 0 至通道 7,每个外设对应固定的通道,DMA_SxCR :CHSEL[2:0]。
DMA_PeripheralBaseAddr: 外设地址, DMA_SxPAR。
DMA_Memory0BaseAddr:存储器 0 地址,DMA_SxM0AR。
DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设以及存储器到存储器,DMA_SxCR :DIR[1:0] 。
DMA_BufferSize:设定一次传输的数据个数,DMA_SxNDTR 。
DMA_PeripheralInc:外设地址是否递增, DMA_SxCR :PINC 。
DMA_MemoryInc:存储器地址是否递增,DMA_SxCR :MINC。
DMA_PeripheralDataSize: 外设数据宽度,可选字节(8 位)、半字(16 位)和字(32位),DMA_SxCR :PSIZE[1:0]。
DMA_BufferSize:设定一次传输的数据个数,DMA_SxNDTR 。
DMA_MemoryDataSize:存储器数据宽度,可选字节(8 位)、半字(16 位)和字(32位),DMA_SxCR :MSIZE[1:0]。
DMA_Mode : DMA 传 输 模 式 选 择 , 可 选 一 次 传 输 或 者 循 环 传 输 , DMA_SxCR :CIRC 位的值。
DMA_Priority:优先级,非常高、高、中和低,DMA_SxCR :PL[1:0]。
DMA_FIFOMode: FIFO 模式使能,DMA_SxFCR :DMDIS 。
DMA_FIFOThreshold: FIFO 阈值选择,1/4、1/2、 3/4 和满,DMA_SxFCR :FTH[1:0]。
DMA_MemoryBurst:存储器突发模式选择,单次模式、 4 节拍、8 节拍、16 节拍,DMA_SxCR :MBURST[1:0]。
DMA_PeripheralBurst:外设突发模式选择,单次模式、 4 节拍、8 节拍、16 节拍,DMA_SxCR :PBURST[1:0] 。
1-初始化DMA的寄存器到复位状态
DMA_DeInit(DMA_Stream_TypeDef* DMAy_Streamx);
2-DMA初始化函数
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);
3-DMA使能函数
DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState);
DMA 存储器到串口1模式/
uint8_t SendBuff[SENDBUFF_SIZE];
void USART1_TX_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
/*开启DMA时钟*/
RCC_AHB1PeriphClockCmd(USART1_DMA_CLK, ENABLE);
/* 复位初始化DMA数据流 */
DMA_DeInit(USART1_TX_DMA_STREAM);
/* 确保DMA数据流复位完成 */
while (DMA_GetCmdStatus(USART1_TX_DMA_STREAM) != DISABLE) { }
/*usart1 tx对应dma2,通道4,数据流7*/
DMA_InitStructure.DMA_Channel = USART1_DMA_CHANNEL;
/*设置DMA源:串口数据寄存器地址*/
DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_BASE;
/*内存地址(要传输的变量的指针)*/
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)SendBuff;
/*方向:从内存到外设*/
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
/*传输大小DMA_BufferSize=SENDBUFF_SIZE*/
DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
/*外设地址不增*/
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
/*内存地址自增*/
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
/*外设数据单位*/
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
/*内存数据单位 8bit*/
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
/*DMA模式:不断循环*/
//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
/*优先级:中*/
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
/*禁用FIFO*/
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
/*存储器突发传输 单次传输*/
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
/*外设突发传输 单次传输*/
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
/*配置DMA2的数据流7*/
DMA_Init(USART1_TX_DMA_STREAM, &DMA_InitStructure);
/*使能DMA*/
DMA_Cmd(USART1_TX_DMA_STREAM, ENABLE);
/* 等待DMA数据流有效*/
while(DMA_GetCmdStatus(USART1_TX_DMA_STREAM) != ENABLE);
}
uint8_t RevBuff[REVBUFF_SIZE];
void USART1_RX_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
/*开启DMA时钟*/
RCC_AHB1PeriphClockCmd(USART1_DMA_CLK, ENABLE);
/* 复位初始化DMA数据流 */
DMA_DeInit(USART1_RX_DMA_STREAM);
/* 确保DMA数据流复位完成 */
while (DMA_GetCmdStatus(USART1_RX_DMA_STREAM) != DISABLE) { }
/*usart1 tx对应dma2,通道4,数据流2*/
DMA_InitStructure.DMA_Channel = USART1_DMA_CHANNEL;
/*设置DMA源:串口数据寄存器地址*/
DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_BASE;
/*内存地址(要传输的变量的指针)*/
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)RevBuff;
/*方向:从外设到内存*/
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
/*传输大小DMA_BufferSize=REVBUFF_SIZE*/
DMA_InitStructure.DMA_BufferSize = REVBUFF_SIZE;
/*外设地址不增*/
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
/*内存地址自增*/
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
/*外设数据单位*/
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
/*内存数据单位 8bit*/
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
/*DMA模式:不断循环*/
//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
/*优先级:中*/
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
/*禁用FIFO*/
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
/*存储器突发传输 单次传输*/
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
/*外设突发传输 单次传输*/
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
/*配置DMA2的数据流2*/
DMA_Init(USART1_RX_DMA_STREAM, &DMA_InitStructure);
/*使能DMA*/
DMA_Cmd(USART1_RX_DMA_STREAM, ENABLE);
/* 等待DMA数据流有效*/
while(DMA_GetCmdStatus(USART1_RX_DMA_STREAM) != ENABLE);
}
#ifndef __BSP_DMA_H
#define __BSP_DMA_H
#include "stm32f4xx.h"
#include "bsp_led.h"
#include "bsp_systick.h"
#define as 0
/*存储器到外设串口1TX*/
#define USART1_DR_BASE (uint32_t)&(USART1->DR) //数据发送到 USART_DR 寄存器,该寄存器相对 USART 偏移地址为0x04
#define SENDBUFF_SIZE 5000 //发送的数据量 小于65535
#define USART1_DMA_CLK RCC_AHB1Periph_DMA2
#define USART1_DMA_CHANNEL DMA_Channel_4 //通道4
#define USART1_TX_DMA_STREAM DMA2_Stream7 //数据流 7
/*存储器到外设串口1RX*/
#define USART1_DR_BASE (uint32_t)&(USART1->DR) //数据发送到 USART_DR 寄存器,该寄存器相对 USART 偏移地址为0x04
#define REVBUFF_SIZE 5 //发送的数据量 小于65535
#define USART1_DMA_CLK RCC_AHB1Periph_DMA2
#define USART1_DMA_CHANNEL DMA_Channel_4 //通道4
#define USART1_RX_DMA_STREAM DMA2_Stream2 //数据流 2
void USART1_TX_DMA_Config(void);
void USART1_RX_DMA_Config(void);
#endif