STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)

文章目录

  • 1. 所用硬件
  • 2. 生成工程
    • 2.1. 创建工程选择主控
    • 2.2. 系统配置
    • 2.3. 配置工程目录
    • 2.4. 配置用到的外设
  • 3. 查询模式
    • 3.1. 配置串口
    • 3.2. 生成代码
    • 3.3. 编写代码
    • 3.4. 效果验证
  • 4. 中断模式
    • 4.1. 配置串口
    • 4.2. 生成代码
    • 4.3. 运行原理及代码分析
    • 4.4. 效果验证
  • 5. DMA模式
    • 5.1. 配置串口
    • 5.2. 生成代码
    • 5.3. 编写代码
    • 5.4. 效果验证
  • 6. 串口重定向
    • 6.1. 配置原理
    • 6.2. 实现
      • 6.2.1. 使用MicroLIB库
      • 6.2.2. 不使用MicroLIB库
  • 7. 进阶——接收不定长数据
    • 7.1. 串口中断函数、回调函数的补充明⭐⭐⭐
    • 7.1. DMA+空闲中断
    • 7.2. 中断+环形缓冲区

====>>> 文章汇总(有代码汇总) <<<====

1. 所用硬件

正点原子Mini板,主控 STM32F103RCT6.

用到的外设:LED、串口1(PA9、PA10)。原理图:
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第1张图片

2. 生成工程

2.1. 创建工程选择主控

STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第2张图片

2.2. 系统配置

配置时钟源
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第3张图片

配置debug模式(如果需要ST-Link下载及调试可以勾选)
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第4张图片

配置时钟树(可以直接在HCLK那里输入72,然后敲回车会自动配置)
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第5张图片

2.3. 配置工程目录

STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第6张图片

勾选上会单独生成 c文件和头文件
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第7张图片

2.4. 配置用到的外设

PA8:LED0。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第8张图片

3. 查询模式

3.1. 配置串口

STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第9张图片

3.2. 生成代码

STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第10张图片

3.3. 编写代码

编写main.c代码

int main(void)
{
  /* USER CODE BEGIN 1 */
	char light_on[11] = "Light on \r\n";
	char light_off[12] = "Light off \r\n";
	char rec_buf[1] = {0};
  /* USER CODE END 1 */

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

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

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN WHILE */
  while (1)
  {
  	  // 串口1、接收数据的数组、接收的长度、等待时间
	  if(HAL_OK == HAL_UART_Receive(&huart1, (uint8_t *)rec_buf, 1, 0xFFFF))
	  {
			if(rec_buf[0] == 'A')
			{
				// 串口1、发送的数据、发送的长度(单位:字节)、超时时间
				HAL_UART_Transmit(&huart1, (uint8_t *)light_on, 11, 0xFFFF);
				HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);
			}
			else
			{
				// 串口1、发送的数据、发送的长度(单位:字节)、超时时间
				HAL_UART_Transmit(&huart1, (uint8_t *)light_off, 12, 0xFFFF);
				HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET);
			}
	  }
	  
	  
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

3.4. 效果验证

编译、烧录、查看结果。
效果:使用串口调试助手
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第11张图片
电脑发送一个字符A,单片机收到后 发送 Light on 给电脑,同时LED亮。
电脑发送一个字符B(其他字符),单片机收到后 发送 Light off 给电脑,同时LED灭。

4. 中断模式

4.1. 配置串口

STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第12张图片

配置中断
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第13张图片

4.2. 生成代码

STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第14张图片

4.3. 运行原理及代码分析

和分析中断的过程一样。打开工程,在stm32f1xx_it.h中可以看到函数USART1_IRQHandler,显然,这是串口中断处理函数,在中断处理函数中又调用了函数HAL_UART_IRQHandler(&huart1);

STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第15张图片

