PROGRAMOWANIE PORTU LPT

---------------------------------------

WSTĘPNIAK

Standardowo w PC mamy do dyspozycji jeden port równoległy LPT1, który jest przystosowany do komunkacji z drukarką. Dla elektronika ma on szersze zastosowanie. Umożliwia bowiem łatwe sprzęgnięcie PC z własnymi układami cyfrowymi. Chyba najprostszym zastosowaniem jest sterowanie diodami LED podłączonymi do wyprowadzeń danych. Jednak wiele osób, mimo że potrafi napisać prosty program, ma problemy z obsługą portu LPT. Stworzyłem więc ten opis, aby pomóc poczštkującym.

JAK TRAFIĆ, CZYLI ADRES

Jak wspomniałem we wstępie, większość komputerów posiada jeden port równoległy. Najczęściej jego adres bazowy to 378hex. Może się jednak zdarzyć sytuacja, że będzie on pod innym adresem! Domyślną wartość można na przykład zmienić w BIOSie. Należy zatem mieć na uwadze to, że podstawą do określenia adresu portów drukarkowych są wartości typu Word znajdujące się w pamięci BIOS RAM poczynając od adresu 0040:0008. Przedstawia to poniższa tabelka:

Adres (hex) Znaczenie Standardowy adres bazowy (hex) Standardowy adres bazowy
- z kartą Hercules
0040:0008 adres bazowy LPT1 378 3BC
0040:000A adres bazowy LPT2 278 378
0040:000C adres bazowy LPT3 3BC 278
0040:000E adres bazowy LPT4 2BC 2BC

W pierwszej kolumnie podane jest miejsce w pamięci, które zawiera adres bazowy odpowiedniego portu. Adres jest wartością typu Word, czyli dwubajtową.
W kolumnie Standardowy adres bazowy podane są typowe adresy portów. Bardzo rzadko można się spotkać z innymi ustawieniami.
Na koniec podałem typowe adresy portów w przypadku zainstalowanej karty graficznej Hercules, która posiada port równoległy. Dotyczy to jednak bardzo starych komputerów PC.

JAK SIĘ DOSTAĆ, CZYLI REJESTRY

Każdy port równoległy posiada trzy 8-bitowe rejestry rejestry:
- danych
- statusowy
- kontrolny
Rejestr danych znajduje się pod adresem bazowym, następnie statusowy - pod adresem bazowym+1 (przesunięcie/offset = 1) i dalej kontrolny - pod adresem bazowym+2 (przesunięcie/offset = 2).

Do rejestru danych zawsze można zapisać daną 8-bitową. Pojawi się ona na wyprowadzeniach D0-D7. Nowsze płyty główne (powiedzmy od Pentium 300MHz) posiadają port dwukierunkowy, który umożliwia również odczyt stanu linii danych. O tym jak sprawdzić, czy mamy port dwukierunkowy napiszę później. Każda linia danych w stanie niskim może przyjąć prąd maksymalnie 25mA, natomiast w stanie wysokim jest źródłem prądu 2,6mA. Wartości te nie powinny być przekraczane ze względu na możliwość uszkodzenia wyjść.

Poniżej przedstawiam tabelki określające znaczenie poszczególnych bitów w rejestrach oraz opis wyprowadzeń (pinów) gniazda DB-25 od strony komputera.
Rejestr danych
OFFSET = 00h
bit76543210
pin98765432
Porty zgodne z IBM (EPP) są dwukierunkowe. Aby przełączyć w tryb wejścia trzeba w rejestrze kontrolnym ustawić bit 5 (xx1xxxxxbin).

Rejestr statusowy
OFFSET = 01h
bit76543210
pin1110121315---
 Kolorem żółtym zaznaczono negację bitu!
Rejestr do czytania

Rejestr kontrolny
OFFSET = 02h
bit76543210
pin--dirint1716141
 Kolorem żółtym zaznaczono negację bitu!
 Kolorem niebieskim zaznaczono bity konfiguracyjne
Bit 4: 1-dozwolona generacja IRQ, 0-zabroniona

Łącze równoległe może być źródłem przerwania sprzętowego IRQ5 lub IRQ7, jeżeli na linii 10 wystąpi zbocze opadające.

NO DZIAŁAJ!, CZYLI PROGRAMOWANIE

Podam tu tylko najistotniejsze elementy dotyczące obsługi LPTów. Ze względu na to, że początkowo pisałem w Turbo Pascalu, a teraz w Delphi, przyjąłem zapis w tym języku.
ZAPIS
Aby wystawić bajt na linie danych D0..D7 wpisujemy go pod adres bazowy portu np. LPT1 (378h). W Pascalu napiszemy tak:
	port[$3F8]:=bajt;
