zurück
Autor: Hannes Preishuber
Erstellt am: 22 Feb 2002 11:44

Schreiben und lesen in RS232 über VB .NET

Ein COM Port ohne Device dahinter macht wenig Spaß zum Testen. Also stehen Sie auf, gehen Sie zum Schrank und wühlen Sie von ganz unten ein Modem hervor! Ganz egal wie alt, jedes reicht!

An dieser Stelle müssen wir allerdings noch einen schnellkurs in Modemprogrammeriung machen. In 101 % der Fälle (also praktisch immer) kann ein Hayes kompatibler Befehlssatz verwendet werden. Die Befehle sind Zeichenketten die über "AT" eingeleitet werden. Wir brauchen folgende Zeichenketten.

ATZ Reset
ATL3 Lautsprecher ein und auf volle Pulle
ATX3 ignoriere das Freizeichen und wähle drauf los
ATDT Wähle eine Nummer z.B. ATDT123
ATH auflegen

Das besondere daran ist, das man eigentlich nicht so drauf los die Befehle rübersenden kann, sondern erst immer ein ACK abwarten sollte. Wenn der Befehl erfolgreich war kommt ein "OK".
Zum Testen des Modems und der Befehle eignet sich am besten das Hyperterminal.
im folgenden Bild wurden die eingetippten Befehle klein geschrieben und die Antwort des Modems in Grossbuchstaben.

Im nächsten Schritt erstellen wir eine WinForms Anwendung um die Klasse für Testzwecke zu nutzen. Dabei sollte man das öffnen der Schnittstelle und das senden von Befehlen in eine extra Routine legen. Wenn auf das Öffnen unmittelbar ein Write folgt, hängt sich das Modem wahrscheinlich auf. Je nach Anwendungstyp wird man dafür eine Button für öffnen und einen für senden einbauen.

Dim myCom As New ppComm()
myCom.Open(1, 9600, 0, 1, 1, 512)

Nach meinen Test stellt ein anschliessender Read auf die Schnittstelle auch sicher, das sie offen ist. Ein weiteres Problem ist, das die Kommunikation mit dem Modem asynchron erfolgt. Sie müssen also sicherstellen, das der nächste Befehl erst gesendet wird, wenn Sie sich über den Status im klaren sind. Das könnte ein erhaltenes OK sein oder aber auch ein Zeitablauf.

Uns fehlt aber aktuell noch immer die Read Funktion in der Basisklasse. Auch hier ist die Situation nicht ganz einfach. Die serielle Schnittstelle stellt einen Eingangsbuffer zu Verfügung der gelöscht wird, beim Lesen oder beim Überlauf. Das heist Ihre Software muss dauernd auf Draht sein um nichts zu verpassen.
Ideal wäre dafür eine Callback Event, das (laut) schreit wenn der Buffer voll ist. Beim Microsoft MSCOMM.OCX heisst dieses Event OnComm. Dazu aber später mehr. Back to the roots.

Zunächst definieren wir in der COM Klasse einen Container für die empfangenen Daten. Da diese durchaus binäre Daten wie 0 Bytes enthalten können, wählen wir als Datentyp Byte.

Private bytRecvBufARR As Byte()

Diese Variable wird in der Read Funktion beschrieben. Die API Funktion Readfile liest und leert den Buffer. Die Anzahl der gelesenen Bytes wird aus der Variable iReadChars geholt die per ByRef an die Funktion übergeben wird.

Public Function Read(ByVal ReadSize As Integer) As Integer
   Dim iReadChars, iResult As Integer
   If ReadSize = 0 Then ReadSize = 512 'wenn keine Bytes zu lesen Standard Size
      If hCOMM = -1 Then
                Throw New ApplicationException("Der Port ist nicht geöffnet")
      Else
        Try 'abholen
          ReDim bytRecvBufARR(ReadSize - 1)
          iResult = ReadFile(hCOMM, bytRecvBufARR, ReadSize, iReadChars, 0)
          If iResult = 0 Then 'Lese Fehler
          Else
              Return (iReadChars)'Anzahl Zeichen gelesen
          End If
        Catch Ex As Exception
                    'Execption
        End Try
       End If
