Hilfe bei serieller Schnittstelle

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
haderlump
Beiträge: 188
Registriert: Fr 18. Jan 2013, 09:29
OS, Lazarus, FPC: Windows 10, Windows XP, Lazarus 1.6
CPU-Target: Celeron

Hilfe bei serieller Schnittstelle

Beitrag von haderlump »

Hallo zusammen
ich verwende Ser.Com von Dynamo Software.
in der Ursprungsversion mit einer Com-Schnittstelle klappt das auch problemlos.
Ich möchte das ganze aber auch verstehen , da ich mehrere Schnittstellen einsetzen muß.
Als erstes die Funktion SerialInit.
Hier der Code

Code: Alles auswählen

function SerialInit(PortNr, InBuffer, OutBuffer: integer): SerialPtr;
//var //Serialport: SerialPtr;
begin
  New(Serialport_zentrale);
  if (SerialPort_zentrale <> nil) then with SerialPort_zentrale^ do
    begin
      ComName[0] := 'C';
      ComName[1] := 'O';
      ComName[2] := 'M';
      ComName[3] := char(PortNr + 48);
      ComName[4] := ':';
      ComName[5] := #0;
      hFile := INVALID_HANDLE_VALUE;
      sBaud := 19200;
      sData := 8;
      sStop := 1;
      sParity := 'N';
      Fillchar(TimeOut, sizeof(TimeOut), 0);
      with TimeOut do
      begin
        ReadIntervalTimeout := MAXDWORD;
        ReadTotalTimeoutConstant := 0;
        ReadTotalTimeoutMultiplier := 0;
      end;
      SizeIn := InBuffer;
      SizeOut := OutBuffer;
    end;
  SerialInit := SerialPort_zentrale;
end; 

Nun die Fragen:
1. In dem Array ComName wird wohl der Schnittstellenname zusammengebastelt aber wofür dient [4] und [5]. Braucht das die API von Windows?
2. Wird hier erst der Datensatz für die Schnittstelle erstellt, oder erfolgt hier schon die Anforderung beim Betriebssystem (XP).

Danke im Voraus
Gruß Fritz

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Hilfe bei serieller Schnittstelle

Beitrag von Christian »

Solltest du das nicht den Hersteller der Komponente fragen ?
Die KOmponente kenn ich jedenfalls nicht.
Die meissten hier nutzen synaser oder die fpc eigenen routinen.

Windows routinen brauchen das jedenfalls nicht.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Re: Hilfe bei serieller Schnittstelle

Beitrag von theo »

haderlump hat geschrieben: 1. In dem Array ComName wird wohl der Schnittstellenname zusammengebastelt aber wofür dient [4] und [5]. Braucht das die API von Windows?
Warum kümmert dich das? Wenn die Library das so machen will, dann kann dir das doch egal sein.
haderlump hat geschrieben: 2. Wird hier erst der Datensatz für die Schnittstelle erstellt, oder erfolgt hier schon die Anforderung beim Betriebssystem (XP).
Ich sehe hier nichts, was eine Aktion auslösen würde. MMn wird hier nur SerialPtr mit Werten bestückt.

Sonst gibt es wie gesagt auch Synaser mit TBlockSerial
http://synapse.ararat.cz/doc/help/synaser.html

Soner
Beiträge: 726
Registriert: Do 27. Sep 2012, 00:07
OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
CPU-Target: x86_64-win64
Wohnort: Hamburg

Re: Hilfe bei serieller Schnittstelle

Beitrag von Soner »

1. Bei Windows werden Geräte Namen mit Doppelpunkt abgeschlossen, z.B. com1:, lpt1:, c:
2. '#0' hat mit Programmiersprache C zutun. Dort werden Strings mit Null abgeschlossen. Weietere Infos findest du, wenn du nach C-Strings nachschlägst.

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Hilfe bei serieller Schnittstelle

Beitrag von Christian »

Sicher ? Kenn ich anders und in der Wiki steht auch was anderes.
http://wiki.lazarus.freepascal.org/Hard ... on_Windows
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

haderlump
Beiträge: 188
Registriert: Fr 18. Jan 2013, 09:29
OS, Lazarus, FPC: Windows 10, Windows XP, Lazarus 1.6
CPU-Target: Celeron

