拔出和重新插拔连接器后无法从串行设备读取

时间:2013-06-24 16:04:54

标签: c linux serial-port posix

我有一个应该从串行设备/dev/ttyS0读取的Linux应用程序。串行设备以下列方式打开:

// Open the serial port
if((serial_device = open("/dev/ttyS0", O_RDWR | O_NOCTTY)) < 0){
    fprintf(stderr, "ERROR: Open\n");
    exit(EXIT_FAILURE);
}

// Get serial device attributes
if(tcgetattr(serial_device,&options)){
    fprintf(stderr, "ERROR: Terminal Get Attributes\n");
    exit(EXIT_FAILURE);
}

cfsetspeed(&options,speed);             // Set I/O baud rates
cfmakeraw(&options);                    // Set options to transceive raw data
options.c_cflag |= (CLOCAL | CREAD);    // Enable the receiver and set local mode
options.c_cflag &= ~CSTOPB;             // 1 stop bit
options.c_cflag &= ~CRTSCTS;            // Disable hardware flow control
options.c_cc[VMIN]  = 1;                // Minimum number of characters to read
options.c_cc[VTIME] = 10;               // One second timeout

// Set the new serial device attributes
if(tcsetattr(serial_device, TCSANOW, &options)){
    fprintf(stderr, "ERROR: Terminal Set Attributes\n");
    exit(EXIT_FAILURE);
}

然后我使用select函数尝试从串行设备读取:

// Flush I/O Bffer
if(tcflush(serial_device,TCIOFLUSH)){
    fprintf(stderr, "ERROR: I/O Flush\n");
    exit(EXIT_FAILURE);
}
// Write message to board
if(write(serial_device,msg, strlen(msg)) != (int)strlen(msg)){
    fprintf(stderr, "ERROR: Write\n");
    exit(EXIT_FAILURE);
}


switch(select(serial_device+1, &set, NULL, NULL, &timeout)){
    // Error
    case -1:
        fprintf(stderr, "ERROR: Select\n");
        exit(EXIT_FAILURE);
    // Timeout
    case 0:
        success = false;
        break;
    // Input ready
    default:
        // Try to read a character
        switch(read(serial_device, &c, 1)){
            // Error (miss)
            case -1:
                success = false;
                break;
            // Got a character
            default:
                msg[i++] = c;
                break;
        }
        break;
    }
    // Set 200ms timeout
    this->timeout.tv_sec = 0;
    this->timeout.tv_usec = 200000;
}

我尝试通过确定读取是否成功来重新打开端口:

if(!success)
    close(serial_device);
    openPort(); // Same as above
}

但是,物理拔出串行连接器的行为将导致应用程序无法进一步读取任何内容,并且选择除了超时之外什么都不做。在应用程序运行时将连接器重新插入将无法解决问题,并且选择将继续检测不到任何内容。

再次成功读取串口的唯一方法是重启应用程序。我想知道为什么会这样,以及如何从串口连接器恢复运行时拔掉它。

1 个答案:

答案 0 :(得分:1)

仅使用一个文件描述符select()是不常见的。它还增加了一定程度的复杂性 由于串行端口配置为非规范输入,并且通过正确选择VMIN and VTIME,您可以使用更简单的代码一次完成char的读取。例如。试试VMIN = 1VTIME = 10*timeout.tv_sec

然而,正如您所知,并且如果您愿意处理(或想要)超时而不是等待至少一个字符到达,那么VMIN = 0将使用{{1}模拟您的原始代码}。

  

VMIN = 0且VTIME&gt; 0
      这是一个纯粹的定时阅读。如果输入队列中有数据,则会将其传送到调用者的缓冲区,最大为nbytes,并立即返回给调用者。否则,驱动程序将阻塞,直到数据到达,或者VTIME十分之一从呼叫开始到期。如果计时器在没有数据的情况下到期,则返回零。单个字节足以满足此读取调用,但如果输入队列中有更多可用,则将其返回给调用者。请注意,这是一个整体计时器,而不是一个字符计时器。

然而(就像OP一样)我很困惑为什么重新连接端口连接器会破坏任何读取或选择监控,并且从未遇到过这样的问题。