Podobnie zapisuje się do kolejnych dwóch rejestrów, przy czym trzeba podać ich adresy. Ponieważ wszystkie trzy rejestry danego portu są umieszczone w kolejnych adresach wystarczy do tzw. adresu bazowego dodawać przesunięcia (offsety) od 0 do 2.
W Delphi nie da się w ten sposób dostać do rejestrów portu (brak predefiniowanej tablicy port). Należy zrobić wstawkę asemblerową. Aby uprościć sprawę podaję całą procedurę, która zapisuje bajt pod odpowiedni adres:
	procedure ZapiszLPT(Bajt:Byte; Portadr:Word);
	begin
	  asm
	    mov   dx,Portadr
	    mov   al,Bajt
	    out   dx,al
	  end;
	end;
Wystarczy teraz w programie użyć tej procedury, np. tak:
	ZapiszLPT($0F,$378);
ODCZYT
Aby odczytać rejestr, wystarczy podać jego adres w podobny sposób jak to miało miejsce poprzednio, tj.:
	bajt:=port[$3F9];
A w Delphi tak:
	function CzytajLPT(Portadr:Word):byte;
	var bajt:byte;
	begin
	  asm
	    mov   dx,Portadr
	    in    al,dx
	    mov   bajt,al
	  end;
	  CzytajLPT:=bajt;
	end;

"WSPANIAŁY" WINDOWS XP }:

Powyższe procedury niestety nie zadziałają w Windows NT, 2000, XP i temu podobnych. System uznaje takie programy za niebezpieczne dla systemu i nie pozwala na dostęp w ten sposób między innymi do portu równoległego. Jeśli spróbujesz - dostaniesz komunikat "Privileged instruction".
Na szczęście jest na to sposób. ZLPortIO jest biblioteką dla Delphi, która zawiera funkcje czytania i zapisu do dowolnego portu. W zasadzie wystarczą nam 3 funkcje:
- ZLIOStarted, która rozpoznaje wersję systemu operacyjnego i decyduje, w jaki sposób procedury będą się odwoływać do rejestrów, zwraca informację o poprawnym bądź niepoprawnym załadowaniu sterownika
- portreadb(adres), zwraca wartość znajdującą się pod adresem
- portwriteb(adres,wartość), wpisuje wartość pod wskazany adres

Użycie sterownika jest bardzo proste. Należy skopiować pliki zlportio.pas, ddkint.pas do katalogu z projektem lub wspólnego katalogu z unitami, a plik zlportio.sys do katalogu z projektem. Ten ostatni powinien znajdować się w katalogu z Twoją aplikacją.

W programie musisz dołączyć do używanych modułów zlportio i przed pierwszym odczytem lub zapisem do portu wywołać procedurę ZLIOStarted.

Oto najprostszy program wykorzystujący ZLPortIO:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Zlportio;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 if ZLIOStarted then
   Label1.Caption := 'Sterownik załadowany poprawnie.'
 else
   Label1.Caption := 'Nie można załadować sterownika.';
 portwriteb($378,$55); // wpisanie wartości 01010101 (bin)
end;

end.
Jeśli do wyjść D0..D7 podłączymy diody świecące, będzie świeciła co druga.

Zamieszczam program demonstracyjny lpt-demo.zip, który zaświeca po kolei diody LED podłączone do wyprowadzeń danych D4..D7.
Wybrałem te wyjścia ponieważ najłatwiej jest podłączyć diody (patrz rysunek powyżej) - pierwszą do wyprowadzeń 6 i 19, drugą 7 i 20, trzecią 8 i 21 oraz czwartą do wyprowadzeń 9 i 22. Wyprowadzenia 19-22 to masa, czyli podłączamy katody LEDów.
Pokazuję tutaj "dobry przykład złego przykładu". Otóż diody podłączyłem bezpośrednio do wyjść, bez rezystorów ograniczających prąd. Okazuje się jednak, że port ma wewnętrzne zabezpieczenia, najczęściej w postaci szeregowego rezystora, który to ogranicza płynący prąd w przypadku wystąpienia zwarcia na linii. Ja zastosowałem żółte diody, które mają większy spadek napięcia w kierunku przewodzenia niż czerwone. Podłączając diody czerwone należy liczyć się z przepływem większego prądu i większym ryzykiem uszkodzenia linii portu! Dlatego zalecam szeregowo z każdą diodą podłączyć rezystor o wartości około 330Ω.

Ciąg dalszy (chyba) nastąpi --------------------------------------- http://www.geocities.com/dsaproject/computers/lpt_port.html SPP-normal

2006.01.24

Powrót do strony domowej