Re: Hilfe bei serieller Schnittstelle

Beitrag von haderlump »

Danke Leute ich bin nun schon etwas weiter.
Zur Verwendung: Ich will damit mit Harware zur steuerung einer Modellbahn communizieren. Dazu brauche ich 3 Schnittstellen.
1.Umsetzer der Seuerinformationen in das DCC Format.
2. Abfrage von Kontaktinformationen im Gleis.
3. Anschaltung eines Reglers zur Geschwindigketssteuerung.
Es gibt auch keine Modems, die Karten werden direkt angeschlossen. (über gekreuzte Leitungen)
Ich habe das hier nicht optimal gepostet. Das war natürlich nur ein Teil des Codes. Ich wollte da schrittweise vorgehen, ist aber blöd gewesen.
Da kommt natürlich noch mehr. Hier nun alles:

Code: Alles auswählen

 
{*******************************************************}
{                                                       }
{       SerCOM  1.2 (3) Windows                         }
{                                                       }
{       Copyright (c) 1990,2005 DYNAMO Software         }
{                                                       }
{*******************************************************}
 
{
  Pascal Routinen für die Verwendung der seriellen
  Schnittstelle. Diese Unit existiert in 3 Versionen
  für folgende Plattformen:
 
  (1) MS-DOS     (Turbo Pascal)
  (2) Macintosh  (CodeWarrior)
  (3) Windows    (Delphi)
 
  Dieser Quellcode ist Freeware und darf NICHT verkauft
  werden. Download der aktuellen Version unter:
 
  http://www.dynamo-software.de/serial/code.htm
 
  Viel Spaß am Gerät!
  Jens-Erich Lange
  info@dynamo-software.de
}
 
unit SerCom;
 
interface
 
uses
  Windows;
 
const
  SER_OK = 0;      // Kein Fehler
  SER_NIL = -1;    // SerialPtr nicht initialisiert / Zuwenig RAM für Puffer
  SER_ERR = -2;    // Unzulässige Parameter oder Schnittstelle geschlossen
  SER_SYS = -3;    // Fehlermeldung vom Betriebssystem. Nummer in "SerialCode"
 
 
type
  SerialRec = record
    SysErr: longint;
    ComName: array [0..5] of char;
    hFile: tHandle;
    sBaud: longint;
    sData: integer;
    sStop: integer;
    sParity: char;
    Output: longint;
    SizeIn: longint;
    SizeOut: longint;
    TimeOut: tCommTimeOuts;
  end;
 
  SerialPtr = ^SerialRec;
  SerialErr = longint;
 
 
{-------------------------------------------------------}
{  Initialisierung / Deinitialisierung                  }
{-------------------------------------------------------}
function SerialInit(PortNr, InBuffer, OutBuffer: integer): SerialPtr;
function SerialCode(SerialPort: SerialPtr): SerialErr;
function SerialExit(SerialPort: SerialPtr): SerialErr;
 
{-------------------------------------------------------}
{  Öffnen / Schließen / Parametrieren                   }
{-------------------------------------------------------}
function SerialPara(SerialPort: SerialPtr; Baud: longint; Data, Stop: integer; Parity: char): SerialErr;
function SerialOpen(SerialPort: SerialPtr): SerialErr;
function SerialStop(SerialPort: SerialPtr): SerialErr;
 
{-------------------------------------------------------}
{  Senden / Empfangen                                   }
{-------------------------------------------------------}
function SerialXmit(SerialPort: SerialPtr; data: byte): SerialErr;
function SerialRecv(SerialPort: SerialPtr; var data: byte): SerialErr;
function SerialDone(SerialPort: SerialPtr): SerialErr;
 
function SerialRSet(SerialPort: SerialPtr; Value: byte): SerialErr;
function SerialRGet(SerialPort: SerialPtr): SerialErr;
 
 
implementation
uses unit21;                
 
{----------------------------------------------------------------------}
{  Funktion "SerialOpen"                                               }
{  Öffnet die serielle Schnittstelle und ermöglicht somit den Daten-   }
{  verkehr. Nachdem die Schnittstelle geöffnet ist, werden alle ein-   }
{  gehenden Zeichen im Zwischenpuffer gespeichert bis diese mit der    }
{  Funktion "SerialRecv" aus dem Puffer entnommen werden.              }
{  Das Gegenstück zu "SerialOpen" ist die Funktion "SerialStop".       }
{  Die Zwischenpuffer werden beim öffnen und schließen einer Schnitt-  }
{  stelle gelöscht.                                                    }
{----------------------------------------------------------------------}
 
