0

長さ8バイトのシリアルデータフレームをPCに送信するマイクロコントローラープロジェクトがあり、PCシリアルモニターツールを使用してマイクロコントローラーからのデータフレームを監視しましたが、すべてのデータは正常に見え、各8バイトフレームは一貫して送信されているようですこのパターンのデータの長さは 8 バイトです。

私が経験している問題は次のとおりです。

シリアル データを読み取るたびに、構造化された 8 バイト データ フレームの 1 番目のバイトが存在しません。

例:

各ループは以下のとおりです。

マイクロコントローラが 8 バイトのフレームを送信: 01 FF 8E 01 00 00 00 0A

  1. 最初のループ反復。

    .NET Serial PORT がデータ フレームを受信: 01 00 00 00 00 00 00 00

    読み取り (バフ、0、8); 読み取り 1 バイト読み取り

  2. 2 回目のループ反復。

.NET Serial PORT がデータ フレームを受信: FF 8E 01 00 00 00 0A 00 (7 バイト読み取り)

読み取り (バフ、0、8); 読み取り 7 バイト読み取り

sr.Read(buff, 0, 8) は、ループの反復ごとに常に 8 バイトの長さを読み取ることを期待しています。

シリアルポートから読み取るために使用するコードは次のとおりです

    static void Main(string[] args)
    {
        using (SerialPort sr = new SerialPort("COM5"))
        {
            sr.BaudRate = 9600;
            sr.DataBits = 8;
            sr.Parity = Parity.None;
            sr.StopBits = StopBits.One;
            sr.Open();
            StringBuilder sb = new StringBuilder();

            while (true)
            {
                byte[] buff = new byte[8];
                int r =  sr.Read(buff, 0, 8);
                Console.WriteLine("Number of bytes read : " + r);
                for (int i = 0; i < buff.Length; i++)
                {
                    var hex = string.Format("{0:x2}", buff[i]);
                    sb.Append(hex.ToUpper());
                    sb.Append(" ");
                }
                Console.WriteLine(sb);
                sb.Clear();
            }
        }

前もって感謝します。


非常に有益なフィードバックをありがとう。

8 バイト フレームのバッファリング ロジックを次のように再構築しました。

すべてが、ポート モニターから見たデータ パターンと同期しているように見えます。最も重要なのは、マイクロコントローラーからのデータです。

重要な側面がまだ欠けています:

  1. フレーム CRC。
  2. 各バイトの順序を検証します。
  3. ....... など

    ここに更新されたコードがあります

    static void Main(string[] args)
    {
        using (SerialPort sr = new SerialPort("COM5"))
        {
            sr.BaudRate = 9600;
            sr.DataBits = 8;
            sr.Parity = Parity.None;
            sr.StopBits = StopBits.One;
            sr.Handshake = Handshake.None;
            sr.DtrEnable = false;
            sr.Open();
            sr.ReceivedBytesThreshold = 1;
            StringBuilder sb = new StringBuilder();
    
            byte[] buff = new byte[8];
            byte[] temp_buffer = new byte[8];
    
            while (true)
            {
                int r = sr.Read(temp_buffer, 0, 8);
                Console.WriteLine("Number of bytes read : " + r);
    
                // 1 - byte from the serial frame ?
                if (r == 1)
                {
                    buff[0] = temp_buffer[0];
                }
    
                // 7 - bytes from the serial frame ?
                if (r == 7)
                {
                    // get the remaining 7 - bytes 
                    for (int i = 0; i <= temp_buffer.Length - 1; i++)
                    {
    
                        if (i != 0)
                            buff[i] = temp_buffer[i]; // construct a complete frame 
                    }
    
                    // okay, we ready to display the 8-byte serial frame. 
                    for (int i = 0; i < buff.Length; i++)
                    {
                        var hex = string.Format("{0:x2}", buff[i]);
                        sb.Append(hex.ToUpper());
                        sb.Append(" ");
                    }
    
                    Console.WriteLine(sb);
                    sb.Clear();
                }
            }
    
        }
    }
    

また、16 バイトのシリアル フレームを読み取る、変更されたバージョンもここにあります。

        using (SerialPort sr = new SerialPort("COM5"))
        {
            sr.BaudRate = 9600;
            sr.DataBits = 8;
            sr.Parity = Parity.None;
            sr.StopBits = StopBits.One;
            sr.Open();
            sr.ReceivedBytesThreshold = 1;
            StringBuilder sb = new StringBuilder();

            byte[] io_buffer = new byte[16];
            byte[] temp_buffer = new byte[16];

            do
            {
                int data_length = sr.Read( temp_buffer, 0, 16 );

                // 1 - Byte from the serial frame ?
                if ( data_length == 1 && temp_buffer[ 0 ] == 0x28 )
                {
                    io_buffer[ 0 ] = temp_buffer[ 0 ];
                }

                // 15 - Bytes from the serial frame ?
                if (data_length == 15 && temp_buffer[14] == 0x29)
                {

                    // Here we construct the 16- byte frame.(start from 1 as we already have our start frame stored) "x028" => ")"
                    for ( int i = 1; i < temp_buffer.Length; i++ )
                    {
                        io_buffer[ i ] = temp_buffer[ i - 1 ];
                    }

                    // okay, we ready to display the 16-byte serial frame. 
                    for ( int i = 0; i < io_buffer.Length; i++ )
                    {
                        var hex = string.Format( "{0:x2}", io_buffer[ i ] );
                        sb.Append( hex.ToUpper() );
                        sb.Append( " " );
                    }

                    Console.WriteLine(sb);
                    sb.Clear();

                }
            } while (sr.IsOpen);

ここでは 16 バイトのフレーム出力、4 番目のバイトは (I/O 4 チャンネル アナログ - デジタル コンバーター) からのチャンネル 0 から 3 を示します。

28 FF FF 00 01 DB 00 00 00 00 00 00 0E 00 00 29

28 FF FF 01 01 02 00 00 00 00 00 00 0E 00 00 29

28 FF FF 02 01 02 00 00 00 00 00 00 0E 00 00 29

28 FF FF 03 01 A8 00 00 00 00 00 00 0E 00 00 29

4

4 に答える 4

2

sr.Read(buff、0、8)は、ループの反復ごとに常に8バイトの長さを読み取ることを期待しています。

これは根拠のない期待です。Readメソッド(および基盤となるWin32ルーチン)は、ポートの入力バッファーで使用可能なデータを読み取ります。使用可能な1バイトがある場合は、1バイト以下を読み取ることができます。

データの可用性は多数の要因に依存し、Read方法はそれらに影響を与えることはできません。自分で受信側でデータパケットを作成する必要があります。

PSはい、通信回線(ソケット、シリアルポートなど)からの読み取りは、その回線への書き込みよりもはるかに困難です。

于 2012-09-26T07:52:52.333 に答える
0

sr.Read(temp_buffer, 0, 8) はバッファから最大 8 バイトを読み取ります。これは最大数であるため、temp_buffer を上書きしません。8 バイトを読み取りたい場合は、少なくとも 8 バイト待機する必要があります。これは、BytesToRead メソッドおよび/または ReceivedBytesThreshold によって実現できます。

ReceivedBytesThreshold を 8 に設定すると、イベントが発生したときに、少なくとも 8 バイトを読み取る必要があることがわかります。8 バイトまで待機する場合は、BytesToRead プロパティをポーリングし、8 バイトに達したら読み取りを実行します。

于 2013-01-02T01:19:33.000 に答える
0

sr.Read()最初に呼び出したときにバイトを送信するデバイスと同期されるという保証はありません。

sr.Readバッファに繰り返し呼び出したいと思っていたでしょう。次に、期待するプロトコルに一致する 8 バイトが見つかるまで、バッファー上で 8 バイト幅のウィンドウをスライドさせます。ウィンドウの前のすべてを破棄し、バッファの (現在の) 開始点から開始して、8 バイトのチャンクを読み取ります。その後、プロトコルと「同期」されます。この上にロジックを実装します (おそらく、受信した有効な 8 バイト グループごとにイベントを発生させ、それを 1 層上で処理します)。

于 2012-09-26T08:02:46.943 に答える
0

デバイスが「フレームを送信する」とは、単にバイトの送信を開始することを意味します。バイトは送信された順序で受信されますが、タイミングは保証されていません。オペレーティング システムはしばらくバイトを収集してから (処理の遅延を減らすために) それらを送信します。フレームがいつ「終了」したかを知る方法はありません。したがって、デバイスから送信されるデータがまだキューに入れられている間に、部分的なパケットを受け取ることができます。コードは、データ ストリームを解釈してフレームに分割する必要があります。

あなたの例では、1 バイトに続いて 7 バイトを受け取ります。デコードできる 8 バイトの完全なフレームを構築するのに十分な量になるまで、これらのバイトをバッファリングするのはユーザー次第です。

送信エラーが発生した場合、不完全なフレーム (フレームの最後の 5 バイトしか取得できない場合があります。それらを破棄して、次の有効なフレームの読み取りを開始する必要があります) または破損したデータ (フレームが到着したように見える) に対処する必要があります。この場合、フレームに CRC またはその他の整合性チェックが含まれている場合は、それを検証して無効なフレームを破棄できますが、フレームを検証するのに役立つ情報がない場合は、送信が明確であることを願っています)。

基本的に、シリアル ポートは、散発的なバーストで到着するバイト ストリームを提供するだけです。そのデータ ストリームに適用されるデータ エンコーディングとプロトコルを実装するのはコード次第です。

于 2012-09-26T08:21:51.020 に答える