9、DMA原理

STM32 最多有 2 个 DMA 控制器(DMA2 仅存在大容量产品中), DMA1 有 7 个通道。 DMA2 有 5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个 DMA 请求的优先权。
小容量产品:有1个DMA----------DMA1有7个通道

大容量产品:有2个DMA----------DMA2有5个通道

DMA就是将数据从一个区域复制到另一个区域,不占用CPU资源。

1、 每个通道都直接连接专用的硬件DMA请求,都支持软件触发,通过软件来配置。

2、同一DMA的各个通道之间的优先权,可以通过编程设置(4个等级):很高、高、中等、低。

如在相等优先权时,同时产生几个DMA请求,则通道0优先于通道0,依此类推。

3、独立的源和目标数据区的传输宽度(字节8bit,半字16bit,全字32bit),模拟打包和拆包的过程。源和目标地址必须按照数据传输宽度对齐。

4、可以单次传输或循环传输

5、每个通道都有3个事件传输标志:(DMA半传输[数据传输一半],DMA传输完成,DMA传输出错),这三个事件可以分别产生单独的中断请求

6、传输方向(3个方向):存储器-->外设、外设-->存储器、存储器-->存储器

7、存储器:闪存、SRAM、外设的SRAM

      外设:APB1、APB2、AHB的外设均可作为访问的源和目标

8、一次可传输数据的最大数据量:65536

从外设(TIMx、 ADC、 SPIx、 I2Cx 和 USARTx)产生的 DMA 请求,通过逻辑或输入到DMA 控制器,这就意味着同时只能有一个请求有效。外设的 DMA 请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭。

--------------------------------------------------------------------------------------------------------------------------

DMA1通道:7个

 通过上面外设所对应的通道表来看,外设和通道之间并不是随便选择的,而是需要对应的。

如:要使用USART1将数据从内存发送到外设(UART1_TX) ,则需要使用DMA1的通道4。

DMA2(5个)同理:

 --------------------------------------------------------------------------------------------------------------------------

DMA处理过程

DMA_CPARx:存放外设的基地址

DMA_CMARx:存放内存的基地址

每一次DMA传输都由3个过程组成:(个人理解,不一定对)

1、找到要操作的两个寄存器的地址(从DMA_CPARx和DMA_CMARx取值)

2、存一次数据,从起始地址到目的地址

3、执行一次DMA_CNDTRx寄存器递减操作(该寄存器存放剩余待传输数据数目)

指针增量(存储器地址增量):传输数组地址递增

循环模式:循环传输

存储器到存储器模式:把外设地址设置为存储器地址。需要设置MEM2MEM位之后才可以传输。

注意:存储器到存储器模式,不能与循环模式同时使用。

中断:

通道的配置过程:

1、设置外设存储器的基地址:DMA_CPARx

2、设置数据存储器的基地址:DMA_CMARx

3、设置传输方向:DMA_CCRx

4、设置传输数据量:DMA_CNDTRx

5、传输通道的优先级:DMA_CCRx

6、设置循环模式/单次传输 :DMA_CCRx

7、设置寄存器的增量模式:内存递增/不递增 :DMA_CCRx

8、设置外设和存储器的数据宽度:DMA_CCRx

9、是否使用3个(传输过半,传输完成,传输异常)中断:DMA_CCRx

10、使能DMA_CCRx寄存器的ENABLE位,启动该通道:DMA_CCRx

-------------------------------------------------------------------------------------------------------------------

 常用DMA库函数:

DMA_DeInit()初始化DMA,设置通道、传输地址,数据量等……

DMA_Cmd()使能DMA

DMA_ITConfig()可选择是否开启三个中断

DMA_SetCurrDataCounter()设置传输数据量大小

uint16_t DMA_GetCurrDataCounter()获取传输数据量大小

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)获取状态标志位

DMA_ClearFlag(uint32_t DMAy_FLAG) 清除状态标志位

ITStatus DMA_GetITStatus(uint32_t DMAy_IT) 获取中断标志位
DMA_ClearITPendingBit(uint32_t DMAy_IT) 清除中断标志位

 常用DMA外设库函数:(在相应外设头文件库中)

USART_DMACmd() @file    stm32f10x_usart.h

ADC_DMACmd() @file    stm32f10x_adc.h

DAC_DMACmd()@file    stm32f10x_dac.h

I2C_DMACmd()@file    stm32f10x_i2c.h

SDIO_DMACmd  () @file    stm32f10x_sdio.h

SPI_I2S_DMACmd  () @file    stm32f10x_spi.h

TIM_DMAConfig() @file    stm32f10x_tim.h

TIM_DMAConfig() @file    stm32f10x_tim.h

代码配置:

1、使能DMA时钟(找不到挂载在哪里?)APB1和APB2桥上没有的,应该就是在AHB上

 2、初始化DMA通道参数(通用):DMA_Init()

DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);

  assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx));//DMA与通道

如:使用串口1,发送:DMA1通道4

结构体成员:

typedef struct
{
  uint32_t DMA_MemoryBaseAddr //存储器地址
  uint32_t DMA_PeripheralBaseAddr //外设地址
  uint32_t DMA_DIR //数据传输方向(DST 内存到外设 | SRC 外设到内存)
  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;

3、使能串口DMA(发送):USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE)

4、启动DMA:DMA_Cmd()

*4、选择是否开启中断:DMA_ITConfig()

*5、设置传输数据量大小:DMA_SetCurrDataCounter()

*6、获取传输数据量大小:uint16_t DMA_GetCurrDataCounter()

DMA.C

char DMA_Buffer[]="abcdefghigklmnopqrstuvwxyz\r\n";

void DMA1_Config(u32 MemoryBaseAddr,u32 PeripheralBaseAddr,u16 DataNumber,u32 DIR)
{
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	DMA_InitTypeDef DMA_InitStruct;
	
	DMA_InitStruct.DMA_MemoryBaseAddr=MemoryBaseAddr;
	DMA_InitStruct.DMA_PeripheralBaseAddr=PeripheralBaseAddr;
	DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	DMA_InitStruct.DMA_DIR=DIR;
	DMA_InitStruct.DMA_BufferSize=DataNumber;
	DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
	DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;
	DMA_InitStruct.DMA_Priority=DMA_Priority_VeryHigh;
	DMA_InitStruct.DMA_M2M=DMA_M2M_Disable;
	DMA_Init(DMA1_Channel4,&DMA_InitStruct);
}

void DMA1_Enable(DMA_Channel_TypeDef* DMAy_Channelx,u16 DataNumber)
{
	DMA_Cmd(DMAy_Channelx,DISABLE);
	DMA_SetCurrDataCounter(DMAy_Channelx,DataNumber);
	DMA_Cmd(DMAy_Channelx,ENABLE);
}

void Usart1_Tx_DMA(u32 MemoryBaseAddr,u32 PeripheralBaseAddr,u16 DataNumber)
{
	DMA1_Config(MemoryBaseAddr,PeripheralBaseAddr,DataNumber,DMA_DIR_PeripheralDST);
	USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);//这句不要漏掉!
	DMA_Cmd(DMA1_Channel4,ENABLE);
}




USART1.C(还是之前的串口1配置,内容有多余,未删除)

void Usart1_Config(u32 bound)
{
	USART_InitTypeDef USART1_nitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	USART_DeInit(USART1);
	Usart1_GPIO_Config();
	Usart1_NVIC_Config();
	USART1_nitStructure.USART_BaudRate=bound;
	USART1_nitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART1_nitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART1_nitStructure.USART_Parity=USART_Parity_No;
	USART1_nitStructure.USART_StopBits=USART_StopBits_1;
	USART1_nitStructure.USART_WordLength=USART_WordLength_8b;
	USART_Init(USART1,&USART1_nitStructure);
	USART_Cmd(USART1,ENABLE);

}

void Usart1_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
}

void Usart1_NVIC_Config(void)
{
	NVIC_InitTypeDef USART1_NVIC_InitStructure;
	
	USART1_NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
	USART1_NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=15;
	USART1_NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
	USART1_NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接收(非空)中断
	NVIC_Init(&USART1_NVIC_InitStructure);
}

//重定义fputc函数
int fputc(int ch,FILE *f)
{
	while((USART1->SR&0x40) == 0);
	USART1->DR = (u8)ch;
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
	return ch;
}

mian.c(内容有多余,自行甄别)


int main(void)
{	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);	
	SysTick_Config(SystemCoreClock/1000);
	LED_GPIO_Config();
	Usart1_Config(115200);
	Usart1_Tx_DMA((u32)DMA_Buffer,(u32)&USART1->DR,(u16)strlen(DMA_Buffer));
	

	while(1)
	{
		if(SysTickTime>=1000)//1S流水灯
		{
			GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13)?
			GPIO_ResetBits(GPIOC, GPIO_Pin_13),
			SysTickTime=0: 
			GPIO_SetBits(GPIOC, GPIO_Pin_13),
			SysTickTime=0;
			DMA1_Enable(DMA1_Channel4,(u16)strlen(DMA_Buffer));
			
		}
		if(USART1_RX_State & (1<<15))//接收到一组数据
		{
			len = (USART1_RX_State & 0x3fff);//从13位数据中获取数据长度
						printf("\r\n接收数据为:\r\n");			
			for(u16 n=0;n<len;n++)
					{
						USART_SendData(USART1, USART1_Rx_Data[n]);
						while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
					}
					printf("\r\n");
			
			USART1_RX_State=0;
		}
	}
}

/--------------------------------附加功能:显示传输进度---------------------------------------------/

待续。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值