STM32F429 >> 10. DMA_直接存储器访问

版权声明:如需转载请标注 https://blog.csdn.net/weixin_40973138/article/details/85268778

本工程板级支持包文件适用于野火stm32f429 开发板。

DMA 传输实现高速数据移动过程无需任何CPU 操作控制

其支持以下三种传输方式:

  1. 外设到存储器传输;
  2. 存储器到外设传输;
  3. 存储器到存储器传输。

功能框图:

在这里插入图片描述

① 外设通道选择

外设通道选择所解决的问题是决定哪一个外设作为数据传输的源地址或目标地址。

DMA1 请求映射:
在这里插入图片描述
在这里插入图片描述

DMA2 请求映射:
在这里插入图片描述

每个外设请求都占用一个数据流通道,相同外设请求可以占用不同数据流通道。

② 仲裁器

仲裁器管理数据流方法分为两个阶段。

  1. 第一阶段属于软件阶段,我们在配置数据流时可以通过寄存器设定其优先级别,具体配置DMA_SxCR 寄存器PL[1:0] 位,可以设置为非常高、高、中和低四个级别;
  2. 第二阶段属于硬件阶段,若两个或两个以上数据流软件设置优先级一样,则他们优先取决于数据流编号,编号越低越具有优先权。

③ FIFO

每个数据流都独立拥有四级32位FIFO(先进先出存储器缓冲区)。DMA 传输具有FIFO 模式和直接模式。

  1. 直接模式在每个外设请求都立即启动对存储器传输。在直接模式下,如果 DMA配置为存储器到外设传输那 DMA会建一个数据存放在 FIFO 内,如果外设启动 DMA 传输请求就可以马上将数据传输过去。
  2. FIFO 用于在源数据传输到目标地址之前临时存放这些数据。可以通过 DMA 数据流xFIFO 控制寄存器 DMA_SxFCR 的 FTH[1:0]位来控制 FIFO 的阈值,分别为 1/4、1/2、3/4和满。如果数据存储量达到阈值级别时,FIFO 内容将传输到目标中。

FIFO 对于要求源地址和目标地址数据宽度不同时非常有用,比如源数据是源源不断的字节数据,而目标地址要求输出字宽度的数据,即在实现数据传输时同时把原来 4 个 8位字节的数据拼凑成一个 32位字数据。此时使用 FIFO 功能先把数据缓存起来,分别根据需要输出数据。

FIFO 另外一个作用使用于突发(burst)传输。

④ 存储器端口、外设端口

DMA2(DMA 控制器 2)的存储器端口和外设端口都是连接到 AHB 总线矩阵,可以使用AHB 总线矩阵功能。DMA2 存储器和外设端口可以访问相关的内存地址,包括有内部Flash、内部 SRAM、AHB1 外设、AHB2外设、APB2 外设和外部存储器空间。

DMA1 的存储区端口相比 DMA2 的要减少 AHB2 外设的访问权,同时 DMA1外设端口是没有连接至总线矩阵的,只有连接到 APB1外设,所以 DMA1 不能实现存储器到存储器传输。
在这里插入图片描述

⑤ 编程端口

AHB 从器件编程端口是连接至 AHB2外设的。AHB2 外设在使用 DMA传输时需要相关控制信号。

DMA 数据配置

在这里插入图片描述
在这里插入图片描述

1. DMA 传输模式

DMA2 支持全部三种传输模式,而 DMA1 只有外设到存储器和存储器到外设两种模式。

直接模式要求外设和存储器数据宽度大小一样,实际上在这种模式下 DMA数据流直接使用PSIZE,MSIZE 不被使用。、

2. 源地址和目标地址

DMA数据流 x 外设地址 DMA_SxPAR(x为 0-7)寄存器用来指定外设地址,它是一个32 位数据有效寄存器。DMA数据流 x 存储器 0地址 DMA_SxM0AR(x 为 0-7) 寄存器和DMA数据流 x 存储器 1 地址 DMA_SxM1AR(x 为 0~7) 寄存器用来存放存储器地址,其中DMA_SxM1AR 只用于双缓冲模式,DMA_SxM0AR和 DMA_SxM1AR 都是 32 位数据有效的。

3. 流控制器

