ストップバイトとしての\ n \ rフェイルセーフはどのくらいですか?


8

私のUART通信では、送信されたメッセージの開始バイトと停止バイトを知る必要があります。スタートバイトは簡単ですが、ストップバイトはそれほどではありません。メッセージの最後に2つのストップバイト、つまり\ nと\ r(10進数で10と13)を実装しました。UARTはバイト0〜255の値でのみ機能するので、これはフェイルセーフですか?確率は低いですが、メッセージにストップバイトではない場合、メッセージに「10と13」という値が次々に含まれる可能性があると想像できます。

これを実装するより良い方法はありますか?


7
任意のデータを送信するには、パケットまたはバイトスタッフィングを使用する必要があります。あなたの場合、パターンが特定の場所に現れる確率は1/65536です。ランダムデータストリームが十分に長い場合、これは1になります。
Oldfart

4
コンテキストを教えてください。ストップビットはUART通信の一部ですが、ストップバイトですか?これは純粋なソフトウェアの問題のように聞こえ、送信者と受信者が合意した内容によって異なります。
ウォーレンヒル

2
@MariusGulbrandsenデータが完全に任意であり、厳密にテキストではない場合(ASCIIと考える)、null終了は機能しません。パケットを実装する必要があります。
RamblinRose

4
ところで、その一般的な方法は、改行の前にキャリッジリターンを置くことです "\x0D\x0A"
Adrian McCarthy

3
@AdrianMcCarthyそれを逆転させるポイントは、それが有効なシーケンスである確率を最小限に抑えることだと思います。とは言っても、Windowsの2つの行末が連続していると、中央にシーケンス\r\n\r\nが含まれます\n\r...
Mike Caron

回答:


14

これを防ぐ方法はいくつかあります。

  • 通常のメッセージで10/13の組み合わせを送信しないようにしてください(ストップバイトとしてのみ)。例:20 21 22 23 24 25:

