Die parallele Schnittstelle




    Hier soll beschreiben werden wie die parallele Schnittstelle via C/C++ angesteuert wird. Dieser Beitrag ist an jene gerichtet die einzelne Signale der Schnittstelle ansteuern wollen. Es wird gezeigt wie man unter DOS, Win95/98, NT, Win2000, XP und LINUX auf die Schnittstele zugreift. Am Ende habe ich den Quellcode meiner Lpt-Tools für Win98, NT, Win2000 und XP samt Treiber zum downloaden bereit gestellt. 

    Die Schnittstelle besteht aus 8 Datenleitungen, 5 Statuseingängen und 4 Steuerleitungen. Diese Leitungen sind nach dem folgenden Schema belegt:


    Die Leitungen D0 bis D7 sind die Datenausgänge, auf ihnen lassen sich TTL Signale ausgeben. Low entspricht 0 Volt, und High entspricht 5 Volt.

    Strobe, Autofeed, Initial und Select Input sind ebenfalls Ausgänge. Diese Ausgänge sind aber bis auf Initial alle invertiert. Das bedeutet wenn man ein High-Bit ausgibt, geht der Ausgang auf 0 Volt.

    Die parallele Schnittstelle besitzt auch 5 Eingänge: Error,Select,Paper out, Acknowlege und Busy. Legt man an diese Eingänge eine Spannung kleiner 0.7 Volt an so bedeutet das Low, und eine Spannung größer 2 Volt an dem Eingang beteutet High. Die einzige Ausnahme ist der Busy Eingang, dieser ist invertiert.

    Die Steuerung dieser Eingänge erfolgt durch einen Zugriff auf ein IO-Port des PC's. Die parallele Schnittstelle hat 3 IO-Ports für ihre Steuerung. Die Adressen dieser Ports sind im normal Fall auf :


      0x378 bis 0x37A für LPT1
      0x278 bis 0x27A für LPT2
      0x38C bis 0x38E für LPT3

    Die einzelnen Ports sind wie folgt belegt:



    Anmerkung zu Port 2:


      Wenn man Bit 4 in Port 2 auf High setzt, dann ist es möglich über den Acknowlege Eingang einen Interrupt auszulösen. Dazu muss Acknowlege auf High gesetzt werden. Im normal Fall wird Irq7 ausgelöst (Interruptvektor 15). Es darf dabei nicht vergessen werden diesen Interrupt auch auf dem Motherboard frei zugeben (für ISA !).

      Die Bits 5 bis 7 in Port 2 sollten immer auf Low gestellt werden, da sie für EPP-Erweiterungen, der Schnittstelle benutzt werden. Mit diesen Erweiterungen ist es möglich die Datenleitungen D0 bis D7 auch als Eingänge zu benutzen.

      Die EPP-Erweiterungen aktiviert man über Port 0x402. In den Bits 5 bis 7 stellt man den Modus für die Schnittstelle ein.

      000 = Standard-Parallel-Port-Modus (SPP)
      001 = PS/2 Parallel-Port-Modus 
      010 = FIFO-Modus 
      011 = Extended Capabilities Port (ECP)
      100 = Enhanced Parallel Port (EPP)
      101 = Reserviert 
      110 = Test-Modus 
      111 = Configuration-Modus

Ich will nur auf den Modus 1 (PS/2 Parallel-Port-Modus) näher eingehen. Wenn man den Modus aktiviert, in dem man auf Port 402h den Wert 20h schreibt, kann man die Datenleitungen als bidirektionelle Leitungen nutzen. Über das Bit 5 im Port 2 ist es möglich die Signalrichtung einzustellen. Wenn das 5. Bit auf Null stellt, sind die Datenleitungen (D0 bis D7) Ausgänge, wenn das Bit  auf Eins ist dann sind die Datenleitungen Eingänge. Hier ist ein Beispiel dazu.

