- PR -

シリアル通信で受信データ欠落

投稿者投稿内容
Keisuke
大ベテラン
会議室デビュー日: 2003/10/24
投稿数: 105
投稿日時: 2003-12-09 20:13
TCP/IP プロトコルスタックとは、LANのドライバ&TCP/IPライブラリの意味で使いました。
DOSにはネットの機能は無いのでUDPを使っている以上なにかしらのTCP/IPプロトコルスタックを使っているはずです。

もしかしてシリアルの割り込みハンドラで割り込みをマスクしたまま、重たい処理をさせていませんか?
ちいにぃ
大ベテラン
会議室デビュー日: 2002/05/28
投稿数: 244
投稿日時: 2003-12-09 23:38
・「UDPの割り込み」っての用語は違和感あります。
 NICがEthernetフレームを受信したら割り込みがかかると思いますが、
 それをTCP/IPプロトコルスタック(ドライバ→IP層→TCP,UDP層)が処理して、
 アプリケーションレベルに届くわけで。
 ちなみにEthernetフレームの大きさは最大1514バイトだったかと。

・「TCP/IP プロトコルスタック」についてはKeisukeさんのフォローどおり。
 また、Googleで「用語 "TCP/IP プロトコルスタック"」と引いてみましょう。

・UARTとはチップのことです。で、16450や16550, 16550Aはその型番ですが、
 現在のPCには、その互換機能を内蔵した 'SuperI/O' と呼ばれるチップが
 載っています。
 パネルコンピュータの中身を見たり、そのメーカに聞けば実際に載っている
 チップやその仕様を入手できるでしょう、たぶん。
 でも、いまどき(たぶん1994年より後くらい)なら、16550A上位互換のが
 載って可能性が大きいです。

・受信バッファはおっしゃるとおり、16450が1バイト、16550Aが16バイトです。
 WindowsXP付属のドライバの初期設定だと、受信時にはバッファが14バイト
 埋まったところで割り込みがかかる設定がデフォルトだったかと。

 ですので、UARTが16550A互換品で、このような設定になっていれば、
 9600bps(スタートビット1bit,データ8bit、ストップビット1bitと仮定)なら、
 14 / (9600/(1+8+1)) = 14.5msに1回割り込みがかかるわけで、この期間に
 UARTから受信データを取得できれば、取りこぼさないわけです。

 ただ、現在お使いのシリアルポートドライバが、16550Aのバッファを
 活用していないかもしれません。

……などといろいろ考えてみましたが、結局、MS-DOS上でマルチタスクを
やること、TCP/IPを扱うこと、はいずれもMS-DOS標準の機能ではできない
ことですから、回答は難しいです。


[ メッセージ編集済み 編集者: ちいにぃ 編集日時 2003-12-09 23:50 ]
koji
大ベテラン
会議室デビュー日: 2002/12/25
投稿数: 100
投稿日時: 2003-12-10 07:51
Keisukeさん、ちいにぃさんどうもアドバイスありがとうございました。

昨日、OSI参照モデルなど、イーサネットの基本について調べ、だいたいの基本的なこと分かりました。

昨日、シリアルのほうのエラーをみていたら、送受信とも「オーバーランエラー」が発生していました。「受信バッファオーバーフローエラー」は発生していませんでした。

いいかどうかはわかりませんが、シリアルの受信時に1文字でも受信した段階で
他の割り込みを禁止して受信が完了した段階で禁止を解除するなてのはどうでしょうか?
イーサネットはしっかりしているので、受信に失敗しても、下の層で上手にリトライとかしてくれそうでは。。。。

よろしくお願いします。
koji
大ベテラン
会議室デビュー日: 2002/12/25
投稿数: 100
投稿日時: 2003-12-10 08:12
先ほどの「シリアルの受信時に1文字でも受信した段階で他の割り込みを禁止して。。。」はよく考えたら、できませんでした。
今、自分がさわっているのはソフトバッファのほうなので、チップのバッファの受信状態を見に行くのは大変ですね。
やむ
会議室デビュー日: 2003/08/02
投稿数: 17
投稿日時: 2003-12-10 10:48
相手の通信チップも16550Aレベル以上だと仮定して現象をみると、相手側のデータ送出停止が間に合っていないのではないかと思います。
 こちらの通信チップはバッファが一杯になったらRTSをOFFにして、送信を止めるよう指示します。すると、相手チップのCTSがOFFになり、それ以降の送信を止めるのですが、相手チップのFIFOバッファに溜まっているデータの送信は止められません。
 相手チップのバッファが満杯の時に、こちらの受信バッファが満杯になると、最大で相手チップのバッファ分の取りこぼしが発生する可能性があります。
 対策としては、
1:こちらの受信トリガレベルを1バイトに設定し(FCRのFCR7,8=0)、1バイト受信毎に割込みを発生させる。ハードウエアフロー制御があると、その時点でRTS=OFFになるので、16バイトはこちらの受信バッファでカバーできます。