然后去看看函数HAL_UART_IRQHandler(&huart1);都干了什么事。在HAL_UART_IRQHandler(&huart1);函数中考虑了很多情况,从注释可以看出,不出错误的话,会调用函数UART_Receive_IT(huart);然后就返回了。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第16张图片

因此,我们看看函数UART_Receive_IT(huart);,在这个函数中也分了很多很多情况,我们直接看最后,最后有个函数HAL_UART_RxCpltCallback(huart);,从注释可以看出,这是个接收完成的回调函数。(学完配置以后,一定要看看7.1小节,关于这个函数的触发条件说明)
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第17张图片

然后再去看看这个回调函数。还是在这个文件中,定义如下。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第18张图片

可以看到这个函数是个弱函数,用户可以再次定义该函数。也就是说,我们可以重新定义这个函数,并在函数中编写我们处理中断的逻辑。

注释写的也很清楚:
当需要回调时,不应修改此函数,HAL_UART_RxCpltCallback可以在用户文件中实现

这段放在哪都行。大的工程可以创建一个文件放进去;这里直接放在main.c中了。

/* USER CODE BEGIN PV */
char light_on[11] = "Light on \r\n";
char light_off[12] = "Light off \r\n";
char rec_buf[1] = {0};
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	// 判断是不是串口1 中断
	if(huart->Instance == USART1)
	{
		if(rec_buf[0] == 'A')
		{
			// 串口1、发送的数据、发送的长度(单位:字节)、超时时间
			HAL_UART_Transmit(&huart1, (uint8_t *)light_on, 11, 0xFFFF);
			HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);
		}
		else
		{
			// 串口1、发送的数据、发送的长度(单位:字节)、超时时间
			HAL_UART_Transmit(&huart1, (uint8_t *)light_off, 12, 0xFFFF);
			HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET);
		}
		// 重新使能接收中断
		HAL_UART_Receive_IT(&huart1, (uint8_t *)rec_buf, 1);
	}
}
/* USER CODE END PFP */

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

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

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	// 开启串口接收中断
	HAL_UART_Receive_IT(&huart1, (uint8_t *)rec_buf, 1);
	// 发送开始提示信息
	HAL_UART_Transmit(&huart1, (uint8_t *)"start...\r\n", 10, 0xFFFF);
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

4.4. 效果验证

编译、烧录、查看结果。

效果:使用串口调试助手
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第19张图片

  1. 复位开发板,电脑收到 start…
  2. 电脑发送字符 A,单片机收到后 发送 Light on 给电脑,同时 LED 亮;
  3. 电脑发送字符 B(其他字符也可),单片机收到后 发送 Light off 给电脑,同时 LED 灭。

5. DMA模式

5.1. 配置串口

STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第20张图片

配置DMA

DMA (Direct Memory Access)直接存储器访问,可以不使用CPU,将数据在存储器与外设之间传输(也可以从存储器 到 存储器)。
STM32 最多有 2 个 DMA 控制器(DMA2 仅存在大容量产品中),STM32F103RCT6 有两个 DMA 控制器, DMA1 和 DMA2,DMA1 有 7 个通道。DMA2 有 5 个通道。

接收通道(设置为循环接收)STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第21张图片

发送通道(设置为普通模式)
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第22张图片

DMA Request:

  • USART1_TX:表示在发送数据的时候使用 DMA通道。
  • USART1_RX:表示在接收数据的时候使用 DMA通道。

Mode:

  • Normal 模式:表示 CPU 发起 DMA 传输请求,就把数据通过DMA通道传输出去,传输完成之后会触发中断;
  • Circular模式,表示会循环把数据传输出去,传输完成之后从头再来。

Increment Address:这里在内存上勾选,没勾选外设,因为外设(这里是串口)地址是固定的,只需要把数据的 起始地址传输过去,地址不断累加就可以了。

勾选中断(DMA中断优先级设置的比串口中断要低,不过其实不低也能运行)。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第23张图片

5.2. 生成代码

STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第24张图片

5.3. 编写代码

