[2016/09/10]『7.SetupComm関数を使ってみろ!』を追加
[2016/09/08]『6.シリアルデータを受信するEXEのセッションがConsoleじゃない!』を追加
[2016/09/02]本記事公開
ちょっと、ちょっと、
アンタが持ってる悩みは、コレでっか~~~~!?
- RS-232Cのシリアル通信で、受信データに取りこぼしが発生する
- 取りこぼし位置もサイズもタイミングもバラバラで法則は無い
- ワケあって通信速度を落とせない(通信相手は既に稼働中で変更なんてできない等)
- ワケあってフロー制御を使えない(通信相手は既に稼働中で変更なんてできない等)
- ワケあって再送要求等のプロトコルを組めない(通信相手は既に稼働中で変更なんてできない等)
- Receive Overrun Error(CE_OVERRUN/0x0002) が発生(←ClearCommError()で確認)
- 受信側はWindows
もしそうであれば、この記事がお役に立つかも知れまへんでぇ~!
と言うか、、、、
最初に言い切ってしまいましょう!!!
この記事を読んで解決せぇへん場合、
これ以上ググっても無駄でっせぇ~!!!
なんでそんな事が言い切れるのかって?
ヤフりまくりも、
ビングりまくりも、
ライコまくりも、
やったけど・・・・
でも、答えは見つからんかった・・・
なんとか一応解決したけど、超大変やった!!
本記事に書いてある事を試される場合、ご自身の責任で実行してください。
本記事に書いてある事を実行されて不具合や損害が発生しても、筆者はその責任を負いません。
もしかしたら、この記事で解決できるかも知れない内容
こんにちは。kei(@boot_kt)です。
冒頭にも書いたけど、この記事が役に立つとしたら、以下の悩みを持ってる人になります。
- RS-232Cのシリアル通信で、受信データに取りこぼしが発生する
- 欠落位置もサイズもタイミングもバラバラで法則は無い
- ワケあって通信速度を落とせない(通信相手は既に稼働中で変更なんてできない等)
- ワケあってフロー制御を使えない(通信相手は既に稼働中で変更なんてできない等)
- ワケあって再送要求等のプロトコルを組めない(通信相手は既に稼働中で変更なんてできない等)
- Receive Overrun Error(CE_OVERRUN/0x0002) が発生(←ClearCommError()で確認)
- 受信側はWindows
先に対応案一覧を書いとくでぇ~!
シリアル通信の受信でデータの欠損と言うか、取りこぼしが発生する場合、対応方法は以下しかないねん。(おそらく・・・)
ここに書いてある事を全て実行してもデータ漏れがなくならへん場合、、、、
もうできることはありまへぇ~ん
あきらめておくんなはれぇ~!!!!
もしくは、欠損発生頻度が許容できる程度に低いんやったら、それは仕方がないとして諦めておくんなはれぇ~。
- 1.シリアル通信の速度を落とす!
- 2.フロー制御を使う
- 3.FIFOバッファを変更する
- 4.レジストリを触って割り込み優先度を上げる(★ワイが解決したのはこれ!)
- 5.フロー制御に使う制御線の使えない分配器を使わない
- 6.シリアルデータを受信するEXEのセッションがConsoleじゃない!(★これもワイには効果があった!)
- 7.SetupComm関数を使ってみろ!(★これも効果あってん!)
- 8.FIFOバッファの大きな通信ボードを買って交換してみろ!(★ここまでの対策してもダメだったなら、これが最終手段!、悩んで試行錯誤する工数がもったいないから最初からこれやっとけ!、これでもダメならRS-232Cは諦めて違う通信を使いなはれ・・・)
解決案ではないし、必ず直るとは限りまへん。
あくまでも 対応案 やで。
やれる事がこれぐらいしか無いっちゅうこっちゃ。
そもそもRS-232Cのシリアル通信なんてそんなに信用度の高いものでもないしね。
信用度を少しでも上げるためにはフロー制御を使うべきやと思うけど、実際にはフロー制御を使っていないパターンも多い。(と言うか、普通は使わないみたい)
何が何でも100%正常に受信できないといけない!!
っていうのであれば、通信成功率100%保証しているものを使うべきでっせ。
(そんな通信あるんかな?)
確実にデータの受け渡しを成功させるためには、
- 通信手順を組む
- 再送要求の仕組みを作る
- 別の通信手段に切り替える or 併用する
みたいな事が必要になりまっせぇ~。
申し訳ないですけど、本記事に書かれている内容が正しいとは限りません。
と言いますか、私はシリアル通信について大した知識はありません。
あくまでも『受信データの欠落を改善した手順』を書いております。
ですので、シリアル通信について間違った事を書いているかも知れません。
間違を発見されましたら、ぜひご指摘ください。
対応案をやる前に、調べておいて欲しいこと
以下の二点を調べておいてやぁ~
- シリアルのデータは確実にマシンのCOMポートまで届いていること
Analyze232C や LINE EYE 等のアナライザで実際にCOMポートまでデータが届いていることを確認しましょう。アナライザで見てもデータが欠落しているなら、以下を確認してや。
- シリアルケーブルに断線は無いか?(テスターを使えば導通確認できます)
- シリアルケーブルが長すぎないか?
- フロー制御を使っている場合、途中で分配器等を使っていて、それらがフロー制御用の制御線に対応しているのか?
- シリアルケーブルの通り道に、電波や電器で干渉するものがないか?
- 自分が作った受信プログラムに不具合はないこと
シリアル通信できるソフトは沢山あるから、それらを使っても受信データの欠落が発生することを確認する。- COMポートまでデータは正常に届いている
- 他のソフトで正常に受信できる
Vectorにもあるでしょうし、SocketDebuggerシリーズとかでもいいと思います。
他のソフトでは正常に受信できる!!
という状態であれば、アンタが作った受信プログラムに不具合がありまっせ!!!
対応案の説明
1.シリアル通信の速度を落とす!
これが一番効果があるはずやねん!
可能であれば、まずはこれをやってくだっさい!
とは言え・・・・・・・
実際の業務においては、先に動いているサーバーやマシンがあって、それと通信する事があるよね。
そのサーバーが既に 115,200 bps とかの高速で動いていることがあるよね。
さすがに現在動いているサーバーの設定を変更することはでけへんやろし、他のマシンやサーバーにも影響を与えるよね。
なので、現実問題としては通信速度を変更することなんて出来へんという状況が多いよね。
変更することが出来へんのであれば、この手段は ボツ ですわ。
ですが、ニッチもサッチもいかへんのであれば、既存アプリの再テストを含めてでも通信速度を落とすことを話し合われる方が良いかも知れまへんで。
新規のプログラムやマシンだけでなんとかしようとしてもなかなか大変ですし。
通信速度を落とすのが最終的な工数は安く済む場合もありまっせ。
通信速度が速いと、マシン内部の処理が追いつかへんことがあるねん。
処理が追いつかん時に受信したデータは取りこぼしてもて、オーバーランエラーが発生するねん。
受信データ量・受信間隔・処理時間によるけど、可能な限り遅いbpsにするとええで。
2.フロー制御を使う
可能であればやけど、フロー制御を使いましょう。
フロー制御の詳しい説明はせぇへんけど、簡単に言うと、、、
(と言うか、ワイもよう知らんねんwwww)
送信側:『データ送りたいけど受信側が送るな言うてるから、送るの待つか・・・』ってのを制御線を見て確認する
受信側:『ヒマになったからデータ送ってきてもエエで!』ってのを制御線を使って送信側へ知らせる
送信側:『おっ! 受信側が送ってもエエって言うてるから、ほな送るわ!』ってのを制御線を見て確認する
っていう感じのもんですわ。
ただ、使わへんことが多いようですね。
また、制御線をショートさせて、常に『送信してきてオッケー!』となるシリアルケーブルがあるらしいので注意しましょう。
そういうケーブルを使うとフロー制御が役に立ちまへん。
フロー制御を使うことで受信不可のタイミングでは送信してこなくなるはず。
完璧ではないやろうけど、少しでも通信の成功度を上げるならフロー制御を使ってみてはどないでっしゃろ?
3.FIFOバッファを変更する
- コントロールパネル
- 管理ツール
- コンピューターの管理
- デバイスマネージャー
- COM1とかCOM2とか、使用しているCOMポートをダブルクリック
- [ポートの設定]タブ
- [詳細設定]ボタン
- [FIFOバッファを使用する(16550互換のUARTが必要)]のチェックON
- [受信バッファ(R)]の値を変更する
受信バッファを小さくしたり大きくしたりして確認してみてね。
多分やけど、受信バッファを小さくすれば、割り込みが増えるんじゃないかな?
シリアル通信のチップ内のバッファが枯渇する前に、頻繁にメインメモリへ受信データを移動しよっちゅう考えやと思いますわ。
こまめに割り込み発生(メインメモリに移動)させてバッファを空けてオーバーしないようにしているんでしょうね。
でも、割り込みが頻繁に発生するのは、それはそれで処理に時間取られて別の影響が出そうやけど。
ワイの場合、これの数値を小さくしたら余計に通信エラーが出まくった。
なので、どういう効果があるのかよく分かりまへん・・・
ま、お試しで調整してみてぇ~や。
4.レジストリを触って割り込み優先度を上げる
ワイがやって効果出たのはコレやねん!!!
シリアルデータ受信時の動き
- COMポートまで来たデータは、一旦シリアル通信用チップ内のFIFOバッファに溜まる。(FIFOバッファのサイズは16バイトのはず >> PC16550Dのデータシート )
- 14バイト(詳細設定ダイアログにて設定した 受信バッファー の値)溜まれば、(シリアル通信用チップ内のFIFOバッファ→メインメモリーへデータを移動させるための)割り込みを発生させる
- 割り込みが認められるのを待つ(待ってると言っても、ボケーっとしてるワケではなく、上記1番をこなしつつ待ってる)
- 割り込みが認められれば、14バイトをメインメモリへ移動(移動させた分、シリアル通信用チップ内のFIFOバッファに空きができる)
シリアル通信の事はようワカランけど、おそらく上記のような動きをしてるんとちゃうかな、って思っとります。
14バイト毎にメインメモリへ移動する事によって、チップ内のFIFOバッファが満杯になるのを防ごうとしてるんやろな、きっと。
でも、割り込みが認められる時間が長いと、チップ内のFIFOバッファにはデータが溜まる一方なのでそのうち満杯になってしまいますわな。
オーバーランエラー発生のメカニズム
で・・・・満杯を迎えた時に・・・・
鼻から牛乳!!!
めでたくオーバーランエラー発生ですわ(笑)
オーバーランエラー発生後に割り込みが認められれば14バイトをメインメモリに移動して、チップ内のバッファに空きが出来て、また正常に受信できるけど、満杯の間に来たデータは消滅ですわ。
オーバーランエラーが発生するポイント
んで、何が一番の問題かと言うと・・・・
割り込みが認められるのを待つ
&
認められるのが遅い
ってとこじゃないかなと思うねん。
大事な事なので
Windowsが何かの作業で忙しい時に、シリアル通信のチップが割り込みをかけてもすぐに処理できず待たされるのが問題。
ワシを待たせるっちゅうのはどゆことやねんっ!!!
ワシやのうて、他のヤツを待たせんかいっ!!
ちゃっちゃっとワシの割り込みを処理したらんかいっ! しかしっ!
しかしっ!
ってなりますよね。
で、、、、、優先度を上げましょう って話になりますわな。
優先度を上げる方法
これを書きたいがために、この記事を書いたようなモンですわ!!!
シリアル通信のチップの割り込みの優先度を上げるには、レジストリを変更しまっせぇ~
手順1:COMポートの割り込み番号を確認しましょう
手順2:レジストリを変更しましょう
- HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ PriorityControl
- 右側の画面で、右クリック → 新規 → DWORD(32ビット)値
- 値の名前: IRQ4Priority とする。
4 の部分は手順1で見たIRQ番号です。
- 値のデータ: 1
1~38まで設定するらしいですが、通常は1か2を入れるみたいです。
3以上入れても良いのかどうかは知りません。
1以上の値を入れておけば、割り込みの優先度が上がります。
シリアル通信用チップ内のFIFOバッファから、メインメモリへ移動するための割り込みが待たされる事が原因と考えたねん。
この設定によってその割り込みの優先度が上がり、割り込みが待たされる事がなくなって、改善した、、、、、と思ってます。
完璧に直ったワケでは無いけど、かなり改善したでぇ~!
5.フロー制御に使う制御線の使えない分配器を使わない
一つの送信データを複数マシンで受信したい場合、シリアル信号を分配する分配器を使っている場合があるでしょう。
フロー制御を使うにはシリアルケーブルの制御用のピンという線を使うねん。
でも、分配器が制御線に対応していない事があって、フロー制御が効かない事がありまんねん。
ってことで、分配器を止めるか、フロー制御無しでも正常受信できるようにする必要がありまっせぇ!
6.シリアルデータを受信するEXEのセッションがConsoleじゃない!
コマンドプロンプトを起動して、以下のコマンドを打ってみてぇ~や。
tasklist
シリアルデータを受信しているEXEのセッションは何になってまっか?
Console であればええけど、RDP-Tcp#0 とかになってまへんか?
WindowsのVerにもよるけど、Console も RDP-Tcp#0 も同じはずやねんけど、経験則としては動きが微妙に違うねん!!!
いや、EXEの動きは同じやけど、深い部分でEXE権限が違う気がするねん。
なので、理屈の説明はようせんけど、シリアルデータを受信するEXEはConsoleセッションの方が良さそうやねん。
リモートデスクトップでログインしてEXEを起動すると、そのEXEは RDP-Tcp#0 とか RDP-Tcp#1 とかになるねん。
マシンの前で直接ログインすれば Console セッションになりまっせ。
リモートデスクトップでログインすると、どうやっても Console セッションにはなりまへん。
tscon コマンド使っても無駄でっせ。
本記事は Windows Server 2008 R2 を対象としています。
Linuxで言うところの ps コマンドみたいものです。
7.SetupComm関数を使ってみろ!
これも効果があってん!
理由はワカランけど・・・
SetupComm関数は送信バッファと受信バッファの確保なんやけど・・・・・・
SetupComm関数を使わんでも送受信バッファは確保されてて、それも無限に使えるはず。
なので、わざわざSetupComm関数を使う必要が無いと思うねんけどなぁ・・・
でも・・・・
SetupComm( hFile, 1024 * 1024, 1024 * 1024 );
と書いたらうまく動くようになってん。
SetupComm関数でバッファを明示的に確保してる方が、何かと処理が速いんやろか?
ごめんなさい、理由は分かりません。
シリアル通信の仕組み
ワイはシリアル通信について詳しくないけど、CE_OVERRUNのメカニズムはこういう感じなのかなぁ?というイメージを持ってるねん。
正しいかどうかワカランけど、辻褄合ってるので、おそらく正しいんじゃないかな・・・
★ の部分で待たされるとまずいわけですよ、きっと。
割り込みが入ってすぐに許可されるわけでもないし。
参考品
Wi-FiやBluetoothの変換アダプタがあるだなんて、、、、知らなかった。