STM32MP157实验(六)——DMA(Memory To Memory)


设计需求

使用DMA将内存A处数据搬运到内存B处


一、基础知识

DMA(Direct Memory Access)直接内存访问,可以大大减轻CPU工作量。CPU根据代码内容执行指令,这些众多指令中,有用于计算,有控制程序、有转移数据的指令,尤其是转移大量数据,会占用大量CPU。如果是把外设A的数据,传给外设B,这种情况不需要CPU一直参与,只需要在A、B之间创建一个通道,让他们自己传输即可。这就是DMA设计的目的,减少大量数据转移指令消耗CPU,DMA专注数据转移,CPU专注计算、控制

DMA主要实现将A处的数据直接搬运到B处,这里的A、B可以是内存,也可以是外设,因此所有的场景如下四种

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

无论是以上何种方式,都是先设置好DMA的数据源地址、数据目的地址、数据长度。设置好后,启动DMA就可以自动的把数据从源地址依次传输到目的地址。
在这里插入图片描述从上图可知,STM32MP157有四个DMA,其中DMA1,MSMA是A7独享的,DMAMUX需要指定分配A7或M4,DMA2是M4独享。
这里是使用DMA2,演示如何设置DMA搬运数据,后续在其他外设章节,还会使用DMA。

二、STM32CubeIDE设计

1.硬件设计

不涉及电路设计

2.MX设计

在这里插入图片描述进行DMA2配置
在这里插入图片描述在这里插入图片描述在DMA搬运完数据以后,还需要设置一个中断来告知搬运完成,所以需要设置DMA的中断
在这里插入图片描述

代码设计

在这里插入图片描述
driver_dma.h

/*
 * driver_dma.h
 *
 *  Created on: Jan 4, 2022
 *      Author: lenovo
 */

#ifndef DRIVER_DMA_H_
#define DRIVER_DMA_H_

#include "main.h"
#include "stm32mp1xx_hal.h"

extern __IO uint32_t transferErrorDetected;
extern __IO uint32_t transferCompleteDetected;



#endif /* DRIVER_DMA_H_ */

driver_dma.c

/*
 * driver_dma.c
 *
 *  Created on: Jan 4, 2022
 *      Author: lenovo
 */

#include "driver_dma.h"

__IO uint32_t transferErrorDetected;  //传输出错检测
__IO uint32_t transferCompleteDetected; //传输完成检测

static void TransferComplete(DMA_HandleTypeDef *DmaHandle);
static void TransferError(DMA_HandleTypeDef *Dmahanle);

//注册回调函数
void RegisterCallbackFunction(void){
	//注册传输完成和传输错误回调函数
	HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream0,HAL_DMA_XFER_CPLT_CB_ID,TransferComplete);
	HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream0,HAL_DMA_XFER_ERROR_CB_ID,TransferError);
	
	//配置DMA的中断优先级及其使能(系统自动配置生成)
	//HAL_NVIC_SetPriority(DMA2_Stream0_IRQn,0,0);
	//HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}

//输入参数:DmaHandle-Dma句柄
//函数作用:如果DMA传输完成且不发生错误,则在此函数将传输完成标志置1

static void TransferComplete(DMA_HandleTypeDef *DmaHandle){
	transferCompleteDetected=1;
}
//输入参数:DmaHandle-Dma句柄
//函数作用:如果DMA传输过程中发生错误,则在此函数中将传输错误标志置1
static void TransferError(DMA_HandleTypeDef *DmaHandle){
	transferErrorDetected=1;
}

/*输入参数:srcAddr-数据原地址
 * 		 dstAddr-DMA搬移的目的地址
 * 		 bufsz-DMA搬移数据大小,单位是初始化的时候选择的:我们上面设置的是word
 * 		 函数作用:初始化DMA-channel1,配置为内存-内存模式,每次搬运一个word即4bytes
*/

void DMA_M2M_Start(uint32_t *srcAddr,uint32_t *dstAddr,uint16_t bufsz){
	if(HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0,(uint32_t)srcAddr,(uint32_t)dstAddr,bufsz)!=HAL_OK){
		Error_Handler();
	}
}

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart4;

DMA_HandleTypeDef hdma_memtomem_dma2_stream0;
/* USER CODE BEGIN PV */
static uint32_t srcBuffer[20]={
	0x1234, 0x5678, 0x9876, 0x4586, 0xABCD,
	0x5678, 0xABCD, 0x4586, 0x4586, 0xABCD,
	0xABCD, 0x5678, 0x4586, 0x9876, 0x1234,
	0x1234, 0xABCD, 0x9876, 0x5678, 0xABCD,	
};

