はじめに
オープンソースに関して言えば、手放しで賞賛しない人はおそらくほとんどいないでしょう。学生はオープンソース コードを通じて知識を学び、プログラマーはオープンソース クラス ライブラリを通じて他の人の成功体験を獲得し、目の前にあるプロジェクトを時間通りに完了することができ、販売者はオープンソース ソフトウェアを通じてお金を稼ぎます...つまり、誰もが幸せです。ただし、オープンソース ソフトウェアやクラス ライブラリの主な欠点は、そのほとんどに詳細なドキュメントや使用例がないか、ソフトウェア コードは使用するためだけのものであり、ドキュメント、サンプル、ポストサービス サービスに対してのみ料金が発生することです。 。結局のところ、有名な NBA 選手が「私はまだ家族を養わなければならないので、1,000 万ドル未満の契約交渉はしないでください。そうでなければ、失業したほうがマシです。」と言ったように、それも不思議ではありません。確かに、オープンソースを支持する人は家族も養わなければならないので、多少のお金を請求するのは大げさではありません。お金をかけずに知識を学びたい場合は、インターネットとインターネットを使用するしかありません。プロジェクトの小さな問題を解決するだけで十分です。
この連載で紹介するのはWebフレームワークやオープンソースサーバーではありませんが、プログラマーであれば様々な問題に遭遇すると思います。問題が単純であればあるほど難しくなり、それを実行できる人を見つけるのが難しくなります。一日中「アーキテクチャ」、「コンポーネント」、「フレームワーク」だけを扱っていなければ、私が言ったことは必ず役立つと思います。
1 シリアルポート通信の概要
1.1 一般的な Java シリアルポートパッケージ
1.2 シリアルポートパッケージのインストール (Windows 環境)
2 シリアルポート API の概要
2.1 javax.comm.Com mPort
2.2 javax.comm.commportidentifier
2.3javax.comm.serialport
3 シリアル通信 よくあるパターンとその問題点
3.1 イベントリスニングモデル
3.2 シリアルポート読み取りデータのスレッドモデル
3.3 3 番目の方法
4 結論
1 シリアルの概要通信多くの組み込みシステムまたはセンサー ネットワーク アプリケーションやテストでは、PC を介した組み込みデバイスまたはセンサー ノードとの通信が必要です。その中で、最も一般的に使用されるインターフェイスは、RS-232 シリアル ポートとパラレル ポートです (USB インターフェイスの複雑さと大量のデータ転送を必要としないという事実を考慮すると、USB インターフェイスはまだ贅沢すぎて使用できません)さらに、現在、SUN に加えて、USB を直接サポートする Java ライブラリは他にありません。 SUN の CommAPI は、一般的に使用される RS232 シリアル ポート通信と IEEE1284 パラレル ポート通信をそれぞれサポートします。 RS-232-C (EIA RS-232-C としても知られ、以下 RS232 と呼びます) は、電子工業会 (EIA) が Bell Systems、モデム メーカー、およびシリアル通信用のコンピュータ端末メーカーと共同で 1970 年に開発しました。規格。 RS232 は、データの送受信を同時に行うことができる全二重通信プロトコルです。 1.1 共通 Java シリアル ポート パッケージ
現在、一般的な Java シリアル ポート パッケージには、1998 年に SUN によってリリースされたシリアル通信 API が含まれています。シリアル通信 API とオープンソース実装。 SUN の API は Windows 上で一般的に使用されており、IBM の実装は SUN と API レベルで同じであるため、オープンソースの実装は 2 つの大手メーカーの製品ほど安心できません。ここでは、SUN のシリアル ポート API の使用方法のみを紹介します。 Windows プラットフォーム。
1.2 シリアル ポート パッケージのインストール (Windows 環境)
SUN の Web サイトにアクセスして、javacomm20-win32.zip をダウンロードします。内容は次のとおりです:
必要に応じて、その手順 (Readme.html) に従ってください。シリアル通信にシリアル ポート パッケージを使用するには、環境変数の設定に加えて、win32com.dll をネットワーク アプリケーションでシリアル ポート API を使用すると、他のより複雑な問題が発生することに注意してください。ご興味がございましたら、CSDN コミュニティの投稿「Javacomm20 を使用して Web ページ上のクライアント シリアル ポートを読み取るアプレットの問題について」をご覧ください。
2 シリアル ポート API の概要
2.1 javax.comm.CommPort
これは、基礎となるシステムによってサポートされるポートを記述するために使用される抽象クラスです。これには、すべての異なる通信ポートに共通するいくつかの高レベル IO 制御メソッドが含まれています。 SerialPort と ParallelPort はどちらもそのサブクラスであり、前者はシリアル ポートの制御に使用され、後者はパラレル ポートの制御に使用されます。両方とも、基礎となる物理ポートの制御方法が異なります。ここでは SerialPort のみに注目します。
2.2 javax.comm.CommPortIdentifier
このクラスは主にシリアルポートの管理と設定に使用され、シリアルポートのアクセス制御のためのコアクラスです。主に次の方法が含まれています。uavax.comm.comm.serialportの利用可能な通信ポートがあるかどうかを決定しますシリアル通信に必要な最小限の機能セットを定義します。これを通じて、ユーザーはシリアル ポートの読み取り、書き込み、設定を直接行うことができます。
2.4 シリアル ポート API の例
長い文章は、小さな例ほど明確にすることはできません。シリアル ポート パッケージに付属するサンプルを見てみましょう。SerialDemo の小さなコードで、理解を深めます。シリアル ポート API コア クラスの使用方法に関する知識。 "//ポートを介して繰り返します。hasmorelements()){
system.out.println(portid.getName());
2.4.2 シリアルポートパラメータの設定
シリアルポートには通常、シリアルポートを開く前に設定できる次のパラメータがあります:
ボーレート、入出力フロー制御、データビット数、ストップを含むビットと偶数パリティ。 OrSserialport Sport; Sport.setSerialportparams (ボーレート、データビット、ストップビット、パリティ) // 入出力制御フロー FlowControlout);2.4.3 シリアル ポートの読み取りと書き込み
シリアル ポートの読み取りと書き込みを行う前に、シリアル ポートを開く必要があります:
CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(PortName);
try {
SerialPort sPort = (SerialPort ) portId.open("シリアルポート所有者名", タイムアウト待ち時間);
} catch (PortInUseException e) {//ポートが占有されている場合はこの例外をスローします
E Throw New SerialConnectionException (e.getMessage ()); } // データの書き込みに使用されます OutputStream OS = New BufferEtputStream (Sport.GetoutPutstream ()) ;シリアルポートからデータを読み取る場合InputStream is = new BufferedInputStream(sPort.getInputStream());int selectedData = is.read();読み出されるデータはint型であり、必要に応じて他の型に変換できます。 ここで注意すべき点は、Java 言語には符号なし型がない、つまりすべての型が符号付きであるため、byte から int に変換するときに特別な注意を払う必要があるということです。バイトの最上位ビットが 1 の場合、int 型に変換するときに 1 がプレースホルダーとして使用されるためです。このように、元のバイト型番号 10000000 は int 型になり、1111111110000000 になります。これは非常に深刻な問題であり、回避する必要があります。 3 シリアル ポート通信の一般的なモードと問題点 ようやく私が最も嫌いな基礎知識の話が終わりました。今回は、シリアル ポート アプリケーションの研究に焦点を当ててみましょう。シリアル ポートへのデータの書き込みは非常に簡単なので、ここではシリアル ポートからのデータの読み取りだけに焦点を当てます。通常、シリアル通信アプリケーションには 2 つのモードがあります。1 つは、SerialPortEventListener インターフェイスを実装してさまざまなシリアル ポート イベントを監視し、それに応じて処理することです。もう 1 つは、特にデータの受信を担当する独立した受信スレッドを確立することです。これら 2 つの方法は場合によっては重大な問題を抱えているため (問題については、今はオープンのままにしておきます)、私の実装では、この問題を解決するために 3 番目の方法を使用します。 3.1 イベントリスニングモデル次に、イベントリスニングモデルがどのように機能するかを見てみましょう:l まず、ポート制御クラス (SManager など) に「implements SerialPortEventListener」を追加する必要がありますl初期化 次のコードを追加します: try { SerialPort sPort.addEventListener(SManager); } catch (TooManyListenersException e) { sPort.close(); Throw new SerialConnectionException("追加されたリスナーが多すぎます") ;または}
Sport.notifyondataavaability (TRUE);
L で Public Void Serialevent (SerialPortevent E) メソッドを作成し、次のインシデントを判断します:
Bi -CD -CTS test .
CTS - 送信許可。
DATA_AVAILABLE - データが到着しました。
DSR - データデバイスの準備ができました。
FE - フレームエラーです。
OE - オーバーフローエラーです。
OUTPUT_BUFFER_EMPTY - 出力バッファが空になりました。
PE - パリティチェックエラーです。
RI - 呼び出し音の表示 一般に最もよく使用されるのは DATA_AVAILABLE - シリアル ポートでのデータ到着イベントです。つまり、シリアル ポートにデータが到着すると、serialEvent で受信したデータを受信して処理できます。しかし、実際にやってみると、非常に深刻な問題に遭遇しました。 まず私の実験について説明します。私のアプリケーションは、シリアル ポートからセンサー ノードによって返送されたクエリ データを受信し、結果をアイコンの形式で表示する必要があります。シリアル ポートによって設定されたボー レートは 115200 です。Kawaguchi は 128 ミリ秒ごとに一連のデータ (約 30 バイト) を返し、その周期 (つまり持続時間) は 31 秒です。実際の測定では、1 サイクルで 4900 バイト以上が返されるはずですが、イベント リスニング モデルを使用すると、最大でも 1500 バイト未満しか受信できず、これらのバイトがどこに行ったのかわかりません。データのその部分が失われたことを知っています。これは、serialEvent() 内のすべての処理コードをコメント アウトし、印刷コードのみを残した結果であることに注意してください。深刻なデータ損失に耐えられなかったので、他の方法を使用することにしました。 dataのシリアルポート読み取りのスレッドモデル名前は示唆されています、このモデルはスレッドの形でデータを受信する操作を書き込みます: Runnable() { public void run() { newData = is.read();System.out.println(newData); ………
}}} A Readdataprocess.start () }} 私のアプリケーションでは、受信したデータを 1 つのキャッシュにパックしてから、別のキャッシュを開始します。キャッシュからデータを取得して処理するスレッド。 2 つのスレッドはプロデューサー/コンシューマー モードで連携し、データ フローは次の図のようになります。
このようにして、データ損失の問題を解決することに成功しました。しかし、満足してから間もなく、同様に深刻な問題を発見しました。今回はデータの損失はなくなりましたが、センサーの省電力機能により 1 サイクル (31 秒) 後にデータの送信が停止されましたが、シリアル ポートのスレッドは依然としてハードワーク中のままでした。シリアル ポートの読み取り操作を実行すると、受信したデータが引き続き継続的に印刷されていることもコンソール上で確認できます。センサー ノードから送信されたデータが速すぎて受信スレッドが処理できないため、InputStream は到着したがまだ処理されていないバイトを最初にキャッシュし、センサー ノードがデータを送信しなくなることがわかりました。データが表示されますが、コンソールでは引き続きデータが出力されるという奇妙な現象が発生します。唯一良い点は、最終的に受信したデータが確かに約 4900 バイトであり、損失がないことです。ただし、最後のデータが処理されるまでに約 1 分半かかりました。これはノードの動作サイクルよりもはるかに長かったです。この遅延はリアルタイム表示システムにとって大惨事です。
後から思ったのですが、データ受信が遅いのは2つのスレッド間の同期や通信が原因なのでしょうか?そこで、受信スレッドのコード内の処理コードをすべて削除し、受信したデータを出力する文だけを残しましたが、結果は同じでした。データ受信速度を妨げているのはスレッド間の通信ではなく、送信側のデータ送信速度が速すぎる場合にデータ受信遅延を引き起こすスレッドモデルのようです。ここで注意すべき点は、前の 2 つのモデルは、データ伝送速度がそれほど速くない場合には依然として有用ですが、特殊な場合には特別に処理する必要があるということです。
3.3 3番目の方法
長い間苦しんだ後(上司に毎日Lに促されました)、偶然、TinyOS(オープンソース)に私のアプリケーションに似た部分があると聞きました。 1.x バージョンの Java コード部分をダウンロードし、その処理方法を参照しました。問題を解決する方法は実際には非常に簡単で、根本原因から始めることです。根本的な原因は受信スレッドにあるのではないでしょうか? そうですね、受信スレッドと共有キャッシュを仲介としてキャンセルし、処理スレッドでシリアル ポート読み取りメソッドを直接呼び出して問題を解決します。処理スレッドも使用します。そしてキャンセルしますか? ----キャンセルするとアプリケーションのインターフェースがロックされるので、保持する必要があります) したがって、プログラムは次のようになります。 / PacketLength はデータ パケットの長さです
byte[] msgPack = new byte[PacketLength];W if ((newdata = is.read ())! = -1) {
msgpack [i] = (byte) newData; System.out.println (msgpack [i]); msgPack を返します。ここで注意すべき唯一の点は、シリアル ポートがデータの送信を停止した場合、またはデータがない場合、is.read() は常に -1 を返すということです。データの受信を開始したときに -1 が見つかった場合は、それを無視して受信を継続します。実際のデータをデータに受信します。4 まとめ
この記事では、シリアル通信の基礎知識とよく使われるいくつかのモードを紹介します。実践を通じて、いくつかの問題が提起され、最後には解決されました。最初の方法では、センサーの送信時間を 128 ミリ秒から 512 ミリ秒に増やしましたが、依然として重大なデータ損失が発生したことに注意してください。そのため、アプリケーションが非常に正確な結果を必要とする場合は、データを送信します。速度が非常に速い場合は、データを送信します。最初の方法は使用しないことをお勧めします。 2 番目の方法は、スレッドによって引き起こされる問題であるため、マシンによってパフォーマンスが異なるはずです。マルチスレッドの処理に優れたマシンの方が適しています。しかし、私のマシンはInter Pen 4 3.0デュアルコアCPU + 512DDRメモリです。遅延がひどいのですが、CPUはどのくらい強力ですか?したがって、比較的大量のデータを送信する場合には、3 番目の方法を使用することをお勧めします。しかし、世の中には問題がたくさんあり、既知の問題よりも未知の問題の方が多いかもしれません
Java シリアル通信に関する詳細な記事については、PHP を参照してください。中国語のウェブサイト!