DMA

耗尽温柔 提交于 2020-01-12 04:28:07

DMA有3种实现方式:内存->内存;内存->外设;外设->内存
(外设->内存的DMA属于ADC)
stm32有俩个DMA控制器:
DMA1:7通道
DMA2:5通道,只存在于大容量(256K-512K)和互联型产品(f105和f107系列)

rbt6开发板属于中容量,标准型,只有DMA1;
在这里插入图片描述
由图可知,内存->外设的方式,只能使用对应的通道,不能使用其他的。
而内存->内存的方式,可以使用所有的通道。

DMA一次可以传输2^16个字节的数据
那么,如果多个通道请求到来,应该怎么办?
这时,需要DMA的仲裁器进行仲裁:
1.首先,判断DMA通道x配置寄存器(DMA_CCRx)的PL位;
2.其次,如果PL位相同,判断通道的编号,编号越小,优先级越高。(同时,DMA1的优先级大于DMA2)

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;

void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);//DMA初始化函数

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);//获取DMA的状态
//共有3种状态:传输完成(TC),传输过半(HT),传输出错(TE)
//除此之外,还有一个全局标志(GL)(不知道什么用)

void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);//DMA的使能函数

void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);//如果要使用中断,需要相应的DMA使能中断

一、内存->内存(Flash->SRAM)
在嵌入式中,Flash一般用来存储代码,如果要存放数据,加一个const,表示这个数据是常量,就可以存放在Flash当中;SRAM一般用来存储变量`。

那么,内存->内存的方式可不可以是其他的?
显然,由于Flash中的数据必须是常量,肯定不能作为目的地址。
而SRAM->SRAM则完全没必要。
我们一般认为,SRAM是内存,Flash是外存,所以,也可以认为Flash是外设。因此在初始化结构体时,可以认为是从内存到外设。

步骤:
由于是内存到内存的DMA方式,所以需要初始化DMA
1.初始化DMA的结构体
2.打开DMA的时钟
3.调用初始化DMA函数
4.使能DMA
由于DMA并没有使用任何的引脚,所以不需要初始化GPIO。
dma_m2m.h

#ifndef	 _DMA_M2M_H
#define  _DMA_M2M_H
#include "stm32f10x.h"
#define  BUFFER_SIZE  20
void dma_mtom_Init(void);
int strcmps(const uint32_t *buffer,uint32_t *buffers,int size);

#endif

dma_m2m.c

#include "dma_m2m.h"
//Flash中定义的数据
const uint32_t data_SRC[BUFFER_SIZE]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
//SRAM中用来接受数据的变量
uint32_t data_DST[BUFFER_SIZE];

void dma_mtom_Init()
{
	DMA_InitTypeDef  DMA_InitStruct;
	DMA_InitStruct.DMA_PeripheralBaseAddr =	(uint32_t)data_SRC;
	DMA_InitStruct.DMA_MemoryBaseAddr =	(uint32_t)data_DST;
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//外设Flash是源地址
	DMA_InitStruct.DMA_BufferSize =	BUFFER_SIZE;
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_PeripheralDataSize =	DMA_PeripheralDataSize_Word;
	DMA_InitStruct.DMA_MemoryDataSize =	DMA_MemoryDataSize_Word;
	DMA_InitStruct.DMA_Mode	= DMA_Mode_Normal;
	DMA_InitStruct.DMA_Priority	=  DMA_Priority_High;
	DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;

	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	DMA_Init( DMA1_Channel6, &DMA_InitStruct);
	DMA_Cmd(DMA1_Channel6, ENABLE);

}
int strcmps(const uint32_t *buffer,uint32_t *buffers,int size)
{
	while(size--)
	{
		if(*buffer!=*buffers)
		{
			return 0;
		}
		buffer++;
		buffers++;
	}
	return 1;
}

led.h

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

#define LED0_GPIO_CLK    RCC_APB2Periph_GPIOC
void LED_GPIO_Config(void);

#endif

led.c

#include "led.h"

void LED_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

	GPIO_InitStruct.GPIO_Pin=  GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;

	GPIO_Init(GPIOC,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin=  GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;

	GPIO_Init(GPIOC,&GPIO_InitStruct);

	
}


main.c

#include "stm32f10x.h"
#include "led.h"
#include "dma_m2m.h"

extern const uint32_t data_SRC[BUFFER_SIZE];
extern uint32_t data_DST[BUFFER_SIZE];
int main()
{
	int status;
	LED_GPIO_Config();
	dma_mtom_Init();//初始化后,数据会由DMA方式自动传输
	status=strcmps(data_SRC,data_DST,BUFFER_SIZE);
	while(DMA_GetFlagStatus(DMA1_FLAG_TC6)==RESET);
	if(status) //传输成功,熄灭LED3
	{
	   GPIO_SetBits(GPIOC,GPIO_Pin_10);
	}
	else	  //传输失败,熄灭LED2
	{
	   GPIO_SetBits(GPIOC,GPIO_Pin_9);
	}

}

二、内存到外设(SRAM->UART->计算机)
步骤:
首先,由于是内存到内存的DMA方式,所以需要初始化DMA
1.初始化DMA的结构体
2.打开DMA的时钟
3.调用初始化DMA函数
4.使能DMA

其次,由于是和外设进行通信,需要初始化外设(这里使用UART2)
1.初始化UART2的结构体(由最开始那个表可知,UART2使用DMA1的通道6和7)
2.打开UART2的时钟
3.调用初始化UART2函数
4.使能UART2

最后,UART2使用了GPIO的引脚,需要初始化GPIO
1.初始化GPIOA引脚2和3的结构体
2.打开UART2的时钟
3.调用初始化GPIO函数

dma_m2p.h

#ifndef	 _DMA_M2P_H
#define  _DMA_M2P_H
#include "stm32f10x.h"
#define  BUFFER_SIZE  500
void dma_mtop_Init(void);
void usrt_init(void);
#endif

dma_m2p.c

#include "dma_m2p.h"

//SRAM中用来接受数据的变量
uint8_t data_SRC[BUFFER_SIZE];

void dma_mtop_Init()
{
	DMA_InitTypeDef  DMA_InitStruct;
	DMA_InitStruct.DMA_PeripheralBaseAddr =	(uint32_t)(USART2_BASE+0x04);
	DMA_InitStruct.DMA_MemoryBaseAddr =	(uint32_t)data_SRC;
	DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;//外设Flash是目的地址
	DMA_InitStruct.DMA_BufferSize =	BUFFER_SIZE;
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_PeripheralDataSize =	DMA_PeripheralDataSize_Byte;
	DMA_InitStruct.DMA_MemoryDataSize =	DMA_MemoryDataSize_Byte;
	DMA_InitStruct.DMA_Mode	= DMA_Mode_Normal;
	DMA_InitStruct.DMA_Priority	=  DMA_Priority_High;
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	DMA_Init( DMA1_Channel7, &DMA_InitStruct);
	DMA_ClearFlag(DMA1_FLAG_TC7);
	DMA_Cmd(DMA1_Channel7, ENABLE);
}


void usrt_init()
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	USART_InitTypeDef UART_InitStruct;
//配置uart的GPIO口	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//配置Tx
	GPIO_InitStruct.GPIO_Pin=  GPIO_Pin_2;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置usrt
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	UART_InitStruct.USART_BaudRate=  115200;
	UART_InitStruct.USART_WordLength=  USART_WordLength_8b;
	UART_InitStruct.USART_StopBits=  USART_StopBits_1;
	UART_InitStruct.USART_Parity=  USART_Parity_No;
	UART_InitStruct.USART_Mode=  USART_Mode_Rx|USART_Mode_Tx;
	UART_InitStruct.USART_HardwareFlowControl=  USART_HardwareFlowControl_None;
	USART_Init(USART2,&UART_InitStruct);
//串口使能
	USART_Cmd(USART2,ENABLE);
}

main.c

#include "stm32f10x.h"
#include "led.h"
#include "dma_m2p.h"


extern uint8_t data_SRC[BUFFER_SIZE];
int main()
{

	uint16_t i=0;
	for(i=0;i<BUFFER_SIZE;i++)
	{
		data_SRC[i]='r';
	}
	usrt_init();
	dma_mtop_Init();//初始化后,数据会由DMA方式自动传输

	USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);


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