STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)

目录

一、DMA是什么?

二、DMA的意义

三、DMA搬运数据

1.搬运什么数据

2.从哪里搬到哪里

四、DMA通道

五、DMA处理        

六、DMA传输模式

七、指针递增模式

八、DMA中断

九、存储器搬到存储器

1.STM32的hal库关于DMA的函数

2.配置

3.main.c代码

十、存储器搬到外设

1.用到的hal函数

2.配置

3.main.c代码

十一、外设搬到存储器

1.用到的hal函数

2.配置

3.main.c代码

4.stm32f1xx_it.c代码


一、DMA是什么?

        DMA(Direct Memory Access,直接存储器访问) 提供在外设与内存、存储器和存储器、外设 与外设之间的高速数据传输使用。它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU,在这个时间中,CPU对于内存的工作来说就无法使用。简单来说就是一个数据搬运工

二、DMA的意义

        代替 CPU 搬运数据为 CPU 减负

        1. 数据搬运的工作比较耗时间。

        2. 数据搬运工作时效要求高(有数据来就要搬走)。

        3. 没啥技术含量(CPU 节约出来的时间可以处理更重要的事)。

三、DMA搬运数据

1.搬运什么数据

        搬运存储器、外设的数据

        外设指的是spi、usart、iic、adc 等基于APB1 、APB2或AHB时钟的外设。

        存储器包括自身的闪存(flash)或者内存(SRAM)以及外设的存储设备都可以作为访问地源或者目的。

2.从哪里搬到哪里

        1.存储器→存储器(例如:复制某特别大的数据buf)

        2.存储器→外设 (例如:将某数据buf写入串口TDR寄存器)

        3.外设→存储器 (例如:将串口RDR寄存器写入某数据buf)

四、DMA通道

        STM32F103C8T6芯片有2个DMA控制器,DMA1有7个通道,DMA2有5个通道。一个通道每次只能搬运一个外设的数据!,如果同时有多个外设的 DMA 请求,则按照优先级进行响应。

        1.DMA1有7个通道:

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第1张图片

         2.DMA2有5个通道:

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第2张图片

        3.通道的优先级:优先级管理采用软件+硬件

        软件优先级: 每个通道的优先级可以在DMA_CCRx寄存器中设置,有4个等级:最高级>高级>中级>低级         

        硬件优先级: 如果2个请求,它们的软件优先级相同,则较低编号的通道比较高编号的通道有较高 的优先权。

        比如:如果软件优先级相同,通道2优先于通道4

五、DMA处理        

        在发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。
        DMA传送由3个操作组成:

        1.(从旧地点取数据)从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。
        2.(到新地点存数据)存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。
        3.(操作数递减1次)执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目。

六、DMA传输模式

        1.(正常模式)DMA_Mode_Normal一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次

        2.(循环传输模式)DMA_Mode_Circular当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式

七、指针递增模式

        外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值。一般设计存储器的都开启指针增量模式。

        1.源指针和目标指针都设置为增量模式

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第3张图片

        2.源指针设置为增量模式

 STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第4张图片

八、DMA中断

        每个DMA通道都有3个事件(DMA半传输、DMA传输完成和DMA传输错误),这3个事件都可以成为一个单独的中断请求。

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第5张图片

九、存储器搬到存储器

1.STM32的hal库关于DMA的函数

原型:HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)

作用:打开DMA然后开始传输数据

四个参数:

DMA_HandleTypeDef *hdma,DMA通道句柄

uint32_t SrcAddress,源数据地址

uint32_t DstAddress,目标内存地址

uint32_t DataLength,传输数据长度。注意:需要乘以sizeof(uint32_t)

返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)

原型:#define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__) (DMA1->ISR & (__FLAG__))

作用:获取DMA的FLAG的一些值

二个参数:

HANDLE,DMA通道句柄

FLAG,数据传输标志: x是通道几,通道1就x=1

        DMA_FLAG_TCx:传输完成标志
        DMA_FLAG_HTx:半传输完成标志
        DMA_FLAG_TEx:传输错误标志
        DMA_FLAG_GLx:全局中断标志

返回值:FLAG的值(SET/RESET)

2.配置

        实现目的:把内存里的一个数组的数据搬运的另一个内存的数组,然后用串口把新数组数据发送。

        1.DMA配置

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第6张图片

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第7张图片

         2.打开串口USART1、异步、非中断

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第8张图片

         3.时钟72KHz

3.main.c代码

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 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"
#include "dma.h"
#include "usart.h"
#include "gpio.h"

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


#include 
#include 
//数组大小===========================================================
#define BUF_SIZE 16

