2

termios を使用して、シリアル ポートと通信するプログラムを作成しました。このプログラムは、非ブロッキング モードでシリアル ポートを読み取り、データを読み取るとシリアル ポートに応答を書き込みます。シリアルポートから読み取ったデータがない場合、プログラムは別のことを行い、次のループでプログラムはシリアルポートを再度読み取ります。

問題は、時々、数分、または数時間経った後、シリアルポートが私のプログラムに応答しなくなったことです。実行してもecho 'HB\n' > /dev/ttyUSB0(シリアルポートは「HACK」と応答するはずです)、もう応答しません..

シリアルポートがいつ「死んでいる」のかさえわかりません。

ここに私の設定があります:

/// set local mode options 
//tminfo.c_lflag |= ICANON;                                                                       
tminfo.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);                                             

/// set control mode options                                                                   
tminfo.c_cflag |= (CLOCAL | CREAD);                                                               
tminfo.c_cflag |= HUPCL; 

// set hardware flow control 
tminfo.c_cflag &= ~CRTSCTS;       

// set how many bits in a character                                                               
tminfo.c_cflag &= ~CSIZE;
tminfo.c_cflag |= CS8;      

// set parity mode (default to odd validation), this option (PARENB) will both enable input and output parity checking
tminfo.c_cflag &= ~PARENB; // we don't need prity checking now                                                                                                                                 

/// set input mode options  
// set input parity checking
tminfo.c_iflag &= ~INPCK; 
tminfo.c_cflag &= ~CSTOPB;                                                                                                                                                                

/// set output mode options                                                                                                                      
tminfo.c_oflag &= ~OPOST;                                                                


tminfo.c_cc[VMIN] = 1; 
tminfo.c_cc[VTIME] = 1;                                                                  

/// set line speed, defaults to 38400bps, both for input and output                           
// this call will set both input and output speed                                                 
cfsetspeed(&tminfo, B38400);

この状況でシリアルをデバッグするのは困難です。シリアルポートが地球上で「死んでいる」原因が本当にわかりません。私はほとんど狂っています...

考えられる理由は?どんな助けでも大歓迎です!

アップデート:

シリアルポートが「死んでいる」場合、その構成は次のとおりです。

speed 38400 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;      swtch = <undef>;
start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 1;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc   -ixany -imaxbel
-iutf8
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke

更新 2

/proc/tty/driver/ar933x-uart手動でシリアルに書き込んでも、プログラムの実行中にこの値とフィールド値が変わらないことに
気付きました。txrx

serinfo:1.0 driver revision:
0: uart:AR933X UART mmio:0x18020000 irq:11 tx:169 rx:0 RTS|DTR|CD

/proc/tty/ドライバー/シリアル

serinfo:1.0 driver revision:
0: uart:unknown port:00000000 irq:0
1: uart:unknown port:00000000 irq:0
2: uart:unknown port:00000000 irq:0
3: uart:unknown port:00000000 irq:0
4: uart:unknown port:00000000 irq:0
5: uart:unknown port:00000000 irq:0
6: uart:unknown port:00000000 irq:0
7: uart:unknown port:00000000 irq:0
8: uart:unknown port:00000000 irq:0
9: uart:unknown port:00000000 irq:0
10: uart:unknown port:00000000 irq:0
11: uart:unknown port:00000000 irq:0
12: uart:unknown port:00000000 irq:0
13: uart:unknown port:00000000 irq:0
14: uart:unknown port:00000000 irq:0
15: uart:unknown port:00000000 irq:0

/proc/tty/ドライバー/usbserial

usbserinfo:1.0 driver:2.0
0: module:pl2303 name:"pl2303" vendor:067b product:2303 num_ports:1 port:1 path:usb-ehci-platform-1

そして、以下はより詳細なコードです...