static uint32_t dstBuffer[20]={0};

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_DMA_Init(void);
static void MX_GPIO_Init(void);
static void MX_UART4_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	uint8_t i=0;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  if(IS_ENGINEERING_BOOT_MODE())
  {
    /* Configure the system clock */
    SystemClock_Config();
  }

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_DMA_Init();
  MX_GPIO_Init();
  MX_UART4_Init();
  /* USER CODE BEGIN 2 */
  printf("\rDMA Memory To Memory Test\n");
  RegisterCallbackFunction();
  transferCompleteDetected=0;
  transferErrorDetected=0;
  DMA_M2M_Start(srcBuffer,dstBuffer,20);//启动DMA搬运数据
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
	  
    /* USER CODE BEGIN 3 */
	  if(transferCompleteDetected==1){//判断前面DMA搬运是否完成(标志在回调函数被修改)
		  transferCompleteDetected=0;//重新将数据完成标志清0
		  for(i=0;i<20;i++){
			  if(dstBuffer[i]!=srcBuffer[i]){//按个对比是否有差异
				  printf("\rDMA Data Error\n");
			  }
		  }
		  if(i==20){//没有差异表示本次传输正常完成,再次启动传输测试
			  DMA_M2M_Start(srcBuffer,dstBuffer,20);
			  printf("\rDMA Test ok\n");
			  
		  }
	  }else if(transferErrorDetected==1){
		  transferErrorDetected=0;
		  printf("\rDMA TransferData Error\n");
	  }
	  HAL_Delay(1000);
	 
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI
                              |RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.HSIDivValue = RCC_HSI_DIV1;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
  RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL3.PLLSource = RCC_PLL3SOURCE_HSE;
  RCC_OscInitStruct.PLL3.PLLM = 2;
  RCC_OscInitStruct.PLL3.PLLN = 52;
  RCC_OscInitStruct.PLL3.PLLP = 3;
  RCC_OscInitStruct.PLL3.PLLQ = 2;
  RCC_OscInitStruct.PLL3.PLLR = 2;
  RCC_OscInitStruct.PLL3.PLLRGE = RCC_PLL3IFRANGE_1;
  RCC_OscInitStruct.PLL3.PLLFRACV = 2048;
  RCC_OscInitStruct.PLL3.PLLMODE = RCC_PLL_FRACTIONAL;
  RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** RCC Clock Config
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_ACLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_PCLK3|RCC_CLOCKTYPE_PCLK4
                              |RCC_CLOCKTYPE_PCLK5;
  RCC_ClkInitStruct.AXISSInit.AXI_Clock = RCC_AXISSOURCE_HSI;
  RCC_ClkInitStruct.AXISSInit.AXI_Div = RCC_AXI_DIV1;
  RCC_ClkInitStruct.MCUInit.MCU_Clock = RCC_MCUSSOURCE_PLL3;
  RCC_ClkInitStruct.MCUInit.MCU_Div = RCC_MCU_DIV1;
  RCC_ClkInitStruct.APB4_Div = RCC_APB4_DIV1;
  RCC_ClkInitStruct.APB5_Div = RCC_APB5_DIV1;
  RCC_ClkInitStruct.APB1_Div = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2_Div = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB3_Div = RCC_APB3_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Set the HSE division factor for RTC clock
  */
  __HAL_RCC_RTC_HSEDIV(1);
}

/**
  * @brief UART4 Initialization Function
  * @param None
  * @retval None
  */
static void MX_UART4_Init(void)
{

  /* USER CODE BEGIN UART4_Init 0 */

  /* USER CODE END UART4_Init 0 */

  /* USER CODE BEGIN UART4_Init 1 */

  /* USER CODE END UART4_Init 1 */
  huart4.Instance = UART4;
  huart4.Init.BaudRate = 115200;
  huart4.Init.WordLength = UART_WORDLENGTH_8B;
  huart4.Init.StopBits = UART_STOPBITS_1;
  huart4.Init.Parity = UART_PARITY_NONE;
  huart4.Init.Mode = UART_MODE_TX_RX;
  huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart4.Init.OverSampling = UART_OVERSAMPLING_16;
  huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart4.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart4) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart4, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart4, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart4) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN UART4_Init 2 */

  /* USER CODE END UART4_Init 2 */

}