/* USER CODE BEGIN PV */
char buf[13] = "running... \r\n";
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

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

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN WHILE */
	
	  // 开启DMA接收数据通道,将串口数据存到buf数组。
	  // 这里设置的循环模式,会不断接收串口数据到内存。
	  HAL_UART_Receive_DMA(&huart1, (uint8_t *)buf, 13);
	
	// 串口其他 DMA 相关函数
//	HAL_UART_DMAResume(&huart1); 恢复串口DMA
//	HAL_UART_DMAPause(&huart1) 暂停串口DMA
//	HAL_UART_DMAStop(&huart1); 结束串口DMA

  while (1)
  {
	  // 开启DMA发送传输通道
	  // 这里设置的普通模式,发送一次就关闭了。延迟一段时间后,重新打开发送。
	  HAL_UART_Transmit_DMA(&huart1, (uint8_t *)buf, 13);
	  HAL_Delay(1000);

    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

这里有个bug。

  • MX_DMA_Init();
  • MX_USART1_UART_Init();

这两个初始化,必须DMA在前,USART在后,否则编译通过,但是运行会卡死。
如果一开始就设置了DMA和串口,一般没啥问题。但是如果一开始只设置了串口,生成代码后,又加了DMA,就容易出现 DMA在后面的情况。
另外:可以通过设置,来设置先后顺序。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第25张图片

5.4. 效果验证

编译、烧录、查看结果。

效果:使用串口调试助手
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第26张图片

  1. 初始化后,串口助手每隔一秒收到 字符串 running…
  2. 使用串口助手向开发板发送 任意字符(比如 1234567890ab),该字符会被存储到buf数组中,之后串口助手会不断收到 发送的任意字符串(如果发送的长度不足13个字符,则只会替换前几个字符,后面几个还是原来的)。
    STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第27张图片

6. 串口重定向

为了方便发送数据,我们使用printf函数发送数据,但是使用此函数需要先进行配置。

6.1. 配置原理

printf函数定义在 头文件中,printf 函数根据 format 字符串给出的格式打印输出到 std::out(标准输出)中。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第28张图片
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第29张图片
printf 函数会调用更底层的 I/O 函数:fputc去逐个字符打印。fputc 也定义于头文件 中。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第30张图片
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第31张图片
因此,我们如果想用 printf 打印数据到串口,只需要重新定义fputc函数,在fputc的函数中将数据通过串口发送,称之为:fputc重定向或者printf重定向

6.2. 实现

配置还是上面三种的配置,随便啦。

6.2.1. 使用MicroLIB库

MicroLib是对标准C库进行了高度优化之后的库,供MDK默认使用,相比之下,MicroLIB的代码更少,资源占用更少。

步骤一:在MDK中使用MicroLib重定向printf
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第32张图片
步骤二:在生成的usart.c中添加重定向代码。这里放到最后
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第33张图片

/* USER CODE BEGIN 1 */
/**********************printf重定向****************************/
// 需要调用stdio.h文件
#include 
int fputc(int ch, FILE *f)
{  
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
	return ch;
}
/* USER CODE END 1 */

main.c

  while (1)
  {
      printf("Hello, %s \r\n", "world");		// 字符串
      printf("Test int: i = %d \r\n", 100);		// 整数
      printf("Test float: i = %f \r\n", 1.234);	// 浮点数
      printf("Test hex: i = 0x%2x \r\n",100);	// 16进制
	  printf("中文测试 \r\n");					// 中文
	  HAL_Delay(2000);
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }

效果验证
编译、烧录、查看结果。

效果:使用串口调试助手
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第34张图片
每隔两秒,发送数据。

6.2.2. 不使用MicroLIB库

不用勾选 Use MicroLIB,直接在 usart.c 文件中添加以下代码。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第35张图片

// 需要调用stdio.h文件
#include 
//取消ARM的半主机工作模式
#pragma import(__use_no_semihosting)//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
}; 
FILE __stdout;       
void _sys_exit(int x) //定义_sys_exit()以避免使用半主机模式
{ 
	x = x;
} 