function SerialOpen(SerialPort: SerialPtr): SerialErr;
var commprop: tCommProp;
begin
  result := SER_OK;
  if (SerialPort <> nil) then with SerialPort^ do
    begin
      if hFile = INVALID_HANDLE_VALUE then
      begin
        hFile := CreateFile(ComName, GENERIC_READ + GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0);
 
Hier geht es woh dann tatsächlich zu Windows ?

Code: Alles auswählen

 
        if (hFile <> INVALID_HANDLE_VALUE) then
        begin
          if GetCommProperties(hfile, commprop) then
          begin
            SetupComm(hfile, SizeIn, SizeOut);
            SetParameter(SerialPort);
            SetCommTimeouts(hfile, TimeOut);
            SetCommMask(hfile, 0);
            Output := commprop.dwCurrentTxQueue;
          end
          else begin
            SysErr := GetLastError;
            result := SER_SYS;
          end;
        end
        else begin
          SysErr := GetLastError;
          result := SER_SYS;
        end;
      end;
    end
  else result := SER_NIL;
end;                                              
 
{----------------------------------------------------------------------}
{  Funktion "SerialPara"                                               }
{  parametriert die serielle Schnittstelle. Es ist dabei unerheblich,  }
{  ob die Schnittstelle bereits geöffnet ist oder nicht.               }
{  Für eine ordentliche Kommunikation ist es erforderlich, daß alle    }
{  Parameter mit denen der Gegenstelle übereinstimmen. Viele Modems    }
{  können die verwendeten Parameter selbständig erkennen und können    }
{  daher mit verschiedenen Parametern angesprochen werden.             }
{  Die Standardeinstellung für Daten- und Stopbits sowie Parität sind  }
{  8 - 1 - Keine (None).                                               }
{----------------------------------------------------------------------}
 
function SerialPara(SerialPort: SerialPtr; Baud: longint; Data, Stop: integer; Parity: char): SerialErr;
begin
  if (SerialPort <> nil) then with SerialPort^ do
    begin
      sBaud := Baud;
      sData := Data;
      sStop := Stop;
      sParity := Parity;
      result := SetParameter(SerialPort);
    end
  else result := SER_NIL;
end;    
{----------------------------------------------------------------------}
{  Funktion "SerialStop"                                               }
{  schließt die serielle Schnittstelle. Die Datenkommunikation wird    }
{  unterbunden und der Gegenstelle wird auf den Handshakeleitungen     }
{  signalisiert, daß die Schnittstelle geschlossen ist. Ein ordent-    }
{  lich konfiguriertes Modem sollte dadurch eine bestehende Telefon-   }
{  verbindung trennen.                                                 }
{----------------------------------------------------------------------}
 
function SerialStop(SerialPort: SerialPtr): SerialErr;
begin
  if (SerialPort <> nil) then with SerialPort^ do
    begin
      if hFile <> INVALID_HANDLE_VALUE then
      begin
        EscapeCommFunction(hFile, CLRRTS);
        SetCommMask(hfile, 0);
        CloseHandle(hFile);
        hFile := INVALID_HANDLE_VALUE;
      end;
      result := SER_OK;
    end
  else result := SER_NIL;
end;
 
 
{----------------------------------------------------------------------}
{  Funktion "SerialXmit"                                               }
{  Übergibt ein Zeichen an den Sendepuffer. Falls die Schnittstelle    }
{  zu diesem Zeitpunkt geöffnet ist, werden die Zeichen im Hinter-     }
{  grund asynchron übertragen. Die Funktion kehrt nach ihrem Aufruf    }
{  schnell zurück und wartet nicht auf das Ende der Ðbertragung.       }
{  Zeichenketten und binäre Arrays werden vom Aufrufer in einer        }
{  Schleife an diese Funktion übergeben.                               }
{  Mit der Funktion "SerialDone" kann geprüft werden, ob schon alle    }
{  Zeichen übertragen wurden.                                          }
{----------------------------------------------------------------------}
 
