You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 21:25:04