高波特率下C语言串口read()丢字节问题排查求助
问题原因分析
1. 读取循环的错误退出逻辑
你在打开串口时使用了O_NDELAY标志,使串口处于非阻塞模式。此时read()返回0并不代表数据传输结束,仅说明当前内核缓冲区中无可用数据。你的代码在遇到read_num == 0时直接退出读取循环,导致后续延迟到达的字节被丢弃——高波特率下数据常分多批次到达,前几次读取后缓冲区暂时为空,就被误判为读取完成,实际仍有字节在传输途中。
2. 缺少发送完成的同步机制
write()返回成功仅表示数据已写入内核的串口发送缓冲区,不代表数据已全部通过硬件发送完毕。极高波特率下,发送缓冲区的数据可能还在持续传输,而读取端未等待足够时间就终止读取,导致部分字节未到达接收端就被停止。
3. 串口缓冲区大小限制(潜在因素)
默认的串口输入缓冲区可能无法应对高波特率下的突发数据流量,当数据到达速度超过读取速度时,缓冲区溢出会直接导致丢字节。
解决方法
1. 修复读取循环逻辑,添加超时机制
使用select()等待串口数据并设置合理超时,避免因单次read()返回0提前退出。修改后的读取循环示例:
int read_total = 0; struct timeval timeout; fd_set read_fds; while (read_total < BUFFER_SIZE) { FD_ZERO(&read_fds); FD_SET(serial_fd2, &read_fds); // 设置100ms超时,可根据波特率调整 timeout.tv_sec = 0; timeout.tv_usec = 100000; int select_ret = select(serial_fd2 + 1, &read_fds, NULL, NULL, &timeout); if (select_ret == -1) { perror("select error"); continue; } else if (select_ret == 0) { fprintf(stderr, "Read timeout, total read: %d\n", read_total); break; } int read_num = read(serial_fd2, serial_buffer + read_total, BUFFER_SIZE - read_total); if (read_num == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { perror("read error"); } continue; } else if (read_num > 0) { read_total += read_num; printf("read num = %d | read total = %d \n", read_num, read_total); } }
2. 写入后等待数据发送完成
在write()循环结束后,调用tcdrain(serial_fd)确保内核发送缓冲区的数据全部通过硬件发送,避免读取端提前开始读取:
// 原write循环完成后添加 if (tcdrain(serial_fd) == -1) { perror("tcdrain error"); }
3. 增大串口输入缓冲区
通过ioctl调整串口输入缓冲区大小,降低高波特率下的溢出风险:
struct serial_struct ser_info; if (ioctl(serial_fd2, TIOCGSERIAL, &ser_info) == 0) { ser_info.xmit_fifo_size = 4096; // 设置为4096字节,可根据硬件调整 if (ioctl(serial_fd2, TIOCSSERIAL, &ser_info) == -1) { perror("Failed to set serial buffer size"); } }
4. 调整串口阻塞模式(可选)
若无需非阻塞IO,可去掉O_NDELAY标志,让串口处于阻塞模式,此时read()会一直等待直到有数据可读,简化逻辑,但需注意添加超时机制避免无限等待。
内容的提问来源于stack exchange,提问作者callumQ
相关产品推荐
相关产品推荐

