Cteni ISA portu
Igor Cesko
cesko
Středa Březen 17 11:49:06 CET 2004
Zdravim Vsetkych
> Ahoj,
>
>
> Rad by som sa zapojil do diskusie k teme. Ja mam podobny problem s prenosom
> dat cez LPT ako bol spominany na HW. Hladal som nejaky zdroj, ktory by mi
> pomohol nastavit LPT pre spravny prenos cez ECP ( interrupt prenos, DMA
> prenos). Ak by si bol taky dobry a poslal mi aj kusok pascalov - skeho kodu
> ( ak je to mozne ), budem rad.
>
> S pozdravom.
>
> Lubos Uher
Posielam prodram v Pascale, ktory generuje na ECP datove piny
obsah "pwm.txt" suboru. Ja som to pouzival ako 8-kanalovy
generator PWM-priebehu (na kazdom pine jeden kanal). Obsah
pwm-suboru je iba 8192 riadkov, a v kazdom riadku je napisane
cislo (ASCII), ktore sa ma poslat na LPT cez DMA-ECP mod.
Taktovanie rychlosti generovania si robi externy HW (vcelku
primitivne - staci iba generator pravouhlych impulzov - pozri ECP-
specifikaciu). Dolezite je iba akym postupom treba nastavovat LPT
do ECP-modu a este to, ze ak chceme generovat priebeh stale,
tak na konci kazdeho DMA prenosu (vo vyvolanom interrupte)
musime opat DMA prenos spustit. A samozrejme musime vediet
ako ovladat DMA-radic v PC.
Program je uvedeny dalej. Posielam ho priamo v maile, lebo z
prilohami su problemy. Pisal som to uz dost davno, preto tu mozu
byt aj urcite vylepsenia (akoze isto su), lebo teraz robim vacsinou v
Delphi (myslim tu obsluhu ECP). V programe su aj nepouzite
funkcie, ktore som pouzival pri vyvoji (mozte ich skusit - napr.
zistenie velkosti FIFO). Pripomienky k programu pri nejasnostiach
objasnim (ale roboty je nad hlavu a idem asi na >tyzden do Ciech :
takze ked bude cas :-) ).
Tu je zdrojak "ecp.pas":
program Ecp;
uses
Dos,Crt;
var
oldHandler:procedure;
buffer:pointer;
bufferOrigin:pointer;
bufsize:word;
channelDMA:byte;
modeDMA:byte;
IntNo:word;
i:word;
ticks:integer;
OldTimerHandler:procedure;
CyclicECPOpen:boolean;
klavesznak:char;
pomstr:string;
const
maxsizebuffer=$4000;
outport=false;
inport=true;
enable=true;
disable=false;
base:word=$378;
data:word=$378+$000;{Paralel Port Data Register}{StandardMode,PS2Mode}
ecpAFifo:word=$378+$000;{ECP FIFO: Address/RLE}{ECPMode}
ECPAddress=$80;
dsr:word=$378+1;{Device Status Register}{All mode}
nBusy=$80;{hardware inverted}
nAck=$40;
PError=$20;
Select=$10;
nFault=$08;
dcr:word=$378+$002;{Device Control Register}{All mode}
direction=$20;
ackIntEn=$10;{enables interrupt on rising edge of nAck}
SelectIn=$08;{hardware inverted}
nInit=$04;
autoFd=$02;{hardware inverted}
strobe=$01;{hardware inverted}
cFifo:word=$378+$400;{Paralel Port Data Fifo}{ParalelFIFOMode}
ecpDFifo:word=$378+$400;{ECP Data FIFO}{ECPMode}
tFifo:word=$378+$400;{Test FIFO}{TestMode}
cnfgA:word=$378+$400;{Configuration Register A}{ConfigurationMode}
irqLevel=$80;
implD=$70;{PWord size}
nByteInTransceiver=$04;{event 35}
PWordRemainderBytes=$03;{when reset from ECPMode to StandardMode or PS2Mode}
cnfgB:word=$378+$401;{Configuration Register B}{ConfigurationMode}
compress=$80;{need not br implemented}
intrValue=$70;{ISA IRQ line}
intrLine=$31;{IRQ table}
dmaChannel=$07;{DMA table}
ecr:word=$378+$402;{Extended Control Register}
StandardMode=0*32;
PS2Mode=1*32;
ParalelFIFOMode=2*32;
ECPMode=3*32;
EPPMode=4*32;
TestMode=6*32;
ConfigurationMode=7*32;
nErrIntrEn=$10;{disables interrupt generated on the asserting edge of nFault in ECPMode}
dmaEn=$08;{DMA starts when serviceIntr=0}
serviceIntr=$04;{disables DMA and all of the service interrupts}
full=$02;{FIFO full}
empty=$01;{FIFO empty}
procedure ChangeAddressConfig(BazAddr:word);
begin
base:=BazAddr;
data:=base+$000;{Paralel Port Data Register}{StandardMode,PS2Mode}
ecpAFifo:=base+$000;{ECP FIFO: Address/RLE}{ECPMode}
dsr:=base+1;{Device Status Register}{All mode}
dcr:=base+$002;{Device Control Register}{All mode}
cFifo:=base+$400;{Paralel Port Data Fifo}{ParalelFIFOMode}
ecpDFifo:=base+$400;{ECP Data FIFO}{ECPMode}
tFifo:=base+$400;{Test FIFO}{TestMode}
cnfgA:=base+$400;{Configuration Register A}{ConfigurationMode}
cnfgB:=base+$401;{Configuration Register B}{ConfigurationMode}
ecr:=base+$402;{Extended Control Register}
end;
procedure OutECPData(databyte:byte);
begin
port[ecpDFifo]:=databyte;
end;
procedure OutECPAddress(addressbyte:byte);
begin
port[ecpAFifo]:=addressbyte;
{port[ecpAFifo]:=counts and $7f;}
end;
procedure SetMode(mode:byte);
var
pom:byte;
begin
pom:=port[ecr] and $1f;
port[ecr]:=PS2Mode or pom;
port[ecr]:=mode or pom;
end;
function GetMode:byte;
begin
GetMode:=port[ecr] and $e0;
end;
procedure SetDirection(smer:boolean);
var
mode:byte;
begin
mode:=GetMode;
SetMode(PS2Mode);
if smer=outport then
port[dcr]:=port[dcr] and (not(direction))
else
port[dcr]:=port[dcr] or direction;
SetMode(mode);
end;
procedure SetdmaEn(enableDMA:boolean);
begin
if enableDMA=enable then
port[ecr]:=port[ecr] or dmaEn {enable DMA}
else
port[ecr]:=port[ecr] and (not(dmaEn));{disable DMA}
end;
procedure SetserviceIntr(enableIntr:boolean);
begin
if enableIntr=enable then
port[ecr]:=port[ecr] and (not(serviceIntr)){enable od FIFO + DMA}
else
port[ecr]:=port[ecr] or serviceIntr;{disable od FIFO + DMA}
end;
procedure SetnErrIntrEn(enableIntr:boolean);
begin
if enableIntr=enable then
port[ecr]:=port[ecr] and (not(nErrIntrEn)){enable od ERROR pin 15}
else
port[ecr]:=port[ecr] or nErrIntrEn;{disable od ERROR pin 15}
end;
procedure SetackIntEn(enableIntr:boolean);
begin
if enableIntr=enable then
port[dcr]:=port[dcr] or ackIntEn{enable od ACK pin 10}
else
port[dcr]:=port[dcr] and (not(ackIntEn));{disable od ACK pin 10}
end;
procedure Setstrobe(enablestrobe:boolean);
begin
if enablestrobe=enable then
port[dcr]:=port[dcr] or strobe
else
port[dcr]:=port[dcr] and (not(strobe));
end;
procedure SetautoFd(enableautoFd:boolean);
begin
if enableautoFd=enable then
port[dcr]:=port[dcr] or autoFd
else
port[dcr]:=port[dcr] and (not(autoFd));
end;
function GetFifoLength:word;
var
i:word;
pom1,mode:byte;
begin
mode:=GetMode;
SetMode(TestMode);{test hlbky FIFO}
i:=0;{naplnenie FIFO}
while ((port[ecr] and full)=0) and (i<>$ffff) do
begin
port[tFifo]:=0;
inc(i);
end;
if i=$ffff then
begin
GetFifoLength:=0;
SetMode(mode);
Exit;
end;
i:=0; {vyprazdnenie FIFO}
while ((port[ecr] and empty)=0) do
begin
pom1:=port[tFifo];
inc(i);
end;
SetMode(mode);
GetFifoLength:=i;
end;
function GetwriteIntrThreshold:word;
var
i:word;
pom1:byte;
mode:byte;
begin
mode:=GetMode;
GetwriteIntrThreshold:=0;
SetDirection(outport);
SetserviceIntr(disable);
SetMode(TestMode);{test hlbky FIFO}
i:=0;{naplnenie FIFO}
while ((port[ecr] and full)=0) and (i<>$ffff) do
begin
port[tFifo]:=0;
inc(i);
end;
if i=$ffff then
begin
GetwriteIntrThreshold:=0;
SetMode(mode);
Exit;
end;
{vyprazdnenie FIFO}
SetserviceIntr(enable);
i:=0;
while ((port[ecr] and empty)=0) do
begin
pom1:=port[tFifo];
inc(i);
if (port[ecr] and serviceIntr)<>0 then
GetwriteIntrThreshold:=i;
end;
SetMode(mode);
end;
function GetreadIntrThreshold:word;
var
i:word;
pom1:byte;
mode:byte;
begin
mode:=GetMode;
GetreadIntrThreshold:=0;
SetserviceIntr(disable);
SetDirection(inport);
SetMode(TestMode);
i:=0;{vyprazdnenie FIFO}
while ((port[ecr] and empty)=0) and (i<>$ffff) do
begin
pom1:=port[tFifo];
inc(i);
end;
if i=$ffff then
begin
GetreadIntrThreshold:=0;
SetMode(mode);
Exit;
end;
{naplnenie FIFO}
i:=0;
SetserviceIntr(enable);
while ((port[ecr] and full)=0) do
begin
port[tFifo]:=0;
inc(i);
if (port[ecr] and serviceIntr)<>0 then
GetreadIntrThreshold:=i;
end;
i:=0;{vyprazdnenie FIFO}
while ((port[ecr] and empty)=0) and (i<>$ffff) do
begin
pom1:=port[tFifo];
inc(i);
end;
SetMode(mode);
end;
{$F+}
procedure MyTimerHandler;interrupt;
begin
port[$37A]:= port[$37A] xor $08;
asm pushf end;
OldTimerHandler;
port[$37A]:= port[$37A] xor $08;
end;
{$F-}
{$F+}
procedure LPTHandler; interrupt;
begin
if CyclicECPOpen then
begin
port[ecr]:=port[ecr] and (not(serviceIntr));
ticks:=(ticks+1) mod 50;
if ticks=0 then
begin
{sound(5000);delay(1);nosound;{kazdu sekundu tikni}
end;
end;
port[$20]:= $20;
end;
{$F-}
{$F+}
function AllocateDMABuffer(channel:byte; var buffer:pointer; bufsize:word;var bufferOrigin:pointer)
:boolean;
var
fyzAdr,fyzAdrEnd:longint;
PageLength:longint;
begin
PageLength:=$20000;
if channel<4 then PageLength:=PageLength shr 1;
bufsize:=2*bufsize+2;
if MaxAvail < (bufsize) then
begin
Writeln('Not enough memory ',MaxAvail);
AllocateDMABuffer:=false;
Exit;
end
else
begin
GetMem(buffer, bufsize);
bufferOrigin:=buffer;
end;
fyzAdr:=Seg(buffer^);
fyzAdr:=(fyzAdr shl 4)+Ofs(buffer^);
fyzAdrEnd:=fyzAdr+bufsize;
if ((fyzAdrEnd mod PageLength) < (bufsize))
or ( (fyzAdrEnd div PageLength)=(fyzAdr div PageLength) ) then
begin
if (fyzAdr mod 2) <> 0 then
fyzAdr:=(fyzAdr div 2)*2+2;{aby sa zacinalo na adrese delitelnej 2 (word)}
end
else
begin
fyzAdr:=(fyzAdrEnd div PageLength)*PageLength;{na zaciatok 128 kB stranky}
end;
buffer:=Ptr(fyzAdr shr 4,fyzAdr and $0F);
AllocateDMABuffer:=true;
end;
procedure UnallocateDMABuffer(bufferOrigin:pointer;bufsize:word);
begin
FreeMem(bufferOrigin,2*bufsize+2);
end;
function InitDMA(channel:byte; buffer:pointer; bufsize:word; mode:byte):integer;
var
fyzAdr:longint;
begin
fyzAdr:=Seg(buffer^);
fyzAdr:=(fyzAdr shl 4)+Ofs(buffer^);
if channel<4 then
begin
{ port[$08]:=$08;{zrychlene casovanie nesmiem pristupit vo Windows na tento port}
port[$0A]:= channel or 4;{zamaskujem DMA}
port[$0B]:= channel or (mode and $FC);
{ port[$0B]:=$4B;{jednoduchy prenos z pamate do I/O DMA3}
{ port[$0B]:=$5B;{jednoduchy prenos z pamate do I/O DMA3 s autoinicializaciou}
{ port[$0B]:=$8B;{BLOKOVY prenos z pamate do I/O DMA3 NEPOUZIT LEBO SA STRATIA DATA}
{ port[$0B]:=$0B;{poziadavkovy z pamate do I/O DMA3}
{ port[$1B]:=$1B;{poziadavkovy z pamate do I/O DMA3 s autoinicializaciou}
port[$0C]:=0;{reset pointera}
case channel of
0 : port[$87]:=fyzAdr shr 16;
1 : port[$83]:=fyzAdr shr 16;
2 : port[$81]:=fyzAdr shr 16;
3 : port[$82]:=fyzAdr shr 16;
end;
port[channel*2]:=fyzAdr and $00ff;
port[channel*2]:=(fyzAdr and $00ff00) shr 8;
port[channel*2+1]:=(bufsize-1) and $00ff;
port[channel*2+1]:=(bufsize-1) shr 8;
port[$0A]:= channel;{odmaskujem DMA}
end
else
begin
channel:=channel-4;
port[$D4]:= channel or 4;{zamaskujem DMA}
port[$D6]:= channel or mode;
port[$D8]:=0;{reset pointera}
case channel of
1 : port[$8B]:=fyzAdr shr 16;
2 : port[$89]:=fyzAdr shr 16;
3 : port[$8A]:=fyzAdr shr 16;
end;
port[$C0+channel*4]:=fyzAdr and $00ff;
port[$C0+channel*4]:=(fyzAdr and $00ff00) shr 8;
port[$C0+channel*4+2]:=(bufsize-1) and $00ff;
port[$C0+channel*4+2]:=(bufsize-1) shr 8;
port[$D4]:= channel;{odmaskujem DMA}
end
end;
function StopDMA(channel:byte):integer;
begin
if channel<4 then
port[$0A]:= channel or 4 {zamaskujem DMA}
else
port[$D4]:= (channel-4) or 4;{zamaskujem DMA}
end;
procedure InitCyclicECP(IntNo:word);
var
old86flags:word;
begin
asm pusha; pushf; pop ax; mov old86flags, ax; popa; cli;end;{zakaze interrupt so zachovanim predc
hadzajuceho stavu}
SetMode(StandardMode);
port[dcr]:=$f4;
SetMode(ECPMode);
SetDirection(outport);
SetdmaEn(disable);
SetserviceIntr(disable);
SetackIntEn(disable);
SetnErrIntrEn(disable);
SetDirection(outport);
SetserviceIntr(disable);
SetdmaEn(enable);
CyclicECPOpen:=true;
asm pusha; mov ax, old86flags; push ax; popf; popa;end;{obnovi interrupt z predchadzajuceho stav
u}
SetserviceIntr(enable);
end;
procedure CloseCyclicECP;
var
pom:byte;
old86flags:word;
begin
asm pusha; pushf; pop ax; mov old86flags, ax; popa; cli;end;{zakaze interrupt so zachovanim predc
hadzajuceho stavu}
CyclicECPOpen:=false;
SetMode(StandardMode);
port[dcr]:=$f4;
SetMode(ECPMode);
SetdmaEn(disable);
SetserviceIntr(disable);
SetackIntEn(disable);
SetnErrIntrEn(disable);
pom:=port[ecr];
asm pusha; mov ax, old86flags; push ax; popf; popa;end;{obnovi
interrupt z predchadzajuceho stavu}
end;
function FillBufferFromFile(datafilename:string; buffer:pointer; var
bufsize:word):boolean;
var
datafile:text;
bytebuf:^byte;
i:word;
begin
{$I-}
FillBufferFromFile:=false;
Assign(datafile,datafilename);
FileMode := 0;
Reset(datafile);
{$I+}
if (IOResult <> 0) then Exit;
Reset(datafile);
bytebuf:=buffer;
bufsize:=0;
while not eof(datafile) do
begin
{$I-}
readln(datafile,bytebuf^);
{$I+}
inc(bufsize);
inc(bytebuf);
if (bufsize>maxsizebuffer) or (IOResult <>
0) then Exit;
end;
Close(datafile);
FillBufferFromFile:=true;
end; { FillBufferFromFile }
function InstallInterrupt(IRQNumber:byte; NewInterrupt:pointer; var
OldInterrupt:pointer):boolean;
var
IntNumber:word;
old86flags:word;
begin
if (IRQNumber<8) then IntNumber:=IRQNumber+$08
else IntNumber:=IRQNumber+$68;
GetIntVec(IntNumber,OldInterrupt);
InstallInterrupt:=false;
if (OldInterrupt=NewInterrupt) then Exit;
asm pusha; pushf; pop ax; mov old86flags, ax; popa;
cli;end;{zakaze interrupt so zachovanim predchadzajuceho stavu}
SetIntVec(IntNumber,NewInterrupt);
if (IRQNumber<8) then
begin
port[$21]:=(not(1 shl IRQNumber)) and port[$21];
end
else
begin
port[$21]:=(not(1 shl 2)) and port[$21];
port[$A1]:=(not(1 shl (IRQNumber-8))) and port[$A1];
end;
asm pusha; mov ax, old86flags; push ax; popf; popa;end;{obnovi
interrupt z predchadzajuceho stavu}
InstallInterrupt:=true;
end;{InstallInterrupt}
{************************************************************************}
{************************************************************************}
begin
writeln;
writeln('PWM generator on LPT ECP port.');
if ParamCount<1 then
begin
writeln('Usage: ecp.exe PWMfile.txt [hexa
base address] [IRQ] [DMA]');
Halt;
end;
IntNo:=7;{IRQ7}
channelDMA:=3;{DMA3}
base:=$378;pomstr:='378h';
if ParamCount>=2 then begin
Val('$'+ParamStr(2),base,i);pomstr:=Paramstr(2); end;
if (i<>0)or(base=0) then begin writeln('invalid LPT address');
Halt; end
else writeln('LPT address= ',pomstr,'h');
if ParamCount>=3 then begin Val(ParamStr(3),IntNo,i) end;
if (i<>0)or not(IntNo in [2,3,4,5,6,7,9,10,11,12,14,15]) then
begin writeln('invalid IRQ'); Halt; end
else writeln('IRQ= ',IntNo);
if ParamCount>=4 then begin
Val(ParamStr(4),channelDMA,i) end;
if (i<>0)or not (channelDMA in [1,3,5,6,7]) then begin
writeln('invalid DMA'); Halt; end
else writeln('DMA= ',channelDMA );
ChangeAddressConfig(base);
CyclicECPOpen:=false;
bufsize:=maxsizebuffer;{$M 4000,$8000,$8000} {alokacia
pamate pre rezidentne ponechanie v pamati}
modeDMA:=$58;{jednoduchy prenos pamat -> I/O s
autoinicializaciou} {poziadavkovy rezim $1B robi problemy}
if not
AllocateDMABuffer(channelDMA,buffer,bufsize,bufferOrigin) then
Halt;
if not FillBufferFromFile(ParamStr(1),buffer,bufsize) then
begin
writeln('Error in file');
UnallocateDMABuffer(bufferOrigin,bufsize);
Halt;
end;
InstallInterrupt(IntNo,@LPTHandler,@oldHandler);
CloseCyclicECP;
InitDMA(channelDMA, pointer(buffer), bufsize,modeDMA);
InitCyclicECP(IntNo);
klavesznak:='0';
repeat
{ writeln(port[$07]+port[$07]*256.0);}
{ port[$37A]:= port[$37A] xor $08;}
{ delay(2);}
writeln;
writeln('ECP Device Address= ',klavesznak);
writeln('Press number to change "ECP Device
Address" to this number.');
writeln('Press ''R'' to keep program as RESIDENT.');
writeln('Press any key to EXIT.');
OutECPAddress(ord(klavesznak)-48);
klavesznak:=Readkey;
until not(klavesznak in['0'..'9']);
if klavesznak in['r','R'] then
begin
{ GetIntVec($08,@OldTimerHandler);
SetIntVec($08,@MyTimerHandler);}
Keep(0);
end;
writeln('stopping ECP.');
CloseCyclicECP;
writeln('stopping DMA.');
StopDMA(channelDMA);
writeln('unallocate DMA buffer.');
UnallocateDMABuffer(bufferOrigin,bufsize);
writeln('back interrupt vector.');
InstallInterrupt(IntNo,@oldHandler,@oldHandler);
end.
{************************************************************************}
S pozdravom
Igor Cesko
P.S.: Niekedy nabuduce mozno aj nieco do Delphi.
-------------------------
Ing. Igor Cesko
Hardware & Software
E-mail: cesko@appliedp.sk
Applied Precision Ltd.
Technicka 5
821 04 Bratislava
SLOVAKIA
Tel,Fax: +421-7-4342 6628
Tel: +421-7-4363 3151
Tel: +421-7-4333 7340
E-mail: info@appliedp.sk
Web: www.appliedp.sk
-------------------------
Další informace o konferenci Hw-list