2:相手側にお願いして、送信時はバッファを利用しないようにしてもらう。送信条件に、送信バッファとシフトレジスタの両方が空(LSRのTHRE=0,TEMT=0)を入れてもらうということで、CTS=OFF時に全くデータを送信しないようにする。

というのではどうでしょうか。

 根本的には、UDPアプリが割込みをマスクしている時間が長すぎるのではないでしょうか。
koji
大ベテラン
会議室デビュー日: 2002/12/25
投稿数: 100
投稿日時: 2003-12-11 00:36
やむさん、どうもアドバイスありがとうございました。

特に「RTSをOFFにしてもFIFOバッファに溜まっているデータの送信は止められない」
は本当に参考になりました。

ところで、このハードは無線のイーサネットを使用しています。
実際に通信の反応は、有線より無線のほうが鈍くなっていますが、
割り込み時間も長くなるなんてことあるんでしょうか?
Junbow
ぬし
会議室デビュー日: 2002/01/24
投稿数: 373
お住まい・勤務地: saga.jp
投稿日時: 2003-12-11 09:24
 100BASE-TXの有線LANと2Mbpsの無線LANを比べたら、イーサネットの1フレーム?を送るのに掛かる時間がだいぶ違うことにより、割り込みが掛かる時間が違ってくるような気がしますが・・・

 ちなみに、シリアル通信部分ですが、なにかのデバイスドライバを経由したRS-232C制御ですか?それとも、Cでできたソフトが、直にハードウェアのI/Oポートを制御いているのでしょうか?(ひょっとしたら、何か違うかも?)

 ハードとしては、例えば、Windows上で有線/無線LANでルーター経由のインターネット常時接続をしながら、シリアルポートにつないだアナログモデムからダイヤルアップしても、なんの問題もなく使えるので、ハードウェア的な問題は、きっと無いなぁ・・などと思ったりしていますが・・

[ メッセージ編集済み 編集者: Junbow 編集日時 2003-12-11 09:28 ]
koji
大ベテラン
会議室デビュー日: 2002/12/25
投稿数: 100
投稿日時: 2003-12-13 21:36
Junbowさん、どうもアドバイスありがとうございました。

どうなっているか調べてみました。
C言語レベルでは、ドライバが提供している関数をさわっています。
Rs232cクラスを使っています。
(標準的なクラス(??)で皆さんも知っているものなのでしょうか?)
実はそのクラス(cppじゃなくcなので構造体を使っています)
の関数までしか触れないんですが、ヘッダファイルをみたところ
以下のようになっています。

ypedef struct {
int (*Init)( int chan, const rsinit_t *rsinit );/* RS-232C 初期化 */
void (*DtrCmd)( int chan, int dtr ); /* DTR ON / OFF */
void (*RtsCmd)( int chan, int rts ); /* RTS ON / OFF */
int (*DsrStatus)( int chan ); /* DSR ステータス の取得 */
int (*CtsStatus)( int chan ); /* CTS ステータス の取得 */
int (*DcdStatus)( int chan ); /* DCD ステータス の取得 */
int (*isTxEmpty)( int chan ); /* 送信バッファ・エンプティ ステータス の取得*/
void (*BaudRate)( int chan, unsigned long bps ); /* ボーレート設定 */
int (*Getch)( int chan ); /* 1 バイト 受信 */
int (*Getn)( int chan, char *pdat, unsigned n ); /* n バイト 受信 */
int (*Putch)( int chan, int data ); /* 1 バイト 送信 */
unsigned (*Puts)( int chan, const char *pdat ); /* 文字列 送信 */
unsigned (*Putn)( int chan, const char *pdat, unsigned n );/* n バイト 送信*/
unsigned (*RxLength)( int chan ); /* 受信データ数の取得 */
unsigned (*TxFreeLength)( int chan ); /* 送信空きバイト数の取得 */
unsigned (*TxRemLength)( int chan ); /* 残りの送信データ数の取得 */
void (*RxFlush)( int chan ); /* 受信バッファのクリア */
void (*TxFlush)( int chan ); /* 送信バッファのクリア */
int (*Stop)( int chan ); /* RS-232C 停止処理 */
unsigned (*GetPErrcnt)( int chan ); /* パリティ・エラー・カウンタ の取得 */
unsigned (*GetOErrcnt)( int chan ); /* オーバラン・エラー・カウンタ の取得 */
unsigned (*GetFErrcnt)( int chan ); /* フレーミング・エラー・カウンタ の取得 */
unsigned (*GetBOvfcnt)( int chan ); /* 受信バッファ・オーバフロー・カウンタ の取得*/
void (*ResetErrcnt)( int chan ); /* エラー・カウンタ の零クリア */
} Rs232c;

DTR/DSRの制御などはハードが勝手に行うと思っていましたが、
このヘッダをみるとソフトでON/OFFして、またステータスを見て判断するべきなのでしょうか?
また、このヘッダファイルは古そうなんですが、やっぱりドライバが古く
チップ上のバッファは16バイト以上あるのに1バイト分しかない様な振る舞いをしているのでしょうか?

スキルアップ/キャリアアップ(JOB@IT)