5

Linux で termios フレームワークを使用して、UART (usbserial) 経由で非接触型スマート カード リーダーに接続しようとしています。コードは PC で正常に動作しますが、クロスコンパイルして ARM9 ターゲットで試してみると、デバイスを開き、デバイスにコマンドを書き込むことさえできますが、読み取りコマンドは無期限にブロックされます。コード スニペットは次のとおりです。

int mifare_rdr_init(struct mifare_1K * ptr, char *rdr_devnode)
{   
    bzero(ptr, sizeof(struct mifare_1K));           // zero the entire structure
    // open serial device
    int fd = open(rdr_devnode, O_RDWR|O_NOCTTY );
    if (fd == -1) {
    perror("Failed to open serial device ");
    return 1;
    }
    ptr->serialfd = fd;                 // save file descriptor

    ptr->serialdev.c_iflag = 0;                 // no i/p flags
    ptr->serialdev.c_oflag = 0;                 // o/p flags
    ptr->serialdev.c_cflag = ( CS8 | CREAD | B38400 );      // 8 bits, receive enable, baud for rdr
    ptr->serialdev.c_lflag = ( ICANON );                // CANONICAL mode, means read till newline char '\n'.
    // control chars 
        // commented below line as suggested by A.H below, since it's not needed in CANONICAL mode
    // ptr->serialdev.c_cc[VMIN] = 1;               // read unblocks only after at least one received char.

    // flush all i/o garbage data if present
    tcflush(ptr->serialfd,TCIOFLUSH);

    int ret = 0;
    // apply settings
    ret = tcsetattr(ptr->serialfd,TCSANOW,&ptr->serialdev);
    if (ret == -1) {
        perror("tcsetattr() failed ");
        return 2;
    }
    return 0;
    }

int get_mifare_rdr_version(struct mifare_1K *ptr, char *data)
{
    // flush all i/o garbage data if present
    tcflush(ptr->serialfd,TCIOFLUSH);

    int chars_written = write(ptr->serialfd,"$1V\n",4);
    if( chars_written < 0 ) {
        perror("Failed to write serial device ");
        return 1;
    }
        printf("cmd sent, read version...\n");   // this prints, so I know cmd sent...
    int chars_read = read(ptr->serialfd,ptr->data_buf,14);
    if( chars_read < 0 ) {
        perror("Failed to read serial device ");
        return 2;
    }
    // copy data to user buffer
        printf("reading done.\n");    // this doesn't print...
    return 0;
}

mifare_1K 構造体には、シリアル デバイスのファイル記述子、termios 構造体、および使用しているさまざまなバッファーが含まれています。私が言及したデバイスは、usb-to-serial (モジュール: ftdi_sio) デバイスです。termios の Canonical モードで 38400@8-N-1 に設定されています。

リーダーからの応答が '\n' で終わるため正規モードです。したがって、'\n' が受信されるまでデバイスを読み取るため、正規モードでより適切に処理されます (間違っている場合は修正してください)。

最初に init() fn を呼び出し、次に get_rdr_version() を呼び出します。「cmd sent, read version...」という文字列が出力されるので、書き込み可能であることがわかりますが、「reading done.」という文字列は出力されません。その後。

もう 1 つの問題は、カード リーダーを取り外して、そのポートを別の PC の gtkterm (シリアル ポート ターミナル プログラム) に接続すると、その gtkterm で「$1V\n」が表示されないことです??!! . 次に、少しRnDした後、リーダーがリーダーに接続されているシステムを再起動すると、他のGtktermでそのコマンド「$ 1V\n」しか取得できないことがわかりました。再起動せずに再試行すると、そのコマンドはそのGktermに表示されません...手がかりですが、まだ理解していません。

cmdがデバイスファイルに書き込まれているが、実際のデバイスには排出されていないようなものですか? これをチェックする方法はありますか?

私はこのfrでしばらく立ち往生しているので、どんな助けも深く感謝しています....thnks。

アップデート :

OK、以下に示すようにコードを少し変更することで機能するようになりました。

// open serial device
    int fd = open(rdr_devnode, O_RDWR|O_NOCTTY|O_NDELAY );  // O_NDELAY ignores the status of DCD line, all read/write calls after this will be non-blocking
    fcntl(fd,F_SETFL,0);   // restore read/write blocking behavior
    if (fd == -1) {
    perror("Failed to open serial device ");
    return 1;
    }

