次の問題があります: C でマイクロ コントローラー (ATmega 8 ビット、8051 など) をプログラミングしていて、UARTインターフェイスを介してカスタム バス プロトコルを受け取ります。受信したバイトをバッファに入れ、後で処理します。
質問: 受信したフレームをデコードするための設計パターンまたは戦略はありますか? これを行う最善の方法は何ですか?書籍/チュートリアルはありますか?
これは私の最初の質問なので、質問が適切に形成されていない場合は、私にぶつからないでください:)
このようなプロトコルはすべてカスタムであるため、それらにアプローチする標準的な方法はありません。ADT (または「デザイン パターン」) に似ているのは、通常はリング バッファーを介して行われる実際のデータ受信だけです。
実際のプロトコルを解析するために通常行うことは、特別なことではありませんが、常に同じ方法で行われます。次のような結果になります。
(以下のコードでプレフィックス XYZ を使用して、コードが架空の「XYZ」プロトコルをデコードするためのものであることを示しています。カスタム プロトコルの名前に置き換えてください。)
// xyz.h
#ifndef XYZ_H
#define XYZ_H
typedef enum
{
XYZ_OK,
XYZ_ERR_SYNC, // various error codes for things that can go wrong
XYZ_ERR_LENGTH,
XYZ_ERR_CHECKSUM,
...
} xyz_result_t;
xyz_result_t xyz_decode (const uint8_t* buf, size_t n);
#endif /* XYZ_H */
// xyz.c
#include "xyz.h"
xyz_result_t xyz_decode (const uint8_t* buf, size_t n)
{
// various protocol-specific checks:
if(buf[0] != SOME_SYNC_CHARACTER)
{
return XYZ_ERR_SYNC;
}
if(buf[something] < expected_min_length ||
buf[something] > expected_max_length)
{
return XYZ_ERR_LENGTH;
}
...
return XYZ_OK;
}
簡単にデコードできるようにプロトコルを設計することが重要であることがわかりました。私の現在のものには、特定の開始バイトと終了バイトがあります。そのため、UART が何かを受信すると、パケットの開始位置 (バッファ インデックスを 0 に設定) と終了位置 (処理できるようにする) を認識します。
これを行う場合、データの開始/終了バイトを「エスケープ」し、別のバイトを追加する必要があります。つまり、0x7F -> 0x7D, 0x5F 0x7D は「次のバイトがエスケープされる」ことを意味し、0x5F はエスケープされた 0x7F を表します。
これにより、物事が山ほど簡単になりました。IRQ は単純で、パケットのエスケープ解除/チェックは単純であり、パケット バイトからデータを取得するのは簡単です。
IRQ
If (received byte == start flag)
set receive buffer index to zero
else
if (space left in buffer)
store character in buffer
if (received byte == end flag)
process buffer
プロセスバッファ
Unescape the packet data
Check length
Check checksum
Analyse data