function SerialXmit(SerialPort: SerialPtr; data: byte): SerialErr;
var
  count: DWORD;
  comstat: tComstat;
begin
  if (SerialPort <> nil) then with SerialPort^ do
    begin
      if hFile <> INVALID_HANDLE_VALUE then
      begin
        ClearCommError(hfile, count, @comstat);
        if (((Output - SerialErr(comstat.cbOutQue)) - 1) <= 0) then
        begin
          if WriteFile(hfile, data, 1, count, nil) then result := count
          else begin
            SysErr := GetLastError;
            result := SER_SYS;
          end;
        end
        else result := SER_NIL;
      end
      else result := SER_ERR;
    end
  else result := SER_NIL;
end;
 
 
{----------------------------------------------------------------------}
{  Funktion "SerialRecv"                                               }
{  holt das nächste Zeichen aus dem Empfangspuffer. Voraussetzung ist  }
{  eine geöffnete Schnittstelle. Ist das Ergebnis dieser Funktion "1"  }
{  so wurde die Funktion erfolgreich ausgeführt. Alle anderen Werte    }
{  bedeuten, daß kein Zeichen empfangen wurde.                         }
{----------------------------------------------------------------------}
 
function SerialRecv(SerialPort: SerialPtr; var data: byte): SerialErr;
var count: DWORD;
begin
  if (SerialPort <> nil) then with SerialPort^ do
    begin
      if hFile <> INVALID_HANDLE_VALUE then
      begin
        if windows.ReadFile(hfile, data, 1, count, nil) then result := count
        else begin
          SysErr := GetLastError;
          result := SER_SYS;
        end;
      end
      else result := SER_ERR;
    end
  else result := SER_NIL;
end;
 
 
{----------------------------------------------------------------------}
{  Funktion "SerialDone"                                               }
{  ermittelt wieviele Zeichen sich momentan im Sendepuffer befinden.   }
{  Ist das Ergebnis "0", sind keine Zeichen mehr im Sendepuffer.       }
{----------------------------------------------------------------------}
 
function SerialDone(SerialPort: SerialPtr): SerialErr;
begin
  if (SerialPort <> nil)
    then result := SerialPort^.Output
  else result := SER_NIL;
end;
 
 
{----------------------------------------------------------------------}
{  Funktion "SerialRSet"                                               }
{  setzt einzelne Handshakeleitungen der seriellen Schnittstelle.      }
{  Diese Funktion ist nur in dieser Wondows Version vorhanden.         }
{----------------------------------------------------------------------}
 
function SerialRSet(SerialPort: SerialPtr; Value: byte): SerialErr;
begin
  if (SerialPort <> nil) then with SerialPort^ do
    begin
      if hFile <> INVALID_HANDLE_VALUE then
      begin
        EscapeCommFunction(hFile, Value);
        result := SER_OK;
      end
      else result := SER_ERR;
    end
  else result := SER_NIL;
end;
 
 
{----------------------------------------------------------------------}
{  Funktion "SerialRGet"                                               }
{  liest den Status der Handshakeleitungen.                            }
{  Diese Funktion ist nur in dieser Wondows Version vorhanden.         }
{----------------------------------------------------------------------}
 
function SerialRGet(SerialPort: SerialPtr): SerialErr;
var Reg: Cardinal;
begin
  if (SerialPort <> nil) then with SerialPort^ do
    begin
      if (hFile <> INVALID_HANDLE_VALUE) then
      begin
        if GetCommModemStatus(hFile, Reg)
          then Result := Reg
        else Result := SER_ERR;
      end
      else result := SER_ERR;
    end
  else result := SER_NIL;
end;
 
 
end.                                                     
Bei SerialInit wird offensichtlich nur der Record angelegt, und ein Pointer darauf installiert.
Schön langsam wird das für mich schon durchsichtiger.

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Hilfe bei serieller Schnittstelle

Beitrag von Christian »

Wenn du das nicht alles selbermachen willst nimm einfach synaser aus synapse.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

haderlump
Beiträge: 188
Registriert: Fr 18. Jan 2013, 09:29
OS, Lazarus, FPC: Windows 10, Windows XP, Lazarus 1.6
CPU-Target: Celeron

Re: Hilfe bei serieller Schnittstelle

Beitrag von haderlump »

