Difference between revisions of "Hardware Access/de"

From Lazarus wiki
Jump to navigationJump to search
(Updated link for pas-libusb)
 
(40 intermediate revisions by 12 users not shown)
Line 1: Line 1:
 
{{Hardware Access}}
 
{{Hardware Access}}
 +
<br>
 +
Zurück zu den [[Additional information/de|Zusätzlichen Informationen]].<br>
 +
<br>
 
__TOC__
 
__TOC__
 
== Überblick ==
 
== Überblick ==
Diese Seite ist der Beginn eines Tutorials über den Zugriff auf Hardware Geräte mit Lazarus. Diese Geräte enthalten, ohne darauf beschränkt zu sein: ISA, PCI, USB, paralleler und serieller Anschluß.
+
Diese Seite ist der Beginn eines Tutorials über den Zugriff auf Hardware Geräte mit Lazarus. Diese Geräte enthalten, ohne darauf beschränkt zu sein: ISA, PCI, USB, paralleler und serieller Anschluss.
  
 
Der Zugriff auf Hardware Geräte auf einer komplett plattformunabhängigen Weise ist nicht implementiert durch die Free Pascal Runtime Library oder durch die LCL, daher umfasst dieses Tutorial grundsätzlich Hardware Zugriffsmethoden auf verschiedenen Plattformen. Der Code kann unter verschiedenen Umgebungen kompiliert werden unter Verwendung bedingter Kompilierung, etwa so:
 
Der Zugriff auf Hardware Geräte auf einer komplett plattformunabhängigen Weise ist nicht implementiert durch die Free Pascal Runtime Library oder durch die LCL, daher umfasst dieses Tutorial grundsätzlich Hardware Zugriffsmethoden auf verschiedenen Plattformen. Der Code kann unter verschiedenen Umgebungen kompiliert werden unter Verwendung bedingter Kompilierung, etwa so:
  
<code>
+
<syntaxhighlight lang="pascal">
 
  uses
 
  uses
 
   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls,
 
   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls,
Line 15: Line 18:
 
   ports;
 
   ports;
 
  {$ENDIF}
 
  {$ENDIF}
</code>
+
</syntaxhighlight>
  
 
Hilfe ist willkommen, speziell zu MacOS.
 
Hilfe ist willkommen, speziell zu MacOS.
  
== Parallel und Seriell Vergleich ==
+
== Vergleich Parallel <-> Seriell ==
ISA Karten, PCI Karten und der Parallele Anschluß kommunizieren mit dem Computer unter Verwendung eines '''parallelen''' Protokolls. Der Serielle Anschluß und USB Geräte arbeiten mit einem '''seriellen''' Protokoll. Weil der Prozessor und thus programming languages all work on a parallel approach to data, access to this kinds of protocols are easier to be implemented on the software side. When you acess an Integer variable, for example, you can access it's value with a single command. With a serial protocol, however, you can only know one bit at a time, and you need to glue the pieces together latter to understand the data.
+
ISA Karten, PCI Karten und der parallele Anschluss kommunizieren mit dem Computer unter Verwendung eines '''parallelen''' Protokolls. Der serielle Anschluss und USB Geräte arbeiten mit einem '''seriellen''' Protokoll. Da der Prozessor und damit auch alle Programmiersprachen auf Daten parallel zugreifen, ist es wesentlich einfacher solche Protokolle softwareseitig zu programmieren. Beispielsweise erfolgt der Zugriff auf eine Ganzzahl-Variable (INTEGER) nur durch einen einzelnen Befehl. Mit einem seriellen Protokoll kann jedoch nur jeweils auf ein Bit zugegriffen werden. Im Nachhinein ist es notwendig, die Teildaten zusammen zu fügen um sie zu verwenden.  
  
Serielle Kommunikation ist schwierig direkt zu implementieren, aber es kann ziemlich einfach sein, wenn sie eine vorgefertigte Komponente verwenden. Es ist auch schwieriger auf der Hardware-Seite, daher verwenden viele Geräte spezialisierte IC-Bausteine oder sogar Mikrokontroller um es zu implementieren.
+
Serielle Kommunikation ist schwierig direkt zu programmieren. Wenn allerdings eine fertige Komponente zur Verfügung steht wird es deutlich einfacher. Auch hardwareseitig sind serielle Schnittstellen deutlich umständlicher, daher benötigen die meisten seriellen Schnittstellen spezielle IC-Bausteine oder Microcontroller für die Kommunikation.
  
Jetzt wird ein kurzer Vergleich der Hardware Zugriffsprotokolle gezeigt:
+
Hier eine kurze Gegenüberstellung der Hardware-Zugriffsprotokolle:
  
 
{| border=2 width="100%"
 
{| border=2 width="100%"
Line 31: Line 34:
 
!
 
!
 
! Geschwindigkeit
 
! Geschwindigkeit
! Hardwareimplementierungsschwierigkeit
+
! Hardwaredesign
  
 
|-
 
|-
! Serieller Anschluß
+
! Serieller Anschluss
| align="center" | sehr langsam (< E5 bit/s)
+
| align="center" | sehr langsam (< 100 kBit/s)
 
| align="center" | mittel
 
| align="center" | mittel
  
 
|-
 
|-
! Paralleler Anschluß
+
! Paralleler Anschluss
| align="center" | langsam (~ E6 bit/s)
+
| align="center" | langsam (~ 1 MBit/s)
 
| align="center" | einfach
 
| align="center" | einfach
  
Line 46: Line 49:
  
 
! ISA Karte
 
! ISA Karte
| align="center" | mittel (~ E7 bit/s)
+
| align="center" | mittel (~ 10 MBit/s)
 
| align="center" | mittel
 
| align="center" | mittel
  
 
|-
 
|-
! USB
+
! USB 1.x
| align="center" | mittel (~ E7 bit/s)
+
| align="center" | mittel (~ 1.6 MBit/s)
| align="center" | schwierig
+
| align="center" | komplex
 +
 
 +
|-
 +
! USB 2.0
 +
| align="center" | schnell (~ 480 MBit/s)
 +
| align="center" | sehr komplex
  
 
|-
 
|-
 
! PCI Karte
 
! PCI Karte
| align="center" | sehr schnell (> E9 bit/s)
+
| align="center" | sehr schnell (> 1 GBit/s)
| align="center" | sehr schwierig
+
| align="center" | sehr komplex
  
 
|}
 
|}
Line 64: Line 72:
  
 
=== Verwendung von inpout32.dll für Windows ===
 
