STM32-DMA讲解

DMA简介

DMA:Data Memory Access,直接存储器访问。主要功能是可以把数据从一个地方搬到另外一个地方而且不占用CPU。
 

DMA1:有7个通道,可以实现P->M,M->P,M->M
DMA2∶有5个通道,可以实现P->M,M->P,M->M
这里的通道 可以理解为传输数据的一种管道。要注意的是DMA2只存在于大容量产品和互联型产品中。

DMA功能框图

1-DMA请求

如果外设要想通过DMA来传输数据,必须先给DMA控制器发送DMA请求,DMA收到请求信 号之后,控制器会给外设一个应答信号,当外设应答后且DMA控制器收到应答信号之后,就会 启动DMA的传输,直到传输完毕。

2-通道

不同的DMA控 制器的通道对应着不同的外设请求,这决定了我们在软件编程上该怎么设置

DMA1请求映像表

DMA2请求映像表 

M->M 时全部通道都可以使用

其中ADC3、SDIO和TIM8的DMA请求只在大容量产品中存在,这个在具体项目时要注意。 

每个通道对 应不同的外设的DMA请求。虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一 个,不能同时接收多个

3-仲裁器

当发生多个DMA通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器也管理。

仲裁器管理DMA通道请求分为两个阶段。

第一阶段属于软件阶段

可以在DMA_CCRx寄存器 中设置,有4个等级:非常高、高、中和低四个优先级。

第二阶段属于硬件阶段

如果两个或以 上的DMA通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高, 比如通道0高于通道1。

在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2 控制器的 优先级。

DMA相关库函数讲解

DMA_InitTypeDef

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;

DMA_DIR_PeripheralDST              外设是目的地 即:   M->P
DMA_DIR_PeripheralSRC              外设是源 即:   P->M

前面三个成员决定了要从哪里来到那里去

可编程的数据传输宽度、对齐方式和数据大小端 

总结:小方大 将小的放在头部   大放小  要头不要尾 

实验

实验1-M to M:

FLASH to SRAM,把内部FLASH的数据传输到内部的SRAM。

编程要点

1-在FLASH中定义好要传输的数据,在SRAM中定义好用来接收FLASH数据的变量

 定义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];

2-初始化DMA,主要是配置DMA初始化结构体。

DMA挂载到AHB总线

void dma_mtm_config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
		// 目标地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer;
		// 方向:外设到存储器(这里的外设是内部的FLASH)	
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
		
		
		// 传输大小	
	DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
	// 外设(内部的FLASH)地址递增	    
	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_Cmd(DMA1_Channel6, ENABLE);
	
}

在编写M-M的时候可以将Flash看作外设

3-编写比较函数。
uint8_t Buffercmp(const uint32_t* pBuffer, 
                  uint32_t* pBuffer1, uint16_t BufferLength)
{
  /* 数据长度递减 */
  while(BufferLength--)
  {
    /* 判断两个数据源是否对应相等 */
    if(*pBuffer != *pBuffer1)
    {
      /* 对应数据源不相等马上退出函数,并返回0 */
      return 0;
    }
    /* 递增两个数据源的地址指针 */
    pBuffer++;
    pBuffer1++;
  }
  /* 完成判断并且对应数据相对 */
  return 1;  
}

判断指定长度的两个数据源是否完全相等,如果完全相等返回1,只要其中一对数据不相等返回0

4-编写main函数。 
int main(void)
{
	uint8_t sta;
	
	led_init();
	led_B_ON();
	delay(700);
	led_OFF();
	dma_mtm_config();
	while(DMA_GetFlagStatus(DMA1_FLAG_TC6)==0);
	DMA_ClearFlag(DMA1_FLAG_TC6);
	sta = Buffercmp(aSRC_Const_Buffer,aDST_Buffer, BUFFER_SIZE);
		if(sta==0)
			led_R_ON();
		else
			led_G_ON();
	while(1)
	{
	
	}
}

实验代码

main.c代码 
#include "stm32f10x.h"   
#include "led.h"
#include "bsp_systick.h"
#include "bsp_dma_mtm.h"

extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];
int main(void)
{
	uint8_t sta;
	
	led_init();
	led_B_ON();
	delay(700);
	led_OFF();
	dma_mtm_config();
	while(DMA_GetFlagStatus(DMA1_FLAG_TC6)==0);
	DMA_ClearFlag(DMA1_FLAG_TC6);
	sta = Buffercmp(aSRC_Const_Buffer,aDST_Buffer, BUFFER_SIZE);
		if(sta==0)
			led_R_ON();
		else
			led_G_ON();
	while(1)
	{
	
	}
}

bsp_dma_mtm.h代码

#ifndef _BSP_DMA_MTM_H
#define _BSP_DMA_MTM_H
#include "stm32f10x.h"   

#define BUFFER_SIZE     32
void dma_mtm_config(void);
uint8_t Buffercmp(const uint32_t* pBuffer, 
                  uint32_t* pBuffer1, uint16_t BufferLength);
#endif

bsp_dma_mtm.c代码

#include "bsp_dma_mtm.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];
									
void dma_mtm_config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
		// 目标地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer;
		// 方向:外设到存储器(这里的外设是内部的FLASH)	
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
		
		
		// 传输大小	
	DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
	// 外设(内部的FLASH)地址递增	    
	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_Cmd(DMA1_Channel6, ENABLE);
	
}
/**
  * 判断指定长度的两个数据源是否完全相等,
  * 如果完全相等返回1,只要其中一对数据不相等返回0
  */
uint8_t Buffercmp(const uint32_t* pBuffer, 
                  uint32_t* pBuffer1, uint16_t BufferLength)
{
  /* 数据长度递减 */
  while(BufferLength--)
  {
    /* 判断两个数据源是否对应相等 */
    if(*pBuffer != *pBuffer1)
    {
      /* 对应数据源不相等马上退出函数,并返回0 */
      return 0;
    }
    /* 递增两个数据源的地址指针 */
    pBuffer++;
    pBuffer1++;
  }
  /* 完成判断并且对应数据相对 */
  return 1;  
}

实验2-M to P:

SRAM to 串口,同时LED灯闪烁,演示DMA传数据不需要占用CPU。

bsp_dma_mtp.h代码

#ifndef _BSP_DMA_MTP_H
#define _BSP_DMA_MTP_H
#include "stm32f10x.h"  
#include <stdio.h>
//串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd    //debug
    
#define  DEBUG_USART_TX_GPIO_PORT         GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN          GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define SENDDATA_SIZE     5000
void dma_mtp_config(void);
void USART_Config(void);
#endif

bsp_dma_mtp.c代码

#include "bsp_dma_mtp.h"	
uint8_t senddata[SENDDATA_SIZE];
void dma_mtp_config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	
	DMA_InitStructure.DMA_PeripheralBaseAddr = (USART1_BASE+0x04);
	// 目标地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)senddata;
	// 方向:外设到存储器(这里的外设是内部的FLASH)	
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
		
		
	// 传输大小	
	DMA_InitStructure.DMA_BufferSize = SENDDATA_SIZE;//5000
	// 外设(内部的FLASH)地址递增	    
	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_PeripheralDataSize_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_Cmd(DMA1_Channel4, ENABLE);
	
}

void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);		

  // 清除发送完成标志
	//USART_ClearFlag(USART1, USART_FLAG_TC);     
}
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

main.c代码 

#include "stm32f10x.h"   
#include "led.h"
#include "bsp_systick.h"
#include "bsp_dma_mtp.h"
extern uint8_t senddata[SENDDATA_SIZE];

int main(void)
{
	uint32_t i;
	led_init();
	USART_Config();
	dma_mtp_config();
	for(i=0;i<SENDDATA_SIZE;i++)
	{
		senddata[i]='H';
	}
	USART_DMACmd(USART1,USART_DMAReq_Tx, ENABLE);
	while(1)
	{

		printf("测试");
		led_R_ON();
		delay(500);
		led_OFF();
		delay(1500);
	}
}


  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值