STM32学习笔记8(DMA)

  • Post author:
  • Post category:其他


一、DMA简介

DMA的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候, CPU 可以干其他的事情。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH。 DMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 个通道,DMA2 有 5 个通道。

DMA功能框图

1、DMA请求

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


DMA1各个通道请求映像 :


DAM2各个通道请求映像:

2、通道

同一时间只能接收一个,不能同时接收多个。

3、仲裁器

仲裁器管理 DMA 通道请求分为两个阶段。第一阶段属于软件阶段,可以在

DMA_CCRx

寄存器中设置,有 4 个等级:非常高、高、中和低四个优先级。第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道 0 高于通道 1。在大容量产品和互联型产品中,DMA1 控制器拥有高于 DMA2 控制器的优先级。

二、DMA数据配置

1、从哪里来到哪里去

DMA 传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储器到存储器。具体的方向

DMA_CCR 位 4 DIR

配置: 0 表示从外设到存储器, 1 表示从存储器到外设。这里面涉及到的外设地址由

DMA_CPAR

配置,存储器地址由

DMA_CMAR

配置。

外设到存储器

以 ADC 采集为例。 DMA 外设寄存器的地址对应的就是 ADC 数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(用来接收存储 AD 采集的数据)的地址。方向我们设置外设为源地址。

存储器到外设

当我们使用从存储器到外设传输时,以串口向电脑端发送数据为例。 DMA 外设寄存器的地址对应的就是串口数据寄存器的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目标地址。

存储器到存储器

当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例。DMA 外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址, DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址。跟上面两个不一样的是,这里需要把

DMA_CCR 位 14: MEM2MEM

:存储器到存储器模式配置为 1,启动 M2M 模式。

2、要传多少,单位是什么

要想数据传输正确,源和目标地址存储的数据宽度必须一致,串口数据寄存器是8位的,所以我们定义的要发送的数据也必须是 8 位。外设的数据宽度由

DMA_CCR 的PSIZE[1:0]

配置,可以是 8/16/32 位,存储器的数据宽度由

DMA_CCR 的 MSIZE[1:0]

配置,可以是 8/16/32 位。

在 DMA 控制器的控制下,数据要想有条不紊的从一个地方搬到另外一个地方,还必须正确设置两边数据指针的增量模式。外设的地址指针由

DMA_CCRx 的 PINC

配置,存储器的地址指针由

MINC

配置。以串口向电脑发送数据为例,要发送的数据很多,每发送完一个,那么存储器的地址指针就应该加 1,而串口数据寄存器只有一个,那么外设的地址指针就固定不变。

3、什么时候传输完成

可以通过查询标志位或者通过中断的方式来鉴别。每个DMA 通道在 DMA 传输过半、传输完成和传输错误时都会有相应的标志位,如果使能了该类型的中断后,则会产生中断。参考 DMA 中断状态寄存器DMA_ISR 的详细描述。

传输完成还分两种模式,是一次传输还是循环传输。具体的由

DMA_CCR 寄存器的 CIRC

循环模式位控制。

三、DMA初始化结构体

四、存储器到存储器

uint16_t MyDMA_Size;

void DMA_MTM_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)
{
	MyDMA_Size=Size;
	DMA_InitTypeDef  DMA_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	DMA_InitStructure.DMA_BufferSize=MyDMA_Size;
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_M2M=DMA_M2M_Enable;
	DMA_InitStructure.DMA_MemoryBaseAddr=AddrB;
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
	DMA_InitStructure.DMA_PeripheralBaseAddr=AddrA;
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Enable;
	DMA_InitStructure.DMA_Priority=DMA_Priority_High;
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1,DISABLE);
}


//实现多次转运
void DMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size); 
	DMA_Cmd(DMA1_Channel1,DISABLE);
	
	//等待转运完成
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);
	
	//清除标志位
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

五、 存储器到外设

uint8_t SendBuff[SENDBUFF_SIZE];

void USART_Config(void)
{
	GPIO_InitTypeDef   GPIO_InitStructure;
	USART_InitTypeDef  USART_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,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_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitStructure.USART_BaudRate=115200;
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode=USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStructure.USART_Parity=USART_Parity_No;
	USART_InitStructure.USART_StopBits=USART_StopBits_1;
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;
	
	USART_Init(USART1, &USART_InitStructure);
	USART_Cmd(USART1, ENABLE);
}

void DMA_MTP_Config()
{
	DMA_InitTypeDef  DMA_InitStructure;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)USART_DR_ADDRESS;
	DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)SendBuff;
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
	DMA_InitStructure.DMA_BufferSize=SENDBUFF_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_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);
}

main函数:

extern uint8_t SendBuff[SENDBUFF_SIZE];

int main(void)
{
	uint16_t i=0;
	
	USART_Config();
	for (i=0;i<SENDBUFF_SIZE;i++)
	{
		SendBuff[i]='P';
	}
	DMA_MTP_Config();

    //USART1 向 DMA 发出 TX 请求 
	USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
	while(1)
	{
		
	}
}



版权声明:本文为xxw971128原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。