Entwickler-Ecke
Delphi Tutorials - Serielle Schnittstelle ansprechen und pollen
SchelmVomElm - Do 03.02.05 17:26
Titel: Serielle Schnittstelle ansprechen und pollen
Serielle Schnittstelle ansprechen und pollen
Da ich bisher eigentlich nur Fragen gestellt habe, wollte auch mal was für die Allgemeinheit tun…
Da ich viel Erfahrungen mit seriellen Schnittstellen gemacht habe, hier ein kleines, Crashkurs- artiges Tutorial. Gewisse Kenntnisse über die RS232 und Delphi bzw. API Programmierung setze ich voraus.
1. Benutzte GlobaleVariablen
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| var PortTimeout : _COMMTIMEOUTS; PortHandle : Integer; PortDCB : TDCB; PortNr : Integer; PortState : Cardinal; WriteOverlapped,ReadOverlapped,StatusOs: TOverlapped; |
2. Die Schnittstelle öffnen / schließen / testen
Um die Schnittstelle zu öffnen und ein gültiges Schnittstellen- Handle zu bekommen, ruft man die Funktion CreateFile auf. Vereinfachtes Bsp:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| function OpenCOM(Port: byte): boolean; stdcall; begin PortHandle := CreateFile(PChar('\\.\COM'+IntToStr(Port)),GENERIC_READ or GENERIC_WRITE,0, nil,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,LongInt(0)); if PortHandle > 0 then begin Result := true; InitOverlapped(WriteOverlapped); InitOverlapped(ReadOverlapped); InitOverlapped(StatusOs); end else Result := false; end; |
Die Ini Funktion für die Overlapped Strukturen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| procedure InitOverlapped(var Overlapped : TOverlapped); begin Overlapped.Offset := 0; Overlapped.OffsetHigh := 0; Overlapped.Internal := 0; Overlapped.InternalHigh := 0; Overlapped.hEvent := CreateEvent(nil,True,False,''); end; |
Um die Schnittstelle wieder zu schließen (und es gibt X mistige Programm die das vergessen, was zur Folge hat, dass danach andere Programme nicht mehr auf die Schnittstelle zugreifen können) geht man wie folgt vor:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| procedure CloseCOM; stdcall; begin PurgeComm(PortHandle, PURGE_RXABORT or PURGE_RXCLEAR or PURGE_TXABORT or PURGE_TXCLEAR); SetCommMask(PortHandle,0); CloseHandle(PortHandle); PortHandle := 0; end; |
Bevor man jedoch die Schnittstelle öffnet, sollte man vielleicht auch testen, ob Sie denn verfügbar ist (oder ob die tolle Com-Konsole von Pipi Lüdi- Soft vergessen hat die Schnittstelle zu schließen). Dies mach ich ziemlich billig (Verbesserungsvorschläge jederzeit willkommen):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| function ComAvailable(ComNr: byte): boolean; stdcall; var TestHandle : integer; begin TestHandle := CreateFile(PChar('\\.\COM'+IntToStr(ComNr)),GENERIC_READ or GENERIC_WRITE,0, nil,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,LongInt(0)); if (TestHandle <= 0) then Result := false else begin Result := true; CloseHandle(TestHandle); end; end; |
2. Die Schnittstelle konfigurieren:
Die Konfiguration der Schnittstelle erfolgt über den Device Control Block (PortDCB) siehe Windows.pas – TDCB. Mit dieser Struktur lassen sich Parameter wie Baudrate, Flusssteuerung, StopBits usw. setzen und zwar wie im folgenden Beispiel die Baudrate:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| function SetBaudRate(baud: cardinal): boolean; stdcall; begin GetCommState(PortHandle,PortDCB); PortDCB.BaudRate := baud; Result := SetCommState(PortHandle,PortDCB); end; |
Die Hilfe des MS-SDK gibt auch bereitwillig Auskunft zu den verwendeten Funktionen.
3. Abfragen und Setzen der Status Leitungen
Um CTS, DCD, DSR und Ring abzufragen, eignet sich die Funktion GetCommModemStatus.
Bsp:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| var PortState: cardinal;
begin GetCommModemStatus(PortHandle,PortState); if ((Portstate and MS_CTS_ON) <> 0) then Result := true else Result := false; |
Um RTS, DTR, XOFF, XON oder einen Break zu setzen oder zurückzusetzen benutzt man die Funktion EscapeCommFunction:
Bsp. Setzen und zurücksetzen der DTR Leitung
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| procedure SetDTR; stdcall; begin if (PortHandle <> 0) then EscapeCommFunction(PortHandle,5); end;
procedure ClearDTR; stdcall; begin if (PortHandle <> 0) then EscapeCommFunction(PortHandle,CLRDTR); end; |
In diesem Zusammenhang bedeutet XON/XOFF, dass ein Empfangen der entsprechenden XON/XOFF Chars simuliert wird. Ein Break ist, wenn ich die TX – Leitung dauerhaft auf 1 setze. ACHTUNG, ist ein Break gesetzt und ich versuche zu senden (mit WriteFile), wird das Programm eine sauberen Abgang hinlegen. Am Besten man lässt den Quatsch – braucht eh keiner.
4. Lesen und Schreiben
Jetzt wird’s endlich interessant. Zum Lesen und Schreiben verwendet man die Funktionen ReadFile und wie sollte es auch sonst sein, WriteFile. Eigentlich ist die Benutzung dieser Funktionen im Zusammenhang mit der seriellen Schnittstelle ganz einfach – wenn man ein Programmierbeispiel hat:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48:
| function SendString(Str: PChar): boolean; stdcall; var written : cardinal; tmpStr : string; i: LongBool; begin if (PortHandle <> 0) then begin tmpStr := string(Str); Result := not WriteFile(PortHandle,tmpStr[1],Length(tmpStr),written,@WriteOverlapped); end; end;
function ReadString(var Buf: PChar): Cardinal; stdcall; var StrBuf: string; Error,BytesRead: DWORD; Status: TComStat; RdSuccess: boolean; begin BytesRead := 0; if (PortHandle <> -1) then begin ClearCommError(PortHandle,Error,@Status); case Error of CE_BREAK,CE_DNS,CE_FRAME,CE_IOE,CE_MODE,CE_OOP,CE_OVERRUN,CE_PTO,CE_RXOVER, CE_RXPARITY,CE_TXFULL: begin BytesRead := 0; end else begin SetLength(StrBuf,Status.cbInQue); RdSuccess := ReadFile(PortHandle,StrBuf[1],Status.cbInQue,BytesRead,@ReadOverlapped); if not RdSuccess then BytesRead := 0; end; end; end; if (BytesRead > 0) then begin Result := BytesRead; try StrPCopy(Buf,StrBuf); except Result := 0; end; end else Result := 0; end; |
An dieser Stelle eine Anmerkung:
Ich habe mir für den Schnittstellen Zugriff zunächst eine Delphi Unit und dann eine DLL geschrieben. Aus der stammen die ganzen Funktionen. Daher übergebe ich auch PChars. Ich bitte etwaige Unelegantheiten im Bezug auf PChar – string zu entschuldigen – die kommen durch das Hin- und Her. Um das ganze als Delphi Unit zu verwenden, sollte überall die Direktive stdcall entfernt werden.
Und nun *Trommelwirbel* das interessanteste:
5. Schnittstellen Polling
Möchte man benachrichtigt werden, wenn auf die Schnittstelle (von aussen) zugegriffen wird (Daten gesendet oder Statusleitungen gesetzt werden), so musste man in grauer Vorzeit die Schnittstelle manuell zyklisch abfragen (Polling). Dies kann man heute sehr elegant mit einem Thread erledigen. Das ganze funktioniert im Groben so:
Man erstelle einen Thread, man halte in an mit der Funktion WaitCommEvent. Der Thread ist nun angehalten und zwar bis eines der Ereignisse eingetreten ist, die man vorher mit SetCommMask spezifiziert hat. Wird der Thread fortgesetzt ist eines der Ereignisse eingetreten. Will man den Thread von „aussen“ aus dem Wartezustand befreien macht man das so:
Delphi-Quelltext
1:
| SetCommMask(PortHandle,0); |
Hier nun ein Bsp. Für einen Polling- Thread. In diesem Beispiel wird, wenn ein Ereignis eintritt, die Funktion ReleaseEvent aufgerufen, die ich hier weglasse, da sie zu speziell wäre.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
| type TSerialThread = class(TThread) protected fComEvent: cardinal; procedure Execute; override; public constructor Create; property ComEvent: cardinal read fComEvent; end;
constructor TSerialThread.Create; begin FreeOnTerminate:= True; inherited Create(False); end;
procedure TSerialThread.Execute; var SerialEvent, WaitResult, BytesRead: Cardinal; begin SetCommMask(PortHandle,EV_CTS or EV_BREAK or EV_DSR or EV_ERR or EV_RING or EV_RLSD or EV_RXCHAR or EV_RXFLAG or EV_TXEMPTY); if not WaitCommEvent(PortHandle,SerialEvent,@StatusOs) then begin if (GetLastError = ERROR_IO_PENDING) then begin WaitResult := WaitForSingleObject(StatusOs.hEvent,INFINITE); case WaitResult of WAIT_OBJECT_0: begin if GetOverlappedResult(PortHandle,StatusOs,BytesRead,false) then ReleaseEvent(SerialEvent); end; end; end; end else ReleaseEvent(SerialEvent); end; |
Dieser Thread stellt lediglich die Mindestanforderung an einen Polling thread dar. Für den Hausgebrauch (und mehr) sollte das reichen.
So, bevor ich mich noch mehr zu dem Thema auslasse, will ich erst mal sehen ob das irgendein Schwein interessiert.
MfG SchelmVomElm
jasocul - Do 03.02.05 17:31
Titel: Serielle Schnittstelle ansprechen und pollen
Vor 6 Jahren hätte ich das gebrauchen können :wink:
digi_c - Mo 07.02.05 21:12
Danke füt den Artikel, was mich noch interessieren würde, wäre das Timing unter Windows. Der normale Timer ist ja sehr ungenau, reicht es da für periodische Messungen z.B. TcTimer(Multimediatimer) zu benutzen oder gibt es da andere Wege?
Achso und Elektor hat auch ne sehr nützliche DLL dazu geschrieben, die das alles Kapselt, Link such ich nochmal;)
SchelmVomElm - Di 08.02.05 10:28
Kenn ich schon 8) Läuft nicht unter W2K :?
Du kannst ja Die Funktionen der Elektor DLL mal mit
meinen vergleichen da wirst Du einige Unterschiede
feststellen....
Wieso Timing? Solange Du nur über RX/TX Zeichen austauscht
braucht Du eigentlich kein Timing - das ist der Sinn des
Polling threads. Anders sieht es aus wenn Du die Leitungen
selbst (bitweise) manipulierst um Daten zu übertragen.
Dann findest Du im Forum glaub ich jede Menge zum Thema Timer.
PS Du hast mit diesen Methoden keine Kontrolle darüber, wann
genau ein Zeichen physikalisch über die Schnittstelle
gesendet oder empfangen wurde.
digi_c - Di 08.02.05 20:13
Doch die haben eine neue wo in 2 Heften der WinAPI COMM Zugriff erklärt wird, ich fin den Link schon noch ...;)
Pkan - So 27.03.05 14:28
Hi
:wink: super turial
aber ich hab ein riesen Problem.
Ich will meine Rs232 schnittstelle ganz einfach steuern.
die 3 sendeleitungen gaanz einfach an oder aus sachalten (zur Pumpensterung)
und die 4 empfamsleitungen einfach nur auslesen, spannung oder keine ( an oder aus)
ich habs versucht aber ich komm einfach nicht dahinter wie ich das richtig mache.
Wenn mir einer helfen könnte oder einen Vorschlag machen könnte.....
Danke :D
hedie - So 27.03.05 18:02
Klar Doch
wen du Den LPT Port benutzen würdest, dann wäre gleich alles viel einfacher (für mich :D )
fals du dich deshalb entschliesst den LPT Zu benutzen , dann kann ich dir helfen (wirklich ganz einfach gesteuert 8) )
Pkan - So 27.03.05 18:09
für dich einfacher?? :D
neija...hast du schon ein programm??
wenns wirklich viel einfacher ist nehm ich den und bau den stecker um
Danke :)
hedie - So 27.03.05 18:12
Klar war heute Fleissig am Proggen 8)
wenn du willst, kann ich dir eines meiner programme geben.
dazu musst du mir nur sagen, welche der leitungen du verwenden willst und welcher LPT (LPT1, LPT2 ....)
Pkan - So 27.03.05 18:19
loooooooooooool mein HIGH END PC besitz nur eine Druckerschnittstelle
^^du bist elktroniler?? is ja der hammer ich auch....und das kann ich wesentlcih besser als delphi... :) hast du icq??
:)
hedie - So 27.03.05 18:21
nein :( aber MSN :D
Elektroniker mit Leidenschaft :D
dein Progg ist gleich fertig 8)
Pkan - So 27.03.05 18:26
goil....
^^bin gerade auf deiner homepage.
Ich hab auch son ritig goile Projekte gemacht...
neija ich müsste auch mal homepage machen..aber die zeit
vlt. erkennst du die extrmen vorteile von icq..dann können wir ja mal chatten :wink:
mit welchen delphi schreibst du das programm(vers??)
ich werde später noch timer und so dazu machen
hedie - So 27.03.05 18:31
Weshalb lädst du dir nicht msn (hat ton und sogar webcam übertragung :D )
also ich schreibe es in Delphi7
fals du nicht draus kommst, einfach fragen (im forum oder
hedie2@tiscali.ch)
deine programm habe ich angehängt 8)
(freue mich über mails)
Pkan - So 27.03.05 18:40
Danke
jo ich hab auch delphi 7
^^ja webcam ist auch auch gut aber ich lass mich nit gern von msn ausspioniern..und bei meinen system ist das auch besser wenn onkel bil gates nicht alles weis :)
ich probier gleich mal es programm aus
Pkan - So 27.03.05 18:59
is ja alles ganz fein :D aber du steuerst ja die LPT an...
welchen pin schaltest du jetzt( hast du manchmal en plan con LPT port mit leitungsnamen(und welchen du ansteuerst))
muss mir erstmal en neuen stecker anlöten
hedie - So 27.03.05 19:03
besch du schwizer wen ja dan können wir ja so schreiben (einfacher)
und ich steuere den pin 4 an also D2 weil es auch noch d 0 gibt
Karlson - So 27.03.05 19:08
hedie hat folgendes geschrieben: |
besch du schwizer wen ja dan können wir ja so schreiben (einfacher) |
Mag sein, aber das Forum hier ist Deutsch, folglich werdet ihr auch bitte Deutsch schreiben, schließlich sollen nicht-schweizer auch was verstehen.
Pkan - So 27.03.05 19:10
gut...Nee bin ich net auch wenn meine rechtschreibung/schrift manchmal wie "hinter chinesisch aussieht...ich komm ausn :!: OSTEN :!:
auch sachsen..
neija müssen uns unbedingt mal über unsere elkto projekte unterhalten :D
hedie - So 27.03.05 19:11
Klar machen wir freu mich schon :D
SchelmVomElm - Mi 30.03.05 10:12
@Hedie: Ich habe mir mal deine Seite angeschaut. Bin auch leidenschaftlicher Elektroniker - und Elektor Leser. Ich wollte dich nur warnen :!: - ich glaube nicht dass es Elektor toll findet dass Du Artikel von denen veröffentlichst. Die Dinger sind kostenpflichtig - Download 1,50 €. Da kannst Du 'nen mords Ärger kriegen...
hedie - Mi 30.03.05 13:25
NA GUT WERDE ES LÖSCHEN :cry: ich weis es übrigens schon :D
SchelmVomElm - Mi 30.03.05 16:16
:mrgreen: Hi, hi... mir ist das egal! Meinetwegen kannst Du die drauflassen - zumal der GoldCap- Akku ja echt cool ist... Naja sollte nur 'ne Warnung sein
hedie - Mi 30.03.05 16:19
na gut ich lass es drauf aber nichts sagen :D
Smartie - Mo 18.04.05 09:05
hej, super sache! ISt GENAU das was ich grad brauche!
Hätt ich schon letzte Woche finden können.
NUr ne "kleine" Frage
Wie mach ich des, wenn ich eine Datei, die ich auf der Festplatte gespeichert habe, versenden möchte, bzw. eine Datei die ankommt, auf der FEstplatte abspeichern will?
Lieben Gruß
Smartie
Pytho - Mi 25.05.05 10:21
Zitat: |
Und nun *Trommelwirbel* das interessanteste:
5. Schnittstellen Polling
...
Hier nun ein Bsp. Für einen Polling- Thread. In diesem Beispiel wird, wenn ein Ereignis eintritt, die Funktion ReleaseEvent aufgerufen, die ich hier weglasse, da sie zu speziell wäre.
|
Nur leider findet man garade über eben diese Funktion ReleaseEvent so gut wie gar nichts im INet, schon gar nicht in Bezug auf Delphi. Bestenfalls soviel, dass sie wohl aus der WinAPI stammt.
@SchelmVomElm:
Hättest Du eine eigene Beschreibung oder einen brauchbaren Link dazu parat ? Aus Deiner eigenen Anwendung müsste doch sogar eine exemplarische Anwendung von ReleaseEvent() für diesen Zweck hier vorliegen, aus der man lernen könnte.
Gruss
Pytho
j-a-n@gmx.de - Fr 01.07.05 12:14
Titel: Re: Serielle Schnittstelle ansprechen und pollen
Hallo Schelm,
Danke für das gute Tutorial, ich kann das jetzt gut gebrauchen. <ot>Aber warum diskutiert Ihr hier über LPT und ICQ?</ot>
Ich habe da noch ein paar Fragen/Anmerkungen allgemein zur Seriellen Schnittstelle.
Auf der seriellen Schnittstelle kommen ja permanent Daten rein oder gar keine. Das bedeutet ja, dass wenn ich ihm sage "lese mir 5 Zeichen" und es kommen nur 3, dann wartet das ding bis mehr kommt, oder? Ich müsste also quasi permanent in einer Schleife lesen und auf ein definiertes Start- und End-Zeichen warten.
Das mit den Status-Leitungen habe ich nicht kapiert, wozu die da sind und vorallem was ich damit machen muss. Ich hoffe mal, Deine Functions machen das richtige damit. :-)
Du hat gesagt, du hättest das ganze in eine Unit/Klasse gepackt. Kann ich mir die irgendwo runterladen?
oink, oink, mich interessiert es brennend; lass dich gerne noch ein bischen mehr darüber aus.
Jan
Sputnik - Mo 19.06.06 15:24
Zitat: |
5. Schnittstellen Polling
Möchte man benachrichtigt werden, wenn auf die Schnittstelle (von aussen) zugegriffen wird (Daten gesendet oder Statusleitungen gesetzt werden), so musste man in grauer Vorzeit die Schnittstelle manuell zyklisch abfragen (Polling). Dies kann man heute sehr elegant mit einem Thread erledigen. Das ganze funktioniert im Groben so:
Man erstelle einen Thread, man halte in an mit der Funktion WaitCommEvent. Der Thread ist nun angehalten und zwar bis eines der Ereignisse eingetreten ist, die man vorher mit SetCommMask spezifiziert hat. |
Gibts sowas auch für den LPT? Also das ein Thread solange wartet bis sich an den Eingängen was tut?
(Polling ist zu ungenau und in einer Schleife abfragen = 100% Auslastung)
delphimike - Do 27.07.06 11:40
schöne Routine, bei mir scheitert alles daran, daß ich nicht weiß welche units ich einbinden muß um die Typendeklarationen unterzubringen....
Mike
SchelmVomElm - Do 18.10.07 14:42
Frequently Annoying Questions
1. Die Funktion ReleaseEvent, die in meinem Tutoial vorkommt ist keine API, Delphi, .net, C++ oder sonst was Funktion sondern von mir geschrieben.
2. Der Quelltext der Funktion ReleaseEvent wird von mir nicht zur Verfügung gestellt.
Ich wüßte auch nicht wozu ich das tun sollte, denn dies ist die Funktion mit der ich auf ein Schnittstellen Event reagiere. Und dies tu ich nicht immer gleich. Mal wird eine Message an ein Fenster gesendet, mal eine Callback Funktion aufgerufen und ein anderes mal sende ich meiner Großmutter ein eMail. Der geneigte User könnte in der Funktion den Schnittstellenpuffer auslesen (Mal so als Anregung...)
3. Der Schnittstellenpuffer wird mit ReadString ausgelesen.
4. Mit den beschriebenen Funktionen kann man nur die serielle Schnittstelle auslesen.
5. Diese Funktionen existieren nicht als Klasse - selbermachen!
Grüße, Schelm
rusty - Di 30.10.07 23:01
@ SchelmVomElm
Danke für Deinen Programmcode bezüglich der Ansteuerung der RS232-Schnittstelle. Ich bin Delphi-Anfänger und stolz das Programm gleich zum Laufen gebracht zu haben - außer das Pollen, aber da werde ich später mal danach sehen.
Aktuell habe ich das Problem, daß der String, den ich einlese zum Teil aus Steuerzeichen besteht. Wie kann ich den String in z.B. einen Array mit Integer-Werten umwandeln?
Danke.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!