Nachdem wir bereits im ersten Teil die nötigen Funktionen der API für die RS232 eingebunden haben, geht es nun an die Umsetzung. Die Schwierigkeit steckt weniger in der Programmierung mit VB .NET sondern mehr im tiefen Wissen um die API Funktionen.
Für die ersten Schritte, werden wir minimalistisch vorgehen. Zunächst muss die Schnittstelle geöffnet werden. dafür
implementieren wir eine Funktion Open. Dieser übergeben wir per Parameter alle nötigen Informationen.
Port Schnittstelle als Ganzzahl
Geschwindigkeit in Boud (z.B. 9600)
Datenbits Anzahl der Datenbits ( Typischerweise 8)
Parität (Typischerweise N für none, hier als 0)
Stopbit Anzahl der Stopbits als Ganzzahl
Buffergröße des IO Buffers
Diese Vorgehensweise bringt einige Nachteile mit sich. Vor allem, das alle Parameter korrekt gesetzt werden müssen. Optionale Parameter sind unter VB .NET nicht mehr möglich. So müsste die Funktion Open mehrfach überladen werden, was hier einen erhebelichen Coding Aufwand bedeutet.
Alternativ könnte man auch in der generellem Klasse Propertys verwenden um die Schnittstelle zu setzen. Dazu muss aber auch für jede Eigenschaft der Code über SET und GET implementiert werden zum lesen und schreiben.
Zunächst wird über Createfile ein Handle auf die Schnittestelle erzeugt. Die Fehlerbehandlung ist and dieser Stelle nur
Ansatzweise implementiert. Die Hexzahlen (&H80000000) geben den Schreib- und Lese Modus an.
Der 5te Parameter bewirkt, das ein offner Port nichmals geöffnet wird.
Wenn dann alles gut gegangen ist, ist der Handle gesetzt. Aufbauend auf diesem Handle wird der Buffer und die Errors gelöscht. Jede Funktion liefert einen Rückgabewert, der im Fehlerfalle, wenn Catch zuschlägt, ausgegeben werden kann.
Erst nachdem der Port offen ist, kann er mit den Settings versorgt werden. Dazu wird aus den Parametern ein String gebildet.
Über die API Funktion BuildComDCB wird damit eine Struktur gefüllt. Diese ist weiter unten im Text definiert.
Mit diesem Objekt wird über SetCommstate die Schnittstelle initialisiert. Im vorerst letzten Schritt werden noch die
Buffergröße und der Timeoput gesetzt. Wobei die Funtkion comSettimeout nicht aus der API kommt, sondern weiter unten definiert ist.
Public Sub Open(ByVal iPort As Int16, ByVal ioSpeed As Integer, ByVal ioData As Int16, ByVal ioParity As Int16, ByVal ioStop As Int16, ByVal comBufferSize As Integer) Dim uDCB As DCB, iResult As Int32 Try hCOMM = CreateFile("COM" & iPort.ToString, _ &H80000000 Or &H40000000, 0, 0, _ 3, 0, 0) If hCOMM <> -1 Then Dim sCOMSettings As String iResult = ClearCommError(hCOMM, ErrCode, 0&) 'Fehler löschen iResult = PurgeComm(hCOMM, PurgeBuffers.RXClear Or PurgeBuffers.TxClear) 'Buffer löschen iResult = GetCommState(hCOMM, uDCB) 'Setting von COM`? ' Port Settings sCOMSettings = ioSpeed & "," & ioParity & "," & ioData.ToString & "," & CInt(ioStop).ToString iResult = BuildCommDCB(sCOMSettings, uDCB) iResult = SetCommState(hCOMM, uDCB) iResult = SetupComm(hCOMM, comBufferSize, comBufferSize) 'Buffer setzen comSetTimeout(100) 'Timeout Struktur sezen Else ' Fehler werfen End If Catch Ex As Exception 'allgemeiner Fehler End Try End Sub |
Für ein sauberes Arbeiten gehört natürlich auch noch das Schliessen des Ports dazu. Hier wird einfach die API Funktion gekapselt.
Public Sub Close() If hCOMM <> -1 Then CloseHandle(hCOMM) hCOMM = -1 End If End Sub |
Es folgt die Struktur für die Rückgabe der API Funktion BuildCommDCB. Die Anregung zur Erstellung ist wiederum aus der Platform SDK übernommen.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/hardware/commun_965u.aspDa nicht alle Eingeschaften benötigt werden kann man diese aus der Struktur weglassen. Wichtig ist nur, das der Name der Struktur identisch ist.
<StructLayout(LayoutKind.Sequential, Pack:=1)> Private Structure DCB Private DCBlength As Int32 Private BaudRate As Int32 Private Bits1 As Int32 Private wReserved As Int16 Private XonLim As Int16 Private XoffLim As Int16 Private ByteSize As Byte Private Parity As Byte Private StopBits As Byte Private XonChar As Byte Private XoffChar As Byte Private ErrorChar As Byte Private EofChar As Byte Private EvtChar As Byte Private wReserved2 As Int16 End Structure |
Die Funktion comSetTimeout kapselt die API Funktion SetCommTimeouts. Dies ist von Vorteil, da als Parameter eine komplexere Struktur benötigt wird. Diese ist auch weiter unten definiert.
Private Sub comSetTimeout(ByVal comTimeout As Int16) Dim uCtm As COMMTIMEOUTS If hCOMM = -1 Then Exit Sub Else With uCtm .ReadIntervalTimeout = 0 .ReadTotalTimeoutMultiplier = 0 .ReadTotalTimeoutConstant = comTimeout .WriteTotalTimeoutMultiplier = 10 .WriteTotalTimeoutConstant = 100 End With SetCommTimeouts(hCOMM, uCtm) End If End Sub |
<StructLayout(LayoutKind.Sequential, Pack:=1)> Private Structure COMMTIMEOUTS Public ReadIntervalTimeout As Int32 Public ReadTotalTimeoutMultiplier As Int32 Public ReadTotalTimeoutConstant As Int32 Public WriteTotalTimeoutMultiplier As Int32 Public WriteTotalTimeoutConstant As Int32 End Structure |
So bisher haben wir schon sehr viel geleistet, aber noch keine Ergebnisse am Tisch. Im nächsten Teil beschäftigen wir uns mit dem schreiben und lesen von Daten auf die Seriellen Schnittstelle.
Ein paar Dinge wie z.B. die Art der Strukutrdefinition wurden hier nicht erläutert. Ich kann zum Studium folgende Links empfehlen:
Namespace System.Runtime.InteropServices