シリアルプロトコルの区切り/同期技術


24

非同期シリアル通信は今日でも電子機器に広く普及しているため、私たちの多くはそのような質問に時々出くわしたと思います。電子デバイスDと、PCシリアル回線(RS-232または同様のもの)で接続され、継続的に情報を交換する必要があるコンピューターを検討してください。すなわち、PCそれぞれコマンドフレームを送信しており、それぞれステータスレポート/テレメトリーフレームで応答しています(レポートはリクエストへの応答として、または独立して送信できます-ここでは実際には関係ありません)。通信フレームには、任意のバイナリデータを含めることができます。通信フレームが固定長パケットであると仮定します。X msDY ms

問題:

プロトコルは継続的であるため、受信側は同期を失ったり、進行中の送信フレームの途中で「結合」したりする可能性があるため、フレームの開始(SOF)がどこにあるかはわかりません。Aデータは、SOFに対する相対的な位置に基づいて異なる意味を持ち、受信したデータは破損する可能性があり、永久に破損する可能性があります。

必要なソリューション

短い回復時間でSOFを検出するための信頼性の高い区切り/同期スキーム(つまり、再同期に1フレーム以上かかることはありません)。

私が知っている(そして使用している)既存のテクニック:

1)ヘッダー/チェックサム -事前定義されたバイト値としてのSOF。フレームの最後のチェックサム。

  • 長所:シンプル。
  • 短所:信頼できません。不明な回復時間。

2)バイトスタッフィング:

  • 長所:信頼性が高く高速な回復で、どのハードウェアでも使用可能
  • 短所:固定サイズのフレームベースの通信には適していません

3)9番目のビットマーキング -各バイトに追加ビットを追加します。SOFでマークされたSOF 1とデータバイトには次のマークが付けられ0ます。

  • 長所:信頼性が高く、高速な回復
  • 短所:ハードウェアサポートが必要です。ほとんどのPCハードウェアおよびソフトウェアでは直接サポートされていません。

4)8番目のビットマーキング -上記の一種のエミュレーション。9番目ではなく8番目のビットを使用し、各データワードに7ビットのみを残します。

  • 長所:信頼性の高い高速リカバリは、どのハードウェアでも使用できます。
  • 短所:従来の8ビット表現と7ビット表現の間のエンコード/デコードスキームが必要です。やや無駄だ。

5)タイムアウトベース -定義されたアイドル時間の後に来る最初のバイトとしてSOFを想定します。

  • 長所:データオーバーヘッドなし、シンプル。
  • 短所:それほど信頼できません。Windows PCなどのタイミングの悪いシステムではうまく動作しません。潜在的なスループットのオーバーヘッド。

質問: 問題に対処するために存在する他の可能な技術/解決策は何ですか?上記のリストで簡単に回避できる短所を指摘できますか?システムプロトコルをどのように設計しますか(または設計しますか)?

serial  communication  protocol  brushless-dc-motor  hall-effect  hdd  scr  flipflop  state-machines  pic  c  uart  gps  arduino  gsm  microcontroller  can  resonance  memory  microprocessor  verilog  modelsim  transistors  relay  voltage-regulator  switch-mode-power-supply  resistance  bluetooth  emc  fcc  microcontroller  atmel  flash  microcontroller  pic  c  stm32  interrupts  freertos  oscilloscope  arduino  esp8266  pcb-assembly  microcontroller  uart  level  arduino  transistors  amplifier  audio  transistors  diodes  spice  ltspice  schmitt-trigger  voltage  digital-logic  microprocessor  clock-speed  overclocking  filter  passive-networks  arduino  mosfet  control  12v  switching  temperature  light  luminous-flux  photometry  circuit-analysis  integrated-circuit  memory  pwm  simulation  behavioral-source  usb  serial  rs232  converter  diy  energia  diodes  7segmentdisplay  keypad  pcb-design  schematics  fuses  fuse-holders  radio  transmitter  power-supply  voltage  multimeter  tools  control  servo  avr  adc  uc3  identification  wire  port  not-gate  dc-motor  microcontroller  c  spi  voltage-regulator  microcontroller  sensor  c  i2c  conversion  microcontroller  low-battery  arduino  resistors  voltage-divider  lipo  pic  microchip  gpio  remappable-pins  peripheral-pin-select  soldering  flux  cleaning  sampling  filter  noise  computers  interference  power-supply  switch-mode-power-supply  efficiency  lm78xx 

