STM32——DMA

一、简介

*DMA(Direct Memory Access)直接存储器存取

*DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。

*DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了*CPU的资源

*12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发

*STM32F103C8T6 DMA资源:DMA1(7个通道)

*DMA请求映射

PS:DMA的工作不需要经过CPU,进行数据传输是在DMA通道中执行,这样内部工作犹如有两个CPU。它的作用就是解决大量数据转移过度消耗CPU资源的问题。

二、DMA传输方式

  • 外设到内存
  • 内存到外设
  • 内存到内存
  • 外设到外设

内存到内存:


#include "dma.h"
#include "stm32f10x.h"
//常量或者代码存储在FLASH
const uint32_t SRC_Buffer[BUFFER_SIZE] =     //源
{
	0X01020304,0X05060708,0x090a0b0c,0x0d0e0f10,
	0X11121314,0X15161718,0x191a1b1c,0x1d1e1f20,
	0X21222324,0X25262728,0x292a2b2c,0x2d2e2f30,
	0X31323334,0X35363738,0x393a3b3c,0x3d3e3f40
};

//定义DMA传输的目标存储器,变量存储在SARM
uint32_t DST_Buffer[BUFFER_SIZE];         //目标
uint32_t SEND_Buffer[SEND_SIZE]; //定义M->P存储器的地址


//M->M
void DMA_MTM_Init(void)
{
	DMA_InitTypeDef DMA_StructInit;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						//打开时钟
	
	DMA_StructInit.DMA_MemoryBaseAddr     =	(uint32_t)DST_Buffer;			//存储器地址
	DMA_StructInit.DMA_PeripheralBaseAddr =	(uint32_t)SRC_Buffer;			//外设地址 
	DMA_StructInit.DMA_DIR				  = DMA_DIR_PeripheralSRC;			//传输方向(外设作为源,flash作为外设)
	
	DMA_StructInit.DMA_BufferSize         =	BUFFER_SIZE;					//一次的传输数目(所有需要传输内容的传输一遍的数目)
	DMA_StructInit.DMA_PeripheralDataSize =	DMA_PeripheralDataSize_Word;	//外设数据宽度
	DMA_StructInit.DMA_MemoryDataSize	  = DMA_MemoryDataSize_Word;		//存储器数据宽度
	DMA_StructInit.DMA_PeripheralInc 	  =	DMA_PeripheralInc_Enable;		//外设地址增量模式
	DMA_StructInit.DMA_MemoryInc		  = DMA_MemoryInc_Enable;			//存储器地址增量模式
	
	DMA_StructInit.DMA_Mode				  =	DMA_Mode_Normal;				//模式选择(发送一次还是循环发送)
	DMA_StructInit.DMA_Priority			  =	DMA_Priority_High;				//通道优先级
	DMA_StructInit.DMA_M2M  			  = DMA_M2M_Enable;					//存储器到存储器模式
	
	DMA_Init( DMA1_Channel6, &DMA_StructInit);
	DMA_ClearFlag(DMA1_FLAG_TC6);											//清除标志
	DMA_Cmd(DMA1_Channel6, ENABLE);
}
//判断flash到sram 的内容是否一致
uint8_t BufferCmp(const uint32_t *pBuffer1,uint32_t *pBuffer2,uint32_t bufferLenght)
{
	while(bufferLenght--)
	{
		if(*pBuffer1 != *pBuffer2)
			return 0;
		pBuffer1++;
		pBuffer2++;
	}
	return 1;
}




int  main()
{
	extern const uint32_t SRC_Buffer[BUFFER_SIZE];
	extern uint32_t DST_Buffer[BUFFER_SIZE]; 
	uint8_t status = 0;
	
	initSysTick();
	usart_init();
	DMA_MTM_Init();
	
	ms_delay(1000);
	while(DMA_GetFlagStatus(DMA1_FLAG_TC6) == RESET);  //检测DMA是否传输完成
	status = BufferCmp(SRC_Buffer,DST_Buffer,BUFFER_SIZE);
	if(status == 0)
		printf("DMA M-M 传输有误\r\n");
	else 
		printf("DMA M-M 传输正确\r\n");
	
//	DMA_USART1_Init();
//	USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);//打开串口DMA使能

   while(1)
	 {
		 
	 }

内存到外设:

//M->P
void DMA_USART1_Init(void)
{
	DMA_InitTypeDef DMA_StructInit;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);						//打开时钟
	
	DMA_StructInit.DMA_MemoryBaseAddr     =	(uint32_t)SEND_Buffer;			//存储器地址
	DMA_StructInit.DMA_PeripheralBaseAddr =	(uint32_t)USART1_DR_ADDR;		//外设地址 
	DMA_StructInit.DMA_DIR				  = DMA_DIR_PeripheralDST;			//传输方向(外设作为目的)
 
	DMA_StructInit.DMA_BufferSize         =	SEND_SIZE;						//传输数目
	DMA_StructInit.DMA_PeripheralDataSize =	DMA_PeripheralDataSize_Byte;	//外设数据宽度
	DMA_StructInit.DMA_MemoryDataSize	  = DMA_MemoryDataSize_Byte;		//存储器数据宽度(DR寄存器是8位)
	DMA_StructInit.DMA_PeripheralInc 	  =	DMA_PeripheralInc_Disable;		//外设地址增量模式(外设不需要增量,串口的地址是固定)
	DMA_StructInit.DMA_MemoryInc		  = DMA_MemoryInc_Enable;			//存储器地址增量模式
	
	DMA_StructInit.DMA_Mode				  =	DMA_Mode_Normal;				//模式选择(将所有内容只发一次)
	DMA_StructInit.DMA_Priority			  =	DMA_Priority_High;				//通道优先级
	DMA_StructInit.DMA_M2M  			  = DMA_M2M_Disable;				//存储器到存储器模式
	
	DMA_Init( DMA1_Channel4, &DMA_StructInit);
	DMA_ClearFlag(DMA1_FLAG_TC4);											//清除标志(发送完成)
	DMA_Cmd(DMA1_Channel4, ENABLE);
}

外设到内存:去看ADC DMA传输

三、中断

四、仲裁

仲裁器根据通道请求的优先级来启动外设 / 存储器的访问。
优先权管理分 2 个阶段:
● 软件:每个通道的优先权可以在 DMA_CCRx 寄存器中设置,有 4 个等级:
最高优先级
高优先级
中等优先级
低优先级
● 硬件:如果 2 个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优
先权。举个例子,通道 2 优先于通道 4

多个DMA的时候 就需要仲裁 来判断哪个DMA先触发,当软件阶段的DMA的优先级一样时,就需要比较硬件阶段的通道编号,编号小的优先级大,要是硬件阶段也是一样,则比较DMA1和DMA2(DMA1优先级大于DMA2的优先级)

五、数据传输宽度、对齐方式

*外设的数据宽度如果大于源的数据宽度,外设每一个数据宽度接收源的一个数据宽度。外设没有存满的空间直接抛弃。

*如果源大于目标的数据宽度,源一次性发送过给外设的数据宽度,外设只能接收自己设定的数据宽度,至于源发送超出的数据宽度,直接抛弃(例如:源有四个数据宽度,外设只有一个,那么外设只接收一个 超出的数据宽度不要)

PS:

*外设是串口(目标),内存是源。这时外设地址不需要设置递增,因为串口的地址是固定的,内存的数据如果是多个那要设置递增。

*需不需要循环发送 就是内存所有数据都发过一次之后,还需不需要再次发送。

  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;                                   //存储器到存储器模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值