在嵌入式开发中,通过串口接收不定长数据是一个常见需求。使用HAL_UART_Receive_IT()结合'\n'等特定结束符来实现不定长字符串接收是一种高效且常用的方法。这种方式通过中断逐字节接收,直到检测到换行符,既不会阻塞CPU,又能明确判断数据结束。

本文章项目使用STM32HAL库开发。通过CubeMX配置USART2串口

实现原理说明

  1. 初始化阶段:调用UART_Start_Receive()启动第一次中断接收,每次只接收1个字节
  2. 中断回调机制:每收到1个字节就触发HAL_UART_RxCpltCallback()
  3. 结束判断逻辑:在回调中检查当前字节是否为'\n',如果是则标记接收完成
  4. 缓冲区管理:使用RxIndex变量跟踪接收位置,避免数据覆盖
  5. 溢出保护措施:当缓冲区即将满时强制结束接收,防止内存溢出

优势特点

  • 非阻塞运行:接收过程不占用CPU时间,主循环可以同时处理其他任务
  • 明确结束标识:通过'\n'精确判断数据帧结束,比空闲中断更可靠
  • 内存安全保障:包含缓冲区溢出保护机制,避免数组越界
  • 实现简洁高效:无需配置DMA,仅通过HAL库中断函数即可实现功能

使用注意事项

  1. 缓冲区大小RxBuffer应根据实际最大数据长度合理设置
  2. 如果需要兼容\r\n(Windows换行格式),可在判断中增加RxBuffer[RxIndex] == '\r'的处理
  3. 中断回调函数中应避免复杂操作,保持轻量化以确保实时性
  4. 处理完数据后必须重新启动接收(调用HAL_UART_Receive_IT
  5. 全局变量需要在头文件中声明,便于多文件访问

代码实现

首先需要在适当的位置定义全局变量(通常在usart.c中):

/* 全局变量定义 */
uint8_t RxBuffer[100];    // 接收缓冲区,可根据需求调整大小
uint8_t RxIndex = 0;      // 接收索引
uint8_t RxComplete = 0;   // 接收完成标志

然后是串口相关函数实现(usart.c):

/* USER CODE BEGIN 1 */
// 重定向printf函数到串口
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
    return ch;
}

// 启动UART接收
void UART_Start_Receive() {
  // 启动第一次中断接收(1个字节)
  HAL_UART_Receive_IT(&huart2, &RxBuffer[RxIndex], 1);
}

// 处理接收到的完整数据
void UART_Receive_data(){
  if (RxComplete) {
    // 处理接收到的完整数据
    printf("Received: %s", RxBuffer);
    
    // 重置缓冲区和标志,准备下一次接收
    memset(RxBuffer, 0, sizeof(RxBuffer));
    RxIndex = 0;
    RxComplete = 0;
    
    // 重新启动接收
    HAL_UART_Receive_IT(&huart2, &RxBuffer[RxIndex], 1);
  }
}

// UART接收完成回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
  if (huart == &huart2) {  // 确认是目标UART
    // 检查当前接收的字节是否为结束符'\n'
    if (RxBuffer[RxIndex] == '\n') {
      // 添加字符串结束标志
      RxBuffer[RxIndex + 1] = '\0';
      RxComplete = 1;  // 标记接收完成
    } else {
      // 不是结束符,继续接收下一个字节
      RxIndex++;
      
      // 检查缓冲区是否即将溢出(预留1个位置给结束符)
      if (RxIndex >= sizeof(RxBuffer) - 1) {
        // 缓冲区已满,强制结束
        RxBuffer[RxIndex] = '\0';
        RxComplete = 1;
      } else {
        // 继续接收下一个字节
        HAL_UART_Receive_IT(&huart2, &RxBuffer[RxIndex], 1);
      }
    }
  }
}
/* USER CODE END 1 */

主函数中的初始化与调用(main.c):

/* USER CODE BEGIN 2 */
printf("Hello World!\r\n");
UART_Start_Receive();  // 启动串口接收
/* USER CODE END 2 */

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

  /* USER CODE BEGIN 3 */
  UART_Receive_data();  // 在主循环中检查并处理接收完成的数据
  // 可以添加其他任务代码
}
/* USER CODE END 3 */

代码解析

  1. fputc重定向:实现了printf函数输出到串口,方便调试信息输出
  2. UART_Start_Receive:初始化并启动第一次中断接收
  3. UART_Receive_data:在主循环中轮询检查接收完成标志,处理完整数据帧
  4. HAL_UART_RxCpltCallback:中断回调函数,每接收一个字节触发一次
    • 检查是否为结束符’\n’
    • 管理接收索引,防止缓冲区溢出
    • 控制是否继续接收下一个字节

这种实现方式既高效又可靠,适合大多数需要通过串口接收不定长字符串的嵌入式应用场景。在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