図4は3よりも無駄なだけ1/8である
ニック・ジョンソン

@NickJohnson同意しますが、(3)にも「Wasteful」なものを追加することを提案しているだけです:)
Eugene Sh。

通信エラーに関する前提を十分に説明したとは思わない。通信が「完全」、すなわちエラーなし、またはすべてのエラーが通信ハードウェアによって検出および識別される(「通信はパリティを使用し、それらは単一ビットエラーのみである」など)と仮定していますか?
gbulmer

受信者はバイトの途中で参加でき、たとえばビット8をビット4として解釈できます。したがって、9番目のビットマーキングは信頼できません。
ティモシーボールドウィン

@gbulmer元の仮定は、チャネルが完全であり、問​​題は最初のミスシンクロナイゼーションによってのみ発生する可能性があるということです。これらの仮定の下で、私が言及していた「信頼性」は再同期のみに関連しています。上記のリストでは、これらの手法はすべて、最初の手法を除き、100%の成功を保証しています。ただし、おそらくエラーチェック方式とフレーミングをこのように分離しないでください。
ユージーンSh。

回答:


15

システムプロトコルをどのように設計しますか(または設計しますか)?

私の経験では、誰もが予想以上に通信システムのデバッグに多くの時間を費やしています。したがって、通信プロトコルを選択する必要があるときはいつでも、可能な限りシステムをデバッグしやすくするオプションを選択することを強くお勧めします。

いくつかのカスタムプロトコルを設計することをお勧めします-楽しくて非常に教育的です。ただし、既存のプロトコルを確認することもお勧めします。ある場所から別の場所にデータをやり取りする必要がある場合、他の誰かがすでに多くの時間をデバッグに費やしていた既存のプロトコルを使用しようと非常に努力します。

独自の通信プロトコルをゼロから作成することは、新しいプロトコルを作成するときに誰もが抱えている同じ一般的な問題の多くに反する可能性が高いです。

組み込みのコンピューター通信用の優れたRS232ベースのプロトコルにリストされているダースの組み込みシステムプロトコルがあります。要件に最も近いのはどれですか。

何らかの状況により既存のプロトコルを正確に使用できなくなったとしても、要件にほぼ適合するプロトコルから始めて、それを調整することで、何かをより迅速に動作させる可能性が高くなります。

悪いニュース

私が前に言いました

残念ながら、すべての通信プロトコルがこれらの便利な機能をすべて備えていることは不可能です。

  • 透過性:データ通信は透過的で「8ビットクリーン」-(a)可能なデータファイルはすべて送信でき、(b)ファイル内のバイトシーケンスは常にデータとして処理され、他の何かと誤解されることはありません、および(c )宛先は、追加や削除をせずに、エラーなしでデータファイル全体を受信します。
  • 単純なコピー:パケットの形成は、変更せずにパケットのソースからデータフィールドにデータを盲目的に単純にコピーする場合に最も簡単です。
  • 一意の開始:パケットの開始記号は、ヘッダー、ヘッダーCRC、データペイロード、またはデータCRCのどこにも発生しない既知の固定バイトであるため、簡単に認識できます。
  • 8ビット:8ビットバイトのみを使用します。

通信プロトコルにこれらの機能をすべて搭載する方法があれば、私は驚き、喜んでいます。

良いニュース

問題に対処するために存在する他の可能なテクニック/ソリューションは何ですか?