流控制器主要涉及到一个控制 DMA传输停止问题。DMA传输在 DMA_SxCR 寄存器的 EN位被置 1 后就进入准备传输状态,如果有外设请求 DMA 传输就可以进行数据传输。
在传输之前设置 DMA_SxNDTR 寄存器为要传输数目值,DMA控制器在传输完这么多数目数据后就可以控制 DMA停止传输。

在完成一次数目传输后DMA_SxNDTR 计数值就会自减,当达到零时就说明传输完成。

如果某些情况下在传输之前我们无法确定数据的数目,那 DMA 就无法自动控制传输停止了,此时需要外设通过硬件通信向 DMA控制器发送停止传输信号。这里有一个大前提就是外设必须是可以发出这个停止传输信号,只有 SDIO 才有这个功能,其他外设不具备此功能。

4. 循环模式

循环模式相对应于一次模式。一次模式就是传输一次就停止传输,下一次传输需要手动控制,而循环模式在传输一次后会自动按照相同配置重新传输,周而复始直至被控制停止或传输发生错误。

通过 DMA_SxCR 寄存器的 CIRC 位可以使能循环模式。

5. 传输类型

DMA传输类型有单次(Single)传输和突发(Burst)传输。突发传输就是用非常短时间结合非常高数据信号率传输数据,相对正常传输速度,突发传输就是在传输阶段把速度瞬间提高,实现高速传输,在数据传输完成后恢复正常速度。
为达到这个效果突发传输过程要占用 AHB总线,保证要求每个数据项在传输过程不被分割,这样一次性把数据全部传输完才释放 AHB总线;而单次传输时必须通过 AHB 的总线仲裁多次控制才传输完成。
在这里插入图片描述
PINC 位和MINC 位是寄存器 DMA_SxCR 寄存器的第 9和第 10位,如果位被置 1则在每次数据传输后数据地址指针自动递增,其增量由 PSIZE 和 MSIZE 值决定,比如,设置 PSIZE 为半字大小,那么下一次传输地址将是前一次地址递增 2。

突发传输与 FIFO 密切相关,突发传输需要结合 FIFO 使用,具体要求 FIFO 阈值一定要是内存突发传输数据量的整数倍。FIFO 阈值选择和存储器突发大小必须配合使用。
在这里插入图片描述

6. 直接模式

默认情况下,DMA 工作在直接模式,不需配置 FIFO 阈值级别。

直接模式在每个外设请求都立即启动对存储器传输的单次传输。直接模式要求源地址和目标地址的数据宽度必须一致,所以只有 PSIZE 控制,而 MSIZE 值被忽略。突发传输是基于 FIFO 的所以直接模式不被支持。另外直接模式不能用于存储器到存储器传输。

7. 双缓冲模式

设置 DMA_SxCR 寄存器的 DBM 位为 1 可启动双缓冲传输模式,并自动激活循环模式。双缓冲不应用与存储器到存储器的传输。双缓冲模式下,两个存储器地址指针都有效,即DMA_SxM1AR 寄存器将被激活使用。开始传输使用 DMA_SxM0AR寄存器的地址指针所对应的存储区,当这个存储区数据传输完 DMA 控制器会自动切换至 DMA_SxM1AR 寄存器的地址指针所对应的另一块存储区,如果这一块也传输完成就再切换至 DMA_SxM0AR寄存器的地址指针所对应的存储区,这样循环调用。

当其中一个存储区传输完成时都会把传输完成中断标志 TCIF位置 1,如果我们使能了DMA_SxCR寄存器的传输完成中断,则可以产生中断信号。

在未使能DMA数据流传输时,可以直接写 CT位,改变开始传输的目标存储区。

8. DMA 中断

每个DMA 数据流可以在发送以下事件时产生中断:

  1. 达到半传输:DMA 数据传输达到一半时 HTIF 标志位被置 1,如果使能 HTIE 中断控制位将产生达到半传输中断;
  2. 传输完成:DMA 数据传输完成时 TCIF 标志位被置 1,如果使能 TCIE 中断控制位将产生传输完成中断;
  3. 传输错误:DMA 访问总线发生错误或者在双缓冲模式下试图访问“受限”存储器地址寄存器时 TEIF 标志位被置 1,如果使能 TEIE 中断控制位将产生传输错误中断;
  4. FIFO 错误:发生 FIFO 下溢或者上溢时 FEIF 标志位被置 1,如果使能 FEIE 中断控制位将产生 FIFO 错误中断;
  5. 直接模式错误:在外设到存储器的直接模式下,因为存储器总线没得到授权,使得先前数据没有完全被传输到存储器空间上,此时 DMEIF 标志位被置 1,如果使能 DMEIE中断控制位将产生直接模式错误中断。

