/dev/ttyUSB0串口回环读取失败求助:Linux系统调用传输异常
问题描述
使用USB转TTL转换器并短接Rx与Tx引脚(回环模式),需通过Linux系统调用open()、read()、write()、close()实现从input.txt(内容为"hello")读取数据,经/dev/ttyUSB0串口传输后写入output.txt。目前已成功将input.txt的数据写入串口,但无法从serialPort_fd读取数据到缓冲区。
相关代码
#include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <termios.h> void main() { const char *device = "/dev/ttyUSB0"; // Replace with your USB TTL device name char buffer[10],buffer2[10]; int spread,spwrite,serialPort_fd,input_fd,no_of_bytes_read = 0, written_bytes = 0,output_fd; serialPort_fd = open(device,O_RDWR | O_NOCTTY | O_NDELAY); input_fd = open("input.txt",O_RDONLY); output_fd = open("output.txt",O_RDWR | O_CREAT); if(input_fd <= -1) { printf("Input file is not opened\n"); return; } struct termios options; tcgetattr(serialPort_fd, &options); cfsetispeed(&options, B115200); // Set baud rate cfsetospeed(&options, B115200); options.c_cflag |= (CLOCAL | CREAD); // Enable receiver and set local mode options.c_cflag &= ~PARENB; // No parity options.c_cflag &= ~CSTOPB; // 1 stop bit options.c_cflag &= ~CSIZE; // Mask the character size bits options.c_cflag |= CS8; // Set 8 data bits tcsetattr(serialPort_fd, TCSANOW, &options); no_of_bytes_read = read(input_fd,buffer,sizeof(buffer));// from input_fd to buffer //no_of_bytes_read = read(input_fd,buffer,sizeof(buffer)); printf("No of bytes read %d\n",no_of_bytes_read); written_bytes = write(serialPort_fd,buffer,sizeof(buffer));//from buffer to serial_fd spread = read(serialPort_fd,buffer2,sizeof(buffer2)); printf("buffer2 is %s\n",buffer2); close(serialPort_fd); close(input_fd); close(output_fd); }
问题原因及修复方案
1. 非阻塞模式导致读取时机错误
打开串口时使用了O_NDELAY标志,会将读操作设置为非阻塞模式。写入串口后立即调用read(),此时数据还未回环到接收缓冲区,read()会直接返回0或-1,无法读取到有效数据。
2. 未禁用规范模式
默认串口处于规范模式,会等待换行符才返回读取数据,而"hello"中没有换行符,导致read()一直阻塞或无法触发返回。
3. 写入长度错误
写入串口时使用sizeof(buffer)(10字节),但实际从input.txt读取的有效数据只有5字节,多余的垃圾数据会干扰回环读取逻辑。
4. 未处理字符串终止符
read()不会自动为缓冲区添加\0,直接用printf("%s")输出会导致乱码,影响结果判断。
5. 缺失错误检查
仅检查了input_fd的打开错误,未处理串口、输出文件的打开失败,以及串口属性设置失败的情况,隐藏了潜在问题。
修改后的代码
#include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <termios.h> #include <errno.h> int main() { const char *device = "/dev/ttyUSB0"; char buffer[10], buffer2[10]; int spread, serialPort_fd, input_fd, no_of_bytes_read = 0, written_bytes = 0, output_fd; // 打开串口,移除O_NDELAY,使用默认阻塞模式 serialPort_fd = open(device, O_RDWR | O_NOCTTY); if (serialPort_fd == -1) { perror("Failed to open serial port"); return 1; } input_fd = open("input.txt", O_RDONLY); if (input_fd == -1) { perror("Failed to open input file"); close(serialPort_fd); return 1; } // 打开输出文件,指定权限并清空原有内容 output_fd = open("output.txt", O_RDWR | O_CREAT | O_TRUNC, 0644); if (output_fd == -1) { perror("Failed to open output file"); close(serialPort_fd); close(input_fd); return 1; } struct termios options; if (tcgetattr(serialPort_fd, &options) == -1) { perror("Failed to get serial attributes"); goto cleanup; } // 设置波特率 cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); // 配置串口硬件参数 options.c_cflag |= (CLOCAL | CREAD); // 启用接收器,本地模式 options.c_cflag &= ~PARENB; // 无校验 options.c_cflag &= ~CSTOPB; // 1停止位 options.c_cflag &= ~CSIZE; // 清除数据位掩码 options.c_cflag |= CS8; // 8数据位 // 禁用规范模式、回显等终端特性 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 禁用软件流控 options.c_iflag &= ~(IXON | IXOFF | IXANY); // 设置原始输出模式 options.c_oflag &= ~OPOST; // 设置超时:至少读取5字节,超时时间1秒 options.c_cc[VMIN] = 5; options.c_cc[VTIME] = 10; if (tcsetattr(serialPort_fd, TCSANOW, &options) == -1) { perror("Failed to set serial attributes"); goto cleanup; } // 读取input.txt,预留一位存终止符 no_of_bytes_read = read(input_fd, buffer, sizeof(buffer) - 1); if (no_of_bytes_read == -1) { perror("Failed to read input file"); goto cleanup; } buffer[no_of_bytes_read] = '\0'; printf("No of bytes read: %d, data: %s\n", no_of_bytes_read, buffer); // 写入串口,使用实际读取的字节数 written_bytes = write(serialPort_fd, buffer, no_of_bytes_read); if (written_bytes == -1) { perror("Failed to write to serial port"); goto cleanup; } printf("Written %d bytes to serial port\n", written_bytes); // 读取回环数据 spread = read(serialPort_fd, buffer2, sizeof(buffer2) - 1); if (spread == -1) { perror("Failed to read from serial port"); goto cleanup; } buffer2[spread] = '\0'; printf("Read %d bytes from serial port, data: %s\n", spread, buffer2); // 将读取的数据写入output.txt if (write(output_fd, buffer2, spread) == -1) { perror("Failed to write to output file"); goto cleanup; } cleanup: // 统一关闭所有文件描述符 close(serialPort_fd); close(input_fd); close(output_fd); return 0; }
关键修改说明
- 移除
O_NDELAY标志,让串口读操作处于阻塞模式,确保等待回环数据到来。 - 禁用规范模式,避免等待换行符才返回读取结果。
- 写入串口时使用实际读取的字节数,避免垃圾数据干扰。
- 给缓冲区手动添加
\0终止符,保证字符串输出正常。 - 补充所有文件、串口操作的错误检查,方便定位问题。
- 设置串口超时参数
VMIN和VTIME,平衡数据完整性和等待时间。
内容的提问来源于stack exchange,提问作者Naveen Krishna Muvva
相关产品推荐
相关产品推荐

