【STM32】串口不定长接收 保姆级教程

目录

为什么要使用串口不定长接收

什么是串口空闲中断

通过串口空闲中断 实现串口不定长接收

步骤:

方法二:串口空闲中断+DMA

步骤:

 效果展示

为什么要使用串口不定长接收

普通的阻塞接收函数

HAL_UART_Receive(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size,uint32_t Timeout)
huart:句柄

pData:盛放接收数据的变量

Size:接收数据的大小

    中断接收函数

    
    HAL_UART_Receive_IT(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size)
    
    huart:句柄
    
    pData:盛放接收数据的变量
    
    Size:接收数据的大小
    

              不论是阻塞接收还是中断接收都有一个共性,就是定长接收,参数Size就是期望接收数据的长度,当串口发送长度不固定的数据时,会出现吞字符的现象,使得接收信息不完整(使用定长接收方式接收不定长数据的效果如下图所示)。我们为了保证下位机接收到准确的指令,常需要串口可以自由接收不定长度的数据。一般需要用串口空闲中断来实现功能。

      什么是串口空闲中断

              串口空闲中断是指当串口接收缓冲区中没有数据时,串口控制器产生的一种中断信号。在串口通信中,当接收到一个完整的数据帧后,通常会有一个停止位,表示数据的传输结束。当接收缓冲区中没有数据时,串口控制器会检测到停止位的连续空闲状态,并产生空闲中断信号。使用串口空闲中断可以消除轮训接收缓冲区的需要,提高系统的性能和效率。

      注意:

      (1)空闲中断的允许需要手动解除中断屏蔽

      (2)空闲中断必须手动清零,不然中断会一直置位,系统会不断进入中断处理函数

      通过串口空闲中断实现不定长接收

              每次接收一个字符触发中断,接收中断保存当前接收到的字符到一个接收缓冲区,当下一个字符来临,继续将字符保存到数组的下一个元素。

             当接收完所有数据时触发空闲中断,在空闲中断中那个保存数组就是接收到的数据。然后就可以对这个数组进行判断发送等操作了。

      步骤:

      1、main.c 内添加全局变量

      uint8_t buf[1]={0};        // 用于存放单次接收的字符
      

      2、主函数中使能串口空闲中断并开启接收中断

      __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //使能串口空闲中断
      
      
      HAL_UART_Receive_IT(&huart1,buf,1); //开启接收中断,每次只接收一个字符
      

      3、stm32xxxx_it.c 内添加全局变量

      uint8_t len=0; // 接收计数器
      
      uint8_t buf_recv[256]; // 接收缓冲区,用于存储完整数据帧
      
      extern uint8_t buf[1]; // 用于存放接收的单个字符

      4、在串口中断服务函数(USART1_IRQHandler)中添加如下代码:

              当有数据到来时,将字节依次存入 buf_recv

              当数据停止传输(产生空闲中断),处理已接收的完整数据帧并重置接收状态

              记得添加头文件 #include<string.h> 

      /* USER CODE BEGIN USART1_IRQn 0 */
      
      
      if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) // 检查是否发生空闲中断
      
      {
      
      __HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_IDLEF); // 清除空闲中断标志
      
      
      /**** 在此区域处理接收到的完整数据
      
      我这里把接收到的数据回传只是为了功能测试,一般实际项目打印不会出现在中断函数(打印相对耗时)
      
      
      HAL_UART_Transmit(&huart1,buf_recv,sizeof(buf_recv),1000);
      
      
      ****/
      
      
      len=0; // 重置接收计数器
      
      memset(buf_recv,0,sizeof(buf_recv)); // 清空接收缓冲区
      
      }else // 普通接收中断处理
      
      {
      
      buf_recv[len]=USART1->RDR; // 将接收到的单个字节存入缓冲区
      
      len++; // 递增接收计数器
      
      HAL_UART_Receive_IT(&huart1,buff,1); // 重新开启接收中断,准备接收下一个字节
      
      }
      
      
      /* USER CODE END USART1_IRQn 0 */

      串口空闲中断+DMA方式(推荐)

      传统中断方式的缺点:

              每接收一个字节都会触发中断,CPU需要频繁响应,导致效率低下。在高波特率(如115200、1Mbps)下,CPU可能被大量中断占用,无法处理其他任务。

      DMA方式的优势:

              数据传输完全由DMA控制器管理,CPU仅在数据接收完成(或缓冲区满)时被通知,可以专注于其他任务(如算法处理、协议解析等)。适用于高波特率、大数据量的串口通信(如GPS模块、无线通信、高速数据采集)。

      步骤:

      1.配置STM32CubeMX

      2、在main.c定义全局变量

      uint8_t buf_ctl[256];    // 接收数据缓冲区,大小按需求自定义即可
      

      3、主函数中开启串口空闲中断,使能串口DMA接收

      __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //开启串口空闲中断
      
      
      HAL_UART_Receive_DMA(&huart1,buf_ctl,256); //接收DMA使能

      4、修改stm32xxxx_it.c里的串口中断服务函数

      添加全局变量

      extern uint8_t buf_ctl[256]; // 接收数据缓冲区
      
      uint8_t len=0; // 记录实际接收长度

      在串口中断服务函数(USART1_IRQHandler)处理串口空闲中断,停止DMA传输,计算接收长度处理完整数据,重启DMA准备接收下一帧数据。

      void USART1_IRQHandler(void)
      
      {
      
      /* USER CODE BEGIN USART1_IRQn 0 */
      
      
      if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) // 获得串口空闲中断标志
      
      {
      
      __HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_IDLEF); // 清除空闲中断标志
      
      HAL_UART_DMAStop(&huart1); // 停止DMA传输,防止数据覆盖
      
      
      /**** 在此区域处理接收到的完整数据,我这里把接收到的数据回传只是为了功能测试,一般实际项目打印不会出现在中断函数(打印相对耗时)
      
      
      len = 256 - hdma_usart1_rx.Instance->CNDTR; // 计算实际接收的数据长度
      
      
      buf_ctl[len]='\0'; // 在接收数据的末尾添加字符串结束符(适用于ASCII数据)
      
      HAL_UART_Transmit(&huart1,buf_ctl,len+1,500); // 将接收数据回传,ASCII文本发送长度len+1,二进制数据发送长度len
      
      
      ****/
      
      
      memset(buf_ctl,0,sizeof(buf_ctl)); // 清空接收缓冲区
      
      HAL_UART_Receive_DMA(&huart1, buf_ctl, 256); // 重新开启DMA,准备接收下一帧数据。
      
      
      }
      
      
      /* USER CODE END USART1_IRQn 0 */
      
      HAL_UART_IRQHandler(&huart1);
      
      /* USER CODE BEGIN USART1_IRQn 1 */
      
      
      /* USER CODE END USART1_IRQn 1 */
      
      }

       效果展示

      其他备注:

       

      建议在实际开发中参考具体型号的参考手册和HAL库头文件,以确认正确的标志位操作方式。

      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值