You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32 UART读取异常:仅能完成首次迭代读取求助

UART仅能首次读取的问题分析与修复

问题根源

  • 计数器未重置:读取完缓冲区数据后,counter没有清零,后续新接收的数据会存在缓冲区的后续位置,但uart_read始终从索引0开始读旧数据;若后续新接收的数据量不足指定size,函数直接无返回值,导致调用方无法获取有效结果。
  • 无缓冲区溢出保护:counter持续累加,会超出Rx_Buff的数组长度,引发内存越界,破坏其他内存区域的数据。
  • 无中断同步保护:counter是全局变量,中断服务函数(ISR)中写、uart_read中读的操作没有同步机制,可能出现读写竞态,导致数据错乱。
  • 函数返回值不完整:当size > counter时,uart_read没有返回任何值,属于C语言未定义行为,会导致调用方得到错误的返回结果。

修复后的代码示例

// 假设已定义缓冲区常量与全局变量
#define BUFF_SIZE 256
uint8_t Rx_Buff[BUFF_SIZE];
volatile uint16_t counter = 0; // 加volatile防止编译器优化

void USART2_IRQHandler(void) {
    if (USART_GetITStatus(USART_MODULE, USART_IT_RXNE) != RESET)
    {
        // 新增缓冲区溢出检查,避免内存越界
        if (counter < BUFF_SIZE) {
            Rx_Buff[counter++] = (uint8_t)USART_ReceiveData(USART_MODULE);
        }
        USART_ClearITPendingBit(USART_MODULE, USART_IT_RXNE);
    }
}

uint16_t uart_read(void *load, uint16_t size) {
    uint8_t *u = (uint8_t *)load;
    uint16_t read_len = 0;
    
    // 禁用中断,保护counter的读写操作,避免竞态
    __disable_irq();
    read_len = (size <= counter) ? size : counter;
    __enable_irq();
    
    if (read_len > 0) {
        // 拷贝数据到目标缓冲区
        for (uint16_t i = 0; i < read_len; i++) {
            u[i] = Rx_Buff[i];
        }
        
        // 移动剩余未读数据到缓冲区头部,重置计数器
        __disable_irq();
        for (uint16_t i = read_len; i < counter; i++) {
            Rx_Buff[i - read_len] = Rx_Buff[i];
        }
        counter -= read_len;
        __enable_irq();
    }
    
    return read_len;
}

关键修复点说明

  • 给counter添加volatile关键字,确保编译器不会优化对它的读写(因为它会在中断中被异步修改)。
  • 中断服务函数中新增缓冲区溢出检查,防止数组越界。
  • uart_read中先计算实际可读取的长度,无论size是否大于当前缓存数据量,都返回有效长度,避免未定义行为。
  • 使用__disable_irq()和__enable_irq()包裹对counter的读写、缓冲区数据移动操作,防止中断打断导致的数据错乱。
  • 读取完成后,将缓冲区剩余未读数据移动到头部并更新counter,确保后续新接收的数据能存入正确位置,且下次读取能获取新数据。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 10:15:33