Thingy91 UART缓冲区管理问题:首次收数据正常后续返回垃圾值
问题根本原因
- 未处理
uart_fifo_read返回值:该函数返回实际从UART FIFO中读取到的字节数,你每次直接按缓冲区总大小100字节读取,而串口中断触发时FIFO中通常只有几字节有效数据,未被覆盖的缓冲区区域仍残留旧数据或初始随机值。 - 缺少字符串终止符:C语言字符串要求末尾必须以
\0结尾,你所用的传感器发送的原始字符串不会自带\0。首次接收时全局缓冲区默认初始化为全0,因此首次打印正常;后续接收时旧数据未清空,也没有主动补终止符,printk("%s")会一直读到内存中随机出现的\0才停止,就会输出垃圾值。 - 无拼包处理:1200波特率传输速率极低,传感器发送的较长字符串会分多次中断触发才能完成接收,你每次中断都直接覆盖整个缓冲区,会导致不完整的帧数据混杂,进一步产生乱码。
正确UART缓冲区管理流程
- 分配带写入偏移标记的接收缓冲区(简单场景用带偏移的线性缓冲区即可,高并发场景建议用环形缓冲区),记录当前已写入的有效字节数,禁止每次直接覆盖整个缓冲区。
- 中断回调中先获取FIFO中可读的字节数,仅读取实际存在的有效数据,逐字节追加到自有接收缓冲区中,同时累加写入偏移。
- 每次追加数据后判断是否收到约定的帧结束标记(通常是传感器字符串末尾的
\r、\n,或自定义的结束字符),收到结束标记后主动在缓冲区末尾补\0,再交给上层逻辑解析,最后重置写入偏移等待下一帧。 - 增加缓冲区溢出保护:写入偏移即将超过缓冲区总大小时,主动重置缓冲区或丢弃旧数据,避免内存越界。
修复参考代码
#include <string.h> uint8_t message_buf[100]; // 记录当前缓冲区已写入的字节偏移 uint8_t buf_offset = 0; void uart_cb(struct device *x) { uart_irq_update(x); int read_bytes; uint8_t tmp_byte; if (uart_irq_rx_ready(x)) { // 逐字节读,避免一次读太多覆盖未处理的数据 while((read_bytes = uart_fifo_read(x, &tmp_byte, 1)) > 0) { // 溢出保护,留1个位置放终止符 if(buf_offset >= sizeof(message_buf) - 1) { buf_offset = 0; memset(message_buf, 0, sizeof(message_buf)); } // 假设传感器字符串以换行符\n为结束标记,可根据实际协议修改 if(tmp_byte == '\n') { message_buf[buf_offset] = '\0'; // 完整帧接收完成,这里可以调用解析逻辑或者打印 printk("%s", message_buf); // 重置缓冲区偏移 buf_offset = 0; memset(message_buf, 0, sizeof(message_buf)); } else { message_buf[buf_offset++] = tmp_byte; } } } } void main(){ struct device *uart = device_get_binding(UART_PORT); // 初始化缓冲区 memset(message_buf, 0, sizeof(message_buf)); buf_offset = 0; // 原有初始化逻辑不变 uart_irq_callback_set(uart, uart_cb); // ...其余逻辑 }
内容的提问来源于stack exchange,提问作者Adeel Ahmed
相关产品推荐
相关产品推荐