20 21 22 23 24 25 10 13

  • エスケープ10および13(または、エスケープ文字を含むすべての非ASCII文字。例:送信するため20 21 10 13 25 26送信:(コメント:/ DanWのクレジットを参照)

20 21 1b 10 1b 13 25 26

  • メッセージを送信するときにパケットを定義します。たとえば、メッセージ20 21 22 23 24 25を送信する場合は、送信するバイト数を追加するので、パッケージは次のようになります。

<nr_of_data_bytes> <データ>

メッセージが最大256バイトの場合、以下を送信します。

06 20 21 22 23 24 25

つまり、6データバイトを受信すると、これで終わりです。その後10 13を送信する必要はありません。また、メッセージ内で10 13を送信できます。メッセージが長くなる可能性がある場合は、データサイズに2バイトを使用できます。

更新1:パケットを定義する別の方法

別の代替案は、特定の長さを持ち、多くのバリエーションを持つことができるコマンドを送信することです

10 20 30(常に2データバイトを持つコマンド10)

11 30 40 50(常に3データバイトを持つコマンド11)

12 06 10 11 12 13 14 15(コマンド12 + 1バイト後のデータバイト数)

13 01 02 01 02 03 ...(コマンド13 + 2バイト(後続の256の場合は01 02 + 258データバイト)

14 80 90 10 13(10 13で終わるASCII文字列が後に続くコマンド14)

更新2:接続不良/バイト損失

上記のすべてが機能するのは、UARTラインがバイトを正しく送信している場合のみです。より信頼できる送信方法を使用したい場合は、多くの可能性もあります。以下はその一部です。

  1. パッケージ内でチェックサムを送信します(CRCについてはGoogleを確認してください:巡回冗長検査)。CRCに問題がなければ、受信者はメッセージが(高い確率で)問題なく送信されたことを認識します。
  2. メッセージを再送信する必要がある場合は、確認応答(ACK /応答)メカニズムを使用する必要があります(送信者が何かを送信する、受信者が破損したデータを受信する、NACKを送信する(確認応答なし)、送信者は再送信することができます)。
  3. タイムアウト:受信者が時間内にACKまたはNACKを受信しない場合、メッセージを再送信する必要があります。

上記のメカニズムはすべて、単純にすることも、必要に応じて(必要に応じて)複雑にすることもできます。メッセージを再送信する場合は、メッセージを識別するメカニズムも必要です(シーケンス番号をパッケージに追加するなど)。


1
「通常のメッセージでは10/13の組み合わせを送信しないでください(ストップバイトとしてのみ送信するようにしてください)。」-あなたはどのデータを送信するためにどのように言っていませんでした 10/13組み合わせを含む-あなたはそれをエスケープする必要があります。したがって、 "20 10 13 23 10 13"は "20 1b 10 1b 13 23"として送信され、1bがエスケープ文字になります。
Dan W

1
提案されているように長さフィールドを使用すると、シリアルリンクが不良で1バイトを失うと問題が発生することに注意してください。すべてが同期しなくなります。
JonasSchäfer19年5

@DanW最初の1バイトまたは2バイトをデータバイト数として使用する場合、10または13がそれらのデータの一部であるかどうかは問題ではありません...したがって、20 10 13 23 10 13は06 20 10 13 23として送信できます。 10 13ここで、06は後続のデータバイト数です。
ミシェルケイツァー

@MichelKeijzers-はい、しかしそれはあなたが言及する2番目の解決策です。最初の解決策では、ストップバイトが送信されないようにするエスケープシーケンスの説明が欠落しています。
Dan W

どちらのアプローチも機能し、一般的に使用されていますが、OPが要求する範囲を超えていますが、必要に応じて追加できるさまざまな利点と欠点があります。
Dan W

13

ストップバイトとしての\ n \ rフェイルセーフはどのくらいですか?

送信した場合、任意のデータを送信->おそらくフェイルセーフでは不十分です。

一般的な解決策は、エスケープを使用することです。

文字0x02(STX-フレームスタート)および0x03(ETX-フレームエンド)は、送信されるデータストリーム内で一意である必要があると定義しましょう。これにより、メッセージの開始と終了を安全に検出できます。

これらの文字の1つをメッセージフレーム内で送信する必要がある場合、エスケープ文字(ESC = 0x1b)を前に付け、元の文字に0x20を追加することで置き換えられます。

元の文字に置き換えられました

0x02 -> 0x1b 0x22  
0x03 -> 0x1b 0x23  
0x1b -> 0x1b 0x3b  

受信者はこのプロセスを逆にします。彼がエスケープ文字を受信すると、この文字はドロップされ、次の文字が0x20だけ減算されます。

これは多少の処理オーバーヘッドを追加するだけですが、100%の信頼性があります(チェックサムメカニズムを追加で実装することで確認できる/送信エラーが発生しない場合)。


1
いい答えだ。ASCIIプロトコルに使用される一般的なエスケープ文字は'\x10'DLE(Data Link Escape)でした。Wikipediaの一部のページでは、DLEは逆の方法で使用されることが多いとされています。つまり、次のバイトはデータバイトではなく制御文字であるということです。私の経験では、それは一般的に脱出の反対の意味です。
エイドリアン・マッカーシー、

2
ここで注意すべき点の1つは、最悪の場合のバッファーサイズが2倍になることです。メモリが本当にタイトな場合、それは最善の解決策ではない可能性があります。
TechnoSam

1
@Rev元のキャラクターに0x20を追加する理由は何ですか?それなしでもエスケープスキームは機能しませんか?
Nick Alexeev

1
@NickAlexeev:ストリームから他の予約済み文字を削除すると、実際のフレーム境界を特定するのが簡単/高速になります。これにより、フレームの受信と解析(エスケープ解除を含む)を分離できます。これは、FIFOのない非常に遅いコントローラーやデータレートが高い場合に特に関係があります。そのため、着信バイト(STX / ETX間)をフレームバッファーにコピーし、フレームに完了のマークを付けて、より低い優先度で処理を行うことができます。
Rev1.0

@TechnoSam:いいですね。
Rev1.0

5

ご存知のとおり、ASCIIにはすでにこれらの関数用のバイトがあります。

  • 0x01:見出しの開始-開始バイト
  • 0x02:テキストの開始-ヘッダーの終了、ペイロードの開始
  • 0x03:テキストの終わり-ペイロードの終わり
  • 0x04:送信終了-ストップバイト
  • 0x17:送信ブロックの終了-メッセージは次のブロックに続く

また、ペイロード内にはさまざまな用途のコードがあります。

  • 0x1b:エスケープ(次の文字をエスケープします-ペイロードで使用して、次の文字がプロトコルで使用されるコードを記述する構造の1つではないことを示します)
  • 0x1c、0x1d、0x1e、0x1f:それぞれファイル、グループ、レコード、ユニットセパレーター-階層データの一部のストップバイトとスタートバイトを同時に使用

プロトコルは、ACK(0x06)およびNAK(0x15)の最も細かい粒度を指定する必要があります。これにより、否定応答データを再送信できます。この最も細かい粒度までは、(エスケープされていない)開始インジケーターの直後に長さフィールドを配置し、(他の回答で説明されているように)すべての(エスケープされていない)停止インジケーターにCRCを付けるのが賢明です。


私は任意のデータを送信しますが、ASCIIデータを送信していない場合、質問で「\ n \ r」を使用するのが混乱していた可能性があります。にもかかわらず、私この回答のように、それは、UART上でASCIIを送信するには非常に有益だ
CK

@MariusGulbrandsen:プロトコルがペイロードの場所と各ペイロードセクションでエスケープする必要があるコードを確立する限り、テキストのようなデータだけでなく、何でも送信できます。
エリックタワーズ

4

UARTはその性質上、フェイルセーフではありません。ここでは1960年代のテクノロジーについて話しています。

問題の根本は、UARTが10ビットごとに1回しか同期しないため、これらの同期期間の間で多くの意味不明なものが通過できることです。たとえば、個々のビットを複数回サンプリングするCANとは異なります。

データ内でダブルビットエラーが発生すると、UARTフレームが破損し、検出されずに通過します。スタート/ストップビットのビットエラーは、オーバーランエラーの形で検出される場合とされない場合があります。

したがって、生データまたはパケットを使用するかどうかに関係なく、EMIによって引き起こされるビットフリップが予期しないデータになる可能性は常にあります。

状況を少しでも改善するための「従来のUARTの誤動作」には、数多くの方法があります。同期バイト、同期ビット、パリティ、ダブルストップビットを追加できます。すべてのバイトの合計をカウントするチェックサムを追加してから(それを反転させる-なぜそうしないのか)、バイナリの1の数をチェックサムとしてカウントすることができます。これらはすべて広く使用されており、非常に非科学的であり、エラーを見逃す可能性が高くなります。しかし、これは1960年代から1990年代にかけて人々が行ったことであり、これらのような多くの奇妙なことが今日生きています。

UARTを介した安全な送信を処理する最も専門的な方法は、パケットの最後に16ビットCRCチェックサムを含めることです。それ以外はすべて安全ではなく、エラーを見逃す可能性が高くなります。

次に、ハードウェアレベルで、差動RS-422 / RS-485を使用して、伝送の堅牢性を大幅に改善できます。これは、より長い距離を安全に伝送するために不可欠です。TTLレベルのUARTは、オンボード通信にのみ使用してください。RS-232は他の目的には使用しないでください。古いものとの下位互換性があります。

全体として、エラー検出メカニズムがハードウェアに近いほど効果的です。効果の点では、差動信号が最も多く、続いてフレーミング/オーバーランなどのエラーをチェックします。CRC16がいくつか追加し、「従来のUARTの誤動作」が少し追加します。


7
このアドバイスはかなり正接です-あなたは実際に尋ねられた質問に対処していません。特に、提案された解決策は他の問題を解決する可能性がありますがこのページの質問の基本的な問題、つまりフレーミングバイとペイロードバイの混同は解決されません。多くても、提案では、CRCまたは同様の障害が原因でフレーミングバイトを埋め込んだ有効なデータを拒否しますが、そのような通信方法はありません。
Chris Stratton、

3
実際、この答えはそれをさらに悪化させます。オリジナルにはデータバイトとストップバイトしかありませんでした。これにより、CRCバイトという3番目のカテゴリが追加されます。そして、ここに示されているように、それらは{10,13}を含む任意の値を取ることができます。
MSalters

1
@MSalters:この問題を防ぐために、CRCをASCIIエンコードされた16進数にすることができます。RS485で見たもう1つのトリックは、開始/アドレスバイトにビット7を設定することです。
トランジスタ

「CANたサンプルごとに個々のビットを複数回。」:ビット値の実際のサンプリングは、ビットごとに1回だけです。ここで何を言ってるの?送信者によるような、ある種のエラーチェック?クロック同期?
Peter Mortensen

チェックサムの反転は、データのブロック全体を合計するとゼロになるように行われました。これは、コーディングが少し簡単で、実行が少し高速です。また、CRCは予想よりもはるかに優れています。Wikipediaで調べてください。
toolforger

0

...確率は低いですが、メッセージにストップバイトではないときに、メッセージに「10と13」という値が次々に含まれる可能性があると想像できます。

シリアルデータパケットのフォーマットを設計するときは、データの一部が終了シーケンスに等しい場合を考慮する必要があります。考慮すべきもう1つのことは、送信中に文字が破損したり失われたりする可能性があることです。開始文字、停止文字、データペイロードバイト、チェックサムまたはCRCバイト、転送エラー修正バイトは破損の影響を受けません。フレーミングメカニズムは、パケットが破損したデータを検出できる必要があります。

これらすべてに取り組むにはいくつかの方法があります。

私は、パケットがシリアルバイトでのみフレーム化されるという作業上の仮定を行っています。ハンドシェイクラインはフレーミングに使用されません。時間遅延はフレーミングに使用されません。

送信パケット長

末尾の終了文字の代わりに[またはそれに加えて]パケットの長さを最初に送信します。

長所: ペイロードは効率的なバイナリ形式で送信されます。

短所: 送信開始時のパケット長を知る必要がある。

特殊文字をエスケープする

ペイロードデータを送信するときに、特殊文字をエスケープします。これは以前の回答ですでに説明されています

長所: 送信者は、送信の開始時にパケットの長さを知る必要はありません。

短所: エスケープする必要があるペイロードバイトの数によっては、効率がやや低下します。

開始文字と停止文字を含まないようにエンコードされたペイロードデータ

パケットのペイロードは、開始文字または停止文字を含めることができないようにエンコードされています。通常、これは、ASCIIまたはHex-ASCII表現として数値を送信することで行われます。

長所: 一般的な端末プログラムで人間が読める形式。エスケープを処理するためのコードは必要ありません。送信の開始時にパケットの長さを知る必要はありません

短所: 効率が低下します。1バイトのペイロードデータの場合、数バイトが送信されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.