【STM32学习3】DMA基础操作

前言

        介绍了DMA的基础概念以及存储器到存储器、存储器到外设两种DMA的使用方式。


一、DMA基础概念

        DMA(Direct Memory Access)翻译过来就是直接存储器访问,顾名思义就是能够直接访问存储器的一个外设。传输数据是单片机的一个重要工作任务,比如各种传感器传输给单片机的数据,以及处理完成的数据要传输给上位机。如果这些任务全部交给CPU核心,那么可能太占用资源,导致CPU来不及处理其它的任务。DMA是STM32里独立于CPU的外设,专门用于传输数据,这样我们可以把传输数据的任务交给DMA完成,计算等其他任务交给CPU,最大化发挥单片机的性能。

        根据数据的位置,DMA有三种工作方式,即外设到存储器、存储器到外设以及存储器到存储器。外设到存储器就比如传感器采样后通过ADC将数据传入存储器;存储器到外设就比如通过串口向上位机发送数据;而存储器到存储器可以用于FLASH向SRAM复制数据。

        其中有关外设的工作方式需要外设向DMA控制器发出请求,DMA做出应答后才能进行数据传输。STM32F1系列芯片中有DMA1与DMA2两个DMA控制器,分别有7个通道与5个通道,每个通道对应着不同的外设请求,外设都要通过专门的通道发出请求才能完成数据的传输。

【STM32学习3】DMA基础操作_第1张图片【STM32学习3】DMA基础操作_第2张图片

以下介绍在使用HAL库时如何设置存储器到存储器DMA传输、存储器到外设DMA传输 。

二、存储器到存储器DMA传输

        通过STM32CubeIDE(或者STM32CubeMX),我们可以十分快速的完成DMA基础配置。选择MEMTOMEM(memory to memory),普通传输模式,数据源与目的地地址均设为增量模式,数据单位为word(32位)。

【STM32学习3】DMA基础操作_第3张图片

  • 传输模式:普通Normal:每调用一次DMA只执行一次数据传输循环;Circular:调用一次DMA后就重复执行数据传输。
  • 地址增量模式Increment Address:设置每完成一次传输地址是否增加。若设置为是,则下一次传输下一个地址的数据;否则反复传输同一个地址的数据。一般存储器设置为增量模式,而外设不用设置。
  • 每次传输的数据量:Byte——1字节、Half Word——2字节、Word——4字节。

        完成初始化配置后,我们尝试通过DMA将FLASH中的数据传输到SRAM中,首先定义数据源数组SRC_Buffer、目的地数组DST_Buffer以及数据长度BUFFER_SIZE,数据类型为uint32_t,通过const关键字将数据源数组定义在FLASH中。

#define BUFFER_SIZE 32

const uint32_t SRC_Buffer[BUFFER_SIZE] = {
		0x01020304,0x05060708,0x090A0B0C,0X0D0E0F10,
		0x11020304,0x15060708,0x190A0B0C,0X1D0E0F10,
		0x21020304,0x25060708,0x290A0B0C,0X2D0E0F10,
		0x31020304,0x35060708,0x390A0B0C,0X3D0E0F10,
		0x41020304,0x45060708,0x490A0B0C,0X4D0E0F10,
		0x51020304,0x55060708,0x590A0B0C,0X5D0E0F10,
		0x61020304,0x65060708,0x690A0B0C,0X6D0E0F10,
		0x71020304,0x75060708,0x790A0B0C,0X7D0E0F10,
};

uint32_t DST_Buffer[BUFFER_SIZE];

根据之前的设置,CubeIDE程序已经在dma.c中帮我们初始化了以下代码:

void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* Configure DMA request hdma_memtomem_dma1_channel1 on DMA1_Channel1 */
  hdma_memtomem_dma1_channel1.Instance = DMA1_Channel1;
  hdma_memtomem_dma1_channel1.Init.Direction = DMA_MEMORY_TO_MEMORY;
  hdma_memtomem_dma1_channel1.Init.PeriphInc = DMA_PINC_ENABLE;
  hdma_memtomem_dma1_channel1.Init.MemInc = DMA_MINC_ENABLE;
  hdma_memtomem_dma1_channel1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdma_memtomem_dma1_channel1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  hdma_memtomem_dma1_channel1.Init.Mode = DMA_NORMAL;
  hdma_memtomem_dma1_channel1.Init.Priority = DMA_PRIORITY_MEDIUM;
  if (HAL_DMA_Init(&hdma_memtomem_dma1_channel1) != HAL_OK)
  {
    Error_Handler();
  }

}

 可以看到我们选择的通道、模式以及传输数据大小都设置到了结构体hdma_memtomem_dma1_channel1中,接下来通过函数HAL_DMA_Start我们就可以完成DMA数据传输。这里我们要设置HAL状态变量供HAL_DMA_Start返回传输状态。

HAL_StatusTypeDef DMA_status = HAL_ERROR;

DMA_status = HAL_DMA_Start(&hdma_memtomem_dma1_channel1, (uint32_t)SRC_Buffer, (uint32_t)DST_Buffer, BUFFER_SIZE);

传输完成后,可以通过如下函数比较二者数据的一致性

TransferStatus = Buffer_cmp(SRC_Buffer, DST_Buffer, BUFFER_SIZE);

uint8_t Buffer_cmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength)
{
	while(BufferLength--){
		if(*pBuffer != *pBuffer1){
			return 0;
		}
		pBuffer++;
		pBuffer1++;
	}

	return 1;
}

三、存储器到外设DMA传输

        存储器到外设DMA传输的典型应用是单片机通过串口向上位机(电脑)传输数据,在上位机上可以通过串口助手显示传上来的数据。我们采用USART1_TX串口1的发送功能完成这次传输。首先在CubeIDE中配置DMA基本设置:

【STM32学习3】DMA基础操作_第4张图片根据DMA1各通道对应的外设请求,选择DMA1的通道4完成串口1的发送。发送设置为循环模式,外设地址非增量、存储器地址增量,传输数据单位长度为1字节。

        DMA初始化代码如下,注意该段代码在usart.c文件中,不再是dma.c。

/* USART1 DMA Init */
    /* USART1_TX Init */
    hdma_usart1_tx.Instance = DMA1_Channel4;
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_NORMAL;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
    {
      Error_Handler();
    }

        代码初始化完成后,设置待传输数据长度以及数组,然后在数组里填入待发送的数据,这里我们选择全部发送字符“A”。

#define SENDBUFF_SIZE 10

uint8_t SendBuff[SENDBUFF_SIZE];
for(uint16_t i=0; i

 然后通过函数HAL_UART_Transmit_DMA便可完成数据传输。

HAL_UART_Transmit_DMA(&huart1, (uint8_t *)SendBuff, SENDBUFF_SIZE);

【STM32学习3】DMA基础操作_第5张图片         由于采用了循环传输模式,数据量非常大,但都是通过DMA完成的,CPU还处于空闲状态,依旧可以完成其他的工作。


总结

        介绍了DMA基础概念以及存储器到存储器、存储器到外设两种DMA使用方式(HAL库、轮询使用)

你可能感兴趣的:(STM32,学习)