多くの場合、テキスト端末の人間が通信デバイスのいずれかを交換できると、デバッグがはるかに簡単になります。これには、プロトコルが比較的時間に依存しないように設計する必要があります(人間が入力したキーストローク間の比較的長い休止中にタイムアウトしません)。また、このようなプロトコルは、人間が入力して画面で読みやすいバイトの種類に制限されています。

一部のプロトコルでは、メッセージを「テキスト」モードまたは「バイナリ」モードで送信できます(すべての可能なバイナリメッセージに、同じことを意味する「同等の」テキストメッセージが必要です)。これにより、デバッグがはるかに簡単になります。

一部の人々は、印刷可能な文字のみを使用するようにプロトコルを制限することは「無駄」であると考えているように見えますが、デバッグ時間を節約することはしばしば価値があります。

既に述べたように、ヘッダーの開始バイトとヘッダーの終了バイトを含む任意のバイトをデータフィールドに含めることを許可すると、レシーバーが最初にオンになったときに、レシーバーが誤って同期する可能性があります1つのパケットの中央にあるデータフィールドのヘッダーの開始(SOH)バイトのように見えるもの。通常、受信者は、その擬似パケットの最後に不一致のチェックサムを取得します(通常、2番目の実際のパケットの中間です)。次のSOHを探す前に、疑似メッセージ全体(その2番目のパケットの前半を含む)を単純に破棄するのは非常に魅力的です。その結果、受信者は多くのメッセージの同期を維持できません。

alex.forencichが指摘したように、はるかに良いアプローチは、レシーバーが次のSOHまでバッファの先頭でバイトを破棄することです。これにより、受信者は(おそらく、そのデータパケット内のいくつかのSOHバイトを処理した後)2番目のパケットですぐに同期できます。

上記のリストで簡単に回避できる短所を指摘できますか?

Nicholas Clarkが指摘したように、Consistent- Overhead バイトスタッフィング(COBS)には固定サイズのフレームでうまく機能する固定オーバーヘッドがあります。

見落とされることが多いテクニックの1つは、専用のフレーム終了マーカーバイトです。伝送の途中でレシーバーがオンになった場合、専用のフレーム終了マーカーバイトがレシーバーの同期を高速化します。

パケットの途中で受信機がオンになり、パケットのデータフィールドにパケットの開始(擬似パケットの開始)のように見えるバイトが含まれている場合、送信機は一連のそのパケットの後のフレーム終了マーカーバイトのデータフィールド内のそのような擬似パケット開始バイトは、次のパケットをすぐに同期して正しくデコードするのに干渉しない-非常に不運でチェックサムであってもその擬似パケットの

がんばろう。


この回答は、以前に受け入れられた回答(申し訳ありませんが、@ DaveTweed)を再考する価値があり、リンクされた記事は確かにこのトピックの必読です。お時間をいただきありがとうございます。
ユージーンSh。

1
COBSを指摘してくれて嬉しいので、答えを書く必要はありません:-)
ニルスピペンブリン

11

バイトスタッフィングスキームは、長年にわたって私にとって非常に効果的です。ソフトウェアまたはハードウェアに簡単に実装でき、標準のUSB-to-UARTケーブルを使用してデータのパケットを送信でき、心配することなく高品質のフレーミングが得られるため、優れています。タイムアウト、ホットスワップ、またはそのような何か。

長さバイト(256を法とするパケット長)とパケットレベルのCRCを組み合わせたバイトスタッフィング方法を提唱し、パリティビット付きのUARTを使用します。長さバイトは、ドロップされたバイトの検出を保証します。これは、パリティビットでうまく機能します(ほとんどのUARTは、パリティに失敗したバイトをドロップするため)。次に、パケットレベルのCRCにより、セキュリティが強化されます。

