STM32F091RC UART接收函数仅返回数据包最后一字节问题求助
问题分析与修复方案:STM32 UART接收仅获取最后一字节
兄弟,你这代码明显踩了几个UART接收的典型坑,导致只能拿到最后一个字节,我给你拆解下问题,再给你修复后的代码:
核心问题点
- 索引变量完全未更新,所有字节覆盖同一位置:你用
rxd[i]存储数据,但代码里既没初始化i,也没在每次接收后递增i!相当于每次读到的字节都往同一个数组下标里塞,最后自然只剩下最后一次写入的内容。 - 循环逻辑错配UART工作机制:
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)是当有一个字节到达时才会进入循环,但你在里面套了个8次的for循环——每次读USART1->RDR后,RXNE标志会自动清零,后面7次循环读的都是无效数据,还全往同一个位置写,完全是无用操作。 - 变量未声明:代码里的
i和j都没定义,编译阶段就会报错,你应该是漏写了。
修复后的轮询版本代码
先给你改个基础的轮询版本,能正确接收8字节数据包:
uint8_t rxd[10]; uint8_t rx_index = 0; // 用来跟踪当前接收的字节位置 void getChar(void) { // 检查是否有新字节到达 if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) { // 只接收前8个字节,避免缓冲区溢出 if(rx_index < 8) { rxd[rx_index++] = (0xFF & (USART1->RDR)); } // 当收满8字节时,可以触发数据包处理逻辑 if(rx_index == 8) { // 这里添加你的数据包处理代码,比如解析、转发到UART2等 // process_8byte_packet(rxd); rx_index = 0; // 重置索引,准备接收下一包 } } }
更推荐的中断接收版本
轮询方式在系统有其他任务时容易丢字节,更专业的做法是用UART接收中断,确保每个字节都不会错过:
uint8_t rxd[10]; uint8_t rx_index = 0; // USART1初始化函数(记得补充GPIO、波特率等基础配置) void USART1_Init(void) { // ... 你的GPIO、波特率初始化代码 ... // 开启RXNE接收中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 使能USART1的中断通道 NVIC_EnableIRQ(USART1_IRQn); } // USART1中断服务函数 void USART1_IRQHandler(void) { // 检查是否是RXNE中断触发 if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { if(rx_index < 8) { rxd[rx_index++] = USART_ReceiveData(USART1); } if(rx_index == 8) { // 处理接收到的8字节数据包,比如转发到UART2 // for(int k=0; k<8; k++) { // while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); // USART_SendData(USART2, rxd[k]); // } rx_index = 0; // 重置索引,准备下一包 } // 清除中断标志位 USART_ClearITPendingBit(USART1, USART_IT_RXNE); } }
为什么这样改?
- 用
rx_index跟踪接收位置,每收一个字节就递增索引,确保每个字节都存在缓冲区的不同位置,不会被覆盖。 - 每次只处理一个到达的字节,完全符合STM32 UART的RXNE标志逻辑:每收到一个字节,RXNE置位;读取RDR后,标志自动清零。
- 中断方式能让CPU在没数据时处理其他任务,有数据时立刻响应,不会因为轮询不及时丢包,更适合实际项目使用。
内容的提问来源于stack exchange,提问作者Gaurav Sharma




