Benutzer-Werkzeuge

Webseiten-Werkzeuge


paralleler_port

paralleler Port

Für den Hobby-Elektroniker ist der Parallelport besonders attratktiv, da er:

  1. sehr leicht zu programmieren ist (keine Baudrate etc.),
  2. direkt LEDs und stromsparende Relais ansteuern kann sowie Taster auslesen kann,
  3. TTL-kompatibel ist, also sowohl 3,3 V- als auch 5 V-Logik angeschlossen werden kann und
  4. die Ansteuerung des Parallelports nur aus einfachen Portzugriffen besteht, die auch in harter Echtzeit und mit einer Genauigkeit von wenigen Mikrosekunden erfolgen kann, sowie
  5. interruptfähig ist.

Über einen gewöhnlichen Parallelport können die Pins mit bis zu 400 kHz geschaltet werden und Interrupts ebenso schnell eingelesen werden.

Der Parallelport

Ein bidirektionaler PC-Parallelportbaustein besitzt drei Register:

  1. Mit dem Datenregister (befindet sich an der Basisadresse, z.B. 0x378) können acht Bit gleichzeitig auf die Datenleitung D0-D7 ausgegeben bzw. eingelesen werden (fals Bit 5 im Control-Register gesetzt ist).
  2. Mit dem Statusregister (=Basisadresse+1) können die Zustände der Eingangsleitungen abgefragt werden. Allerdings ist der BUSY-Eingang (Pin 11) invertierend, d.h. ein anliegendes Low-Signal hat ein gesetztes Bit 7 im Status-Register zur Folge und umgekehrt. Bei einer Low-to-High-Transition an Pin 10 (/ACK) wird der (per Jumper oder BIOS eingestellte) Interrupt ausgelöst - aber nur unter der Vorraussetzung, dass Bit 4 (Interrupt Enable) im Steuerregisater vorher gesetzt wurde.
  3. Mit dem Steuerregister (control register) (=Basisadresse+2) lassen sich weitere vier TTL/CMOS-Ausgänge ansteuern - davon verhalten sich drei invertierend. Ausserdem beherbergt das Steuerregister (wie bereits erwähnt) jene Bits, um die Datenleitung umzukehren bzw. den Interrupt freizuschalten.

Bei allen Experimenten am Parallelport ist höchste Vorsicht geboten weil (bei unsachgemässer Handhabung) lebensgefährliche Kurzschlüsse für den Parallelport oder gar für das gesamte Mainboard entstehen können. Möchte man Taster oder Schalter direkt abfragen, so müssen die Eingänge über Pull-Up-Wiederstände in einen definierten High-Zustand gebracht werden und können dann gezielt auf Masse kurzgeschlossen werden. Falls keine externen +5V Spannungsquellen für die Pull-Up-Wiederstände zur Verfügung stehen, so kann kurzerhand einer der TTL/CMOS-Ausgänge als Spannungsquelle missbraucht werden.