バイトスタッフィングのオーバーヘッドについては、COBSプロトコルを検討しましたか?これは、送信された254(フレーミング、CRC、LENなど)ごとに1バイトの固定オーバーヘッドでバイトスタッフィングを行う天才的な方法です。

https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing


これは、最悪の場合にデータの2倍にバイトスタッフィングが爆発するのを防ぐ優れた方法です。同様の、よりアプリケーション固有のスキームを使用しましたが、これが標準的な方法で説明されているのを見るのは素晴らしいことです。これからCOBSを使用します
...-wjl

1
COBSを指摘してくれてありがとう-非常にきれいな小さなアルゴリズム。
ニックジョンソン

6

オプション#1、SOHとチェックサムは信頼性が高く、次の破損していないフレームで回復します。

メッセージの長さを既に知っているか、SOHの直後のバイトに長さがエンコードされていると仮定しています。チェックバイトは、メッセージの最後に表示されます。また、少なくとも最長のメッセージと同じ長さのデータ用の受信側バッファーも必要です。

バッファの先頭にSOHバイトが表示されるたびに、潜在的にメッセージの始まりになります。バッファをスキャンして、そのメッセージのチェック値を計算し、バッファ内のチェックバイトと一致するかどうかを確認します。その場合、完了です。それ以外の場合、次のSOHバイトに到達するまでバッファからデータを破棄します。

メッセージが実際にデータエラーを持っている場合、このアルゴリズムはそれを破棄することに注意してください。チェックアルゴリズムに前方誤り訂正が含まれる場合、訂正可能なエラーについて、潜在的な各メッセージの整合性を確認できます。

メッセージの長さが固定されている場合は、SOHバイトを完全に省くことができます。有効なチェック値について、可能な限りすべての開始位置をテストするだけです。

また、チェックアルゴリズムを省いてSOHバイトのみを保持することもできますが、これによりアルゴリズムの決定性が低下します。考えは、有効なメッセージ調整のために、SOHは常にメッセージの先頭に表示されるということです。アライメントが正しくない場合、データストリームの次のバイトが別のSOHになることはほとんどありません(メッセージデータにSOHが表示される頻度に依存します)。これだけで有効なSOHバイトを選択できます。(これは基本的に、T1やE1などの同期通信サービスのフレーミングの仕組みです。)


信頼性はいくらか確率的だと思いますか?エラーチェック/訂正コードの強度によっては、ランダム/任意のバイトストリームで正しいと思われるフレーム発生する場合があります。
ユージーンSh。

もちろん、それは可能です。しかし実際には、十分に強力なチェックアルゴリズムを選択するのは比較的簡単です。
デイブツイード

データエラーの割合がゼロ以外の場合、常に無効なメッセージを受け入れる可能性はゼロではありません。
ニックジョンソン

@NickJohnson完全にクリーンなチャネルを想定すると、このアプローチにはまだ(理論的に)不一致があります。もちろん、それらの確率は無視できます。
ユージーンSh。

1
あなたはすでにこれを知っており、すでにそれをパスで言及していることを知っていますが、メッセージ全体をバッファリングしないバージョン、または単にデコード方法について怠zyなバージョンは信頼性が低くなります。「false」SOHの後の次のSOHバイトの代わりに、不一致のチェックサムの後の次のSOHバイトで再同期すると、実際のメッセージの開始を破棄し、多くのメッセージの同期を維持する可能性が非常に高くなります。最悪の場合、永遠に。
ホッブズ

5

言及されていないが(特にインターネットで)広く使用されているオプションの1つは、ASCII /テキストエンコーディングです(実際、ほとんどの最新の実装はUTF-8を想定しています)。私の経験では、ハードウェアの人はこれを嫌いますが、ソフトウェアの人は他のほとんどすべてよりもこれを好む傾向があります(ほとんどすべてのものをテキストベースにするUnixの伝統に由来します)。

