目录

1.DMA功能讲解

2.DMA初始化结构体

3.DMA实验1 (M->M)

4.DMA实验2(M->P)


1.DMA功能讲解

关于DMA的功能手册上是这么说的。

说白了就是DMA可以把数据从一个地方传输到另一个地方,并且不占用CPU。举个例子,比如我们想通过串口发送数据,正常来讲是存放到Flash或SRAM的代码通过总线矩阵,由Cortex-M3的内核进行控制,再回到系统总线的外设当中。如果有DMA的话,Flash和SRAM中的数据可以直接通过总线矩阵访问到DMA可以直接和串口的外设发送数据,可以不用经过CPU。

DMA1有七个通道如下图所示:

并且DMA1支持P->P(内存到内存的访问,如Flash到SRAM或SRAM到Flash),P->M(外设到内存的访问,如ADC的采样),M->P(内存到外设的访问,如将变量发送到串口的寄存器当中)。DMA2也拥有同样的功能,但是只存在大容量,互联型(F105/F107)的产品中。

而DMA2只有五个通道:

 多个DMA同时请求

 1.软件阶段:可以通过配置DMA通道x配置寄存器(DMA_CCRx)的PL位设置通道优先级。

2.硬件阶段:如果用到两个通道且通道优先级一致,那么我们可以通过比较编号的大小来比较优先级,DMA1的优先级大于DMA2的优先级。再比如都是同一个通道内的,串口1在通道4,串口2在通道7,那么串口1的优先级就要大于串口2的优先级。

2.DMA初始化结构体

typedef struct
{
  uint32_t DMA_PeripheralBaseAddr;   //外设地址
 
  uint32_t DMA_MemoryBaseAddr;       //存储器地址
 
  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_M2M;                  //存储器到存储器模式
 
}DMA_InitTypeDef;

可以将以上的结构体分为三类理解。

1.数据输入与输出

  uint32_t DMA_PeripheralBaseAddr;   //外设地址

  uint32_t DMA_MemoryBaseAddr;      //存储器地址

  uint32_t DMA_DIR;                             //传输方向

1.配置外设地址参考外设地址寄存器:DMA_CPARx

 

 2.配置存储器地址参考外设存储器地址寄存器 DMA_CMARx

3.配置传输方式参考DMA通道配置寄存器DIR位。

 2.数据大小以及传输单位

uint32_t DMA_BufferSize;                  //传输数目                 

uint32_t DMA_PeripheralInc;             //外设地址增量模式

uint32_t DMA_MemoryInc;                //存储器地址增量模式

uint32_t DMA_PeripheralDataSize;   //外设数据宽度

uint32_t DMA_MemoryDataSize;      //存储器数据宽度

传输数目参考DMA通道数据存储器,该寄存器总共有16位,所以可以传输2的16次方(65535)个数据。每个数据的大小可以通过DMA通道配置寄存器PSIZE位。接收数据存储器的宽度同样也可以通过MSIZE位来配置。 一般性情况接收和发送的大小我们配成一样的即可。

配置外设地址增量模式和存储器地址增量模式可以配置MINC位和PINC位,用来决定发送的时候存储器地址是否会递增。

 3.传输结束

uint32_t DMA_Mode;                 //模式选择                                     
模式选择:配置DMA_CCRx寄存器的CIRC位。

循环模式:发送完一组数据之后是否再重新发送该数据。

如果不配置循环模式则发送完一次后就不再发送了。

结构体参数详解

 uint32_t DMA_DIR;  //传输方向

#define DMA_DIR_PeripheralDST     //外设作为目标
#define DMA_DIR_PeripheralSRC     //外设作为源

uint32_t DMA_PeripheralInc;  //外设地址增量模式

#define DMA_PeripheralInc_Enable       //DMA外设增量使能
#define DMA_PeripheralInc_Disable      //DMA外设增量失能   

uint32_t DMA_MemoryInc;   //存储器地址增量模式

#define DMA_MemoryInc_Enable        //存储器地址增量使能  
#define DMA_MemoryInc_Disable       //存储器地址增量失能     

 uint32_t DMA_PeripheralDataSize;   //外设数据宽度 

uint32_t DMA_Mode;                 //模式选择 

#define DMA_Mode_Circular         //循环模式
#define DMA_Mode_Normal           //只发送一次

uint32_t DMA_Priority;          //通道优先级

uint32_t DMA_M2M;            //存储器到存储器模式

DMA结构体初始化

	DMA_Init(DMA1_Channel6,&DMA_InitStructure);

DMA传输标志位--------------选择发送接收标志位

DMA_ClearFlag(DMA1_FLAG_TC6);

DMA使能

  DMA_Cmd(DMA1_Channel6, ENABLE);

3.DMA实验1 (M->M)

