j-a-n@gmx.de - Do 07.07.05 09:25
Titel: Datenübertragung serielle Schnittstelle (COM)
Auch mal ein kleines Tutorial von mir, da ich die Anforderung hatte, lange gesucht habe und dann doch ganz erfolgreich gelöst habe. Hier mein Weg:
Einleitung
Ziel ist es, die serielle Schnittstelle zum Datenübertragen zu benutzen. Speziell bei mir geht es darum, aus einer Paketwaage das Gewicht zu lesen.
Erste Anlaufstelle und Suche war natürlich
http://www.delphi-forum.de und
http://www.dsdt.info. Mein erster Versuch mit den Beispielen von
SchelmVomElm ist leider gescheitert. Also bin ich weiter auf die Suche gegangen und habe SerialNG von Domis gefunden:
http://www.domis.de/serialng.htm Das ist Freeware mit Quelltext. Und damit hat es sofort funktioniert.
Die Komponente SerialNG ist Objektorientiert und beinhaltet als Main-Klasse TSerialPortNG. Nach dem Instanziieren wird Port und Baud-Rate eingetragen und aktiviert. Die Klasse erstellt sofort einen Thread, welcher im Hintergund die Schnittstelle permanent liest. Ausserdem gibt es ein entsprechendes Event, welches den Empfang von Daten signalisert. Der Datenempfang erfolgt also permanent in einen Puffer, welchen man als Benutzer einfach abfragen kann.
Die Komponente SerialNG kann auch als No-Gui-VCL Komponente installiert werden, die entsprechenden Anleitungen sind auf der Homepage. Ich habe mir das aber alles einfach gespart, die serialng.pas ins Projekt aufgenommen, im uses eingebunden und los.
Soviel zur Theorie, nun zur Praxis:
Ich gehe davon aus, dass Du ein Projekt hast, bei dem Du irgendwann auf die Stelle triffst, "jetzt will ich Daten an COM1 schicken" bzw. "jetzt sollte ich die Daten von COM1 haben".
1. COM1 öffnen und schliessen
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| procedure machwas; var port: TSerialPortNG; begin port := TSerialPortNG.Create(nil); try port.CommPort := 'COM1'; port.BaudRate := 9600; port.Active := true; machwas2(port);
finally port.Active := false; freeandnil(port); end; end; |
2. Daten an Schnittstelle senden
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| procedure machwas2(port: TSerialPortNG); var text: String; begin text := 'gib mir dein gewicht'; port.SendString(text + #13);
machwas3(port); end; |
3. Daten empfangen
Daten empfangen geht auf zwei Wegen.
a) man registriert eine Funktion am Event OnRxClusterEvent oder
b) man wartet selbst auf die Daten.
a) eignet sich für z.B. einen Chat, wo beide Seiten wild durcheinander senden.
b) ist eher für einen Server nach dem Model: ich schicke einen Befehl und warte auf die Antwort. Das ist das, was wir hier tun.
Ach so: Cluster ist einfach nur ein Name für einen Brocken empfangener Daten. Ich habe den Namen nicht ausgesucht, ist aber nichts gefährliches.
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:
| procedure machwas3(port: TSerialPortNG); var buffer: String; timeout: integer; abgelaufen: TDateTime; datenok: boolean; begin buffer := ''; datenok := false; timeout := 2000 abgelaufen := IncMilliSecond(getTime(), timeout); while (getTime() <= abgelaufen) and (not datenok) do begin
if port.nextClusterSize > 0 then begin buffer := buffer + port.ReadNextClusterAsString(); end;
if copy(buffer, length(buffer), 1) = #13 then begin datenok := true; end;
sleep(50); end;
if datenok then begin machwas4(buffer); end; end; |
Die Prozedur liesst also 2 Sekunden lang alle Daten die ankommen. Sind die Daten angekommen, die Du erwartet hast (hier: das letzte zeichen ist ein CR) hört die Schleife auf und gib die Daten weiter. Diese Methode machwas4 definieren wir mal als "vorhanden". Bei mir analysiert diese Methode das erhaltene Gewicht, konvertiert es in einen double und schreibt es in ein Formular.
Kritische Stellen
Durch die Schleife steht dein System mindestens 0,05s maximal 2s und reagiert nicht mehr. Es macht also Sinn, das ganze in einen Thread zu packen. Das war bei mir nicht nötig, da das ganze sowieso in einem Thread ist (der das Gewicht dauern liesst). Das sollte aber auch nicht schwer fallen. Aber bitte das Synchonize beim Schreiben ins Formular nicht vergesssen!
Die Bedingung "habe genug Daten erhalten" sieht so primitiv aus, hat es aber in sich. Nämlich die Frage: Woher weiss ich, wann ich genug empfangen habe. In meinem Fall was das vorgegeben, die Waage beginnt mit einem : und endet mit einem #13. Für alle anderen heisst es "Schnittstelle definieren". Möchtest Du beispielsweise zwei Rechner zusammenschliessen und Deine Daten als XML übertragen (ja, das ist möglich), wäre ein #13 ungeeignet, da das innerhalb der Daten vorkommt. Hier bietet sich eines der Steuerzeichen als Ersatz an, z.B. #0, #1, usw. Aber die Gegenstelle muss das dann aber auch senden.......
Delete - Do 06.10.11 11:28
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: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, SetupAPI, StdCtrls, SerialNG;
type TForm1 = class(TForm) Button1: TButton; Button2: TButton; SerialPortNG1: TSerialPortNG; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private public end;
var Form1: TForm1; port: TSerialPortNG; implementation
{$R *.dfm} procedure machwas3(port: TSerialPortNG); var buffer: String; timeout: integer; abgelaufen: TDateTime; datenok: boolean; begin buffer := ''; datenok := false; timeout := 2000 abgelaufen := IncMilliSecond(getTime(), timeout); while (getTime() <= abgelaufen) and (not datenok) do begin
if port.nextClusterSize > 0 then begin buffer := buffer + port.ReadNextClusterAsString(); end;
if copy(buffer, length(buffer), 1) = #13 then begin datenok := true; end;
sleep(50); end;
end;
procedure machwas2(port: TSerialPortNG); var text: String; begin text := '0000sr 05'; port.SendString(text + #05);
machwas3(port); end;
procedure machwas; var port: TSerialPortNG; begin port := TSerialPortNG.Create(nil); try port.CommPort := 'COM4'; port.BaudRate := 9600; port.Active := true; machwas2(port);
finally port.Active := false; freeandnil(port); end; end;
procedure TForm1.Button1Click(Sender: TObject); begin machwas(); end;
procedure TForm1.Button2Click(Sender: TObject); begin machwas2(port); end;
end. |
Was genau muss jetzt in meine Unit rein die ich für mein Gerät erstellen muss? Ich habe keine Waage sondern ein Messgerät dem ich Befehle übermitteln muss.
Bei mir kommt folgende fehlermeldungen
[Fehler] Unit1.pas(38): Operator oder Semikolon fehlt
[Fehler] Unit1.pas(38): Undefinierter Bezeichner: 'IncMilliSecond'
[Fataler Fehler] progtestseriell.dpr(5): Verwendete Unit 'Unit1.pas' kann nicht compiliert werden
Ich habe jedoch noch keine WaageUnit erstellt. wie mache ich das für mein Gerät ? muss zum beispiel Strings auf das gerät senden ( befehle wie: 030 030 030 030 s73 r72 (danach Control E also 05 hex zum Connecten mit dem Gerät)