Ein Link zu weiteren Informationen zu den EPP-Modes: http://cc.uni-paderborn.de unter IEEE1284.




    Ansprechen der Ports unter Win95/98 und DOS


    Bei diesen Betriebssystemen ist es möglich die Ports direkt anzusprechen. Mit den beiden Funktionen void outp(int Port,int Data) und void inp(int Port,int Data) kann einen Wert in ein Port schreiben bzw. lesen. Ein kleines Beispiel das unter QuickC oder Visual C++ läuft soll das zeigen.

    Noch eine Anmerkung, bei manchen Compilern muss man statt outp / inp die Funktionen _outp / _inp verwenden.

	#include <conio.h>
	#include <stdio.h>

	int main()
	{
	unsigned char port1,port2,i;


	    outp( 0x378, 0x01 );        /* D0       auf High setzen Rest auf Low */
	    outp( 0x378, 0x02 );        /* D1       auf High setzen Rest auf Low */
	    outp( 0x378, 0x07 );        /* D0,D1,D2 auf High setzen Rest auf Low */

	    port1 = inp( 0x379 );

	    printf("\n  Error      ist auf %s",(port1&0x08)? "high":"low" );
	    printf("\n  Select     ist auf %s",(port1&0x10)? "high":"low" );
	    printf("\n  Paper out  ist auf %s",(port1&0x20)? "high":"low" );
	    printf("\n  Acknowlege ist auf %s",(port1&0x40)? "high":"low" );
	    printf("\n  Bussy      ist auf %s",(port1&0x80)? "low" :"high");

	    port2 = inp( 0x37A );
	    outp( 0x37A, port2^1 );     /* Strobe invertieren */

	    for(i=0;i<20;i++)           /* D3 im Sekundentakt ein ausschalten */
	   	{
		outp (0x378,0x08);      /* Nur D3 auf 'High' setzen bei LPT1 */
		Sleep(500);
		outp (0x378,0x00);      /* Alle Pins auf 'Low' setzen bei LPT1 */
		Sleep(500);
		}


	return 0;
	}
	


    Ansprechen der Ports unter NT , Windows 2000 und XP


    Unter NT kann man auf die Ports nicht direkt zugreifen, da sonst das Betriebssystem eine Schutzverletzung ausgeben würde. Auf die Ports kann nur indirekt über einen Device-Treiber zugegriffen werden. Microsoft hat zu diesem Thema in seinem NT-DDK ein kleines Beispiel namens Portio, das zeigt wie man einen solchen Port-Zugriff macht.

    Ein fertiger Treiber und ein API zum ansprechen der LPT Ports findet man in den Lpt-Tools.




    Ansprechen der Ports unter LINUX


    Unter LINUX verhält es sich so ähnlich wie unter DOS. Der einzige Unterschied ist das man bei Linux zuerst die Port-Adressen freigeben muss, hierfür ist die Funktion ioperm(...) zuständig. Das ist aber nur dann möglich wenn das Programm Superuser-Rechte besitzt, und kein anderes Programm auf die Ports zugreift (z.B. ein Drucker Treiber). Das heißt der LINUX-Kernel muss ohne Gerätetreiber für die parallele Schnittstelle kompiliert werden, bzw. der Gerätetreiber darf nicht gemounted werden !

    Falls das nicht möglich ist gibt es noch die Möglichkeit die parallele Schnittstelle indirekt über einen Gerätetreiber anzusprechen, über die ioctl Funktion.. Das funktioniert nach einem ähnlichen Schema wie unter NT. Auf diese Möglichkeit gehe ich aber hier nicht genauer ein.

    Die beiden Port-Zugriffsfunktionen
    void outp(int Port,int Data) und void inp(int Port,int Data) sind hier als inline Assembler realisiert.

     

	#include <sys/perm.h>
	#include <stdio.h>

	inline void outp( unsigned short port , unsigned char value )
	  {
	  __asm__ __volatile__ ( "outb %b0,%w1" : : "a" (value), "d" (port) );
	  }

	inline unsigned char inp(unsigned short port)
	  {
	  unsigned char  vv;

	  __asm__ __volatile__ ( "inb %w1,%b0" : "=a" (vv) : "d" (port) );

	  return vv;
	  }

	int main()
	{
	unsigned char port1,port2;


       	    ioperm( 0x378 ,8,1);	/* Ports freigeben */


	    outp( 0x378, 0x01 );        /* D0       auf High setzen */
	    outp( 0x378, 0x02 );        /* D1       auf High setzen */
	    outp( 0x378, 0x07 );        /* D0,D1,D2 auf High setzen */

	    port1 = inp( 0x379 );

	    printf("\n  Error      ist auf %s",(port1&0x08)? "high":"low" );
	    printf("\n  Select     ist auf %s",(port1&0x10)? "high":"low" );
	    printf("\n  Paper out  ist auf %s",(port1&0x20)? "high":"low" );
	    printf("\n  Acknowlege ist auf %s",(port1&0x40)? "high":"low" );
	    printf("\n  Bussy      ist auf %s",(port1&0x80)? "low" :"high");

	    port2 = inp( 0x37A );
	    outp( 0x37A, port2^1 );     /* Strobe invertieren */
	

       	    ioperm( 0x378 ,8,0);	/* Ports wieder sperren */


	return 0;
	}
		


    Tools für die parallele Schnittstelle

    Die Tools bestehen aus sechs Funktionen:

    LptDetectPorts Ports aufzählen 
    LptInit    
        Treiber initialisieren
    LptExit        Treiber deinitialisieren
    LptPort        Portadresse abfragen
    LptPortIn      Port einlesen
    LptPortOut
         Port-Ausgabe

    Um ein Port ansprechen zu können muss man mit LptInit den Treiber zuerst initialisieren. Unter NT und Win2000 wird ein Systemtreiber automatisch installiert, sofern das Programm Administratorrechte besitzt, und der Systemtreiber LptDriver.sys sich im selben Verzeichnis befindet wie die EXE-Datei. Wenn das nicht der Fall ist muss man die Datei LptDriver.sys von Hand ins  Verzeichnis C:\WINNT\SYSTEM32 kopieren.
    Für Linux muss man die Datei LptToolsLinux.cpp mit kompilieren statt LptTools.cpp.

     

    Hier ist ein kleines Beispiel wie man die Lpt-Tools anwendet:  

    #include <windows.h>
    #include
    "LptTools.h"

    #define LPT1  0
    #define LPT2  1

    int main()
    {
    int i;

    if(!LptInit())                // Treiber initialisieren
       {
       printf("Der Treiber ist nicht installiert."); 
       return -1;
       }

      LptPortOut(
    LPT1,0,0x01);    // D0 auf 'high' setzen bei LPT1
      LptPortOut(LPT2,0,0x01);    // D0 auf 'high' setzen bei LPT2
      LptPortOut(LPT1,0,0x02);    // D1 auf 'high' setzen bei LPT1
      LptPortOut(LPT1,0,0x03);    // D0 und D1 auf 'high' setzen bei LPT1
    i=LptPortIn (LPT1,2);         // Diverse Bits einlesen
      LptPortOut(
    LPT1,2,i| 0x01); // Strobe auf LPT1 setzen
      LptPortOut(
    LPT1,2,i&~0x01); // Strobe auf LPT1 löschen
    i=LptPortIn (
    LPT1,1);         // Diverse Bits einlesen

    for(i=0;i<20;i++)             // D3 im Sekundentakt ein ausschalten
      {
      LptPortOut(LPT1,0,0x08);    // Nur D3 auf 'high' setzen bei LPT1
      Sleep(500);   
      LptPortOut(LPT1,0,0x00);    // Alle Pins auf 'low' setzen bei LPT1
      Sleep(500);
      }

      LptExit
    ();                  // Treiber deinitialisieren

    return 0;
    }

    Anmerkung die Datei LptTools.cpp muss dem Projekt hinzugefügt werden.

    Bei LptPortOut gibt der erste Parameter an auf welche Schnittstelle man zugreifen will (0=LPT1 1=LPT2 ... usw. ...). Der Zweite Parameter gibt an auf  welches Port der Schnittstelle man schreiben will (siehe Tabelle). Der dritte Parameter  ist der Wert der auf das Port geschrieben wird. Bei Port 0 ist das Bit0 der Zustand der Leitung D0, Bit1 ist der Zustand der Leitung D1 und so weiter. Will man z.B. die Leitungen D1 und D4 auf HIGH setzen, so muss man den Wert 0x12  ( (1<<1) + (1<<4) ) auf Port 0 schreiben. Die anderen Leitungen ( D0,D2,D3,D5,D6,D7) werden dabei auf LOW gesetzt.

    Der Treiber LptDriver.sys gibt den direkten Zugriff aus die IO-Ports der parallelen Schnittstelle frei. Mit dem Treiber kann wie unter Win95/98, mit outp, nach dem Aufruf von LptInit, direkt auf die Ports zugegriffen werden. Die Assembler-Befehle out und in kann man auch benutzten. Es ist auch möglich andere Ports frei zu schalten, z.B. von anderen ISA-Buskarten. Dazu gibt es die Funktion BOOL LptMapPorts(unsigned uPort,unsigned uSize) in den LptTools.

    Will man nur den Treiber auf einen Rechner ohne Administratorrechten installieren, geht man wie folgt vor:

    1. Sich als Administrator einloggen.
    2. Diesen Code ausführen:

    #include "LptTools.h"

    int main()
    {
    if(!
    LptDriverInstall())             // Treiber initialisieren
      
    {
       printf("Der Treiber
    wurde nicht installiert."); 
       }
    else
          {
       printf("Der Treiber
    wurde erfolgreich installiert."); 
       }
    return 0;
    }

    3. Als Benutzer ohne Administratorrechte einloggen. 

    Download der Lpt-Tools des Treibers und eines Demo-Programms: Lpt-Tools. Bevor das Beispiel LptDemo in den Tools gestartet werden kann, muss man den Treiber (LptDriver.sys) ins Verzeichnis ./LptDemo___Win32_Debug kopieren !

    Ein kleiner Beispiel-Dialog ist unter dem Namen LptWizard in den Tools ebenfalls enthalten:



    EPP Beispiel

    #include "LptTools.h"

    #define
    LPT1  0

    int main()
    {
    int i;

    if(!LptInit())                  // Treiber initialisieren
      
    {
       printf("Der Treiber ist nicht installiert."); 
       return -1;
       }

      LptPortOut(LPT1,0x402,0x20);  //
    EPP Modus 1 auswählen
      LptPortOut(LPT1,0x002,0x00);  // D0
    -D7 auf Ausgänge stellen
      LptPortOut(LPT1,0x000,0xFF);  // D0
    -D7 auf High setzen

      LptPortOut(LPT1,0x002,0x20);  // D0-D7 auf Eingänge stellen
    i=LptPortIn (LPT1,0x000);       //
    Die anliegenden Signale bei D0-D7 einlesen

      LptPortOut
    (LPT1,0x402,0x00);  //
    SPP Modus 0 auswählen

      LptExit();                    // Treiber deinitialisieren

    return 0;
    }



    Anton Zechner