Pin-Name Pin Funktion
Data Register D0 Pin 02 Ein/Ausgang Bit 0
Data Register D1 Pin 03 Ein/Ausgang Bit 1
Data Register D2 Pin 04 Ein/Ausgang Bit 2
Data Register D3 Pin 05 Ein/Ausgang Bit 3
Data Register D4 Pin 06 Ein/Ausgang Bit 4
Data Register D5 Pin 07 Ein/Ausgang Bit 5
Data Register D6 Pin 08 Ein/Ausgang Bit 6
Data Register D7 Pin 09 Ein/Ausgang Bit 7
Status Register S3 /ERROR Pin 15 Eingang
Status Register S4 SELECT Pin 13 Eingang
Status Register S5 PAPEREND Pin 12 Eingang
Status Register S6 /ACK Pin 10 Interruptfähiger Eingang (L→H)
Status Register S7 (inv) BUSY Pin 11 invertierender Eingang
Control Register C0 (inv) /STROBE Pin 01 invertierender Ausgang Bit 0
Control Register C1 (inv) /AUTOFD Pin 14 invertierender Ausgang Bit 1
Control Register C2 /INIT Pin 16 Ausgang Bit 2
Control Register C3 (inv) /SELECT Pin 17 invertierender Ausgang Bit 3
Control Register C4 Pin – Interrupt: 0=aus, 1=an Bit 4
Control Register C5 Pin – D0-D7: 0=Ausgang, 1=Eingang Bit 5
Masse Pin 18
Masse Pin 19
Masse Pin 20
Masse Pin 21
Masse Pin 22
Masse Pin 23
Masse Pin 24
Masse Pin 25
Pin No (DB25) Signal name Direction Register - bit Inverted
1 nStrobe Out Control-0 Yes
2 Data0 In/Out Data-0 No
3 Data1 In/Out Data-1 No
4 Data2 In/Out Data-2 No
5 Data3 In/Out Data-3 No
6 Data4 In/Out Data-4 No
7 Data5 In/Out Data-5 No
8 Data6 In/Out Data-6 No
9 Data7 In/Out Data-7 No
10 nAck In Status-6 No
11 Busy In Status-7 Yes
12 Paper-Out In Status-5 No
13 Select In Status-4 No
14 Linefeed Out Control-1 Yes
15 nError In Status-3 No
16 nInitialize Out Control-2 No
17 nSelect-Printer Out Control-3 Yes
18-25 Ground - - -
  • Spannung: 5,0 V (5,5 V)
  • Strom: 5-20 mA

Port-Adressen:

  1. Datenregister (D0-D7): Basis-Adresse (Druckerdaten ausgeben)
  2. Statusregister (S0-S7): Basis-Adresse + 1 Byte (Druckerstatus lesen)
  3. Steuerregister (C0-C7): Basis-Adresse + 2 Byte (Druckerfunktionen steuern)

Parallel Port Programmierung mit Pascal

⇒ ISBN 3-928051-42-3

Datenregister (D0-D7) ausgeben:

Port [BA] := n;

Statusregister (S0-S7) einlesen:

A := Port [BA+1];

Steuerregister (C0-C7) ausgeben:

Port [BA+2] := A XOR 11;

Steuerregister (C0-C7) einlesen:

B := (Port [BA+2] := A XOR 11) AND 15;

sinusgenerator.pas

Das Ausgangssignal dieser Schaltung kann z.B. mit dem A/D-Wandler "ZN426" in ein NF-Sinussignal umgewandelt werden. Hiermit kann man den Frequenzbereich bis max. 20 kHz abdecken.

Program Sinusgenerator;

uses CRT;

const BA = $378;
var Tabelle : Array [0..255] of Byte;
      Faktor : Byte;
      Ch : Char;

procedure SinusTabelle;
var   n: Integer;

begin
      for n:= 0 to 255 do Tabelle [n] :=
              round (127.5 + 127.5 * sin (n / 128 * Pi));
end;

procedure Ausgabe (Faktor : Byte);
var   n: Byte;
      m: Word;

begin
      repeat
              Inline ($FA);
              n := 0;
              for m := 1 to 65535 do begin;
                      Port [BA] := Tabelle [n];
                      n:= n + Faktor;
              end;
              Inline ($FB);
      until KeyPressed;
      Ch := ReadKey;
end;

begin
      SinusTabelle;
      repeat
              writeln ('Bitte Faktor eingeben');
              readln (Faktor);
              writeln ('Unterbrechen mit irgendeiner Taste');
              writeln ('Ende mit ESC');
              Ausgabe (Faktor);
      until Ch = chr(27);
end.

Parallel Port Programmierung mit C

Linux

ursprüngliche Quelle: (aus dem LINUX-Magazin vom Oktober 1999, Seite 34-36)