/* 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 ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

// 源数组===========================================================
uint32_t srcBuf[BUF_SIZE] = {
	0x00000000,0x11111111,0x22222222,0x33333333,
	0x44444444,0x55555555,0x66666666,0x77777777,
	0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,
	0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF
	};
// 目标数组
uint32_t desBuf[BUF_SIZE];
	
//重写fputc,利用printf进行串口输出
int fputc(int ch, FILE *f)
{
	unsigned char temp[1]={ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);
	return ch;
}
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(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 */
	int 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 */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	
// 开启数据传输============================================================
	HAL_DMA_Start(&hdma_memtomem_dma1_channel1,//句柄在dma.c可以查看
	(uint32_t)srcBuf, (uint32_t)desBuf, sizeof(uint32_t) * BUF_SIZE);
                                                    //16个数每个数都是uint32_t大小
	
// 等待数据传输完成,完成返回值是SET
	while(__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1) == RESET);//传输完成标志通道1
	
// 打印数组内容
	for (i = 0; i < BUF_SIZE; i++)//输出数组
		printf("Buf[%d] = %X\r\n", i, desBuf[i]);//16进制 %X

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* 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_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* 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 */

十、存储器搬到外设

1.用到的hal函数

原型:HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

作用:把内存里的数据通过串口发送出去

三个参数:

参数一:UART_HandleTypeDef *huart,串口句柄

参数二:uint8_t *pData,待发送数据首地址

参数三:uint16_t Size,待发送数据长度

返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)

2.配置

        实现目的:用DMA的方式将内存数据搬运到串口1发送寄存器,同时闪烁LED1。

        1.需先打开串口,打开串口1、异步、非中断

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第9张图片

         2.配置DMA

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第10张图片

         3.配置灯:GPIO PB8、output输出、开始为高电平灯灭

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第11张图片

         4.时钟 72KHz

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第12张图片

        5.芯片对应的串口引脚反接SUB对应的引脚。

3.main.c代码

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 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"
#include "dma.h"
#include "usart.h"
#include "gpio.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 */

//宏定义数组大小=====================================================================
#define BUF_SIZE 100

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

// 定义待发送的数据数组================================================================
unsigned char sendBuf[BUF_SIZE];

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(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 */
	int 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 */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

//
// 准备数据================================================================
	for (i = 0; i < BUF_SIZE; i++)//往数组添加数据
	sendBuf[i] = 'A';
// 将数据通过串口DMA发送
	HAL_UART_Transmit_DMA(&huart1, sendBuf, BUF_SIZE);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		
		//反转GPIO电平,灯闪烁=========================================================
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);//反转GPIO电平
		HAL_Delay(100);
		
    /* USER CODE BEGIN 3 */
  }
  /* 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_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* 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 */

十一、外设搬到存储器

1.用到的hal函数

1.原型:#define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U) == UART_CR1_REG_INDEX)? ((__HANDLE__)->Instance->CR1 |= ((__INTERRUPT__) & UART_IT_MASK)): \ (((__INTERRUPT__) >> 28U) == UART_CR2_REG_INDEX)? ((__HANDLE__)->Instance->CR2 |= ((__INTERRUPT__) & UART_IT_MASK)): \ ((__HANDLE__)->Instance- >CR3 |= ((__INTERRUPT__) & UART_IT_MASK)))

作用:开启串口需要使能的中断

二个参数:

参数一:HANDLE,串口句柄

参数二:INTERRUPT,需要使能的中断

        UART_IT_CTS: CTS change interrupt
        UART_IT_LBD: LIN Break detection interrupt
        UART_IT_TXE: Transmit Data Register empty interrupt
        UART_IT_TC: Transmission complete interrupt
        UART_IT_RXNE: Receive Data register not empty interrupt
        UART_IT_IDLE: 串口空闲中断
        UART_IT_PE: Parity Error interrupt
        UART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)

返回值:

2.原型:HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

作用:接收串口数据

三个参数:

参数一:UART_HandleTypeDef *huart,串口句柄

参数二:uint8_t *pData,接收缓存首地址

参数三:uint16_t Size,接收缓存长度

返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)

3.原型:#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR & (__FLAG__)) == (__FLAG__))

作用:获取某标志位的值 RESET/SET

二个参数:

参数一:HANDLE,串口句柄

参数二:FLAG

        UART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5)
        UART_FLAG_LBD: LIN Break detection flag
        UART_FLAG_TXE: Transmit data register empty flag
        UART_FLAG_TC: Transmission Complete flag
        UART_FLAG_RXNE: Receive data register not empty flag
        UART_FLAG_IDLE: 串口空闲标志位
        UART_FLAG_ORE: Overrun Error flag
        UART_FLAG_NE: Noise Error flag
        UART_FLAG_FE: Framing Error flag
        UART_FLAG_PE: Parity Error flag

返回值:FLAG的值,RESET/SET

4.原型:#define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)

作用:清除 数据传输标志

