DMA---直接存储器访问

霸气de小男生 提交于 2020-03-12 13:37:22

一、DMA简介

    DMA传输实现高速数据移动过程无需任何CPU操作控制。

    DMA控制器是独立于Cortex_M4内核的。

    STM32F407共有2个DMA控制器,DMA1只有外设到存储器和存储器到外设的传输模式,DMA2具有外设到存储器、存储器到外设以及存储器到存储器的传输模式。

   传输模式:

           1》外设到存储器(P--->M):把外设数据寄存器内容转移到指定的内存空间。

           2》存储器到外设(M--->P):把特定存储区内容转移到外设的数据寄存器中。

           3》存储器到存储器(M--->M):把一个指定的存储区内容拷贝到另一个存储区空间。

二、功能框图

    (1)外设通道选择

            每个DMA控制器具有8个数据流,每个数据流对应8个外设通道,每个通道对应不同的DMA请求。

            外设通道选择要解决的主要问题是决定哪一个外设作为该数据流的源地址或者目标地址。

            每个外设请求都占用一个数据流通道,相同外设请求可以占用不同数据流通道。

    (2)仲裁器

            仲裁器用来管理判断哪个数据流的优先级高。

            仲裁器管理数据流方法分为两个阶段:第一阶段数据软件阶段,在配置数据流时可以通过寄存器设定它的优先级别,可以设置为非常高、高、中和低四个级别。第二阶段数据硬件阶段,如果两个或两个以上数据流软件设置优先级一样,则它们优先级取决于数据流编号,编号越低优先级越高,比如数据流2优先级高于数据流3。

    (3)FIFO

          FIFO是源和目标之间的一个中转站。

          每个数据流都独立拥有四级32位FIFO(先进先出存储器缓冲区)。DMA传输具有FIFO模式和直接模式。

          FIFO模式:用于在源数据传输到目标地址之前临时存放这些数据,只有当数据存储量达到阈值级别(1/4、2/4、3/4和满)时,才会将FIFO中的内容传输到目标中。

          直接模式 :将源数据放在FIFO后会立即将该数据传输到目标地址中,无须满足任何条件即可传输。

三、初始化结构体

    typedef struct

    {

        uint32_t   DMA_Channel;      //通道选择,可选通道0至通道7,每个外设对应固定的通道。

                                                        DMA_Channel_0 ~ DMA_Channel_7

        uint32_t   DMA_PeripheralBaseAddr;  //外设地址

        uint32_t   DMA_Memory0BaseAddr;   //存储器0地址

        uint32_t   DMA_DIR;   //传输方向选择,可选P-->M;M-->P;M-->M。

                                              DMA_DIR_PeripheralToMemory
                                              DMA_DIR_MemoryToPeripheral
                                              DMA_DIR_MemoryToMemory    

        uint32_t   DMA_BufferSize;   //设定一次传输的数据个数

        uint32_t   DMA_PeripheralInc;  //外设地址是否递增

                                                            DMA_PeripheralInc_Enable 
                                                            DMA_PeripheralInc_Disable

        uint32_t   DMA_MemoryInc;    //存储器地址是否递增

                                                          DMA_MemoryInc_Enable  
                                                          DMA_MemoryInc_Disable 

        uint32_t   DMA_PeripheralDataSize;   //外设数据宽度,可选字节(8位)、半字(16位)和字(32位)。

                                                                       DMA_PeripheralDataSize_Byte    
                                                                       DMA_PeripheralDataSize_HalfWord
                                                                       DMA_PeripheralDataSize_Word    

        uint32_t   DMA_MemoryDataSize;      //存储器数据宽度,可选字节(8位)、半字(16位)和字(32位)。

                                                                       DMA_MemoryDataSize_Byte    
                                                                       DMA_MemoryDataSize_HalfWord
                                                                       DMA_MemoryDataSize_Word    

        uint32_t   DMA_Mode;                   //DMA传输模式选择,可选一次传输或者循环传输,在M--->M模式时,只能是一次传输。

                                                                   DMA_Mode_Normal  
                                                                   DMA_Mode_Circular

        uint32_t   DMA_Priority;                   //优先级选择,可选非常高、高、中和低。

                                                                    DMA_Priority_Low     
                                                                    DMA_Priority_Medium  
                                                                    DMA_Priority_High    
                                                                    DMA_Priority_VeryHigh

        uint32_t   DMA_FIFOMode;              //FIFO模式使能,在M-->M传输时,FIFO自动开启,软件禁止不了。

                                                                    DMA_FIFOMode_Disable
                                                                    DMA_FIFOMode_Enable 

        uint32_t   DMA_FIFOThreshold;       //FIFO阈值选择,可选1/4、1/2、3/4和满。

                                                                    DMA_FIFOThreshold_1QuarterFull 
                                                                    DMA_FIFOThreshold_HalfFull     
                                                                    DMA_FIFOThreshold_3QuartersFull
                                                                    DMA_FIFOThreshold_Full         

        uint32_t   DMA_MemoryBurst;         //存储器突发模式选择,可选单次模式、4节拍、8节拍和16节拍。

                                                                   DMA_MemoryBurst_Single
                                                                   DMA_MemoryBurst_INC4  
                                                                   DMA_MemoryBurst_INC8  
                                                                   DMA_MemoryBurst_INC16 

        uint32_t   DMA_PeripheralBurst;     //外设突发模式选择,可选单次模式、4节拍、8节拍和16节拍。

                                                                   DMA_PeripheralBurst_Single
                                                                   DMA_PeripheralBurst_INC4  
                                                                   DMA_PeripheralBurst_INC8  
                                                                   DMA_PeripheralBurst_INC16 

    }DMA_InitTypeDef;
 

