• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:シリアルの送信完了を待つ方法)

シリアルの送信完了を待つ方法

このQ&Aのポイント
  • シリアル通信を行う際に、送信完了を待つ方法について質問しています。
  • AdvantechのPCM-9575ボードにRedHat9を入れ、COM2をRS-485に設定して、シリアル通信を行っています。
  • write()関数が送信完了まで待ってくれないため、送信完了待ちの方法について知りたいです。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

今回の問題はドライバレベル以下で起こっているのだと思います。SIOには例えば16バイトのFIFOがあって、ドライバはハードに書き込んだら、すぐにカーネルに戻ってしまうのでしょう(FIFOの目的を考えれば当然ですね)。だからプロセスレベルでBLOCKINGであろうとなかろうと短時間ではバッファリングが起きているのだろう ... という理解になりました。質問主さんは最初からお気づきだったのでしょう。 ドライバのソースを読んだところでは、closeをするとハードウェアFIFOが空になるのをちゃんと待っています。ですからwrite直後にcloseしたら完全に送信完了ということになります。またcloseでは if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); serial_outp(info, UART_MCR, info->MCR); ということをしているので、RTSも一緒にクリアされるようです。 open/closeを繰り返すのはコストが高いので避けたいですけれど、そうでないなら単にcloseすることで質問主さんの目的は達せられるのではないでしょうか?私のボスと話したところでは、OSの内部をいじりたくないのであれば質問主さんのnanosleepがベストの解決だろう、という結論になりました。 その他の解決さくとしてはNo.3で書いたハードを直接見る方法(Linux流の行儀の悪いプログラム)や、ハードウェアFIFOにバッファリングをさせないためにはサイズを1バイトにしてしまう手があります。でもこれをすると山ほど割り込みが発生するのでダメかもしれません。とにかく全体のパフォーマンスは犠牲にしてもrs_writeがFIFOが空になるのを待つようなオプションをドライバ開発者に付けてもらうのがいいと思います。すでにトランスミッタが完全に送信したかどうかをチェックする関数がありますから、ちょっとの改造でできるはずです。きっと他の人の需要もあると思います。 ところで >出終わってなければ再実行 これは for (nwritten = 0;nwritten < n;nwritten += rv) { rv = write (fd, buf+nwritten, n-nwritten); if (rv < 0) break; // Error: bail out. // } こうしているということですよね?

matyrcry
質問者

お礼

ありがとうございます!! 詳しいところまで...助かります。 ドライバの深いところまではまだ読んでなかったのですが、なんとなく分かってきました。 close() は送信頻度が今は30Hzくらいですが完成予定では600Hzくらいになるので無理っぽいです。 アプリケーションレベルからの周期監視も実行時間を喰うから避けたいかなと考えてます。 (のですが、割り込みがとれなければそれしかないんでしょうね) 他の作業に追われて今は時間をかけられないので、一段落したらドライバいじりに挑戦しようと考えてます。 またそのときにはお知恵を拝借させてください。 >こうしているということですよね? だいたいそんな感じです。(プロセスがFIFOで誤動作すると実行時間を食い尽くすのでスリープで保険かけてます)

その他の回答 (3)

回答No.3

うむむ、そうでしたか.....ううむなんでだろう。すみません、ネタ切れです(^^; 格好悪いですけれども、write直後にUARTのポートを直接読みに行ってLSR内でTHRの状態を見るのはいかがでしょうか。 #今からドライバをDEBUGオプション付きで再コンパイルして眺めてみます。

回答No.2

こちらにはRS232の環境しかないのですけれど、実験してみたところ、普通にopenしてwriteしたらちゃんと待ってくれました。openするときにワザワザO_NDELAYとかO_NONBLOCKなんてセットしてないですよね....?writeが待ってくれないのはどのようにして確認されたのでしょう?writeの戻り値は送信したバイト数と同じ数になっていますか?

matyrcry
質問者

お礼

ありがとうございます。 write()の返値はあっています。(出終わってなければ再実行させています) openの引数は(O_RDWR|O_NOCTTY)です。 確認は、ソフト的にはwrite()直後にioctl()でRTSラインを操作し、信号線の電位をオシロで見ています。 開通後にtcsetattr()を引数(struct termios) c_iflag,c_oflag,c_lflag,c_cc[VTIME],c_cc[VMIN],c_cc[VEOL],c_cc[VEOF] をそれぞれ0にしてコールしています。

回答No.1

経験の無い者ですみません。思いつきでアドバイスです。 termios.hのtcdrainをwriteのあとに入れるのはいかがでしょうか?

matyrcry
質問者

お礼

ありがとうございます。早速やってみました。 プロセスを休眠させるようで、起床は次のカーネル のスケジューラ処理後になるようです。 (今カーネルは10ms周期ですが、応答が1.5 ms程度で来るので間に合いません) Linux 始めて間がないもんで、このへんの動きが さっぱり分からないので、他にも思いつきでいい ので情報いただけると嬉しいです。 とりあえず今は実測で1char=95.5usから待ち時間 を算出してnanosleep() 使って待たせてます。^^;

関連するQ&A

専門家に質問してみよう