int Serial::openup(const char *devfile) {
    if(-1 == (devfds = open(devfile, O_RDWR | O_NOCTTY ))) {
        perror(strerror(errno));
        return -1;
    }

    // set device file io mode to nonblock
    //int oldflags = fcntl(devfds, F_GETFL);
    //fcntl(devfds, F_SETFL, oldflags | O_NONBLOCK);

    // get terminal's attributes
    tcgetattr(devfds, &tminfo);

    memset(&tminfo, 0, sizeof(struct termios));

    /// set local mode options  ///
    //tminfo.c_lflag |= ICANON;
    tminfo.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | IEXTEN);

    /// set control mode options ///
    tminfo.c_cflag |= (CLOCAL | CREAD);
    // disable hardware flow control
    tminfo.c_cflag &= ~CRTSCTS;  
    // set how many bits in a character
    tminfo.c_cflag &= ~CSIZE;
    tminfo.c_cflag |= CS8;
    // we don't need prity checking 
    tminfo.c_cflag &= ~PARENB; 
    tminfo.c_cflag &= ~CSTOPB;  

    /// set input mode options  ///
    // disable input parity checking, this 
    tminfo.c_iflag &= ~(INPCK | PARMRK | IGNBRK | BRKINT | ISTRIP 
        | INLCR | IGNCR | ICRNL | IXON); 

    /// set output mode options  ///
    //tminfo.c_oflag |= (OPOST | ONLCR);
    tminfo.c_oflag &= ~OPOST; // ***

    tminfo.c_cc[VMIN] = 0; // ***
    tminfo.c_cc[VTIME] = 1; // ***

    /// set line speed, defaults to 38400bps, both for input and output ///
    // this call will set both input and output speed
    cfsetspeed(&tminfo, B38400);

    if(-1 == tcsetattr(devfds, TCSANOW, &tminfo)) {
        perror(strerror(errno));
        return -1;
    } 

    return 0;
}


int Serial::serve() {
    char buffer[256] = {0};

    /*
    struct timeval timeo;
    timeo.tv_sec = 0;
    timeo.tv_usec = 2 * 1000;

    select(0, NULL, NULL, NULL, &timeo);
    */

    //print_trace("ready to read data from serial port.\n");
    int read_count = 0;        
    if((read_count = read_line(devfds, buffer, 256))) {
        print_trace("read line: %d bytes, %s\n", read_count, buffer);    

        if(0 == strncmp(buffer, "S", 1)) {
            // do some operation
        } else if(0 == strncmp(buffer, "N", 1)) {
            // do some operation                
        }
    } else {
        //print_trace("read no data.\n");
    }

    // TODO: test only, for find out the reason of serial port 'dead' problem
    tcflush(devfds, TCIFLUSH);
}

他のモジュールがシリアルポートに書き込む別の機能があります

int Serial::write_to_zigbee_co(const char *msg) {
    int write_count = 0;
    int len = strlen(msg);

    struct timeval timeo;
    timeo.tv_sec = 0;
    timeo.tv_usec = 20 * 1000;

    select(0, NULL, NULL, NULL, &timeo);

    tcflush(devfds, TCOFLUSH);

    if(len == (write_count = write(devfds, msg, len))) {
    } else {
        tcflush(devfds, TCOFLUSH);
    }

    return write_count;
}
4

1 に答える 1

2

シリアル ポートは、突然「死ぬ」だけではありません。
シリアル リンクが突然「機能しなくなった」または応答しなくなる典型的な原因は、不要なフロー制御です。ハードウェア フロー制御が無効になっているようですが、ソフトウェア フロー制御が無効になっておらず、raw モードが正しく構成されていません。
初期化の必要性

tminfo.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF);

また、IEXTENinをクリアしc_lflag、 in
をクリアPARENBc_cflagます。

cfmakeraw()この関数を使用して、raw モードの初期化を簡素化することを検討してください。

Raw mode

cfmakeraw() sets the terminal to something like the "raw" mode of the old Version 7
terminal driver: input is available character by character, echoing is disabled, and 
all special processing of terminal input and output characters is disabled. The 
terminal attributes are set as follows:

termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                | INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;

補遺
改訂された termios 設定は問題ないようです。

次のステップは、シリアル リンクのどちら側に障害があるかを判断することです。一方の端が受信していないか、または一方の端が送信していませんか?