存储器到存储器

存储器到存储器传输必须使用 DMA2,但对数据流编号以及通道选择就没有硬性要求,
可以自由选择。

编程要点:

  1. 使能DMA 时钟并复位初始化DMA 数据流;
  2. 配置DMA_InitStructure 数据流参数;
  3. 使能DMA 数据流,进行传输;
  4. 等待传输完成,并比对源数据地址与目标地址数据是否相同;

所有定义都必须在.c 文件中进行,.h 文件只负责声明

bsp_dma.c

/**
  ******************************************************************************
  * @file    bsp_dma.c
  * @author  Waao
  * @version V1.0.0
  * @date    29-Dec-2018
  * @brief   This file contains some board support package's functions for the LED.
  *            
  ******************************************************************************
  * @attention
  *
  * None
	*
  ******************************************************************************
  */

#include <bsp_dma.h>


/* Now we created the first memory base address and the data we want to transfer */
/* Use the key word "const" to ornament the aSRC_Const_Buffer, and render this data 
   can be stored in the internal flash */
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE] = {
                                    0xffffffff,0xffffffff,0x090A0B0C,0xffffffff,
                                    0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                                    0x21222324,0x25262728,0x292A2B2C,0xffffffff,
                                    0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
                                    0xffffffff,0x45464748,0xffffffff,0x4D4E4F50,
                                    0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
                                    0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
                                    0x71727374,0xffffffff,0x797A7B7C,0xffffffff};

/* We can get the another memory base address */															
uint32_t aDST_Buffer[BUFFER_SIZE];

																		
/**
  * @brief  Initialize the DMA.
  * @param  None  
  * @retval None
  */
void DMA_Config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	/* DeInit the DMA */
	DMA_DeInit(DMA_STREAM);
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
	
	/* Specifies the channel */
	DMA_InitStructure.DMA_Channel = DMA_CHANNEL;
	/* Specifies the base address of the peripherals */
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
	/* Specifies the base address of the memory0 */
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aDST_Buffer;
	/* Specifies the transmission direction */
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;
	/* Specifies the data size */
	DMA_InitStructure.DMA_BufferSize = (uint32_t)BUFFER_SIZE;
	/* Specifies the peripheral enable or disable */
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	/* Specifies the memory enable or disable */
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	/* Specifies the peripheral data size */
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
	/* Specifies the mode of the DMA */
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	/* Specifies the priority of the DMA */
	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
	/* Specifies the FIFO Mode enable or disable */
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
	/* Specifies the FIFO Threshold */
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	/* Specifies the mode of the memory burst */
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
	/* Specifies the mode of the peripheral burst */
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

	DMA_Init(DMA_STREAM, &DMA_InitStructure);
	
	/* Clear the transfer-compete flag */
	DMA_ClearFlag(DMA_STREAM, DMA_FLAG_TCIF0);
	
	/* Enable the DMA(set the DMA_SxCR EN pin) */
	DMA_Cmd(DMA_STREAM, ENABLE);
}


/**
  * @brief  Judging whether the two figure is equal or not.
	* @param  pBuffer1: The one of the figures we want to compare.
  *					pBuffer2: The another figure we want to compare.
  *					BufferLength: The length of the figures.
  * @retval SET or RESET;
  */
uint8_t Buffercmp(const uint32_t* pBuffer1, 
									uint32_t* pBuffer2, uint16_t BufferLength)
{
	while(BufferLength--)
	{
		if(*pBuffer1 != *pBuffer2)
		{
			return RESET;
		}
		pBuffer1++;
		pBuffer2++;
	}
	return SET;
}

bsp_dma.h

/**
  ******************************************************************************
  * @file    bsp_dma.h
  * @author  Waao
  * @version V1.0.0
  * @date    29-Dec-2018
  * @brief   This file contains some board support package's definition for the LED.
  *            
  ******************************************************************************
  * @attention
  *
  * None
	*
  ******************************************************************************
  */
#ifndef __BSP_DMA_H_
#define __BSP_DMA_H_

#include <bsp_led.h>
#include "stm32f4xx_dma.h"