M->M:Flash to SRAM,内部Flash(CODE)的数据传输到内部的SRAM(变量)。

对于STM32来说在SRAM中存储的是变量,Flash中存储的是常量,该实验是将常量放到SRAM中。

实验现象:如果成功将定义在Flash中的数组存储在SRAM中,则开发板上的LED灯会亮。

dma.c

#include "stm32f10x.h"
#include "dma.h"
 
const uint32_t SRC_Buffer[Buffer_Size]={
	0x01020304,0x01220364,0x01F20304,0x01E20205,
	0x01E25204,0x01A20B54,0x0E02F324,0x01E2630F,
	0x0602A30B,0x0E0B0564,0x0E0B0794,0x07060E0F,
	0x01AB040D,0x0F09070E,0x0A02536F,0x0904060E
};  //const关键字把数组Buffer定义为常量类型,表示数据存储在内部Flash里
 
uint32_t DST_Buffer[Buffer_Size];  //定义DMA传输的目标存储器,存在内部SRAM里
 
 
void DMA_MTM_Init(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Buffer;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	
	DMA_InitStructure.DMA_BufferSize = Buffer_Size;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
	
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
	
	DMA_Init(DMA1_Channel6,&DMA_InitStructure);
    DMA_ClearFlag(DMA1_FLAG_TC6);
    DMA_Cmd(DMA1_Channel6, ENABLE);
 
}
 
uint8_t Buffercmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t BufferLength)
{
	while(BufferLength--) //数据长度递减
	{
		if(*pBuffer1 != *pBuffer2)  //判断两个数据源是否相等
		{
			return 0;     //对应数据源不相等返回0
		}
		
		pBuffer1++;    //递增两个数据源的地址指针
		pBuffer2++;	
		
	}
	 
	return 1;    //完成判断并且对应数据相等,返回1

dma.h

#ifndef _DMA_H_
#define _DMA_H_
 
#define Buffer_Size 16
 
void DMA_MTM_Init(void);
uint8_t Buffercmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t BufferLength);
 
#endif

main.c

#include "stm32f10x.h"
#include "main.h"
#include "dma.h"
#include "led.h"
 
 
int main(void)
{
	 uint8_t status = 0;
	 extern const uint32_t SRC_Buffer[Buffer_Size];
	 extern uint32_t DST_Buffer[Buffer_Size];
   	
	 LED_Init();
	 DMA_MTM_Init();
  
     status = Buffercmp(SRC_Buffer, DST_Buffer, Buffer_Size);
     
     if(status == 0){
		  GPIO_SetBits(GPIOB, GPIO_Pin_5);
	 }
	 
	 else {
          GPIO_ResetBits(GPIOB, GPIO_Pin_5);
	 }
	 
   while(1)
	 {
 
	 }
		 
}

4.DMA实验2(M->P)

实验描述:该实验通过DMA串口发送数据,在串口助手上看到响应的现象。

该实验是将外设作为目标存储器,将内存中的常量发送到外设。

在dma.c的文件下添加下段代码,该段代码跟实验1中的DMA结构体配置方法一致,但跟实验1不同的是需要把传输方法改成将外设作为目标,外设地址增量模式关闭,因为地址是固定的,都是放在了串口数据寄存器当中。还需要把数据宽度改成8位,因为串口数据寄存器的大小就是8位的。最后就是要关闭M->M。

void USART_DMA(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDR;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Sendbuf;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	
	DMA_InitStructure.DMA_BufferSize = SEND_SIZE;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	
	DMA_Init(DMA1_Channel4,&DMA_InitStructure);
    DMA_ClearFlag(DMA1_FLAG_TC4);
    DMA_Cmd(DMA1_Channel4, ENABLE);
	
}

dma.h

#ifndef _DMA_H_
#define _DMA_H_
 
#define Buffer_Size 16
#define USART_DR_ADDR (USART1_BASE+0x04)  //数据寄存器地址=基地址加偏移量
#define SEND_SIZE 500   //大小定义为500
 
 
void DMA_MTM_Init(void);
uint8_t Buffercmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t BufferLength);
void USART_DMA(void);
 
 
#endif

main.c

#include "stm32f10x.h"
#include "main.h"
#include "dma.h"
#include "led.h"
#include "usart.h"
 
int main(void)
{
  extern uint8_t Sendbuf[SEND_SIZE];
 
	uint16_t i = 0;
	for(i=0;i<SEND_SIZE;i++)  //发送500次
	{
		Sendbuf[i] = 'o';  //发送的数据为o
	}
	
	usart_init();
	USART_DMA();
    USART_DMACmd(USART1, USART_DMAReq_Tx,ENABLE);  //串口DMA使能
 
   while(1)
	 {
 
	 }
		 
}

实验结果:

可以看出发送的数据为o,并且发送了500次。