テキストエンコーディングの利点は、フレーミングに印刷不能文字を使用できることです。たとえば、最も単純なのは0x00、フレームの開始とフレームの0xff終了を示すようなものを使用することです。

データをテキストとしてエンコードする主な方法は2つあります。

  1. ハードウェア/アセンブリの担当者がこれを行うように求められると、おそらく16進エンコーディングとして実装されます。これは、単にバイトをASCIIの16進値に変換することです。オーバーヘッドが大きい。基本的に、実際のデータバイトごとに2バイトを送信します。

  2. ソフトウェア担当者がこれを行うように求められた場合、おそらくbase64エンコーディングとして実装されます。これは、インターネットの事実上のエンコードです。電子メールのMIME添付ファイルからURLデータエンコーディングまで、すべてに使用されます。オーバーヘッドは正確に33%です。単純な16進エンコーディングよりもはるかに優れています。

または、バイナリデータを完全に破棄してテキストを送信することもできます。この場合、最も一般的な手法は、データを改行で区切る(ちょうど"\n"または"\r\n")ことです。NMEA(GPS)、モデムATコマンド、およびAdventech ADAMセンサーは、この最も一般的な例の一部です。

これらすべてのテキストベースのプロトコル/フレーミングには、次の長所と短所があります。

プロ:

  • デバッグが簡単
  • スクリプト言語で簡単に実装できます
  • ハードウェアは、Hyperterminal / minicomを使用して簡単にテストできます
  • ハードウェアに簡単に実装できます(PICのような本当に小さなマイクロでない限り)
  • 固定サイズのフレームまたは可変サイズのいずれかです。
  • 予測可能なフレーミングと高速同期回復時間(現在のフレームの終わりに回復)

短所:

  • 純粋なバイナリ送信と比較して非常に大きなオーバーヘッド(テキストI / Oは"0"、4バイトの0x00000000の代わりに1バイト(0x30)を送信するような数値を「圧縮」することもできます)
  • PICのような非常に小さなマイクロに実装するにはそれほどきれいではありません(ライブラリにsprintf()関数が含まれていない限り)

個人的には、プロはデメリットをはるかに上回っています。デバッグの容易さだけでも5ポイントとしてカウントされます(したがって、単一のポイントだけでもすでに両方の短所を上回っています)。


それから、ソフトウェアの開発者からよく出てくる、あまり慎重に考えられていないソリューションがあります。フレーミングを考えずにエンコードされたデータを送信します。

過去に未加工のXMLを送信したハードウェアとインターフェイスする必要がありました。XMLがすべてのフレーミングでした。幸いなことに、<xml></xml>タグによってフレームの境界を見つけるのはかなり簡単です。私にとって大きな欠点は、フレーミングに複数のバイトを使用することです。また、タグには属性が含まれている可能性があるため、フレーミング自体は修正されない可能性があります。<tag foo="bar"></tag>したがって、最悪の場合にフレームの開始を見つけるためにバッファリングする必要があります。

最近、私は人々がシリアルポートからJSONを送信し始めるのを見てきました。JSONフレーミングでは、せいぜい推測にすぎません。フレームを検出する"{"(または"[")文字しかありませんが、それらはデータにも含まれています。そのため、フレームを把握するには、再帰降下パーサー(または少なくともブレースカウンター)が必要になります。少なくとも、現在のフレームが時期尚早に終了するかどうかを知ることは簡単です。"}{"つまり"]["、JSONで不正であり、古いフレームが終了して新しいフレームが開始されたことを示します。


テキストエンコーディングの場合、base85あり、33%ではなく25%のオーバーヘッドしかありません。
デイブツイード

私はそれを第4の方法のサブセット/バリエーションと考えます。
ユージーンSh。

