STM32G431 CubeMx HAL库——使用IDLE+RXNE实现串口不定长数据接收
Posted FILWY_M
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的配置
- 配置串口的波特率等
- 打开串口的总中断
- 生成工程
三、程序设计
- 按照如下格式添加代码到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 */
- 按照如下格式添加代码到main.h的相应位置。
/* USER CODE BEGIN Private defines */
void USER_UART_IDLECallback(UART_HandleTypeDef *huart);
/* USER CODE END Private defines */
- 按照如下格式添加代码到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处理,所以我们还需要手动清除中断标志。
四、程序说明
- 首先,串口初始化完成之后,需要开启相关的中断
下面程序一个是开启串口的闲时中断IDLE,使能接收中断,并指定接收到的数据存放到Rx_Temp中。
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
HAL_UART_Receive_IT(&huart1,(uint8_t*)&Rx_Temp,1);
- 串口闲时中断需要注意
前面已经说了,IDLE中断,需要由软件序列清除该位,清除的方法也说明白了,就是先读USART_SR,再读USART_DR
也就是代码中所体现的:
USART1->RDR; // 必须要读取数据寄存器中的数据,否则不能清除标志
UART1_IDLE_Flag = 1;
__HAL_UART_CLEAR_IDLEFLAG(huart); // 一定要清楚中断标志
- 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;
}
以上是关于STM32G431 CubeMx HAL库——使用IDLE+RXNE实现串口不定长数据接收的主要内容,如果未能解决你的问题,请参考以下文章