/**
  * Enable DMA controller clock
  * Configure DMA for memory to memory transfers
  *   hdma_memtomem_dma2_stream0
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMAMUX_CLK_ENABLE();
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* Configure DMA request hdma_memtomem_dma2_stream0 on DMA2_Stream0 */
  hdma_memtomem_dma2_stream0.Instance = DMA2_Stream0;
  hdma_memtomem_dma2_stream0.Init.Request = DMA_REQUEST_MEM2MEM;
  hdma_memtomem_dma2_stream0.Init.Direction = DMA_MEMORY_TO_MEMORY;
  hdma_memtomem_dma2_stream0.Init.PeriphInc = DMA_PINC_ENABLE;
  hdma_memtomem_dma2_stream0.Init.MemInc = DMA_MINC_ENABLE;
  hdma_memtomem_dma2_stream0.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdma_memtomem_dma2_stream0.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  hdma_memtomem_dma2_stream0.Init.Mode = DMA_NORMAL;
  hdma_memtomem_dma2_stream0.Init.Priority = DMA_PRIORITY_LOW;
  hdma_memtomem_dma2_stream0.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
  hdma_memtomem_dma2_stream0.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  hdma_memtomem_dma2_stream0.Init.MemBurst = DMA_MBURST_SINGLE;
  hdma_memtomem_dma2_stream0.Init.PeriphBurst = DMA_PBURST_SINGLE;
  if (HAL_DMA_Init(&hdma_memtomem_dma2_stream0) != HAL_OK)
  {
    Error_Handler( );
  }

  /* DMA interrupt init */
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */


**注意:**下面的配置信息可以不用看

工程目录结构
在这里插入图片描述
运行结果:
在这里插入图片描述

实验现象

在这里插入图片描述

总结

完美,多花点时间研究,这些东西都会豁然开朗的。加油!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个 Linux STM32MP157 DAC 对 DMA 的使用示例: 1. 首先,需要在设备树中定义 DAC 控制器和 DMA 控制器。例如: ``` &dac { compatible = "st,stm32-dac"; reg = <0x40007400 0x400>; interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>; #dma-cells = <1>; }; &dma1 { compatible = "st,stm32-dma"; reg = <0x40020000 0x4000>; interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>; }; ``` 2. 在 Linux 驱动程序中,需要获取 DAC 控制器和 DMA 控制器的物理地址,并映射到虚拟地址空间中。例如: ``` static void __iomem *dac_base; static void __iomem *dma_base; static int mydriver_probe(struct platform_device *pdev) { struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dac_base = devm_ioremap_resource(&pdev->dev, res); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); dma_base = devm_ioremap_resource(&pdev->dev, res); ... } ``` 3. 在需要使用 DMA 的地方,需要分配 DMA 缓冲区,并将其映射到虚拟地址空间中。例如: ``` #define BUF_SIZE 1024 static struct dma_chan *chan; static dma_addr_t dma_addr; static void *buf; buf = dma_alloc_coherent(&pdev->dev, BUF_SIZE, &dma_addr, GFP_KERNEL); if (!buf) { return -ENOMEM; } chan = dma_request_chan(&pdev->dev, "dma1"); if (!chan) { dma_free_coherent(&pdev->dev, BUF_SIZE, buf, dma_addr); return -ENODEV; } ``` 4. 配置 DAC 控制器和 DMA 控制器。例如: ``` #define SAMPLE_RATE 48000 #define DMA_PERIODS 2 #define DMA_PERIOD_SIZE (BUF_SIZE / DMA_PERIODS) static void dac_dma_config(void) { u32 cr; u32 ndtr = DMA_PERIOD_SIZE / 2; // 2 bytes per sample // Enable DAC channel 1 cr = readl(dac_base + DAC_CR); writel(cr | DAC_CR_EN1, dac_base + DAC_CR); // Enable DMA for DAC channel 1 cr = readl(dac_base + DAC_CR); writel(cr | DAC_CR_DMAEN1, dac_base + DAC_CR); // Configure DMA dmaengine_slave_config(chan, dma_addr, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); dmaengine_set_dma_callback(chan, dac_dma_complete, NULL); dmaengine_set_periodic_mode(chan, ndtr, ndtr); dmaengine_set_src_addr(chan, buf); dmaengine_set_dst_addr(chan, dac_base + DAC_DHR12R1); dmaengine_set_transfer_size(chan, DMA_PERIOD_SIZE); } ``` 5. 开始 DMA 传输。例如: ``` static void dac_dma_start(void) { dma_async_issue_pending(chan); writel(DAC_SWTRIGR_SWTRIG1, dac_base + DAC_SWTRIGR); } ``` 以上是一个简单的 Linux STM32MP157 DAC 对 DMA 的使用示例,具体实现可能因硬件平台和应用场景而有所不同。需要根据实际情况进行调整和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jacky~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值