STM32G431 CubeMx HAL库——使用IDLE+RXNE实现串口不定长数据接收

STM32G431 CubeMx HAL库——使用IDLE+RXNE实现串口不定长数据接收

一、什么是IDLE?

IDLE:闲置的,空闲的。说直白点就是现在有空了,没事情干。假设我们现在发送一串字符数据到串口,那么串口从接收到第一个字符开始,就相当于现在串口不是空闲的,有事情干,当这一串字符最后一个字节发送完的时候,那么串口的事情就干完了,空闲了,然后就会引发串口的空闲中断标志位置位, 不过并不是数据已发送完马上就置位,而是在最后一个数据发送完,在下一个帧的时间里,如果没有接收到数据,这个时候空闲中断的标志位才被置位。

二、如何通过IDLE实现串口的不定长数据接收?

看到网上基本上都使用的是IDLE+DMA的方式,在这里我使用得是IDLE+RXNE两个中断来实现得,这样得话也可以省去DMA的配置了。

我们知道RXNE中断就是串口的接收中断, 可以使用开辟一个字符串RxBuffer用于数据的接收,和一个计数器RxCounter用于标记位置,每一次进入串口接收中断的时候,将当前串口接收到的数据放入RxBuffer[RxCounter]中,然后RxCounter再加1。

当串口的闲时中断IDLE触发的时候,表示当前的数据已经发送完毕,那么在串口的显示中断中,就可以提取出来当前接收到的数据,然后使用memset将RxBuffer清空,RxCounter设置为0,以接收后面的数据。

这里必须要注意一下IDLE, 这里我们看手册中IDLE标志位的解释,如下图。这里说的很明白,产生IDLE中断,需要由软件序列清除该位,清除的方法也说明白了,就是先读USART_SR,再读USART_DR。一定要注意,否则中断没有被清除,一直会进入中断, 就导致CPU会被阻塞。
在这里插入图片描述

二、CubeMx的配置

  1. 配置串口的波特率等
    在这里插入图片描述
  2. 打开串口的总中断
    在这里插入图片描述
  3. 生成工程

三、程序设计

  1. 按照如下格式添加代码到main.c的相应位置。
/* USER CODE BEGIN Includes */
#include "sdtio.h"			// 用到了printf函数
#include "string.h"			// 用到了memset函数
/* USER CODE END Includes */

/* USER CODE BEGIN PV */uint8_t RxBuffer[20];
__IO uint8_t RxCounter = 0;
uint8_t Rx_Temp;
/* USER CODE END PV */

/* USER CODE BEGIN PFP */
void USER_UART_IDLECallback(UART_HandleTypeDef *huart);
/* USER CODE END PFP */

/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
HAL_UART_Receive_IT(&huart1,(uint8_t*)&Rx_Temp,1);
printf("HELLO WORLD!!\r\n");
/* USER CODE END 2 */

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    
    
	if(huart->Instance == USART1)
	{
    
    
		/* Read one byte from the receive data register */
    RxBuffer[RxCounter++] = Rx_Temp;
//		HAL_UART_Transmit(&huart1, (uint8_t*) &Rx_Temp,1,0xFF);
		HAL_UART_Receive_IT(&huart1,(uint8_t*)&Rx_Temp,1);				// 记得这里一定要重新使能串口接收中断
	}
}

void USER_UART_IDLECallback(UART_HandleTypeDef *huart)
{
    
    
	if(huart->Instance == USART1)
	{
    
    
		if(RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
		{
    
    
			USART1->RDR;													// 必须要读取数据寄存器中的数据,否则不能清除标志
			printf("%s\r\n",RxBuffer); // 将接收到的数据发送回去
			__HAL_UART_CLEAR_IDLEFLAG(huart);			// 一定要清楚中断标志
		}
	}
}

/**
* @brief  Retargets the C library printf function to the USART.
* @param  None
* @retval None
*/
int fputc(int ch, FILE *f)
{
    
    
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART */
	HAL_UART_Transmit(&huart1, (uint8_t*) &ch,1,0xFF);
	
  return ch;
}

/* USER CODE END 4 */
  1. 按照如下格式添加代码到main.h的相应位置。
/* USER CODE BEGIN Private defines */

void USER_UART_IDLECallback(UART_HandleTypeDef *huart);
/* USER CODE END Private defines */
  1. 按照如下格式添加代码到stm32g4xx_it.c的相应位置。
/* USER CODE BEGIN USART1_IRQn 1 */
	USER_UART_IDLECallback(&huart1);
  /* USER CODE END USART1_IRQn 1 */

四、串口的中断回调函数说明

我们这里分析一下进入串口中断的流程。
假设我们接收到了一个字符,会产生一个接收中断,首先会进入串口的总中断服务函数USART1_IRQHandler,然后在这个中断服务函数里面首先调用了函数HAL_UART_IRQHandler(&huart1);有兴趣的可以F12打开这个函数看看,里面主要是判断当前中断的触发源,以及清除当前的中断的标志位。比如现在是接收中断被触发了,那么又在HAL_UART_IRQHandler函数里面调用中断回调函数,这里的中断回调函数就很重要了,这个是我们需要自己实现的,在这里面我们只需要判断是哪一个串口被触发了,清除标志位这些不需要我们自己写,因为刚刚讲了,在上一个函数里面已经帮我们清除了。
我这里把接收中断的回调函数卸载main.c文件里面的,。

void USER_UART_IDLECallback(UART_HandleTypeDef *huart)
{
    
    
	if(huart->Instance == USART1)
	{
    
    
		if(RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
		{
    
    
			USART1->RDR;													// 必须要读取数据寄存器中的数据,否则不能清除标志
			printf("%s\r\n",RxBuffer); // 将接收到的数据发送回去
			__HAL_UART_CLEAR_IDLEFLAG(huart);			// 一定要清楚中断标志
		}
	}
}

然后我们还需要特别注意一下串口的闲时中断,这个不像串口的接收中断一样,系统已经给我们定义好了中断的回调函数,而串口的闲时中断并没有,可以去HAL_UART_IRQHandler里面找一下,是找不到IDLE的,所以需要我们自己实现一个用户的串口闲时中断回调函数,所以在上面的程序中添加了USER_UART_IDLECallback(&huart1),由于串口的显示中断并没有在HAL_UART_IRQHandler处理,所以我们还需要手动清除中断标志。

四、程序说明

  1. 首先,串口初始化完成之后,需要开启相关的中断
    下面程序一个是开启串口的闲时中断IDLE,使能接收中断,并指定接收到的数据存放到Rx_Temp中。
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
HAL_UART_Receive_IT(&huart1,(uint8_t*)&Rx_Temp,1);
  1. 串口闲时中断需要注意
    前面已经说了,IDLE中断,需要由软件序列清除该位,清除的方法也说明白了,就是先读USART_SR,再读USART_DR
    也就是代码中所体现的:
USART1->RDR;													// 必须要读取数据寄存器中的数据,否则不能清除标志
UART1_IDLE_Flag = 1;
__HAL_UART_CLEAR_IDLEFLAG(huart);			// 一定要清楚中断标志
  1. printf的重定向
    首先我们要包含printf的头文件#include “stdio.h”,然后添加如下代码,重定向printf
int fputc(int ch, FILE *f)
{
    
    
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART */
	HAL_UART_Transmit(&huart1, (uint8_t*) &ch,1,0xFF);
	
  return ch;
}

猜你喜欢

转载自blog.csdn.net/qq_43715171/article/details/117132151