You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

STM32F4 UART全数据读取方法及USART轮询接收异常求助

STM32F4 UART 读取问题:轮询方式仅拿首字符的解决办法

先帮你解决最头疼的轮询方式只能读取第一个字符的问题,再给你讲讲怎么读取UART的所有数据。

一、轮询失效的原因&修复方案

你的中断代码能正常跑,但轮询不行,主要是这几个细节没处理好:

1. 没处理UART错误标志(最关键的原因)

当UART接收数据时,如果出现溢出错误(ORE)、噪声错误(NE)或者帧错误(FE),会导致RXNE接收标志没法正常触发,后续的数据就检测不到了。中断模式下,中断触发会自动处理这些错误的影响,但轮询模式得手动清理错误标志才行。

2. 缓冲区索引的判断逻辑有问题

你代码里写的if (i > RX_BUFFERSIZE ),但数组索引是从0开始的啊!比如rxBuff长度是RX_BUFFERSIZE,最大有效索引是RX_BUFFERSIZE-1,等i等于RX_BUFFERSIZE的时候就已经越界了,得改成i >= RX_BUFFERSIZE才对。

修复后的完整轮询代码

#define RX_BUFFERSIZE 64
uint8_t rxBuff[RX_BUFFERSIZE];
uint8_t i = 0; // 一定要给i初始化0!不然会是随机值,直接乱掉

int main(void){
  // 这里别忘了加USART1、GPIO的初始化代码...
  
  while(1){
    // 先检查错误标志!必须先清错误,不然RXNE没法正常工作
    if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) == SET ||
       USART_GetFlagStatus(USART1, USART_FLAG_NE) == SET ||
       USART_GetFlagStatus(USART1, USART_FLAG_FE) == SET){
      // 读一下DR寄存器就能清除错误标志(顺便也清RXNE)
      (void)USART_ReceiveData(USART1);
    }
    
    // 检查有没有新的接收数据
    if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET ){
      rxBuff[i] = USART_ReceiveData(USART1);
      i++;
      // 修正索引判断,防止数组越界
      if (i >= RX_BUFFERSIZE ){
        i = 0;
      }
    }
  }
}

额外要注意的点

  • 一定要确保i的初始值是0,要是没初始化,它会是个随机数,可能直接触发重置逻辑,把新数据覆盖掉。
  • 波特率、奇偶校验、停止位这些配置要和HTERM完全匹配(既然中断模式正常,这部分应该没问题,不过可以再核对一遍)。

二、怎么读取STM32F4 UART的所有数据

读取UART所有数据的核心就是持续监测接收标志,根据你的应用场景选合适的方式:

1. 中断方式(推荐给实时性要求高的场景)

就是你已经写好的那种,用RXNE中断触发接收,每次中断读一个字节到缓冲区。还可以升级成带空闲中断的方式,一次性读完整帧数据:

  • 开启USART的空闲中断(USART_IT_IDLE
  • 当空闲中断触发时,说明一帧数据发完了,这时候就能一次性处理缓冲区里的所有已接收字节
  • 注意要在中断里先读USART_SR寄存器,再读DR寄存器,才能清除空闲中断标志

2. 轮询方式(适合简单场景、低波特率)

除了上面修复的基础轮询,还可以写个阻塞式读多个字符的函数,比如:

// 阻塞读取len个字符到buff里,要是需要超时可以自己加计时逻辑
void USART_ReadBlocking(USART_TypeDef* USARTx, uint8_t* buff, uint16_t len){
  uint16_t count = 0;
  while(count < len){
    // 等RXNE置位,有数据了再读
    while(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET);
    // 先清错误标志
    if(USART_GetFlagStatus(USARTx, USART_FLAG_ORE) == SET){
      (void)USART_ReceiveData(USARTx);
    }
    buff[count++] = USART_ReceiveData(USARTx);
  }
}

3. DMA方式(最高效的批量读取)

要是需要高速接收大量数据,强烈推荐DMA+UART的组合:

  • 配置DMA从USART的DR寄存器自动读数据到缓冲区
  • 开启DMA的半传输或传输完成中断,当缓冲区满一半或者全满的时候触发中断处理数据
  • 这种方式几乎不用CPU干预,适合高波特率、大数据量的场景

内容的提问来源于stack exchange,提问作者Omer Sahin

火山引擎 最新活动