@EugeneSh .:技術的には、バイトスタッフィングのサブセットです。この場合も、ビットマーキングのサブセットであると考えているため、このあいまいさがそれを独自のカテゴリにしている理由を理解できます。また、マーキングビットは使用されないため、テキストエンコーディングのほとんどの実装をビットマーキングのサブセットと見なすことはできません(たとえば、通常、区切り文字として使用し<>電子メールは改行を使用すると考えています。注:電子メールは適切なフレーム形式です。私の友人はRS232を使用して自宅のメール配信サーバーを実行していました)
-slebetman

4

「X番目のビットマーキング」として説明するものは、データを一定の割合で展開するプロパティを持つ他のコードに一般化でき、一部のコードワードは区切り文字として自由に使用できます。多くの場合、これらのコードには他の利点もあります。CDは8〜14の変調を使用します。これにより、各1間の最大実行長が0ビットになります。他の例には、前方誤り訂正ブロックコード、エラー検出および訂正情報をエンコードするために追加ビットを使用するがあります。

言及していないもう1つのシステムは、チップセレクトラインなどの帯域外情報を使用して、トランザクションまたはパケットを区切ることです。


エラー修正コードは、質問から少し外れています。とにかく、これらのスキームのいずれかに追加する必要があります。参照している「帯域外情報」は、「ハードウェアフロー制御」と同じですか?
ユージーンSh。

しゅう -実際には、受信側で計算コストがかかりますが、フレーミングにエラーチェックビットを使用することは完全に有効です。可能性のあるすべてのデータ配置に対してエラー計算を行うだけで、成功するのは破損していないフレームでの有効な配置です。もちろん、フレーム破損している場合は、見つかりません。
デイブツイード

@DaveTweedまあ、それは私が最初のテクニックで意味したこととほとんど同じです。それとも私はあなたを誤解していますか?
ユージーンSh。

いいえ、あなたは誤解していません。それが私が話していたものです。ただし、「詐欺」は間違っています。信頼性が高く、実際の送信エラーに対しても堅牢にすることができます。
デイブツイード

@DaveTweed復旧時間はどうですか?堅牢にする方法の例はありますか?
ユージーンSh。

3

別のオプションは、ラインコーディングと呼ばれるものです。ラインコーディングは、送信を容易にする特定の電気的特性を信号に与え(DCバランスと最大ランレングス保証)、フレーミングとクロック同期の制御文字をサポートします。ラインコードはすべての最新の高速シリアルプロトコルで使用されます-10M、100M、1G、および10Gイーサネット、シリアルATA、FireWire、USB 3、PCIeなど。共通のラインコードは8b / 10b64b / 66bおよび128b / 130bです。また、フレーミング情報を提供せず、DCバランスとクロック同期のみを提供するシンプルなラインコードもあります。これらの例は、MachesterおよびNRZです。すぐに同期する場合は、おそらく8b / 10bを使用します。他の回線コードは急いで同期するようには設計されていません。上記のようなラインコードを使用するには、送受信にカスタムハードウェアを使用する必要があります。

オプション5に関しては、標準のRS232シリアルは、回線が数バイトの時間アイドル状態である場合のブレークの送受信をサポートすることになっています。ただし、これはすべてのシステムでサポートされているわけではありません。

一般的に、最も簡単で信頼性の高いフレーミング方法は、長さフィールドと単純なCRCまたはチェックサムルーチンと組み合わせたオプション1です。デコードルーチンは単純です。開始バイトを取得するまでバイトを破棄し、長さフィールドを読み取り、フレーム全体を待機し、チェックサムをチェックし、可能であれば保持します。チェックサムが悪い場合は、開始バイトを取得して繰り返すまで、バッファからバイトを破棄し始めます。この手法の主な問題は、実際にはフレームバイトの開始ではないフレームバイトの開始を見つけることです。この問題を軽減するための1つの方法は、フレームの開始バイトと同じ値のバイトを別の制御文字でエスケープし、エスケープされたバイトを異なる値に変更することです。この場合、新しい制御バイトでも同じことを行う必要があります。


これは、ニックジョンソンの答えと同じです。
デイブツイード
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.