STM32:串口--DMA

目录

一.DMA

二.CubeMX

三.普通DMA定长接收发送函数

四.DMA+空闲不定长函数(用来接收不定长数据)


一.DMA

通过之前的学习,CPU既要在中断搬运数据,也要处理正常任务的代码。今天来介绍DMA,DMA不仅能更省CPU,同时接收不定长数据 。DMA(Direct Memory Access:直接内存访问)小助手主要作用是可以在寄存器与内存间搬运数据。DMA会在源地址与目标地址之间进行搬运,等全部搬运完,再通过中断提醒我们。

我们需要在串口的接收和发送创建两条DMA通道,就可以让DMA在串口的寄存器与内存变量间搬运数据了

二.CubeMX

1.为USART2分别创建发送与接收的DMA通道

三.普通DMA定长接收发送函数

1.普通DMA发送函数:HAL_UART_Transmit_DMA(huart, pData, Size)

huart:发送的串口指针,这里使用&huart2

pData:发送数据的指针,这里使用receiveData

Size:发送数据的长度:这里使用2

回调函数:

当接收数据半满时,调用回调函数:HAL_UART_RxHalfCpltCallback(huart)

当接收数据全满时,调用回调函数HAL_UART_RxCpltCallback(huart)(与中断的回调函数相同)
2.普通DMA接收函数:HAL_UART_Receive_DMA(huart, pData, Size)

huart:接收的串口指针,这里使用&huart2

pData:接收数据的指针,这里使用receiveData

Size:接收数据的长度:这里使用2

回调函数:

当发送数据达到一半时,调用回调函数:HAL_UART_TxHalfCpltCallback(huart)

当发送数据全发完时,调用回调函数:HAL_UART_TxCpltCallback(huart) (与中断的回调函数相同)

cpp 复制代码
 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	  HAL_UART_Transmit_DMA(&huart2,receiveData,2);
	  //HAL_MAX_DELAY:不设超时时间,直至结束
	  GPIO_PinState state = GPIO_PIN_SET;//初始化默认高电平
	  //判断单片机接收的是0还是1,设置高低电平
	  if(receiveData[1] == '0')
	  {
		  state = GPIO_PIN_RESET;
	  }
	  //判断单片机接收的是R还是G还是B,输出对应的颜色
	  if(receiveData[0] == 'R'){
		  HAL_GPIO_WritePin(LED_RED_GPIO_Port,LED_RED_Pin,state);
	  }else if(receiveData[0] == 'G')
	  {
		  HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin,state);
	  }else if(receiveData[0] == 'B')
	  {
		  HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_BLUE_Pin,state);
	  }
	  HAL_UART_Receive_DMA(&huart2,receiveData,2);
}

注意在main函数里开始的串口接收函数也要将最后的IT改为DMA

RxCpltCallback函数还是由中断触发,只不过不是串口接收中断,而是DMA的接收完成中断

四.DMA+空闲不定长函数(用来接收不定长数据)

HAL_UARTEx_ReceiveToIdle_DMA(huart, pData, Size);

huart:发送的串口指针,这里使用&huart2

pData:发送数据的指针,这里使用receiveData

Size:一次能接收的最大数据长度(一般为接收数组的长度),这里使用sizeof(receiveData)

回调函数:void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)

此回调函数靠串口空闲(Idle)中断,触发条件与接收的字节数无关,只有当RX引脚上无后续数据进入,即串口接收从忙碌转为空闲才会触发。当空闲中断触发时,一帧数据包接收完成,然后再对数据进行分析处理即可。

(1)接收数组大小改为50

(2)将之前的普通定长DMA空闲函数更换为DMA空闲不定长函数:

(3)找回调函数:现在我们不使用之前的RXCpltCallback函数

在打开的文件中找到void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)函数

放进main.c文件中,并写下程序:

注意:使用中断函数一个关键点是确认谁触发了回调函数

cpp 复制代码
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
 {
	 //判断进入此回调函数是否是huart2
	 if(huart == &huart2)
	 {
		 HAL_UART_Transmit_DMA(&huart2, receiveData, Size);
		 //这里函数第三个参数填写RxEventCallback函数的Size入参,来发送与接收相同的字节数
		 HAL_UARTEx_ReceiveToIdle_DMA(&huart2,receiveData,sizeof(receiveData));
	 }
 }

(4)关闭DMA传输过半中断

使用DMA模式,除了串口空闲中断,DMA传输过半中断也会触发RxEventCallback回调函数。因此我们需要关闭DMA传输过半中断

__HAL_DMA_DISABLE_IT()

第一个参数为DMA通道的指针地址,也就是USART2的Rx DMA通道

第二个参数为DMA传输过半中断,即DMA_IT_HT(Half Transfer 过半传输)

因此该函数为**__HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT)**。当不需要DMA传输过半中断时,将该函数放在 HAL_UARTEx_ReceiveToIdle_DMA后面。