int fputc(int ch, FILE *f)
{  
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
	return ch;
}

main.c

  while (1)
  {
      printf("Hello, %s \r\n", "world");		// 字符串
      printf("Test int: i = %d \r\n", 100);		// 整数
      printf("Test float: i = %f \r\n", 1.234);	// 浮点数
      printf("Test hex: i = 0x%2x \r\n",100);	// 16进制
	  printf("中文测试 \r\n");					// 中文
	  HAL_Delay(2000);
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }

效果验证
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第36张图片
每隔两秒,发送数据。
PS:在其他文件中使用printf会有警告 unction “printf” declared implicitly 出现,不影响使用。如果不想看到警告,可以在调用的文件中添加#include 头文件即可。

7. 进阶——接收不定长数据

7.1. 串口中断函数、回调函数的补充明⭐⭐⭐

串口中断函数
每接收到一个字节,就会进入一次串口中断函数。如果一个数据帧发送了多个字节,就会连续进入多次中断处理函数。

比如:通过串口助手给单片机发送"0123456789"(不要勾选发送新行),则单片机会连续进入10次void USART1_IRQHandler(void)中断处理函数。

接收完成回调函数
每接收完成一次,会执行一次接收完成回调函数。

前面中断章节中,说了接收完毕后会执行接收完成回调函数void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart),但是没有具体说明,什么时候才算接收完成。

可以看下面这行代码,显然,这里是想接收10个字节,并把它放到rec_buf数组中。那么,接收够10个字节,就表示接收完成了。

// 开启串口接收中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)rec_buf, 10);

那如果,我只发送5个字节呢?那就只会进入5次中断,不会执行接收完成回调函数,当再次发送5个字节的时候,就会在执行第5次中断的时候,执行接收完成回调函数。
再如果,发送8个字符呢?那就只会进入8次中断,不会执行接收完成回调函数,当再次发送8个字节的时候,会在执行2次中断并执行回调函数,然后再次执行一次中断函数就卡死了…

示例
串口配置和中断章节的配置一样:打开串口,勾选中断即可。
main.c

char rec_buf[10] = {0};
volatile uint8_t num = 0;	 	 // 记录进入中断的次数
volatile uint8_t numback = 0;	 // 记录进入回调的次数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	// 判断是不是串口1 中断
	if(huart->Instance == USART1)
	{
		printf("已经进入了 %d 次中断函数了 \r\n", num);
		printf("第 %d 次进入回调 \r\n", ++numback);
		// 重新开启接收
		HAL_UART_Receive_IT(&huart1, (uint8_t *)rec_buf, 10);
	}
}

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();

	// 开启串口接收中断	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)rec_buf, 10);
  while (1)
  {
		printf("已经进入了 %d 次中断函数了 \r\n", num);
		HAL_Delay(1000);
  }
}

stm32f1xx_it.c

extern volatile uint8_t num;

void USART1_IRQHandler(void)
{
	num++; // 记录进入中断的次数
  HAL_UART_IRQHandler(&huart1);
}

发送“1234567890”10个字符(10个字节),可以看到,会进入10次中断,在最后一次中断时,刚好接收够10个字节,也就执行了回调函数。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第37张图片
而当发送“12345”5个字符(5个字节)时,进入了5次中断,但是并没有接收完成,如果再次发送5个字符,则再进入5次中断,再第5次中断时接收完成,执行了回调函数。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第38张图片
而如果发送“12345678”8个字符(8个字节)时,进入了8次中断,但是并没有接收完成,如果再次发送8个字符,第二次中断会执行回调函数,并再次进入中断,之后再点发送就没用了。。。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第39张图片
总之,前面的三种方案,只能处理数据长度比较固定的数据。

7.1. DMA+空闲中断