/* M2M */
#define DMA_CHANNEL    			DMA_Channel_0
#define DMA_STREAM     			DMA2_Stream0
#define DMA_STREAM_CLOCK    RCC_AHB1Periph_DMA2
#define DMA_FLAG_TCIF    		DMA_Channel_0

#define BUFFER_SIZE      		32
#define TIMEOUT_MAX      		10000

/* M2S */
#define DMA_USART1_CHANNEL  DMA_Channel_4
#define DMA_USART1_STREAM   DMA2_Stream7
#define USART1_DR_BaseAddr  ((uint32_t)0x40011004)//(USART1_BASE+0x04)
#define SENDBUFFER_SIZE     50

#define SET 								1
#define RESET 							0



extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];
extern uint8_t SendBuff[SENDBUFFER_SIZE];
																		
void DMA_M2M_Config(void);
void DMA_M2S_Config(void);
uint8_t Buffercmp(const uint32_t* pBuffer1, uint32_t* pBuffer2, uint16_t BufferLength);
																	
#endif

main.c

main 函数中必须在结尾处加上while 循环,否则程序会出现跑偏,出现不想要的结果

*** Using Compiler 'V5.05 update 2 (build 169)', folder: 'E:\KELI_5\ARM\ARMCC\Bin'
Build target 'Target 1'
compiling main.c...
linking...
Program Size: Code=1628 RO-data=572 RW-data=0 ZI-data=1152  
FromELF: creating hex file...
"..\Output\DMA.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed:  00:00:02
Load "..\\Output\\DMA.axf" 
Erase Done.
Programming Done.
Verify OK.
Application running ...
Flash Load finished at 23:22:57

存储器到外设


bsp_dma.c

/**
  * @brief  Initialize the DMA_M2S.
  * @param  None  
  * @retval None
  */
void DMA_M2S_Config(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	/* DeInit the DMA */
	DMA_DeInit(DMA_STREAM);
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

	/* We want the data show on the screen, so we choose the USART1_TX as the channel destination */
	DMA_InitStructure.DMA_Channel = DMA_USART1_CHANNEL;
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
	DMA_InitStructure.DMA_BufferSize = SENDBUFFER_SIZE;
	/* Because the address of the peripheral is stable, Disable the increasing of the peripheral address */
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	/* Because the sourcedata is the array defined by us, Enable the increasing of the memory address */
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
	DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_BaseAddr;
	/* In C, the array name is equivalent to the pointer that point to the address of the array */
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SendBuff;
	
	DMA_Init(DMA_USART1_STREAM, &DMA_InitStructure);
	
	DMA_Cmd(DMA_USART1_STREAM, ENABLE);
	
	while(DMA_GetCmdStatus(DMA_USART1_STREAM) != ENABLE);
}

main.c

/**
  ******************************************************************************
  * @file    bsp_dma.h
  * @author  Waao
  * @version V1.0.0
  * @date    29-Dec-2018
  * @brief   This file contains some board support package's definition for the LED.
  *            
  ******************************************************************************
  * @attention
  *
  * None
	*
  ******************************************************************************
  */
#ifndef __BSP_DMA_H_
#define __BSP_DMA_H_

#include <bsp_led.h>
#include "stm32f4xx_dma.h"

/* M2M */
#define DMA_CHANNEL    			DMA_Channel_0
#define DMA_STREAM     			DMA2_Stream0
#define DMA_STREAM_CLOCK    RCC_AHB1Periph_DMA2
#define DMA_FLAG_TCIF    		DMA_Channel_0

#define BUFFER_SIZE      		32
#define TIMEOUT_MAX      		10000

/* M2S */
#define DMA_USART1_CHANNEL  DMA_Channel_4
#define DMA_USART1_STREAM   DMA2_Stream7
#define USART1_DR_BaseAddr  ((uint32_t)0x40011004)//(USART1_BASE+0x04)
#define SENDBUFFER_SIZE     50

#define SET 								1
#define RESET 							0



extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];
extern uint8_t SendBuff[SENDBUFFER_SIZE];
																		
void DMA_M2M_Config(void);
void DMA_M2S_Config(void);
uint8_t Buffercmp(const uint32_t* pBuffer1, uint32_t* pBuffer2, uint16_t BufferLength);
																	
#endif

猜你喜欢

转载自blog.csdn.net/weixin_40973138/article/details/85268778