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()1 つのファイル記述子だけで を使用するのは珍しいことです。また、複雑さのレベルも追加されます。
シリアル ポートは非​​標準入力用に構成されているため、 と を適切に選択するVMINVTIME、より単純なコードで一度に 1 文字の読み取りを実行できる場合があります。EGトライVMIN = 1アンドVTIME = 10*timeout.tv_sec

ただし、あなたが理解したように、少なくとも1文字が到着するのを待つのではなく、タイムアウトを処理したい(または望んでいる)場合はVMIN = 0、元のコードをselect().

VMIN = 0 かつ VTIME > 0
これは純粋な時間読み取りです。データが入力キューで使用可能な場合、最大 n バイトまで呼び出し元のバッファーに転送され、すぐに呼び出し元に返されます。それ以外の場合、ドライバーは、データが到着するまで、または呼び出しの開始から VTIME の 10 分の 1 が経過するまでブロックします。タイマーがデータなしで期限切れになると、ゼロが返されます。この読み取り呼び出しを満たすには 1 バイトで十分ですが、入力キューでさらに使用可能な場合は、呼び出し元に返されます。これはキャラクター間のタイマーではなく、全体的なタイマーであることに注意してください。

ただし、(OPのように)ポートコネクタを再接続すると読み取りが中断されたり、監視が選択されたりする理由について困惑しており、そのような問題に遭遇したことはありません。

于 2013-06-27T01:34:49.670 に答える