6

次のコードを使用して、com ポートから値を読み取ります。

Private port As New SerialPort("COM13", 9600, Parity.None, 8, StopBits.One)

Private Sub port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
    Debug.Print(port.ReadExisting())
End Sub

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    AddHandler port.DataReceived, New SerialDataReceivedEventHandler(AddressOf port_DataReceived)
    port.Open()
End Sub

これは問題なく機能しますが、すべてのデータを取得できず、結果として 1 つではなく 2 つの文字列が返されることがあります。

たとえば、com ポートが「HELLO2YOU」という単語を送信している場合、次のようになります。

HEL
LO2YOU

また

HELLO2
YOU

そこにバッファを配置して、表示する前にすべてのデータが読み取られるようにするにはどうすればよいですか?

ありがとう!

4

2 に答える 2

9

シリアル ポート通信をストリーミング データと考える必要があります。データを受信するときはいつでも、それが完全なメッセージ、部分的なメッセージ、または複数のメッセージである可能性があることを期待する必要があります。それはすべて、データが入ってくる速度と、アプリケーションがキューから読み取る速度に依存します。したがって、バッファが必要だと考えるのは正しいです。ただし、まだ気付いていないかもしれませんが、厳密にはシリアル ポートを介して、各メッセージの始まりと終わりを知る方法がないということです。これは、送信者と受信者の間で合意されたプロトコルを介して処理する必要があります。たとえば、多くの人は標準のテキスト開始 (STX) 文字とテキスト終了 (ETX) 文字を使用して、各メッセージ送信の開始と終了を示します。そうすれば、データを受信すると、完全なメッセージをいつ受信したかがわかります。

たとえば、STX および ETX 文字を使用した場合、次のようなクラスを作成できます。

Public Class DataBuffer
    Private ReadOnly _startOfText As String = ASCII.GetChars(New Byte() {2})
    Private ReadOnly _endOfText As String = ASCII.GetChars(New Byte() {4})

    Public Event MessageReceived(ByVal message As String)
    Public Event DataIgnored(ByVal text As String)

    Private _buffer As StringBuilder = New StringBuilder

    Public Sub AppendText(ByVal text As String)
        _buffer.Append(text)
        While processBuffer(_buffer)
        End While
    End Sub

    Private Function processBuffer(ByVal buffer As StringBuilder) As Boolean
        Dim foundSomethingToProcess As Boolean = False
        Dim current As String = buffer.ToString()
        Dim stxPosition As Integer = current.IndexOf(_startOfText)
        Dim etxPosition As Integer = current.IndexOf(_endOfText)
        If (stxPosition >= 0) And (etxPosition >= 0) And (etxPosition > stxPosition) Then
            Dim messageText As String = current.Substring(0, etxPosition + 1)
            buffer.Remove(0, messageText.Length)
            If stxPosition > 0 Then
                RaiseEvent DataIgnored(messageText.Substring(0, stxPosition))
                messageText = messageText.Substring(stxPosition)
            End If
            RaiseEvent MessageReceived(messageText)
            foundSomethingToProcess = True
        ElseIf (stxPosition = -1) And (current.Length <> 0) Then
            buffer.Remove(0, current.Length)
            RaiseEvent DataIgnored(current)
            foundSomethingToProcess = True
        End If
        Return foundSomethingToProcess
    End Function


    Public Sub Flush()
        If _buffer.Length <> 0 Then
            RaiseEvent DataIgnored(_buffer.ToString())
        End If
    End Sub
End Class

また、通信プロトコルでは、送信者と受信者の間の送信中にメッセージが破損したかどうかを判断できるチェックサム バイトを使用するのが一般的であることにも言及しておく必要があります。

于 2012-07-12T19:13:26.990 に答える
3

これはごく普通のことで、シリアルポートは非​​常に遅いデバイスです。9600のようなボーレートがあり、マシンがあまりダウンしていない場合、ReadExisting()を使用すると、ポートから1バイトまたは2バイトしか取得できません。Debug.Print()はラインターミネータを出力するため、受信したものはすべて分割されて表示されます。

これを修正する最も簡単な方法は、代わりにReadLine()を使用することです。そのためには、デバイスが行の終わりに、SerialPort.NewLineプロパティ値と一致する特殊文字を送信する必要があります。これは非常に一般的ですが、改行は定型文です。

そうでない場合は、他の種類のバッファリングスキームが必要になります。

于 2012-07-12T19:14:50.323 に答える