四、常用固件库函数

    (1)初始化DMA的寄存器到复位状态

            void   DMA_DeInit(DMA_Stream_TypeDef* DMAy_Streamx);             //y = 1、2 ; x = 0 ~ 7

    (2)DMA初始化函数

            void   DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);    //y = 1、2 ; x = 0 ~ 7

    (3)DMA使能函数

            void   DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState);    //y = 1、2 ; x = 0 ~ 7

四、程序

(1)存储器到存储器实验(FLASH to SRAM)

bsp_dma.h文件

#ifndef __BSP_DMA_H__
#define __BSP_DMA_H__

#include "stm32f4xx_conf.h"

#define BUFFER_SIZE  32

extern void DMA_Config(void);
extern uint8_t Buffercmp(const uint32_t* pBuffer1,uint32_t* pBuffer2, uint16_t BufferLength);

#endif
 

bsp_dma.c文件

#include "./dma/bsp_dma.h"

extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];;
/*
    存储器到存储器传输,必须使用DMA2,数据流任意,通道任意。
*/

/**********************
    功能:配置DMA
    参数:无
    返回值:无
***********************/
void DMA_Config(void)
{
    DMA_InitTypeDef dmaInitValue;
    
    /*1、打开时钟(DMA)*/
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
    
    /*2、复位初始化DMA数据流*/
    DMA_DeInit(DMA2_Stream0);
    
    /*3、等待复位完成*/
    while(DMA_GetCmdStatus(DMA2_Stream0) != DISABLE)
    {
    
    }
    
    /*4、初始化DMA*/
    dmaInitValue.DMA_BufferSize = BUFFER_SIZE;
    dmaInitValue.DMA_Channel = DMA_Channel_0;
    dmaInitValue.DMA_DIR = DMA_DIR_MemoryToMemory;
    dmaInitValue.DMA_FIFOMode = DMA_FIFOMode_Enable;
    dmaInitValue.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    dmaInitValue.DMA_Memory0BaseAddr = (uint32_t)aDST_Buffer;
    dmaInitValue.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    dmaInitValue.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    dmaInitValue.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dmaInitValue.DMA_Mode = DMA_Mode_Normal;
    dmaInitValue.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
    dmaInitValue.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    dmaInitValue.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    dmaInitValue.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
    dmaInitValue.DMA_Priority = DMA_Priority_High;
    DMA_Init(DMA2_Stream0,&dmaInitValue);
        
    /*5、清除DMA数据流传输完成标志位(DMA_FLAG_TCIF0表示数据流0传输完成)*/
    DMA_ClearFlag(DMA2_Stream0,DMA_FLAG_TCIF0);
    
    /*6、使能DMA*/
    DMA_Cmd(DMA2_Stream0,ENABLE);
    
    /*7、等待DMA数据流有效*/
    while ((DMA_GetCmdStatus(DMA2_Stream0) != ENABLE))
  {
  }

}