Bei der Ausführung dieses Beispiels auf der Komandozeile müssen zwei Argumente übergeben werden (z.B. mit iotest 378 85). Der erste Wert ist die Basis-Adresse eines freien Parallelports. Das zweite Argument wird als Bitmuster verwendet, welches an die Datenleitungen des Parallelports angelegt wird. In der Headerdatei asm/io.h (heißt jetzt sys/io.h) befinden sich die Deklarationen für die benötigten Funktionen ioperm(), inb() und outb(). In den Zeilen mit "atoi" werden die beiden übergebenen Programmparameter (I/O-Basisadresse und Bitmuster) ausgelegt. Der angegebene I/O-Bereich wird durch den Aufruf von ioperm(base,3,1) freigeschaltet: ab der Basisadresse wird der Zugriff auf die nächsten drei I/O-Ports (z.B. 0x378-0x37a) erlaubt, da der dritte Parameter von ioperm(base,3,1) "1" ist. In der darauffolgenden Programmzeile wird das angegebene Bitmuster (hier für "85" ⇒ "01010101") an die Datenleitungen des Druckeranschlusses ausgegeben. Bei einem angeschlossenen Centronics-Relaisinterface wäre bei unserem obigen Kommandozeilen-Beispiel jedes zweite Relais geschaltet. In der Zeile mit inb(base+1) wird ein Byte eingelesen, welches die anliegenden TTL bzw. CMOS-Pegel an den Eingangs-Pins (Status-Register) repräsentiert. Dadurch können z.B. bis zu fünf Schalterzustände abgefragt werden. Schließlich muss der direkte Zugriff auf die Ports mit Hilfe einer "0" als letztem Parameter von ioperm(base,3,0) wieder verboten werden.

Die Basisadressen (erster Übergabeparameter) der Parallelports ("Standard", muss aber nicht immer so sein!):

  • LPT1 → 378
  • LPT2 → 278
  • LPT3 → 3F8

iotest.c

// Sourcecode for Linux gcc

#include <sys/io.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
        if (argc != 2 && argc != 3)
        {
                printf("\n%s [Gerätedatei]\n", argv[0]);
                printf("z.B. Pegel der Status- und Daten-Pins anzeigen: %s 378\n", argv[0]);
                printf("\n%s [Gerätedatei] [Wert]\n", argv[0]);
                printf("z.B. alle Daten-Pins an schalten: %s 378 85\n", argv[0]);
                printf("z.B. alle Daten-Pins aus schalten: %s 378 0\n", argv[0]);
                exit(1);
        }

        int base=atoi(argv[1]);
        int vbase=0;
        int nbase=0;
        int status=0;

        if (argc == 2 || argc == 3)
        {
                // Parallelport öffnen
                if (ioperm(base,3,1) < 0)
                {
                        perror("Fehler: Parallelport kann nicht geöffnet werden\n");
                }

                if (argc == 3)
                {
                        int value=atoi(argv[2]);

                        // Datenregister lesen (Basis-Port-Adresse) => Ein-/Ausgang
                        vbase=inb(base);

                        // Datenregister schreiben (Basis-Port-Adresse) => Ein-/Ausgang
                        outb(value,base);

                        // Steuerregister schreiben (Basis-Port-Adresse + 2 Byte) => Ausgang
                        outb(0,base+2);
                }

                // Datenregister lesen (Basis-Port-Adresse) => Ein-/Ausgang
                nbase=inb(base);

                // Statusregister lesen (Basis-Port-Adresse + 1 Byte) => Eingang
                status=inb(base+1);

                // Parallelport schließen
                if (ioperm(base,3,0) < 0)
                {
                        perror("Fehler: Parallelport kann nicht wieder geschlossen werden\n");
                }

                if (argc == 2)
                {
                        // Bildschirmausgabe
                        printf("%i / %i\n", status, nbase);
                }

                if (argc == 3)
                {
                        // Bildschirmausgabe
                        printf("%i / %i -> %i\n", status, vbase, nbase);
                }
        }
};
iotest anwenden

früher (mit asm/io.h):

# gcc -O2 -o iotest iotest.c

Heutzutage ist für das compilieren ist die Compiler-Optimierungsstufe -O2 ist nicht zwingend notwendig, früher musste aber mindestens "-O" angegeben werden, da sonst die Code-Generierung mit einer lästigen Fehlermeldung abgebrochen wurde.

heute (mit sys/io.h):

# gcc -o iotest iotest.c

Das fertige Binary kann nun so aufgerufen werden:

# ./iotest

./iotest [Gerätedatei]
z.B. Pegel der Status- und Daten-Pins anzeigen: ./iotest 378