=== Verwendung von inpout32.dll für Windows ===
Windows hat verschiedene Wege für die Ansteuerung von Hardware Geräten bei der 9x Serie und bei der NT Serie, und dies kann wirklich problematisch sein. Die 9x Serie (95, 98, Me) erlaubt den Prgrammen direkten Zugriff auf die Ports, genau wie unter DOS. Windows NT, jedoch, verhindert dies komplett, und erlaubt nur den Zugriff durch die Gerätetreiber auf die Ports. Dies ist ein Sicherheitsmechanismus, der aber nervig sein kann, wenn ein Gerätetreiber zu komplex für ein kleines Projekt ist, das Zugriff auf den parallelen Port benötigt.
+
Windows hat verschiedene Wege für die Ansteuerung von Hardware Geräten bei der 9x Serie und bei der NT Serie, und dies kann wirklich problematisch sein. Die 9x Serie (95, 98, Me) erlaubt den Programmen direkten Zugriff auf die Ports, genau wie unter DOS. Windows NT, jedoch, verhindert dies komplett, und erlaubt nur den Zugriff durch die Gerätetreiber auf die Ports. Dies ist ein Sicherheitsmechanismus, der aber nervig sein kann, wenn ein Gerätetreiber zu komplex für ein kleines Projekt ist, das Zugriff auf den parallelen Port benötigt.
  
 
Glücklicherweise gibt es eine Bibliothek, die einen Gerätetreiber enthält. Wenn Windows NT erkannt wird, dann dekomprimiert sie das Gerät und installiert den Hwinterface.sys Kernel Gerätetreiber. Wenn Windows 9x erkannt wird, dann benutzt sie einfach Assembler Opcodes für den Zugriff auf die Hardware.
 
Glücklicherweise gibt es eine Bibliothek, die einen Gerätetreiber enthält. Wenn Windows NT erkannt wird, dann dekomprimiert sie das Gerät und installiert den Hwinterface.sys Kernel Gerätetreiber. Wenn Windows 9x erkannt wird, dann benutzt sie einfach Assembler Opcodes für den Zugriff auf die Hardware.
Line 72: Line 80:
 
Wir werden die Bibliothek dynamisch laden, so lassen sie uns zuerst beide Funktionen definieren:
 
Wir werden die Bibliothek dynamisch laden, so lassen sie uns zuerst beide Funktionen definieren:
  
<code>
+
<syntaxhighlight lang="pascal">
 
  type
 
  type
 
   TInp32 = function(Address: ShortInt): ShortInt; stdcall;
 
   TInp32 = function(Address: ShortInt): ShortInt; stdcall;
 
   TOut32 = procedure(Address: ShortInt; Data: ShortInt); stdcall;
 
   TOut32 = procedure(Address: ShortInt; Data: ShortInt); stdcall;
</code>
+
</syntaxhighlight>
  
* Address representiert die Adresse des Ports, auf den sie zugreifen wollen. Out32 sendet Daten zu dem Port, den sie mit Adress spezifiziert haben. Inp32 gibt ein Byte zurück vom Port, den sie mit Address spezifiziert haben.
+
* Address ist die Adresse des Ports, auf den sie zugreifen wollen. Out32 sendet Daten zu dem Port, den sie mit Adress spezifiziert haben. Inp32 gibt ein Byte von dem Port zurück, den sie mit Address spezifiziert haben.
  
Nun können wir die Bibliothek laden. Dies kann implementiert werden an einer Stelle wie der OnCreate Methode des Haupformulars ihres Programms:
+
Nun können wir die Bibliothek laden. Dies kann implementiert werden an einer Stelle wie der OnCreate Methode des Hauptformulars ihres Programms:
  
<code>
+
<syntaxhighlight lang="pascal">
 
   type
 
   type
 
   TMyForm = class(TForm)
 
   TMyForm = class(TForm)
Line 109: Line 117:
 
  {$ENDIF}
 
  {$ENDIF}
 
  end;
 
  end;
</code>
+
</syntaxhighlight>
  
 
Wenn sie die Bibliothek bei OnCreate laden, vergessen sie nicht, sie bei OnDestroy zu entladen:
 
Wenn sie die Bibliothek bei OnCreate laden, vergessen sie nicht, sie bei OnDestroy zu entladen:
  
<code>
+
<syntaxhighlight lang="pascal">
 
  procedure TMyForm.FormDestroy(Sender: TObject);
 
  procedure TMyForm.FormDestroy(Sender: TObject);
 
  begin
 
  begin
Line 120: Line 128:
 
  {$ENDIF}
 
  {$ENDIF}
 
  end;
 
  end;
</code>
+
</syntaxhighlight>
  
 
Hier ist ein einfaches Beispiel, wie man die Inp32 Funktion benutzt:
 
Hier ist ein einfaches Beispiel, wie man die Inp32 Funktion benutzt:
  
<code>
+
<syntaxhighlight lang="pascal">
 
  {$IFDEF WIN32}
 
  {$IFDEF WIN32}
 
   myLabel.Caption := IntToStr(Inp32($0220));
 
   myLabel.Caption := IntToStr(Inp32($0220));
 
  {$ENDIF}
 
  {$ENDIF}
</code>
+
</syntaxhighlight>
  
 
Dieser Code wurde getestet mit einer maßgeschneiderten ISA Karte auf Port $0220, unter Verwendung von Lazarus 0.9.10 mit Windows XP. Natürlich müssen sie Windows in ihrem uses Abschnitt haben damit dieser Code läuft. Für den Einsatz müssen sie nur "inpout32.dll" in das selbe Verzeichnis wie ihre Anwendung einfügen.
 