/**********************************************
    功能:判断指定长度的两个数据源是否完全相同
    参数:
            pBuffer1---数据源1
            pBuffer2---数据源2
            BufferLength---比较长度
    返回值:
            完全相同返回1;
            只要有一对数据不相同返回0
***********************************************/
uint8_t Buffercmp(const uint32_t* pBuffer1, 
                  uint32_t* pBuffer2, uint16_t BufferLength)
{
  /* 数据长度递减 */
  while(BufferLength--)
  {
    /* 判断两个数据源是否对应相等 */
    if(*pBuffer1 != *pBuffer2)
    {
      /* 对应数据源不相等马上退出函数,并返回0 */
      return 0;
    }
    /* 递增两个数据源的地址指针 */
    pBuffer1++;
    pBuffer2++;
  }
  /* 完成判断并且对应数据相对 */
  return 1;  
}

main.c文件

#include "./usart/bsp_usart.h"
#include "./dma/bsp_dma.h"
#include "stdio.h"

/* 定义aSRC_Const_Buffer数组作为DMA传输数据源
  const关键字将aSRC_Const_Buffer数组变量定义为常量类型(存储在FLASH中) */
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
                                    0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                                    0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
                                    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
                                    0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
                                    0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
                                    0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
                                    0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};

/* 定义DMA传输目标存储器(存储在SRAM中) */
uint32_t aDST_Buffer[BUFFER_SIZE];


int main(void)
{
    uint16_t TransferStatus;
    
    USART_Config();
    DMA_Config();
    
    //等待DMA传输完成
    while(DMA_GetFlagStatus(DMA2_Stream0,DMA_FLAG_TCIF0) == DISABLE)
    {
    
    }
    //比较源数据和目标数据
    TransferStatus = Buffercmp(aSRC_Const_Buffer,aDST_Buffer,BUFFER_SIZE);
    if(TransferStatus == 0)//数据不相同
    {
        printf("\r\n 源数据和目标数据不相同");
    }
    else//数据完全相同
    {
        printf("\r\n 源数据和目标数据完全相同");
    }
    
    
    while(1)
    {
    
    }
}

(2)存储器到外设实验(SRAM to 串口)

bsp_dma.h文件

#ifndef __BSP_DMA_H__
#define __BSP_DMA_H__

#include "stm32f4xx_conf.h"

#define SENDBUFF_SIZE 5000

extern void USART_DMA_Config(void);

#endif

bsp_dma.c文件

#include "./dma/bsp_dma.h"

uint8_t SendBuff[SENDBUFF_SIZE];


/****************************************************
    功能:USART1 TX DMA 配置,内存到外设(USART1->DR)
    参数:无
    返回值:无
*****************************************************/
void USART_DMA_Config(void)
{
    DMA_InitTypeDef damInitValue;
    
    /*1、打开时钟DMA*/
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
    
    /*2、复位初始化DMA数据流*/
    DMA_DeInit(DMA2_Stream7);
  
    /*3、确保DMA数据流复位完成*/
    while(DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)
    {
    
    }
    
    /*4、初始化DMA*/
    damInitValue.DMA_BufferSize = SENDBUFF_SIZE;
    damInitValue.DMA_Channel = DMA_Channel_4;
    damInitValue.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    damInitValue.DMA_FIFOMode = DMA_FIFOMode_Disable;
    damInitValue.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    damInitValue.DMA_Memory0BaseAddr = (uint32_t)SendBuff;
    damInitValue.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    damInitValue.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    damInitValue.DMA_MemoryInc = DMA_MemoryInc_Enable;
    damInitValue.DMA_Mode = DMA_Mode_Normal;
    damInitValue.DMA_PeripheralBaseAddr = USART1_BASE+0x04;//USART1的数据寄存器地址
    damInitValue.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    damInitValue.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    damInitValue.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    damInitValue.DMA_Priority = DMA_Priority_Medium;
    DMA_Init(DMA2_Stream7,&damInitValue);
    
    /*5、使能DMA*/
    DMA_Cmd(DMA2_Stream7,ENABLE);
    
    /*6、等待DMA数据流有效*/
    while(DMA_GetCmdStatus(DMA2_Stream7) != ENABLE)
    {
    
    }
}

main.c文件

#include "./usart/bsp_usart.h"
#include "./dma/bsp_dma.h"
#include "stdio.h"

extern uint8_t SendBuff[SENDBUFF_SIZE];

int main(void)
{
    uint16_t i;
    
    USART_Config();
    USART_DMA_Config();
    
    printf("\r\n USART1 DMA TX 测试 \r\n");
    
    //填充将要发送的数据
    for(i = 0;i<SENDBUFF_SIZE;i++)
    {
        SendBuff[i] = 'A';
    }
    
    //USART1向DMA发出TX请求
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
    
    while(1)
    {
    
    }
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!