更改后代码如下:

cpp 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2026 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 */
#include<string.h>
/* 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 huart2;
DMA_HandleTypeDef hdma_usart2_rx;
DMA_HandleTypeDef hdma_usart2_tx;

/* USER CODE BEGIN PV */
uint8_t receiveData[50];
/* USER CODE END PV */

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

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	  HAL_UART_Transmit_DMA(&huart2,receiveData,2);
	  //HAL_MAX_DELAY:不设超时时间,直至结束
	  GPIO_PinState state = GPIO_PIN_SET;//初始化默认高电平
	  //判断单片机接收的是0还是1,设置高低电平
	  if(receiveData[1] == '0')
	  {
		  state = GPIO_PIN_RESET;
	  }
	  //判断单片机接收的是R还是G还是B,输出对应的颜色
	  if(receiveData[0] == 'R'){
		  HAL_GPIO_WritePin(LED_RED_GPIO_Port,LED_RED_Pin,state);
	  }else if(receiveData[0] == 'G')
	  {
		  HAL_GPIO_WritePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin,state);
	  }else if(receiveData[0] == 'B')
	  {
		  HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_BLUE_Pin,state);
	  }
	  HAL_UART_Receive_DMA(&huart2,receiveData,2);
}
 void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
 {
	 //判断进入此回调函数是否是huart2
	 if(huart == &huart2)
	 {
		 HAL_UART_Transmit_DMA(&huart2, receiveData, Size);
		 //这里函数第三个参数填写RxEventCallback函数的Size入参,来发送与接收相同的字节数
		 HAL_UARTEx_ReceiveToIdle_DMA(&huart2,receiveData,sizeof(receiveData));
		 //关闭DMA传输过半终端:
		 __HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);//DMA通道的指针地址;关闭的中断:DMA传输过半中断
	 }
 }

//void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
//{
//	if(huart == &huart2)
//	{
//		HAL_UART_Transmit_DMA(&huart2,receiveData,Size);
//
//		HAL_UARTEx_ReceiveToIdle_DMA(&huart2,receiveData,sizeof(receiveData));
//		__HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
//	}
//}

/* 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_USART2_UART_Init();
  /* USER CODE BEGIN 2 */

	  HAL_UARTEx_ReceiveToIdle_DMA(&huart2, receiveData, sizeof(receiveData));
	  __HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
  /* 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_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  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_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

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

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

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

  /* DMA interrupt init */
  /* DMA1_Channel6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
  /* DMA1_Channel7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* USER CODE BEGIN MX_GPIO_Init_1 */

  /* USER CODE END MX_GPIO_Init_1 */

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

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, LED_BLUE_Pin|LED_GREEN_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED_RED_GPIO_Port, LED_RED_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : LED_BLUE_Pin LED_GREEN_Pin */
  GPIO_InitStruct.Pin = LED_BLUE_Pin|LED_GREEN_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : LED_RED_Pin */
  GPIO_InitStruct.Pin = LED_RED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED_RED_GPIO_Port, &GPIO_InitStruct);

  /* USER CODE BEGIN MX_GPIO_Init_2 */

  /* USER CODE END MX_GPIO_Init_2 */
}

/* 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 小时前
嵌入式测试学习第 28 天:网络调试助手使用、TCP服务端客户端实操
单片机·嵌入式硬件·学习
不脱发的程序猿14 小时前
AI Coding时上下文不够用咋办?
单片机·嵌入式硬件·嵌入式
jghhh0115 小时前
STM32 在线升级 IAP(远程固件升级)方案
stm32
blevoice15 小时前
杰理工程师日志3:杰理芯片AC6966B开发蓝牙智能音响时,关于自己设置各种提示音常见问题
单片机·jl杰理蓝牙音频芯片·ac6966b蓝牙音响方案·杰理智能音箱开发·杰理蓝牙mp3芯片·杰理ac6965e蓝牙播放器·杰理蓝牙mcu芯片
bbaydnog16 小时前
嵌入式面试高频题第5弹:DMA原理、看门狗机制、低功耗模式,这3个搞不懂简历直接被筛
面试·dma·嵌入式
leoFY12316 小时前
SGM3209(圣邦微 高压负压电荷泵)(与TP7660可只修改4脚,7脚即可替换)
单片机·嵌入式硬件
zlinear数据采集卡16 小时前
基准电压电路深度解析:从理论参数到ZLinear采集卡的精准参考实战
c语言·单片机·嵌入式硬件·fpga开发·自动化
bbaydnog16 小时前
FreeRTOS学习笔记 18:调试方法论——HardFault排查、栈溢出检测、运行时统计,RTOS调试三板斧
笔记·单片机·freertos
下午写HelloWorld16 小时前
GD32F4系列微控制器上电启动流程
单片机·嵌入式硬件