设计需求
使用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 */
**注意:**下面的配置信息可以不用看
工程目录结构
运行结果:
实验现象
总结
完美,多花点时间研究,这些东西都会豁然开朗的。加油!