これは、init() 関数でポートを開くコードの変更されたセクションです。2 つの変更:

1) O_NDELAY が open() 呼び出しのフラグに追加されます。これは、データ キャリア検出 (DCD) ラインを無視して、相手側が接続され、通信の準備ができているかどうかを確認します。これはもともと必要のない MODEM に使用されていたもので、実際には usbserial を使用しているため、まったく持っていません。ただし、このフラグはさらに read() および write() をノンブロッキングとして呼び出します。注意点として、termios 構造体の cflag に CLOCAL を追加することでこれが解決されると思っていましたが、試してみましたが機能しませんでした。

2) fcntl(fd,F_SETFL,0) は、以降の read() および write() 呼び出しのブロッキング動作を復元します。

このコンボは私にとって完璧に機能しています。これを回答として投稿しない唯一の理由は、同じハードウェアであるため、この変更なしで PC で動作した理由をまだ理解していないためです。実際、 minicomを使用して ARM9 TARGET のスマート カード リーダーからデータを読み取ることはできましたが、私のプログラムではできませんでした。FT232BL のドキュメントをチェックして、デフォルトで DCD のステータスがどのようなものかを確認します。

とにかく、POSIXオペレーティングシステムのシリアルプログラミングガイドでこの情報を見つけました。説明誰か??? もちろん、答えが分かり次第追記します。

乾杯 :)

4

2 に答える 2

4

TelegesisUSBモジュールを搭載したRaspberryPiで同じ症状が発生したので、これを別のデータポイントとして追加します。

私の場合、原因はRTSフラグの欠落であることが判明しました。Telegesisは、CRTSCTSフロー制御を想定しており、RTSを確認せずにRaspberryにデータを送信することはありません。ここでの厄介な側面は、a)同じコードがPCで問題なく機能し、b)Telegesisが最初に接続されたときにRaspberryで問題なく機能したが、その後/ dev/ttyUSB0を開いたときにデータが表示されないことでした。ラズベリーによって。

何らかの理由で、ARMではRTSフラグはデバイスを閉じるとクリアされますが、再度設定されないようですが、x86/x64ではRTSフラグは設定されたままです。ここでの修正は、RTSフラグがまだ設定されていない場合は、設定することです。

#include <sys/ioctl.h>
//...
int rtscts = 0;
if (ioctl (fd, TIOCMGET, &rtscts) != 0)
{
  // handle error
}
else if (!(rtscts & TIOCM_RTS))
{
  rtscts |= TIOCM_RTS;
  if (ioctl (fd, TIOCMSET, &rtscts) != 0)
  {
    // handle error
  }
}

あなたの場合、フロー制御を使用しないので、上記はまったく当てはまらない可能性があることに注意してください。それはあなたの質問であり、ミニコムが機能したという言及が私たちの問題の解決策を発見することにつながりました-それでそれをありがとう!

于 2013-01-07T06:30:24.033 に答える
1

あなたがチェックするかもしれない3つのこと:

標準モードと非標準モードの混在 1

正規モードと非正規モードのものを混在させています:

ptr->serialdev.c_lflag = ( ICANON );
// ...
ptr->serialdev.c_cc[VMIN] = 1;

termios(3)マンページには、VMIN について次のように記載されています。

VMIN正規読み取り の最小文字数。

明らかに、タイムアウトはあなたが思うようには機能しません。

標準モード/非標準モードの混同 2

さらに、マンページにはさらに以下のように記載されています。

VTIME、 VMIN がそれぞれ VEOL、 VEOFと同じ値を持つ場合があることを除いて、これらの記号添字値はすべて異なり ます。非標準モードでは、特殊文字の意味がタイムアウトの意味に置き換えられます。VMIN と VTIME の説明については、以下の非標準モードの説明を参照してください。

したがって、これらの定数の定義が 2 つのプラットフォームで異なるかどうかを確認してください。EOL/EOF ロジックが間違った設定によって損なわれる可能性があります。EOL と EOF の両方が からの復帰を引き起こす可能性がありreadます。

初期化されていないc_cc

c_ccあなたのコードは、配列の適切な初期化を示していません。既存の設定を読んでも、正規モードに必要な値に適切なデフォルトを提供していません。これまでに示したコードは、値をクリアすることさえしていません。したがって、予測できない値が使用される可能性があります。

于 2011-12-28T11:38:14.630 に答える