シリアル ポート ドライバーによって管理されているメトリックを使用してみることができます。両側で Linux を実行している場合は、/proc/tty/drivers内のファイルを調べる必要があります。シリアル ポート ダイバーは、各ポートの受信バイト数と送信バイト数を報告します。テスト前と失敗後の Rx と Tx の数を比較します。

CC2530 側から統計情報を取得できない場合は、シリアル リンク モニターが必要になる場合があります。専用のテスト機器のほかに、2 つのシリアル ポートを備えた PC を使用して 1 つを作成/セットアップできます。ポート A をホストに、ポート B を CC2530 に接続して、この PC が「中間者」になるようにします。次に、ポート A の受信データをポート B に再送信し、ポート B の RxD をポート A の TxD に再送信するプログラムを作成する必要があります。

再送信されるこのデータ (両方のチャネル) も、表示またはログに記録する必要があります。目的は、シリアル リンクのどちら側で障害が発生しているかを判断することです。それが確立されたら、それが受信または送信の問題であるかどうかを把握する必要があります。

または
、より多くのコード (完全なopen()初期化ルーチンと読み書きロジック) を投稿して、全員がデスク チェックできるようにすることもできます。



補遺2

投稿したコードにはいくつかの問題があります。


初期化コード

// get terminal's attributes
tcgetattr(devfds, &tminfo);

memset(&tminfo, 0, sizeof(struct termios));

このコードはtermiosデータを取得し、それをゼロにします! ステートメント
を削除する必要があります。memset()


コードを読む

if((read_count = read_line(devfds, buffer, 256))) {

シリアル ポートは非​​正規 (別名 raw) モード用に初期化されていますが、read_line()これは正規の入力操作です。
raw モードをセットアップして行を読み取ろうとしたときに何が起こるかは正確にはわかりませんが、読み取り操作がハングしても驚かないでしょう。

このシリアル リンクを介してこれら 2 つのデバイス間で交換されるデータのタイプを評価する必要があります。
すべてのメッセージは、各行が改行文字で終了する ASCII テキストで構成されていますか?
「はい」の場合、標準モードとread_line().
それ以外の場合は、非標準モードとread()syscall を使用し、受信したデータを解析するコードを記述する必要があります。


if((read_count = read_line(devfds, buffer, 256))) {
    ...
} else {
    //print_trace("read no data.\n");
}

エラー (-1) が返された場合read_line()、このコードはそれを適切な戻り値として扱い、受信バッファー内の古いデータまたはガベージ データを処理しようとします。読み取りエラーがあった場合、それらは検出されず、報告もされていません。


tcflush(devfds, TCIFLUSH);

あなたが誤用しているIMO tcflush()。まれなケースもありますが、通常は、実際に解析してガベージ データであることがわかるまで、データを破棄しないでください。このtcflush()ステートメントを削除する必要があります。


コードを書く

select(0, NULL, NULL, NULL, &timeo);

書き込みの前に時間遅延を実行することは、ユーザー空間では疑わしい操作です。実際の実行時間を中断するスケジューリングとプリエンプションを備えたマルチタスク環境では、ユーザー空間プログラムがすべてのシステムコールにそのような固定遅延を追加する必要はほとんどありませんwrite()


tcflush(devfds, TCOFLUSH);

の別の疑わしい(誤)使用tcflush()
これは削除する必要があります。


if(len == (write_count = write(devfds, msg, len))) {
} else {
    tcflush(devfds, TCOFLUSH);
}

の別の疑わしい/不適切な使用tcflush()
これは、より適切な回復コードに置き換える必要があります。短い書き込みが発生する可能性は低いです。返される可能性が最も高いのは、完全な書き込みカウントまたはエラー リターン (-1) です。errnoエラー リターン (-1) と変数を確認する必要があります。( などの他のシステムコールでもこれを行う必要があります。何が返されるかを知るには、使用する各システムコールのマニュアルページtcgetattr()を読む必要があります。)

于 2013-12-19T10:00:50.387 に答える