5

私のマシンには C/Python のセットアップがあり、シリアル通信でいくつかのテストを行っていますが、何らかの理由で 1 バイト以上を読み取っていません。

私のセットアップ: 仮想ボックスで OpenSUSE を実行している Windows 7 マシンがあります。私は 2 つの USB-RS232 コンバーターとそれらの間にアダプターを持っています (つまり、1 つの USB ポートから別の USB ポートへのループです)。

Windows 側では、Python-to-Python および C-to-Python を介して相互に通信できるようにすることができました。Linux VM を使用すると、C (Linux) から Python (Windows) にデータを取得できますが、逆の場合は 1 バイトしか返されません。ファイルを開いたり、Linux C コードで読み取りを実行したりする方法に問題があると考えていますが、何が問題なのかわかりません。

Python コード (PySerial を使用):

>>> import serial
>>> ser = serial.Serial(3)
>>> ser
Serial<id=0x2491780, open=True>(port='COM4', baudrate=9600, bytesize=8, 
parity='N', stopbits=1, timeout=None, xonxoff=False, rtscts=False, dsrdtr=False)
>>> ser.read(5)
'Hello'
>>> ser.write("hi you")
6L

C コード:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

int open_port()
{
    int fd;
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
    if(fd < 0)
      perror("open_port: Unable to open /dev/ttyUSB0 - ");
    else
      fcntl(fd, F_SETFL, 0);
    return fd;
}

int swrite(int fd, char * str)
{
    int n;
    n = write(fd, str, strlen(str));
    if (n<0)
        printf("write() of %d bytes failed\n", strlen(str));
    return n;
}

int main()
{
    int fd, databytes;
    char buf[100] = {0};
    struct termios options;

    fd = open_port();

    //Set the baud rate to 9600 to match
    tcgetattr(fd, &options);
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);
    tcsetattr(fd, TCSANOW, &options);
    tcgetattr(fd, &options);

    databytes = swrite(fd, "Hello");
    if(databytes > 0)
      printf("Wrote %d bytes\n", databytes);

    databytes = read(fd, buf, 100);
    if(databytes < 0)
      printf("Error! No bytes read\n");
    else
      printf("We read %d bytes, message: %s\n", databytes, buf);

    close(fd);

    return 0;
}

そして、私は戻ってきています:

mike@linux-4puc:~> gcc serial_com.c
mike@linux-4puc:~> ./a.out 
Wrote 5 bytes
We read 1 bytes, message: h

したがって、Linux-> Windows 書き込みは機能しており、python は正しい "Hello" 文字列を表示していますが、何らかの理由で、Windows->Linux 側で 1 バイトしか返されません。

誰かが何か間違っていると思いますか?

EDIT:
私が得たフィードバックに基づいて、コードに2つの微調整を試みました. すべてのデータがそこにあることを保証できないように聞こえるので、試してみました:

1) 睡眠

    if(databytes > 0)
      printf("Wrote %d bytes\n", databytes);
    sleep(15);                 // Hack one to get the data there in time, worked
    databytes = read(fd, buf, 100);

2) while ループ

while(1){  // Hack two to catch the data that wasn't read the first time. Failed
           // this only saw 'h' like before then sat waiting on the read()
  databytes = read(fd, buf, 100);
  if(databytes < 0)
    printf("Error! No bytes read\n");
  else
    printf("We read %d bytes, message: %s\n", databytes, buf);
}

ループがうまくいかないようで、読み取られなかったデータは破棄されますか?? /編集

4

4 に答える 4

7

read(2) マニュアルから;

成功すると、読み取られたバイト数が返され (ゼロはファイルの終わりを示します)、ファイル位置はこの数値だけ進められます。この数が要求されたバイト数よりも小さい場合、エラーにはなりません。これは、たとえば、現在実際に使用できるバイト数が少ないために発生する可能性があります (おそらく、ファイルの終わりに近づいていたため、またはパイプまたは端末から読み取っているためです)。

言い換えると、書き込み直後にソケットから読み取り、コンピューターがシリアル ポートよりもはるかに高速であるため、読み取り可能な文字は 1 つだけで、その文字のみをread(2)返す可能性が高くなります。

于 2012-10-05T13:45:12.173 に答える
4

readのマニュアルページには

...最大 count バイトの読み取りを試みます...

あなたのコードは、完全なバッファが常に単一のread;によって返されると想定しているように見えます。データが複数の呼び出しで返されるのは有効です。

read-1 が返されるかどうかを確認しerrno == EINTR、その後再試行することもお勧めします ( TEMP_FAILURE_RETRYGNU システムで実行している場合に使用します)。 readシグナルによって中断されると、一時的なエラーが返されることがあります。

于 2012-10-05T13:44:47.847 に答える
4

他の人が答えたように、C の read() 関数は、1 バイトだけを返すことで契約を満たしています。

C の read() 関数と Python の read() 関数はまったく異なります。

PySerial は、 read() について、

シリアル ポートから size バイトを読み取ります。タイムアウトが設定されている場合、要求に応じて返される文字数が少なくなる場合があります。タイムアウトがない場合、要求されたバイト数が読み取られるまでブロックされます。

C API ではそのような保証はありませんが。バッファで使用可能な文字を返します (まだ何もない場合は 0 も)。

C 側を Python 側のように動作させたい場合は、別の関数を使用する必要があります。このようなもの:

int read_exact_chars(int fildes, char *buf, size_t nbyte)
{
    ssize_t chars_read;
    int chars_left = nbyte;
    while (chars_left) {
        chars_read = read(fildes, buf, chars_left)
        if (chars_read == -1) {
            /* An error occurred; bail out early. The caller will see that
               we read fewer bytes than requested, and can check errno
             */
            break;
        } else {
            buf += chars_read;
            chars_left -= chars_read;
        }
    }
    /* return the actual number of characters read */
    return nbyte - chars_left;
}
于 2012-10-05T14:06:59.553 に答える
3

いくつかの良い答えを得て、これを扇動します(+1がいたるところにあります!)、すべての答えが正しい結論(読み取りがデータを取得しないことについて)につながりますが、入力のどれも実際に私が抱えていた問題を「修正」しませんでした。

最終的にこれを機能させる方法は次のとおりです。

構造の設定はtermios私を殺していました。いくつかのフラグを設定していましたが、すべてのフラグを設定していませんでした。したがって、この:

tcgetattr(fd, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
tcsetattr(fd, TCSANOW, &options); 

これに変更:

tcgetattr(fd, &options);
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
options.c_iflag = 0; // Disable Input flags
options.c_lflag = 0; // Disable Local mode flags
options.c_oflag = 0; // Disable Output flags
options.c_cflag = (options.c_cflag & ~CSIZE) | CS8; //Data bits per character (8)
options.c_cc[VMIN] = 50;  // Wait for 50 characters or
options.c_cc[VTIME] = 50; // Wait for 5 seconds
options.c_cflag |= (CLOCAL | CREAD | HUPCL);   // Ignore modem status lines, 
                                               // enable receiver, 
                                               // and hang up on last close
options.c_cflag &= ~(PARENB | PARODD); //Clearing even and odd parity
options.c_cflag &= ~CSTOPB;            //Clear double stop bits
tcsetattr(fd, TCSANOW, &options);

これらの変更により、Python から記述した C Linux コードでデータを取得できるようになりました。

termios構造のオプションについて、多くの「事実」を得るために、これら 2 つの情報源を使用しました。

  1. フラグの説明
  2. 簡単な概要
于 2012-10-17T20:12:08.420 に答える