Linux下UART阻塞read()异常:读取自发送数据或垃圾字符求助
搞定串口阻塞读取异常与垃圾字符问题
兄弟,你的串口代码有几个关键问题导致了现在的异常,咱们一步步拆解修复:
核心问题分析
tcflush清空了关键数据:你在uart_read里调用tcflush(fd, TCIOFLUSH),这会直接清空所有未读取的输入数据和待发的输出数据——相当于你发完握手信息后,还没等接收就把数据全清了,自然读不到正确内容,甚至读超时后拿到垃圾值。- VTIME配置让阻塞变成了"限时等待":你设置了
port_settings.c_cc[VTIME] = 10,这个参数的单位是0.1秒,也就是read会等1秒,哪怕没收到足够数据也会返回,这不是你要的完全阻塞。 - 无视read返回的实际字节数:你不管
read到底读了多少字节,直接用buffer_size构造字符串,这会把buf里没被覆盖的垃圾内存也塞进字符串里,导致乱码。 - main函数调用参数缺失:你的
uart_read需要传buffer_size,但main里只传了指针,这会导致buffer_size是随机垃圾值,轻则读错长度,重则内存越界。
一步步修复代码
1. 调整串口配置,实现真正的阻塞读取
在configure_port函数里,把c_cc的VMIN和VTIME改成完全阻塞模式:
// 完全阻塞:直到收到至少1个字节才返回(适合不定长数据) port_settings.c_cc[VMIN] = 1; // 最小读取1字节 port_settings.c_cc[VTIME] = 0; // 无超时,一直等数据
如果你的场景是要读固定长度的数据(比如必须读满N字节才返回),可以把VMIN设为对应长度,VTIME保持0即可。
2. 重构uart_read函数,去掉多余操作并正确处理读取结果
删掉没用的usleep和tcflush,并且用read返回的实际字节数构造字符串:
int UART::uart_read(string *data, int buffer_size) { char * buf = new char[buffer_size + 1]; // 多留一位存结束符 // 真正的阻塞读取 int n = read(fd, buf, buffer_size); // 错误处理 if (n < 0) { cout << "Error reading: " << strerror(errno) << endl; delete[] buf; return 0; } // 给字符串加结束符,用实际读取的字节数构造有效字符串 buf[n] = '\0'; string data_received(buf, n); *data = data_received; delete[] buf; cout << "data_received: " << *data << endl; return n > 0 ? 1 : 0; }
3. 修正main函数的调用参数
调用uart_read时必须传入合理的buffer_size,比如32字节:
int main() { UART uart_connection; string handshake = "handshake!"; uart_connection.uart_write(handshake); string data; // 传入缓冲区大小,根据你的需求调整 uart_connection.uart_read(&data, 32); cout << "Final received data: " << data << endl; }
4. 优化open_port的参数(可选但更清晰)
你现在open时用了O_NDELAY然后又用fcntl去掉,不如直接去掉O_NDELAY,代码更直观:
fd = open("/dev/ttyS4", O_RDWR | O_NOCTTY);
额外硬件&场景提示
- 如果RX没接设备还读到垃圾字符,大概率是RX引脚悬空导致的电平噪声,硬件上可以给RX加个上拉电阻,或者软件里开启奇偶校验来过滤噪声。
- 发送大段数据时,建议做循环读取(比如每次读一部分,直到收完预期长度或遇到协议结束符),避免单次read截断数据。
内容的提问来源于stack exchange,提问作者Luis Cruz
相关产品推荐
相关产品推荐