So Leute
Das funktioniert jetzt alles, Ich habe es auch weitgehend verstanden.
Ich möchte nun folgendermaßen vorgehen.
Schnittstelle öffnen
Zeichen senden
Darauf soll die Gegenstelle mit 'R' bzw 'K' antworten.
Das dauert natürlich ein bisschen.
Wenn nach einer gewissen Zeit kein 'R' oder 'K' kommt, betrachte ich diese Schnittstelle als taub, und ich probier die Nächse.

Wie realisiere ich nun am besten diese Warterei. Da gibt es doch so etwas wie einen Pausebefehl.
oder gibt es eine bessere Lösung.
Gruß Fritz

baumina
Beiträge: 152
Registriert: Mo 3. Feb 2014, 14:07
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Hilfe bei serieller Schnittstelle

Beitrag von baumina »

Zum Warten wird meist ein Timer eingesetzt.
.

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Hilfe bei serieller Schnittstelle

Beitrag von Christian »

Nein ein vernünftig programmiertes Programm wartet nie.

Sauber implementiert macht man das mit einem separaten Thread und blockiert den solange bis ein Zeichen kommt (gibts Betriebsystem routinen für)
Der informiert dann bei neuen Daten das Hauptprogramm mit einer Nachricht oder Ereignis.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

haderlump
Beiträge: 188
Registriert: Fr 18. Jan 2013, 09:29
OS, Lazarus, FPC: Windows 10, Windows XP, Lazarus 1.6
CPU-Target: Celeron

Re: Hilfe bei serieller Schnittstelle

Beitrag von haderlump »

Danke für die Antworten.
Mit eurer Hilfe hab ich nun fast alle Probleme gelöst.
Cristian: das ist mir zu kompliziert. Ich warte einfach mit dem sleep Befehl ein paar Millisekunden, wenn dann keine Antwort kommt, ist das Gerät nicht angeschlossen. Da generiere ich eine Fehlermeldung. Die Procedur wird nur am Programmanfang aufgerufen, insofern spielt das Timing späer keine Rolle mehr.
Wenn ich dann das Gerät anschließe kann ich per Menübefehl das Ganze nochmal wiederholen.

Nebenbei: Es ist schon komisch, dass Windows die Com-Schnittstellen nicht der Reihe nach anlegt. sondern wie bei mir COM1, COM6 COM7
Auf meinem anderen Rechner schaut das wieder anders aus.
Na ja man muss nicht alles kapieren !! Hauptsach man kann damit umgehen.

Also Danke nochmal
Fritz

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Hilfe bei serieller Schnittstelle

Beitrag von Christian »

Doch Windows legt die der Reihe nach an, warscheinlich hatten die fehlenden Schnittstellen zwischendurch schonmal ein Gerät. Windows "merkt" sich für z.b. USB/Seriall Wandler den Com port und die Schnittstelle, wenn du die an nen anderen USB Port hängst bekommen Sie auch nen anderen Com-Port dadurch kommen solche Sachen zustande. Evntl habne auch Manchmal richten Setups für irgendwelche Geräte auch spezifische COm Ports ein (warscheinlich damit der Supports einfacher hat)
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Hilfe bei serieller Schnittstelle

Beitrag von mschnell »

haderlump hat geschrieben: Die Procedur wird nur am Programmanfang aufgerufen, insofern spielt das Timing späer keine Rolle mehr.
Dann kann Dein Programm aber nicht sinnvoll reagieren, wenn aus irgend einem Grund keine Antwort vom angeschlossenen Gerät mehr kommt.

Du solltest Christians Tip befolgen !

-Michael
(Leider gibt es für Lazarus ja keine Komponente, die das Handling von Sereienschnittstellen komplett intern in Threads erledigt und das User-Interface im Mainthread anbietet. Für (altes) Delphi gibt es da (inzwischen kostenlos und open Source) "AsyncPro". Damit geht das alles ganz einfach. )

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: Hilfe bei serieller Schnittstelle

Beitrag von Christian »

Doch gibts, hab ich hier irgendwann mal gepostet.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Hilfe bei serieller Schnittstelle

Beitrag von mschnell »

Du hast recht. Ich kann mich schwach daran erinnern.

Warum wird das nicht massiv empfohlen ?

-Michael

Antworten