3

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

// 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
}

但是,从物理上拔下串行连接器的行为将导致应用程序无法进一步读取任何内容,并且 select 只会超时。在应用程序运行时重新插入连接器不会解决问题,并且 select 将继续检测不到任何东西。

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

4

1 回答 1

1

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

但是,正如您所知道的,如果您愿意处理(或想要)超时而不是等待至少一个字符到达,那么VMIN = 0将使用select().

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

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

于 2013-06-27T01:34:49.670 回答