Dieser Code wurde getestet mit einer maßgeschneiderten ISA Karte auf Port $0220, unter Verwendung von Lazarus 0.9.10 mit Windows XP. Natürlich müssen sie Windows in ihrem uses Abschnitt haben damit dieser Code läuft. Für den Einsatz müssen sie nur "inpout32.dll" in das selbe Verzeichnis wie ihre Anwendung einfügen.
  
 
Dies ist die Homepage für die Bibliothek: [http://www.logix4u.net/inpout32.htm www.logix4u.net/inpout32.htm]
 
Dies ist die Homepage für die Bibliothek: [http://www.logix4u.net/inpout32.htm www.logix4u.net/inpout32.htm]
 +
 +
=== Verwendung von Assembler unter Windows 9x ===
 +
 +
Unter Windows 9x können sie auch Assembler Code verwenden. Suppose you with to write $CC to the $320 port. Der folgende Code erledigt das:
 +
 +
<syntaxhighlight lang="pascal">
 +
{$ASMMODE ATT}
 +
...
 +
    asm
 +
        movl $0x320, %edx
 +
        movb $0xCC, %al
 +
        outb %al, %dx
 +
    end ['EAX','EDX'];
 +
</syntaxhighlight>
 +
 +
=== Mögliche Fehlerquellen in Windows ===
 +
 +
Eine mögliche Fehlerquelle in Windows bezüglich parallel verwendeter Hardware, die kein "Plug and Play" unterstützt kann darin liegen, dass Windows den von der Hardware benötigten Port einem anderen Gerät zugewiesen hat.
 +
Unter der folgenden Adresse können Sie Informationen darüber finden, wie man Windows daran hindern kann, den benötigten Port zu vergeben:
 +
 +
http://support.microsoft.com/kb/135168
  
 
=== Verwendung von ioperm zum Zugriff auf Ports unter Linux ===
 
=== Verwendung von ioperm zum Zugriff auf Ports unter Linux ===
Line 138: Line 167:
 
Der beste Weg um auf Hardware unter Linux zuzugreifen, ist durch Gerätetreiber, aber, wegen der Komplexität der Aufgabe, einen Treiber zu erstellen, ist manchmal eine schnelle Methode sehr nützlich.
 
Der beste Weg um auf Hardware unter Linux zuzugreifen, ist durch Gerätetreiber, aber, wegen der Komplexität der Aufgabe, einen Treiber zu erstellen, ist manchmal eine schnelle Methode sehr nützlich.
  
Damit sie die "ports" Unit unter Linux benutzen können, muß ihr Programm als root laufen, und IOPerm muß aufgerufen werden um eine angemessene Erlaubnis für den Portzugriff zu setzen. Sie können die Dokumentation über die "ports" Unit [http://www.freepascal.org/docs-html/rtl/ports/index.html hier] finden.
+
Damit sie die "ports" Unit unter Linux benutzen können, muss ihr Programm als root laufen, und IOPerm muss aufgerufen werden, um eine angemessene Erlaubnis für den Portzugriff zu setzen. Sie können die Dokumentation über die "ports" Unit [http://www.freepascal.org/docs-html/rtl/ports/index.html hier] finden.
  
 
Zunächst müssen sie auf glibc verlinken und IOPerm aufrufen. Eine Unit, die auf das ganze glibc verlinkt, existiert in Free Pascal, aber diese Unit macht Probleme, wenn sie direkt von der Anwendung benutzt wird und das statische Verlinken auf die gesamte glibc Bibliothek ist keine sehr gute Idee für eine Multiplattform Anwendung. Die Verwendung plattformspezifischer Funktionen sollte auf ein Minimum reduziert werden.
 
Zunächst müssen sie auf glibc verlinken und IOPerm aufrufen. Eine Unit, die auf das ganze glibc verlinkt, existiert in Free Pascal, aber diese Unit macht Probleme, wenn sie direkt von der Anwendung benutzt wird und das statische Verlinken auf die gesamte glibc Bibliothek ist keine sehr gute Idee für eine Multiplattform Anwendung. Die Verwendung plattformspezifischer Funktionen sollte auf ein Minimum reduziert werden.
  
<code>
+
<syntaxhighlight lang="pascal">
 
  {$IFDEF Linux}
 
  {$IFDEF Linux}
 
  function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external 'libc';
 
  function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external 'libc';
 
  {$ENDIF}
 
  {$ENDIF}
</code>
+
</syntaxhighlight>
  
* "from" representiert den ersten Port auf den zugegriffen wird.
+
* "from" repräsentiert den ersten Port auf den zugegriffen wird.
 
* "num" ist die Anzahl (number) der Ports nach dem ersten Port, auf den zugegriffen wird, daher wird ioperm($220, 8, 1) Zugriff ermöglichen für das Programm auf alle Ports zwischen $220 and $227 (einschließlich).
 
* "num" ist die Anzahl (number) der Ports nach dem ersten Port, auf den zugegriffen wird, daher wird ioperm($220, 8, 1) Zugriff ermöglichen für das Programm auf alle Ports zwischen $220 and $227 (einschließlich).
  
 
Nach dem Verlinken auf OIPerm können sie port[<Address>] eingeben für den Zugriff auf die Ports.
 
Nach dem Verlinken auf OIPerm können sie port[<Address>] eingeben für den Zugriff auf die Ports.
  
<code>
+
<syntaxhighlight lang="pascal">
 
  {$IFDEF Linux}
 
  {$IFDEF Linux}
 
   i := ioperm($220, 8, 1);
 
   i := ioperm($220, 8, 1);
Line 161: Line 190:
 
   myOtherLabel.Caption := 'response: ' + IntToStr(i);
 
   myOtherLabel.Caption := 'response: ' + IntToStr(i);
 
  {$ENDIF}
 
  {$ENDIF}
</code>
+
</syntaxhighlight>
  
Dieser Code wurde mit einer maßgeschneiderten ISA Karte auf Port $0220 getested, unter Verwendung von Lazarus 0.9.10 unter Mandriva Linux 2005 und Damn Small Linux 1.5
+
Dieser Code wurde mit einer maßgeschneiderten ISA Karte auf Port $0220 getestet, unter Verwendung von Lazarus 0.9.10 unter Mandriva Linux 2005 und Damn Small Linux 1.5
 +
 
 +
PS: Bei aktuellen Lazarus-Versionen, ist in der Unit "x86", die sowieso von der Unit "ports" aufgerufen wird, die Funktion "ioperm" schon vorhanden, sie hat nur einen anderen Namen "fpIOperm"
  
 
=== Der übliche UNIX Hardwarezugriff ===
 
=== Der übliche UNIX Hardwarezugriff ===
  
<pre>
+
<syntaxhighlight lang="pascal">
 
{$IFDEF Unix}
 
{$IFDEF Unix}
Uses Clib;  // retrieve libc library name.
+
Uses  
 +
  Clib;  // retrieve libc library name.
 
{$ENDIF}
 
{$ENDIF}
  
Line 175: Line 207:
 
function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external clib;
 
function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external clib;
 
{$ENDIF}
 
{$ENDIF}
</pre>
+
</syntaxhighlight>
  
  
'''Beachten''' sie, daß FPC eine Abstraktion für ioperm genannt "fpioperm" bietet in der [[doc:rtl/x86/index.html|Unit x86]], und auch  out und import Funktionen. Diese Funktionen sind gegenwärtig für Linux/x86 und FreeBSD/x86 implementiert.
+
'''Beachten''' sie, dass FPC eine Abstraktion für ioperm genannt "fpioperm" bietet in der [[doc:rtl/x86/index.html|Unit x86]], und auch  out und import Funktionen. Diese Funktionen sind gegenwärtig für Linux/x86 und FreeBSD/x86 implementiert.
  
Es wird nicht empfohlen to link to libc unless absolutely necessary due to possible deployment and portability functions.
+
Es wird nicht empfohlen ''to link to libc unless absolutely necessary due to possible deployment and portability functions. Also manual linking to libc (by declaring ad hoc libc imports for functions that are available elsewhere) like done above is not recommended  (e.g. the above libc import line will unnecessarily fail if the standard C lib is not called libc, wie z.B. libroot unter BeOS (Zeta), oder auf Plattformen mit a non standard C symbol mangling).''
Also manual linking to libc (by declaring ad hoc libc imports for functions that are available elsewhere) like done above is not recommended  (e.g. the above libc import line will unnecessarily fail if the standard C lib is not called libc, like e.g. libroot on BeOS, or on platforms with a non standard C symbol mangling).
 
  
'''Note 2''' Die Verwendung von unit_ libc wird nicht empfohlen unter anderen Umständen als der Kylix Kompatibilität. Dies ist so weil die Unit relativ unportierbar ist (wegen der übermäßigen Beanspruchung von Strukturen und anderen privaten Symbolen) und must only be modified as little as possible out of Kylix compability issues.
+
'''Anmerkung 2''' Die Verwendung von unit_ libc wird nicht empfohlen unter anderen Umständen als der Kylix Kompatibilität. Dies ist so, weil die Unit relativ unportierbar ist (wegen der übermäßigen Beanspruchung von Strukturen und anderen privaten Symbolen) und must only be modified as little as possible out of Kylix compability issues.
  
 
== Serielle Kommunikation ==
 
== Serielle Kommunikation ==
  
Der [[Hardware Access#External Links | Externe Links]] Abschnitt enthält UNIX und Windows Tutorials über den seriellen Anschluß.
+
Dies sollte ein einfach zu verstehendes Beispiel sein, wie man mithilfe der [http://synapse.ararat.cz/doku.php Synaser-Bibliothek] auf eine serielle Schnittstelle zugreifen kann. Für zusätzliche Informationen bitte die [http://synapse.ararat.cz/doc/help/synaser.html Synaser - Dokumentation] zu verwenden.
 +
 
 +
Wichtig ist, das Programm nicht mit STRG-C zu beenden bzw. die Console zu schließen. Dies ist der Grund, weshalb hier am Anfang auch relativ viel Code steht, der im Prinzip nichts anderes zur Aufgabe hat, als den User noch einmal daran zu erinnern.
 +
 
 +
Wirklich interessant wird es erst in der Prozedur "RS232_connect", in der mit TBlockSerial die Klasse aufgerufen wird und anschließend eine Verbindung zu der unter "ser.Connect" angegebenen Schnittstelle aufgebaut wird (im Beispiel wurde ein USB zu Seriell - Adapter verwendet, deshalb /dev/ttyUSB0 und nicht etwa COM0 o.ä.). Ebenfalls wichtig ist die Zeile mit "ser.Config", in der die serielle Schnittstelle konfiguriert wird (Übertragungsgeschwindigkeit in bits/s, data bits, parity bit, stop bit, handshake, ...).
 +
 
 +
Die Konfiguration in dem angegebenen Beispiel ist so ausgelegt, dass das Programm einfach mit einer seriellen Maus, welche an die serielle Schnittstelle angeschlossen ist, getestet werden kann. 
 +
 
 +
<syntaxhighlight lang="pascal">
 +
program serialtest;
 +
 
 +
{$mode objfpc}{$H+}
 +
 
 +
uses
 +
  {$IFDEF UNIX}{$IFDEF UseCThreads}
 +
  cthreads,
 +
  {$ENDIF}{$ENDIF}
 +
  Classes,SysUtils,Synaser,Crt
 +
  { you can add units after this };
 +
 
 +
  var l:boolean;
 +
 
 +
  function check_affirmation():boolean;
 +
  var k:string;
 +
  begin
 +
      Writeln('Zum Abbrechen der Anwendung bitte nicht STRG-C verwenden, sondern'+
 +
      ' irgendeine Taste drücken. Bitte bestätigen sie diesen Hinweis, bevor das Programm'+
 +
      ' fortgeführt wird: [0]=Beenden, [1]=Bestätige, fahre fort! ');
 +
      Writeln('Ihre Eingabe: ');
 +
      Read(k);
 +
      if StrtoInt(k) = 1 then
 +
      begin
 +
            check_affirmation:=true;
 +
            Writeln('OK, fahre fort');
 +
      end
 +
      else
 +
      begin
 +
            check_affirmation:=false;
 +
            Writeln('Abbruch');
 +
      end
 +
  end;
 +
 
 +
  procedure RS232_connect;
 +
  var
 +
    ser: TBlockSerial;
 +
  begin
 +
      ser:=TBlockSerial.Create;
 +
      try
 +
          ser.Connect('/dev/ttyUSB0');
 +
          Sleep(1000);
 +
          ser.config(1200, 7, 'N', SB1, False, False);
 +
          Write('Device: ' + ser.Device + '  Status: ' + ser.LastErrorDesc +' '+
 +
          Inttostr(ser.LastError));
 +
          Sleep(1000);
 +
          repeat
 +
                Write(IntToHex(ser.RecvByte(10000), 2), ' ');
 +
          until keypressed;
 +
      finally
 +
              Writeln('Serielle Schnittstelle wird freigegeben...');
 +
              ser.free;
 +
              Writeln('Serielle Schnittstelle erfolgreich freigegeben!');
 +
      end;
 +
  end;
 +
 
 +
  begin
 +
    l:=check_affirmation();
 +
    if l=true then
 +
    RS232_connect()
 +
    else
 +
    Writeln('Programm beendet');
 +
  end.
 +
 
 +
</syntaxhighlight>
 +
 
 +
Der [[Hardware Access#External Links | Externe Links]] Abschnitt enthält UNIX und Windows Tutorials über den seriellen Anschluss.
 +
 
 +
=== Raspberry Pi ===
 +
Leider gibt es beim Raspberry Pi ab Zeile 232 bei Synaser eine Fehlermeldung. Dies betrifft bis und mit Version 007.006.001 von Synaser.
 +
 
 +
Dies kann man umgehen, wen den fehlerhaften Const-Block durch folgenden ersetzt.
 +
 
 +
<syntaxhighlight lang="pascal">
 +
const
 +
{$IFDEF UNIX}
 +
  {$IFDEF DARWIN}
 +
  MaxRates = 18;  // MAC
 +
  {$ELSE}
 +
  {$IFDEF CPUARM}                    // <------- hier
 +
    MaxRates = 19;                     
 +
  {$ELSE}                                 
 +
    MaxRates = 30; // UNIX
 +
  {$ENDIF}                             
 +
  {$ENDIF}
 +
{$ELSE}
 +
  MaxRates = 19;  // WIN
 +
{$ENDIF}
 +
  Rates: array[0..MaxRates, 0..1] of cardinal = (
 +
    (0, B0),
 +
    (50, B50),
 +
    (75, B75),
 +
    (110, B110),
 +
    (134, B134),
 +
    (150, B150),
 +
    (200, B200),
 +
    (300, B300),
 +
    (600, B600),
 +
    (1200, B1200),
 +
    (1800, B1800),
 +
    (2400, B2400),
 +
    (4800, B4800),
 +
    (9600, B9600),
 +
    (19200, B19200),
 +
    (38400, B38400),
 +
    (57600, B57600),
 +
    (115200, B115200),
 +
    (230400, B230400)
 +
{$IFNDEF DARWIN}
 +
    ,(460800, B460800)
 +
  {$IFDEF UNIX}
 +
  {$IFNDEF CPUARM}                  // <------- und hier
 +
    ,(500000, B500000),
 +
    (576000, B576000),
 +
    (921600, B921600),
 +
    (1000000, B1000000),
 +
    (1152000, B1152000),
 +
    (1500000, B1500000),
 +
    (2000000, B2000000),
 +
    (2500000, B2500000),
 +
    (3000000, B3000000),
 +
    (3500000, B3500000),
 +
    (4000000, B4000000)
 +
  {$ENDIF}                       
 +
  {$ENDIF}
 +
{$ENDIF}
 +
    );
 +
{$ENDIF}
 +
</syntaxhighlight>
 +
 
 +
== USB ==
 +
 
 +
=== libusb ===
 +
 
 +
Eine Cross-Platform-Möglichkeit für Linux, BSDs und macOS ist [http://libusb.sourceforge.net/ libusb].
 +
 
 +
Header gibt es in http://www.freepascal.org/contrib/db.php3?category=Miscellaneous:
 +
 
 +
{|cellpadding="4"
 +
|-
 +
! Name !! Autor !! Version !! Datum !! Link !! Anmerkungen
 +
|-
 +
| libusb.pp || nowrap | Uwe Zimmermann || 0.1.12 || nowrap | 29.06.2006 || http://www.sciencetronics.com/download/fpc_libusb.tgz ||
 +
|-
 +
| libusb.pas || Johann Glaser ||  || nowrap | 02.01.2021 || https://github.com/hansiglaser/pas-libusb/tree/libusb-1.0 || enthält auch einen objektorientierten Wrapper
 +
|-
 +
| fpcusb || Joe Jared || 0.11-14 || nowrap | 02.02.2006 || http://relays.osirusoft.com/fpcusb.tgz || nowrap | Download-Link geht nicht
 +
|}
 +
 
 +
=== FTDI ===
 +
 
 +
Verwendet man Chips von [http://www.ftdichip.com/ FTDI], kann man die Pascal Header für ihr dll Interface benutzen.
  
 
== Externe Links ==
 
== Externe Links ==
Line 203: Line 393:
 
# Unter Windows: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/msdn_serial.asp
 
# Unter Windows: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/msdn_serial.asp
 
# Synaser Komponente: http://synapse.ararat.cz/
 
# Synaser Komponente: http://synapse.ararat.cz/
 +
# Comport Delphi package: http://sourceforge.net/projects/comport/
 +
 +
ISA Digital Oscilloscope - Ein Beispiel für Hardwarezugriff mit dem vollständigen Source-Code:
 +
 +
[http://eletronicalivre.incubadora.fapesp.br/portal/english/oscilloscope/]
 +
<br>
 +
<br>

Latest revision as of 11:50, 2 January 2021

Deutsch (de) English (en) español (es) français (fr) magyar (hu) 日本語 (ja) 한국어 (ko) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

Zurück zu den Zusätzlichen Informationen.

Überblick

Diese Seite ist der Beginn eines Tutorials über den Zugriff auf Hardware Geräte mit Lazarus. Diese Geräte enthalten, ohne darauf beschränkt zu sein: ISA, PCI, USB, paralleler und serieller Anschluss.

Der Zugriff auf Hardware Geräte auf einer komplett plattformunabhängigen Weise ist nicht implementiert durch die Free Pascal Runtime Library oder durch die LCL, daher umfasst dieses Tutorial grundsätzlich Hardware Zugriffsmethoden auf verschiedenen Plattformen. Der Code kann unter verschiedenen Umgebungen kompiliert werden unter Verwendung bedingter Kompilierung, etwa so:

 uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls,
 {$IFDEF WIN32}
   Windows;
 {$ENDIF}
 {$IFDEF Linux}
   ports;
 {$ENDIF}

Hilfe ist willkommen, speziell zu MacOS.

Vergleich Parallel <-> Seriell

ISA Karten, PCI Karten und der parallele Anschluss kommunizieren mit dem Computer unter Verwendung eines parallelen Protokolls. Der serielle Anschluss und USB Geräte arbeiten mit einem seriellen Protokoll. Da der Prozessor und damit auch alle Programmiersprachen auf Daten parallel zugreifen, ist es wesentlich einfacher solche Protokolle softwareseitig zu programmieren. Beispielsweise erfolgt der Zugriff auf eine Ganzzahl-Variable (INTEGER) nur durch einen einzelnen Befehl. Mit einem seriellen Protokoll kann jedoch nur jeweils auf ein Bit zugegriffen werden. Im Nachhinein ist es notwendig, die Teildaten zusammen zu fügen um sie zu verwenden.

Serielle Kommunikation ist schwierig direkt zu programmieren. Wenn allerdings eine fertige Komponente zur Verfügung steht wird es deutlich einfacher. Auch hardwareseitig sind serielle Schnittstellen deutlich umständlicher, daher benötigen die meisten seriellen Schnittstellen spezielle IC-Bausteine oder Microcontroller für die Kommunikation.

Hier eine kurze Gegenüberstellung der Hardware-Zugriffsprotokolle:

Geschwindigkeit Hardwaredesign
Serieller Anschluss sehr langsam (< 100 kBit/s) mittel
Paralleler Anschluss langsam (~ 1 MBit/s) einfach
ISA Karte mittel (~ 10 MBit/s) mittel
USB 1.x mittel (~ 1.6 MBit/s) komplex
USB 2.0 schnell (~ 480 MBit/s) sehr komplex
PCI Karte sehr schnell (> 1 GBit/s) sehr komplex

Parallele Kommunikation

Verwendung von inpout32.dll für Windows

Windows hat verschiedene Wege für die Ansteuerung von Hardware Geräten bei der 9x Serie und bei der NT Serie, und dies kann wirklich problematisch sein. Die 9x Serie (95, 98, Me) erlaubt den Programmen direkten Zugriff auf die Ports, genau wie unter DOS. Windows NT, jedoch, verhindert dies komplett, und erlaubt nur den Zugriff durch die Gerätetreiber auf die Ports. Dies ist ein Sicherheitsmechanismus, der aber nervig sein kann, wenn ein Gerätetreiber zu komplex für ein kleines Projekt ist, das Zugriff auf den parallelen Port benötigt.

Glücklicherweise gibt es eine Bibliothek, die einen Gerätetreiber enthält. Wenn Windows NT erkannt wird, dann dekomprimiert sie das Gerät und installiert den Hwinterface.sys Kernel Gerätetreiber. Wenn Windows 9x erkannt wird, dann benutzt sie einfach Assembler Opcodes für den Zugriff auf die Hardware.

Aber wie benutzt man die Bibliothek? Einfach! Sie hat nur zwei Funktionen, Inp32 und Out32, und deren Benutzung ist ganz intuitiv.

Wir werden die Bibliothek dynamisch laden, so lassen sie uns zuerst beide Funktionen definieren:

 type
   TInp32 = function(Address: ShortInt): ShortInt; stdcall;
   TOut32 = procedure(Address: ShortInt; Data: ShortInt); stdcall;
  • Address ist die Adresse des Ports, auf den sie zugreifen wollen. Out32 sendet Daten zu dem Port, den sie mit Adress spezifiziert haben. Inp32 gibt ein Byte von dem Port zurück, den sie mit Address spezifiziert haben.

Nun können wir die Bibliothek laden. Dies kann implementiert werden an einer Stelle wie der OnCreate Methode des Hauptformulars ihres Programms:

  type
   TMyForm = class(TForm)
   .........
   private
     { private declarations }
     Inpout32: THandle;
     Inp32: TInp32;
     Out32: TOut32;
   .........
 implementation
   .........
 procedure TMyForm.FormCreate(Sender: TObject);
 begin
 {$IFDEF WIN32}
   Inpout32 := LoadLibrary('inpout32.dll');
   if (Inpout32 <> 0) then
   begin
     // needs overtyping, plain Delphi's @Inp32 = GetProc... leads to compile errors
     Inp32 := TInp32(GetProcAddress(Inpout32, 'Inp32'));
     if (@Inp32 = nil) then Caption := 'Error';
     Out32 := TOut32(GetProcAddress(Inpout32, 'Out32'));
     if (@Out32 = nil) then Caption := 'Error';
   end
   else Caption := 'Error';
 {$ENDIF}
 end;

Wenn sie die Bibliothek bei OnCreate laden, vergessen sie nicht, sie bei OnDestroy zu entladen:

 procedure TMyForm.FormDestroy(Sender: TObject);
 begin
 {$IFDEF WIN32}
   FreeLibrary(Inpout32);
 {$ENDIF}
 end;

Hier ist ein einfaches Beispiel, wie man die Inp32 Funktion benutzt:

 {$IFDEF WIN32}
   myLabel.Caption := IntToStr(Inp32($0220));
 {$ENDIF}

Dieser Code wurde getestet mit einer maßgeschneiderten ISA Karte auf Port $0220, unter Verwendung von Lazarus 0.9.10 mit Windows XP. Natürlich müssen sie Windows in ihrem uses Abschnitt haben damit dieser Code läuft. Für den Einsatz müssen sie nur "inpout32.dll" in das selbe Verzeichnis wie ihre Anwendung einfügen.

Dies ist die Homepage für die Bibliothek: www.logix4u.net/inpout32.htm

Verwendung von Assembler unter Windows 9x

Unter Windows 9x können sie auch Assembler Code verwenden. Suppose you with to write $CC to the $320 port. Der folgende Code erledigt das:

 {$ASMMODE ATT}
 ...
    asm
        movl $0x320, %edx
        movb $0xCC, %al
        outb %al, %dx
    end ['EAX','EDX'];

Mögliche Fehlerquellen in Windows

Eine mögliche Fehlerquelle in Windows bezüglich parallel verwendeter Hardware, die kein "Plug and Play" unterstützt kann darin liegen, dass Windows den von der Hardware benötigten Port einem anderen Gerät zugewiesen hat. Unter der folgenden Adresse können Sie Informationen darüber finden, wie man Windows daran hindern kann, den benötigten Port zu vergeben:

http://support.microsoft.com/kb/135168

Verwendung von ioperm zum Zugriff auf Ports unter Linux

Der beste Weg um auf Hardware unter Linux zuzugreifen, ist durch Gerätetreiber, aber, wegen der Komplexität der Aufgabe, einen Treiber zu erstellen, ist manchmal eine schnelle Methode sehr nützlich.

Damit sie die "ports" Unit unter Linux benutzen können, muss ihr Programm als root laufen, und IOPerm muss aufgerufen werden, um eine angemessene Erlaubnis für den Portzugriff zu setzen. Sie können die Dokumentation über die "ports" Unit hier finden.

Zunächst müssen sie auf glibc verlinken und IOPerm aufrufen. Eine Unit, die auf das ganze glibc verlinkt, existiert in Free Pascal, aber diese Unit macht Probleme, wenn sie direkt von der Anwendung benutzt wird und das statische Verlinken auf die gesamte glibc Bibliothek ist keine sehr gute Idee für eine Multiplattform Anwendung. Die Verwendung plattformspezifischer Funktionen sollte auf ein Minimum reduziert werden.

 {$IFDEF Linux}
 function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external 'libc';
 {$ENDIF}
  • "from" repräsentiert den ersten Port auf den zugegriffen wird.
  • "num" ist die Anzahl (number) der Ports nach dem ersten Port, auf den zugegriffen wird, daher wird ioperm($220, 8, 1) Zugriff ermöglichen für das Programm auf alle Ports zwischen $220 and $227 (einschließlich).

Nach dem Verlinken auf OIPerm können sie port[<Address>] eingeben für den Zugriff auf die Ports.

 {$IFDEF Linux}
   i := ioperm($220, 8, 1);
   port[$220] := $00;
   myLabel.Caption := 'ioperm: ' + IntToStr(i);
   i := Integer(port[$220]);
   myOtherLabel.Caption := 'response: ' + IntToStr(i);
 {$ENDIF}

Dieser Code wurde mit einer maßgeschneiderten ISA Karte auf Port $0220 getestet, unter Verwendung von Lazarus 0.9.10 unter Mandriva Linux 2005 und Damn Small Linux 1.5

PS: Bei aktuellen Lazarus-Versionen, ist in der Unit "x86", die sowieso von der Unit "ports" aufgerufen wird, die Funktion "ioperm" schon vorhanden, sie hat nur einen anderen Namen "fpIOperm"

Der übliche UNIX Hardwarezugriff

{$IFDEF Unix}
Uses 
  Clib;   // retrieve libc library name.
{$ENDIF}

{$IFDEF Unix}
function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external clib;
{$ENDIF}


Beachten sie, dass FPC eine Abstraktion für ioperm genannt "fpioperm" bietet in der Unit x86, und auch out und import Funktionen. Diese Funktionen sind gegenwärtig für Linux/x86 und FreeBSD/x86 implementiert.

Es wird nicht empfohlen to link to libc unless absolutely necessary due to possible deployment and portability functions. Also manual linking to libc (by declaring ad hoc libc imports for functions that are available elsewhere) like done above is not recommended (e.g. the above libc import line will unnecessarily fail if the standard C lib is not called libc, wie z.B. libroot unter BeOS (Zeta), oder auf Plattformen mit a non standard C symbol mangling).

Anmerkung 2 Die Verwendung von unit_ libc wird nicht empfohlen unter anderen Umständen als der Kylix Kompatibilität. Dies ist so, weil die Unit relativ unportierbar ist (wegen der übermäßigen Beanspruchung von Strukturen und anderen privaten Symbolen) und must only be modified as little as possible out of Kylix compability issues.

Serielle Kommunikation

Dies sollte ein einfach zu verstehendes Beispiel sein, wie man mithilfe der Synaser-Bibliothek auf eine serielle Schnittstelle zugreifen kann. Für zusätzliche Informationen bitte die Synaser - Dokumentation zu verwenden.

Wichtig ist, das Programm nicht mit STRG-C zu beenden bzw. die Console zu schließen. Dies ist der Grund, weshalb hier am Anfang auch relativ viel Code steht, der im Prinzip nichts anderes zur Aufgabe hat, als den User noch einmal daran zu erinnern.

Wirklich interessant wird es erst in der Prozedur "RS232_connect", in der mit TBlockSerial die Klasse aufgerufen wird und anschließend eine Verbindung zu der unter "ser.Connect" angegebenen Schnittstelle aufgebaut wird (im Beispiel wurde ein USB zu Seriell - Adapter verwendet, deshalb /dev/ttyUSB0 und nicht etwa COM0 o.ä.). Ebenfalls wichtig ist die Zeile mit "ser.Config", in der die serielle Schnittstelle konfiguriert wird (Übertragungsgeschwindigkeit in bits/s, data bits, parity bit, stop bit, handshake, ...).

Die Konfiguration in dem angegebenen Beispiel ist so ausgelegt, dass das Programm einfach mit einer seriellen Maus, welche an die serielle Schnittstelle angeschlossen ist, getestet werden kann.

program serialtest;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,SysUtils,Synaser,Crt
  { you can add units after this };

  var l:boolean;

  function check_affirmation():boolean;
  var k:string;
  begin
       Writeln('Zum Abbrechen der Anwendung bitte nicht STRG-C verwenden, sondern'+
       ' irgendeine Taste drücken. Bitte bestätigen sie diesen Hinweis, bevor das Programm'+
       ' fortgeführt wird: [0]=Beenden, [1]=Bestätige, fahre fort! ');
       Writeln('Ihre Eingabe: ');
       Read(k);
       if StrtoInt(k) = 1 then
       begin
            check_affirmation:=true;
            Writeln('OK, fahre fort');
       end
       else
       begin
            check_affirmation:=false;
            Writeln('Abbruch');
       end
  end;

  procedure RS232_connect;
  var
     ser: TBlockSerial;
  begin
       ser:=TBlockSerial.Create;
       try
          ser.Connect('/dev/ttyUSB0');
          Sleep(1000);
          ser.config(1200, 7, 'N', SB1, False, False);
          Write('Device: ' + ser.Device + '   Status: ' + ser.LastErrorDesc +' '+
          Inttostr(ser.LastError));
          Sleep(1000);
          repeat
                Write(IntToHex(ser.RecvByte(10000), 2), ' ');
          until keypressed;
       finally
              Writeln('Serielle Schnittstelle wird freigegeben...');
              ser.free;
              Writeln('Serielle Schnittstelle erfolgreich freigegeben!');
       end;
  end;

  begin
     l:=check_affirmation();
     if l=true then
     RS232_connect()
     else
     Writeln('Programm beendet');
  end.

Der Externe Links Abschnitt enthält UNIX und Windows Tutorials über den seriellen Anschluss.

Raspberry Pi

Leider gibt es beim Raspberry Pi ab Zeile 232 bei Synaser eine Fehlermeldung. Dies betrifft bis und mit Version 007.006.001 von Synaser.

Dies kann man umgehen, wen den fehlerhaften Const-Block durch folgenden ersetzt.

const
{$IFDEF UNIX}
  {$IFDEF DARWIN} 
  MaxRates = 18;  // MAC 
  {$ELSE} 
   {$IFDEF CPUARM}                     // <------- hier
    MaxRates = 19;                      
   {$ELSE}                                  
    MaxRates = 30; // UNIX 
   {$ENDIF}                               
  {$ENDIF} 
{$ELSE} 
  MaxRates = 19;  // WIN 
{$ENDIF} 
  Rates: array[0..MaxRates, 0..1] of cardinal = ( 
    (0, B0), 
    (50, B50), 
    (75, B75), 
    (110, B110), 
    (134, B134), 
    (150, B150), 
    (200, B200), 
    (300, B300), 
    (600, B600), 
    (1200, B1200), 
    (1800, B1800), 
    (2400, B2400), 
    (4800, B4800), 
    (9600, B9600), 
    (19200, B19200), 
    (38400, B38400), 
    (57600, B57600), 
    (115200, B115200), 
    (230400, B230400) 
{$IFNDEF DARWIN} 
    ,(460800, B460800) 
  {$IFDEF UNIX} 
  {$IFNDEF CPUARM}                   // <------- und hier
    ,(500000, B500000), 
    (576000, B576000), 
    (921600, B921600), 
    (1000000, B1000000), 
    (1152000, B1152000), 
    (1500000, B1500000), 
    (2000000, B2000000), 
    (2500000, B2500000), 
    (3000000, B3000000), 
    (3500000, B3500000), 
    (4000000, B4000000) 
   {$ENDIF}                        
  {$ENDIF} 
{$ENDIF} 
    ); 
{$ENDIF}

USB

libusb

Eine Cross-Platform-Möglichkeit für Linux, BSDs und macOS ist libusb.

Header gibt es in http://www.freepascal.org/contrib/db.php3?category=Miscellaneous:

Name Autor Version Datum Link Anmerkungen
libusb.pp Uwe Zimmermann 0.1.12 29.06.2006 http://www.sciencetronics.com/download/fpc_libusb.tgz
libusb.pas Johann Glaser 02.01.2021 https://github.com/hansiglaser/pas-libusb/tree/libusb-1.0 enthält auch einen objektorientierten Wrapper
fpcusb Joe Jared 0.11-14 02.02.2006 http://relays.osirusoft.com/fpcusb.tgz Download-Link geht nicht

FTDI

Verwendet man Chips von FTDI, kann man die Pascal Header für ihr dll Interface benutzen.

Externe Links

Kommunikationsprotokolle Geschwindigkeitsvergleich:

  1. http://en.wikipedia.org/wiki/Serial_port#Speed
  2. http://www.lvr.com/jansfaq.htm - Jan Axelson's Parallel Port FAQ
  3. http://en.wikipedia.org/wiki/USB#Transfer_Speed
  4. http://en.wikipedia.org/wiki/PCI#Conventional_PCI_bus_specifications

Serielle Kommunikation Links:

  1. Unter UNIX: [1]
  2. Unter Windows: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/msdn_serial.asp
  3. Synaser Komponente: http://synapse.ararat.cz/
  4. Comport Delphi package: http://sourceforge.net/projects/comport/

ISA Digital Oscilloscope - Ein Beispiel für Hardwarezugriff mit dem vollständigen Source-Code:

[2]