先说要一下串口的中断。主要说两个串口接收中断串口空闲中断

  • 串口接收中断(UART_IT_RXNE):一帧数据是表示一个或多个字节组成的有含义的字符串,比如前面案例中发送的一个字符串。在一帧数据发送过程中,每接收到一个字节,就会进入一次到串口中断函数。如果满足接收完成的条件,在中断函数中还会执行了发送完成的回调函数。
  • 串口空闲中断(UART_IT_IDLE):上面发送一个字符串(一帧数据)可能包含多个字节,会不断进入接收中断函数,因此发送两个字节之间的时间很短,串口不能认为是空闲的。但是整个字符串(一帧数据)发送完成之后,串口在可以发送一个字节的时间内没有再次发送数据,此时,就会发生空闲中断。

总结:

接收中断 空闲中断
处理函数 USARTx_IRQHandler() USARTx_IRQHandler()
回调函数 HAL_UART_RxCpltCallback() HAL库没有提供
USART状态寄存器中的位 UART_IT_RXNE UART_IT_IDLE
触发条件 接受到一个字节数据触发一次 接受完一帧数据又过了一个字节时间没有接收到数据

在上面的程序中,当DMA串口接收开始后,DMA通道会不断的将发送来的数据转移到内存,也就是说接收程序一直在运行,那该如何判断串口接收是否完成从而及时关闭DMA通道?如何知道接收到数据的长度,以便于接收不定长的呢?答案便是使用串口空闲中断。

解决办法

  1. 串口收到数据,DMA不断传输数据到内存。
  2. 一帧数据发送完毕,串口暂时空闲,触发串口空闲中断。
  3. 在中断服务函数中,根据空闲中断的标志位判断是否发生空闲中断,可以计算刚才收到了多少个字节的数据。
  4. 存储接收到的数据,清除标志位,开始下一帧接收。

代码(配置就是DMA模式的配置)
main.c

/* USER CODE BEGIN PV */
// 自己定义两个变量
uint8_t rx_buffer[100];   			// 接收数据的数组
volatile uint8_t rx_len = 0; 		// 接收数据的长度
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

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

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();

  /* USER CODE BEGIN WHILE */
	  //开启空闲中断
	  __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
	  // 开启DMA接收数据通道,将串口数据存到buf数组。
	  HAL_UART_Receive_DMA(&huart1, (uint8_t *)rx_buffer, 100);
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

stm32f1xx_it.c

/* USER CODE BEGIN PV */

// 定义在main.c中,在此处声明
extern uint8_t rx_buffer[100];   			// 接收数据的数组
extern volatile uint8_t rx_len; 			// 接收数据的长度

/* USER CODE END PV */

/**
  * @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状态
	uint8_t tmp_flag = __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE); 
	if((tmp_flag != RESET))	// 判断接收是否结束
	{ 
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);	// 清除空闲中断标志
		
		HAL_UART_DMAStop(&huart1); 			// 停止DMA通道
		// 查询DMA剩余传输数据个数
		uint8_t temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
		//	最大接收长度 - 剩余长度 = 已发送长度	
		rx_len = 100 - temp;
		/* === 在下面写自己的处理逻辑(注意这里是中断,尽量简短) === */ 
		
		// 发送接收到的数据
		HAL_UART_Transmit_DMA(&huart1, rx_buffer, rx_len);
		
		/* === 在上面写自己的处理逻辑(注意这里是中断,尽量简短) === */ 
		// 重新开启DMA
		HAL_UART_Receive_DMA(&huart1,rx_buffer,100);
	}

  /* USER CODE END USART1_IRQn 1 */
}

效果验证
不管给开发板发送多长的数据,都会重新发送回来。
STM32CubeMX系列04——串口(查询、中断、DMA、不定长接收、重定向)_第40张图片

7.2. 中断+环形缓冲区

代码有点长,代码链接在主页,代码有详细注释和使用说明。

你可能感兴趣的:(CubeMX,stm32,单片机,arm)