实验19:DMA直接存储器访问实验

一、DMA简介

    DAM全称Direct Memory Access,即直接存储器访问。

    DMA传输将数据从一个地址空间复制到另一个地址空间,传输动作由DMA控制器来实现和完成的。

    DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。

    STM32F4有2个DMA控制器,每个DMA控制器有8个数据流,每个数据流有8个通道,每个通道都有一个仲裁器,用于处理DMA请求的优先级。

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

   传输模式:

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

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

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

二、功能框图

实验19:DMA直接存储器访问实验_第1张图片

    (1)通道选择

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

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

实验19:DMA直接存储器访问实验_第2张图片

实验19:DMA直接存储器访问实验_第3张图片

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

    (2)仲裁器

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

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

    (3)FIFO

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

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

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

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

    (4)中断

实验19:DMA直接存储器访问实验_第4张图片

三、初始化结构体

    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(P->M)
                                              DMA_DIR_MemoryToPeripheral(M->P)
                                              DMA_DIR_MemoryToMemory(M->M)    

        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 (1/4)
                                                                    DMA_FIFOThreshold_HalfFull (1/2)    
                                                                    DMA_FIFOThreshold_3QuartersFull(3/4)
                                                                    DMA_FIFOThreshold_Full (满)        

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

                                                                   DMA_MemoryBurst_Single(单次模式)
                                                                   DMA_MemoryBurst_INC4 (4节拍) 
                                                                   DMA_MemoryBurst_INC8  (8节拍)
                                                                   DMA_MemoryBurst_INC16(16节拍)

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

                                                                   DMA_MemoryBurst_Single(单次模式)
                                                                   DMA_MemoryBurst_INC4 (4节拍) 
                                                                   DMA_MemoryBurst_INC8  (8节拍)
                                                                   DMA_MemoryBurst_INC16(16节拍)

    }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

    (4)设置DMA数据流传输的数据量大小

            void   DMA_SetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx, uint16_t Counter);

    (5)获取当前剩余数据量大小

            uint16_t   DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx);

    (6)等待完成

            FunctionalState   DMA_GetCmdStatus(DMA_Stream_TypeDef* DMAy_Streamx);

    (7)外设DMA使能函数

          去相应的外设的固件库函数中寻找。

四、程序

(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[i] = 'A';
    }
    
    //USART1向DMA发出TX请求
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
    
    while(1)
    {
    
    }
}

你可能感兴趣的:(STM32)