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