参数一:HANDLE,串口句柄

返回值:

5.原型:HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)

作用:关闭DMA

参数一:UART_HandleTypeDef *huart,串口句柄

返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)

6.原型:#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)

作用:获取未传输数据的大小

参数一:HANDLE,串口句柄。hdma_usart1_tx发送 / hdma_usart1_rx接收

返回值:未传输数据大小

2.配置

        实现目的:使用DMA的方式将串口1接收缓存寄存器的数据搬运到存储器中,再通过串口打印存储器数据,同时每隔0.5s翻转一次LED1的电平。

        流程:

                (1)设置一个数组用来接收存放外设搬来的数据。

                (2)使能IDLE空闲中断,使能DMA接收且中断。

                (3)每次串口接收中断,进入中断服务函数,判断是否空闲中断,如空闲中断则表示接收完成。

                (4)清除中断标志位,关闭DMA传输(防止干扰)。

                (5)计算刚才收到了多少个字节的数据。

                (6)输出数组数据。

                (7)处理缓冲区数据,开启DMA传输,开始下一帧接收。

         1.需先打开串口,打开串口1、异步、中断

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第13张图片

        2.DMA配置

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第14张图片

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第15张图片

        3.配置灯:GPIO PB8、output输出、开始为高电平灯灭

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第16张图片

          4.时钟 72KHz

STM32—DMA超详解入门(内存->内存、内存->外设、外设->内存)_第17张图片

3.main.c代码

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 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"
#include "dma.h"
#include "usart.h"
#include "gpio.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 */

//数组大小===========================================================================
#define BUF_SIZE 100
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

//定义数组============================================================================
uint8_t rcvBuf[BUF_SIZE]; // 接收数据缓存数组
uint8_t rcvLen = 0; // 接收一帧数据的长度

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(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 */

  /* 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 */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	
	//=================================================================================
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能IDLE空闲中断
	HAL_UART_Receive_DMA(&huart1,rcvBuf,100); // 使能DMA接收且中断

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    //反转电平,灯闪烁    =========================================================
	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);//反转电平,灯闪烁
	HAL_Delay(300);
    /* USER CODE BEGIN 3 */
  }
  /* 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_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* 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 */

4.stm32f1xx_it.c代码

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32f1xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 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"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

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

#define BUF_SIZE 100//===========================================================

extern uint8_t rcvBuf[BUF_SIZE];//extern调用另一个c文件里的变量或者函数
extern uint8_t rcvLen;

/* USER CODE END TD */

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

/* USER CODE END PD */

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

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

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

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_usart1_tx;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M3 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
  while (1)
  {
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles Hard fault interrupt.
  */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Prefetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles System service call via SWI instruction.
  */
void SVC_Handler(void)
{
  /* USER CODE BEGIN SVCall_IRQn 0 */

  /* USER CODE END SVCall_IRQn 0 */
  /* USER CODE BEGIN SVCall_IRQn 1 */

  /* USER CODE END SVCall_IRQn 1 */
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/**
  * @brief This function handles Pendable request for system service.
  */
void PendSV_Handler(void)
{
  /* USER CODE BEGIN PendSV_IRQn 0 */

  /* USER CODE END PendSV_IRQn 0 */
  /* USER CODE BEGIN PendSV_IRQn 1 */

  /* USER CODE END PendSV_IRQn 1 */
}

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f1xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles DMA1 channel4 global interrupt.
  */
void DMA1_Channel4_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel4_IRQn 0 */

  /* USER CODE END DMA1_Channel4_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart1_tx);
  /* USER CODE BEGIN DMA1_Channel4_IRQn 1 */

  /* USER CODE END DMA1_Channel4_IRQn 1 */
}

/**
  * @brief This function handles DMA1 channel5 global interrupt.
  */
void DMA1_Channel5_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel5_IRQn 0 */

  /* USER CODE END DMA1_Channel5_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart1_rx);
  /* USER CODE BEGIN DMA1_Channel5_IRQn 1 */

  /* USER CODE END DMA1_Channel5_IRQn 1 */
}

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

// 判断空闲中断IDLE标志位是否被置位,被置位则空闲=====================================
if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) == SET)) 
{
	__HAL_UART_CLEAR_IDLEFLAG(&huart1);// 清除IDLE标志位
	HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止干扰
	uint8_t temp=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//未传输数据的大小
	rcvLen = BUF_SIZE - temp; //计算数据长度  
	HAL_UART_Transmit_DMA(&huart1, rcvBuf, rcvLen);//串口发送数据
	HAL_UART_Receive_DMA(&huart1, rcvBuf, BUF_SIZE);//开启DMA,等待下一次接收
}
  /* USER CODE END USART1_IRQn 1 */
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

你可能感兴趣的:(STM32,stm32,单片机,嵌入式硬件)