/home/manfred/iotest [Gerätedatei] [Wert]
z.B. alle Daten-Pins an schalten: ./iotest 378 85
z.B. alle Daten-Pins aus schalten: ./iotest 378 0
# ./iotest 378
255 / 255
# ./iotest 378 85
255 / 255 -> 85

unter der Voraussetzung dass man als root eingeloggt ist.

Für den Übergabewert sind Werte von 0 bis 255 erlaubt.

Die Zahl vor dem "/" gibt den Inhalt des Status-Registers wieder.

FreeBSD

pptest.c

// Sourcecode for FreeBSD clang

#include <fcntl.h>
#include <dev/ppbus/ppi.h>
#include <dev/ppbus/ppbconf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int ppi_port;

int main(int argc, char *argv[])
{
        if (argc != 2 && argc != 3)
        {
                printf("\n%s [Gerätedatei]\n", argv[0]);
                printf("z.B. Pegel der Status- und Daten-Pins anzeigen: %s /dev/ppi0\n", argv[0]);
                printf("\n%s [Gerätedatei] [Wert]\n", argv[0]);
                printf("z.B. alle Daten-Pins an schalten: %s /dev/ppi0 255\n", argv[0]);
                printf("z.B. alle Daten-Pins aus schalten: %s /dev/ppi0 0\n", argv[0]);
                exit(1);
        }

        char ppi_dev[64];
        u_int8_t status=0;
        u_int8_t vdaten=0;
        u_int8_t ndaten=0;

        strlcpy(ppi_dev, argv[1], sizeof(ppi_dev));

        // Parallelport öffnen
        ppi_port = open(ppi_dev, O_RDWR);

        if (ppi_port < 0)
        {
                printf("Could not open %s\n", ppi_dev);
                exit(1);
        }

        if (argc == 2 || argc == 3)
        {
                if (argc == 3)
                {
                        u_int8_t value=0;
                        value = atoi(argv[2]);

                        // Datenregister lesen (Basis-Port-Adresse) => Ein-/Ausgang
                        ioctl(ppi_port, PPIGDATA, &vdaten);

                        // Datenregister schreiben (Basis-Port-Adresse) => Ein-/Ausgang
                        ioctl(ppi_port, PPISDATA, &value);

                        // Steuerregister schreiben (Basis-Port-Adresse + 2 Byte) => Ausgang
                        ioctl(ppi_port, PPISCTRL, 0);
                }

                // Statusregister lesen (Basis-Port-Adresse + 1 Byte) => Eingang
                ioctl(ppi_port, PPIGSTATUS, &status);

                // Datenregister lesen (Basis-Port-Adresse) => Ein-/Ausgang
                ioctl(ppi_port, PPIGDATA, &ndaten);

                // Bildschirmausgabe
                if (argc == 2)
                {
                        printf("%u / %u\n", status, ndaten);
                }

                if (argc == 3)
                {
                        printf("%u / %u -> %u\n", status, vdaten, ndaten);
                }
        }
        return 0;
}
pptest anwenden
# clang -o pptest pptest.c

Das fertige Binary kann nun so aufgerufen werden:

# ./pptest

./pptest [Gerätedatei]
z.B. Pegel der Daten-Pins anzeigen: ./pptest /dev/ppi0

./pptest [Gerätedatei] [Wert]
z.B. alle Daten-Pins an schalten: ./pptest /dev/ppi0 255
z.B. alle Daten-Pins aus schalten: ./pptest /dev/ppi0 0
# ./pptest /dev/
Could not open /dev/
# ./pptest /dev/ppi0
127 / 0
# ./pptest /dev/ppi0 85
127 / 0 -> 85
# ./pptest /dev/ppi0
127 / 85
# ./pptest /dev/ppi0 0 
127 / 85 -> 0
# ./pptest /dev/ppi0   
127 / 0

unter der Voraussetzung dass man als root eingeloggt ist.

Für den Übergabewert sind Werte von 0 bis 255 erlaubt.

Die Zahl vor dem "/" gibt den Inhalt des Status-Registers wieder.

Testschaltung

Hier gibt es eine sehr schöne Testschaltung um mit dem Parallelport rumzuspielen:

/home/http/wiki/data/pages/paralleler_port.txt · Zuletzt geändert: von 127.0.0.1