End Function

Auch das Schreiben ist eigentlich relativ einfach. In der fertigen Version der Klasse sollte die Write Funktion überladen werden um sowohl Strings als auch Bytes schreiben zu können. In unserem Fall wird einfach der String in einem Bytstream über die Helper Funktion ASCIIEncoding umgewandelt. Hier wurde mit Konstanten gearbeitet (z.B. PURGE_RXCLEAR) die weiter unten definiert sind. Über die ByRef übergebene Variable iBytesWritten erhalten Sie die tatsächlich geschriebenen Zeichen zurück.

Public Sub Write(ByVal Buffer As String)
  Dim iBytesWritten, iResult As Integer
  Dim ascBuffer As New System.Text.ASCIIEncoding()
  Try
    PurgeComm(hCOMM, PURGE_RXCLEAR Or PURGE_TXCLEAR) 'löschen der Puffer
    Dim btStream() As BytStream = ascBuffer.GetBytes(Buffer)
    iResult = WriteFile(hCOMM, BytStream, BytStream.Length, iBytesWritten, 0)
    If iResult = 0 Then
                        Throw New ApplicationException("Schreib Fehler - Bytes geschrieben. " & iBytesWritten.ToString & " .. " & Buffer.Length.ToString)
    End If
  Catch Ex As Exception
       Throw
  End Try
End Sub

Da der Code sehr unübersichtlich wird, wenn man Hexwerte als Parameter angibt, habe ich die wichtigsten Konstanten zusammengefügt und im Code definiert.

Private Const PURGE_RXABORT As Integer = &H2
Private Const PURGE_RXCLEAR As Integer = &H8
Private Const PURGE_TXABORT As Integer = &H1
Private Const PURGE_TXCLEAR As Integer = &H4
Private Const GENERIC_READ As Integer = &H80000000
Private Const GENERIC_WRITE As Integer = &H40000000
Private Const OPEN_EXISTING As Integer = 3
Private Const INVALID_HANDLE_VALUE As Integer = -1
Private Const IO_BUFFER_SIZE As Integer = 1024

So wird z.B. der Code zum Öffnen der Schnittstelle lesbarer.

hCOMM= CreateFile("COM" & iPort.ToString, _
    GENERIC_READ Or GENERIC_WRITE, 0, 0, _
    OPEN_EXISTING, 0, 0)

Nun zeigen wir noch den Schnippsel VB.NET Code zum Testen des Codes. Dazu senden wir ein AT + [Carrige Return] an das Modem und müssten im Erfolgsfall ein OK auf der Schnittstelle empfangen. Für die Ausgabe wurde ein Label platziert, das das Ergebnis anzeigt. Auch hier muss wieder zuerst aus einem Byte Array ein String gemacht werden um das Label zu füllen. Beim senden des AT Strings muss noch ein CHR(10) angehängt werden. Dies geschieht am besten über die VB Konstante vbCR. Wenn Ihr Code auf einer anderen Plattform laufen sollte, in der vbCR eine anderen HEx Code darstellt, erledigt das das Framework für Sie.

Dim myCom As New ppComm()
myCom.Open(1, 9600, 0, 1, 1, 512)
myCom.Write("AT" & vbCr)
myCom.Read(20)   '20 Bytes lesen
Dim oEncoder As New System.Text.ASCIIEncoding()
label1.text = oEncoder.GetString(myCom.bytRecvBufARR)
myCom.Close()

Der Code funktioniert allerdings nur, wenn Sie im Debug Modus relativ langsam durch die Zeilen springen. Das Modem und die Schnittstelle verschlucken sich sonst. Wenn das passiert, kommt nur mehr Müll über die Schnittstelle. Schalten Sie dann das Modem Aus und wieder Ein. Wir werden später über WaitCommEvent eine Callback Funktion einbauen. Es hilft auf die Thread.Sleep Funktion.

Im nächsten Teil werden wir eine Callback Funktion einbauen um die Aktivitäten der seriellen Schnittstelle zu überwachen.

 


© Copyright 2008 ppedv AG