![]() |
Su macchine moderne, in linea di massima successive agli IBM PS2 prima serie, l' indirizzo corrispondente alla LPT4 è usato per altri scopi e quindi ne sconsiglio l'uso e raccomando una particolare attenzione nell'interpretazione dei dati letti. Di seguito un frammento di codice che legge l'indirizzo di LPT1 in Turbo C (c) in ambiente DOS. unsigned int far *pAddr; // Puntatore ai vettori di indirizzo unsigned int IOaddr; // Indirizzo di LPT1 { pAddr = (unsigned int far *) 0x408; IOaddr = *pAddr; printf("LPT1 base address is 0x%Xn", IOaddr); } In TurboPascal (c) l'indirizzo di LPT1 si ricava con il seguente codice. Var addr:word; {Indirizzo di LPT1} Begin addr := MemW[$0000:$0408]; writeln("LPT1 base address is", addr); End Per le altre porte il codice è lo stesso, cambiando ovviamente l'area di memoria da cui leggere. Il sistema operativo Linux (c) utilizza convenzioni diverse, assegnando il nome di lpt1, lpt2 ed lpt3 rispettivamente alle porte che individua agli indirizzi 0x378, 0x 278 e 0x3BC. I registri e l'assegnazione dei pinComplessivamente sono disponibili sul connettore della porta parallela 12 bit per l'output e 5 per l'input. Gli ingressi e le uscite sono TTL compatibili (alcuni possono essere a collettore aperto, in genere con resistore di pull-up integrato all'interno del PC) anche se le tensioni e le correnti effettivamente disponibili sono piuttosto variabili in funzione delle tecnologie impiegate per la costruzione della porta. Su molti PC un uno logico appare come una tensione molto vicina ai 5V, uno zero come 0V (a vuoto). Le correnti disponibili sono in genere di almeno 5 mA sia di sink che di source ma spesso molto di più (anche 50 mA e più). Non si tratta però di una regola: in genere deve essere interpretato come uno logico qualunque tensione superiore ai 2V e come zero logico qualunque tensione inferiore a 0.8V. Inoltre si potrebbe ritenere che la corrente assorbita da un'uscita (Isink) sia maggiore, anche di molto, di quella erogata (Isource). E' quindi buona norma progettare circuiti che riescano a gestire correttamente segnali che rappresentano l'uno logico sia con i 2V sia con i 5V (quali per esempio i TTL, gli HCT o gli HC con resistore di pull-up. E' anche opportuno evitare assorbimenti superiori a qualche mA. La seguente tabella riporta l'assegnazione dei pin (sia sul connettore DB25 che su quello Centronics), il loro nome, la direzione (Out significa che il PC invia il bit alla periferica) ed il nome del registro utilizzato per controllarli.
I pin di uscita evidenziati da un * sono utilizzabili anche come ingresso ma solo su alcune porte, come descritto più avanti. In questo caso si tratta di un'uscita a collettore aperto. Alcuni dei pin del connettore Centronics a 36 pin non sono presenti sul connettore a 25 pin. Realizzando un circuito connesso alla porta parallela è opportuno collegare tutti i fili di massa, sia per semplificare il layout del circuito stampato sia per diminuire gli eventuali disturbi. L'immagine seguente evidenzia i pin di uscita con l'indicazione dei relativi registri, visti dal connettore del PC. I pin non indicati sono tutti connessi a massa. Verificate con attenzione la corrispondenza dei pin leggendo sul connettore stesso la numerazione (è sempre presente, almeno per i 4 pin agli angoli), soprattutto se si usano prolunghe o simili. Attenzione in particolare al fatto che, utilizzando un adattatore maschio la corrispondenza dei pin è, ovviamente, simmetrica, dato che deve incastrarsi in un connettore femmina. Analogamente se si guarda un connettore da dietro (cioè dal lato su cui si eseguono le saldature) i pin appaiono, ovviamente, simmetrici. I registri di baseCiascuna porta parallela configurata come SPP è controllabile attraverso tre registri consecutivi nello spazio di I/O del processore, chiamati data register, status register e control register, ciascuno di 8 bit. Per scrivere un byte in questi registri è necessario eseguire una istruzione di output verso l'indirizzo interessato. Per leggere un byte è necessario effettuare una istruzione di input. Non è possibile leggere o scrivere un solo bit alla volta, indipendentemente dal linguaggio adottato; inoltre non è in genere possibile utilizzare le istruzioni corrispondenti alle SET dell'assembler. Infine non è possibile leggere un dato da un registro di sola scrittura o scrivere in una porta a sola lettura (l'operazione non genera nessun errore ma il dato letto o scritto è inconsistente). Una precisazione: quanto detto vale solo in ambiente MS-DOS o derivati. Usando il BorlandC o il TurboC la sintassi è la seguente (tale sintassi verrà adottata nel seguito del tutorial): unsigned char dato; unsigned int addr; outportb (addr, dato); dato = inportb (addr); dove dato è il byte da leggere o da scrivere (una variabile di otto bit, senza segno: normalmente di tipo unsigned char) e addr è l'indirizzo del registro (una variabile o una costante a 16 bit, senza segno). Altri compilatori C usano una sintassi simile ma non è prevista nell'ANSI C la scrittura diretta su dispositivi hardware: occorre quindi verificare sul manuale del proprio compilatore. Usando la sintassi TurboPascal le stesse istruzioni diventano: Port[addr] := dato; dato := Port[addr]; Dove Port è una matrice predefinita di 65k elementi il cui l'indice corrisponde all'indirizzo della porta da utilizzare. Usando la sintassi GW-Basic o altri basic per DOS: OUT addr,dato dato = INP (addr) Usando la sintassi assembler: MOV DX,ADDR MOV AL,DATO OUT DX,AL MOV DX,ADDR IN AL,DX MOV DATO,AL Usando sistemi operativi diversi dal DOS le cose cambiano in quanto ai programmi utente è impedito l'accesso diretto alle risorse hardware. La descrizione approfondita va oltre lo scopo di questo tutorial e mi limiterò quindi ad un breve accenno. Windows NT/ 2000/XPQuesto sistema operativo implementa due modalità operative: il ring0 ed il ring3. Le istruzioni di I/O sono possibili senza limitazioni solo nel ring 0, quello del kernel del sistema operativo. Nel ring3 (il livello delle normali applicazioni utente) sono in teoria possibili istruzioni di ingresso e uscita su particolari indirizzi attraverso la manipolazione della cosiddetta I/O permission table, purtroppo modificabile solo all'interno del ring0. Eseguendo una applicazione DOS viene automaticamente generata da WindowsNT una macchina virtuale (la NTVDM: NT virtual dos machine) che emula le periferiche standard ed in particolare la parallela permettendo in qualche caso il corretto funzionamento dei programmi anche se con notevolissimi rallentamenti. Nei compilatori nativi in ambiente Windows normalmente le istruzioni di IN e OUT non sono neppure implementate: ciò è vero in particolare per VisualBasic, BorlandC 5 e Delphi. La soluzione a questi problemi è quella di scrivere un device driver che, essendo eseguito nel ring0, permette la gestione diretta dell'hardware; purtroppo l'uso della DDK (driver development kit) di Microsoft è tutt'altro che semplice... Forse potrebbe esservi utile WinDriver disponibile in versione demo su http://www.jungo.com/ In alternativa è possibile usare driver generici che mettono a disposizione apposite funzioni contenute in DLL; spesso questi prodotti sono disponibili gratuitamente su internet: posso citare DriverLINX, per Win9x e WinNT e PortTalk (quest'ultimo disponibile sia come sorgente che come eseguibile su http://www.beyondlogic.com/) Analoghe considerazioni valgono anche in Win95/98Me, anche se questi OS sono molto più laschi nella protezione dell'hardware. LinuxAnche in ambiente Linux l'I/O diretto a livello di applicazione utente non è possibile. Chi volesse fare esperimenti con questo sistema operativo deve utilizzare la funzione C ioperm(from, num, on_or_off) Essa permette di rendere accessibile in lettura/scrittura una serie di registri ad una applicazione qualunque. Per usare un programma che invoca questa funzione occorre però essere l'utente root. In alternativa occorre scrivere applicazioni in kernel mode; per questo potrebbe essere utile per esempio il pacchetto GPL short. Il data registerIl data register è un registro di sola scrittura direttamente connesso agli otto pin di dato presenti sul connettore esterno della parallela. L'indirizzo è quello di base della porta. Per impostare un pin alto o basso basta scrivere nel bit corrispondente di questo registro rispettivamente 1 oppure 0. Il bit meno significativo del registro corrisponde al pin Data0. Se per esempio si vogliono impostare i pin in modo tale che i pin 2 (bit 0) e 7 (bit 5) siano alti ed i pin 3, 4, 5, 6, 8 e 9 bassi, occorre eseguire la seguente riga di codice (la costante DATA indica l'indirizzo del registro dei dati della porta interessata, per esempio 0x278): #define DATA 0x278 outportb (DATA, 0x21); // 0x21 = 00100001 in binario Occorre infine notare che una volta scritto il byte nel registro, i pin di uscita non cambiano più il loro valore fino alla scrittura successiva in quanto il circuito di uscita della porta è formato da otto flip-flop. Lo status registerQuesto registro è di sola lettura ed ha indirizzo BASE + 1. Quando il processore effettua la lettura di questo registro vengono riportati i valori dei cinque pin in ingresso, secondo la tabella di seguito riportata.
L'ultima colonna indica se è presente una porta not all'ingresso della porta parallela: nel caso ci sia un SI, vuol dire che un livello logico alto viene letto come 0 logico. Se invece si trova scritto NO, un livello logico alto viene letto come 1 logico. Se voglio conoscere il valore dei pin 11 e 12 devo eseguire il seguente codice #define STATUS (DATA+1) unsigned char data, busy, po // Tre variabili ad 8 bit, senza segno data = inportb (STATUS); // Leggo gli 8 bit di stato data = data ^ 0x80; // Inverto il bit più significativo con uno xor busy = (data & 0x80) >> 7; // Estraggo il valore di busy (bit 7) po = (data & 0x20) >> 5; // Estraggo il valore di Paper Out (bit 5) Il bit IRQ viene resettato dall'hardware in caso di interrupt attivato dalla linea Ack. Vale invece 1 qualora non si sia attivata nessuna interrupt. Il control registerQuesto registro è primariamente un registro di scrittura anche se, in alcuni casi, è possibile l'utilizzo in lettura. L'indirizzo è BASE + 2. Il processore può scrivere in questo registro per impostare il valore di quattro pin di uscita, secondo la seguente tabella. Altri due bit sono per usi interni.
Nel caso dei tre bit invertiti, la scrittura di un 1 causa sull'uscita una tensione di 0V. Per esempio per portare tutti i quattro pin della LPT2 ad un livello logico alto devo eseguire la seguente istruzione #define CONTROL (DATA+2) outportb (CONTROL, 0x04); // 0x04 = 00000100, cioè ricordando che 3 bit sono invertiti, 00001111 Ho accennato al fatto che i quattro segnali connessi al registro di controllo possono essere in alcuni casi utilizzati anche per l'input. Per fare ciò è necessario porre prima tutti i bit di uscita a livello logico alto e quindi leggere il byte: outportb (CONTROL, 0x04); // tutti i pin alti data = inportb (CONTROL) & 0x0F; Questa possibilità è subordinata al fatto che queste uscite siano a collettore aperto (open collector), possibilità prevista nell'originaria porta del PC XT ma non in tutte le porte parallele moderne, soprattutto se configurate come EPP o ECP. Per verificare se la porta funziona secondo questa modalità provate a collegare una resistenza da 1 kohm tra uno dei pin associati al registro di controllo e massa: se viene letto uno 0 le uscite sono open-collector e quindi possono essere usate come ingressi. Personalmente sconsiglio l'uso di tale modalità anche se funzionante in quanto potrebbe portare alla distruzione della porta nel caso di porte senza uscita a collettore aperto, cioè praticamente per tutte le porte dei PC moderni correttamente configurati. Il bit 5 del registro di controllo permette di attivare la modalità bidirezionale delle porte che ne sono provviste: ponendo a 1 questo bit è possibile leggere i dati presenti sugli otto pin 2..9, attraverso la lettura del registro dei dati, come di seguito mostrato outportb (CONTROL, 0x20); // 0x20 = 0010 000 setto in ingresso data = inportb (DATA); Se il bit vale 0, è possibile scrivere nel registro dati, come precedentemente descritto. Non tutte le porte hanno questa possibilità ed alcune funzionano con modalità diverse. Dalla mia esperienza posso dire che le tutte le porte configurate come EPP hanno questo funzionamento. Non l'hanno invece le porte del PC XT originale ed in genere le vecchie macchine. Per effettuare il test che verifica se la propria porta parallela è bidirezionale o meno è possibile usare la seguente procedura:
Se viene letto il numero binario 11111110 (0xFE) la porta supporta la modalità bidirezionale. Se viene letto il dato preventivamente scritto, la porta non supporta la modalità bidirezionale, perlomeno secondo lo schema che ho descritto: molte schede parallele hanno infatti capacità bidirezionali di tipo proprietario e quindi incompatibili con la procedura descritta. Il bit 4 permette di abilitare le interrupt alla ricezione di un fronte sul pin Ack. Se l'interrupt è abilitato e la stampante connessa ad una linea del controllore di interrupt (p.e. attraverso l'apposito ponticello presente su molte schede,), una transizione sul pin Ack causa un'interrupt. Il fronte attivo può cambiare da scheda a scheda. L'uso di tale possibilità richiede la conoscenza delle problematiche correlate alla gestione delle interruzioni, argomento che va oltre lo scopo di questo tutorial. Circuiti applicativiPresento ora qualche piccolo circuito applicativo utile per fare le prime esperienze con la porta parallela. Ho già detto del rischio di danni al PC e quindi vi invito nuovamente prestare la massima attenzione alla realizzazione del circuito e ad evitare di effettuare modifiche al circuito con il PC acceso. Accensione di un ledUn paio di circuiti veramente semplici per accendere un led permettono di vedere come usare la parallela. È necessario disporre di una resistenza di circa 3,3k ed ovviamente di un led e, limitatamente al secondo circuito, di una batteria o un alimentatore da 5 volt (ma va bene anche una batteria da 4,5V). Il primo schema non necessita di particolari spiegazioni: quando il pin 2 viene posto alto, il led si accende, quando viene posto basso, si spegne. Il codice relativo è banale: outportb (DATA, 0x01); // accendo il led collegato al pin 2 (D0) // altre righe di codice non legate all'uso della SPP outportb (DATA, 0x00); // spengo tutti il led Ho sottointeso la dichiarazione delle varabili utilizzate, peraltro già inserire nelle righe di codice più sopra presentate. Occorre ricordare che, una volta acceso il led, esso rimane tale fino allo spegnimento del PC o finché viene esplicitamente spento, indipendentemente dal fatto che il programma sia o meno in esecuzione, in quanto memorizzato dall'hardware. La connessione utilizzata in questo primo circuito (il led è acceso dalla corrente uscente dal pin della porta parallela) ha il vantaggio di non richiedere nessuna alimentazione esterna ma il difetto che su alcune porte parallele (in particolare vecchie o di PC portatili) potrebbe non funzionare a causa del fatto che la corrente di source è troppo piccola. Il secondo circuito utilizza una sorgente di alimentazione esterna e dovrebbe funzionare con tutte le porte parallele. In questo caso è lo zero che accende il led. Per aumentare la luminosità del led è possibile diminuire il valore della resistenza anche a valori molto più piccoli, quale 330 ohm. Ovviamente i due circuiti possono essere ampliati connettendo altri sette led ai pin di dato. Simile il discorso per i pin connessi al registro di controllo. L'unica nota è riferita al fatto che, nel caso di uscite a collettore aperto potrebbe essere necessario per il primo circuito diminuire il valore della resistenza o addirittura toglierla (quest'ultima è un'operazione rischiosa e che personalmente sconsiglio in quanto non è una configurazione adatta alla generalità delle LPT). Collegamento a grossi carichiIl precedente circuito è utile solo se sono richieste correnti molto piccole (pochi mA al massimo) e una tensione di 5V massimo. In altri casi, quale l'accensione di una lampadina da 12V o di un relè, è necessario l'utilizzo di un transistor di adeguata potenza e di un'alimentazione esterna. Lo schema mostra la connessione di un relè (osservate il diodo di ricircolo, che deve essere di tipo veloce) e di una lampadina. La Vcc è la tensione continua richiesta per il funzionamento della lampadina e della bobina del relè. Eventuali carichi in AC richiedono sempre la presenza di un relè o di un altro interruttore adeguato. Ovviamente nel dimensionamento del transistor e della resistenza di base occorre tenere conto delle effettive tensioni e correnti in gioco (in particolare, nel caso di grossi teleruttori o lampade ad elevato assorbimento, si rende necessario l'uso di transistor darlington) ma, con le dovute cautele, è possibile gestire lampade a bassa tensione da diverse decine di watt. Attenzione: nel caso di rottura del transistor o di errori di connessione è possibile che la tensione di batteria si presenti alle uscite della porta parallela mettendo a rischio non solo la porta parallela ma anche l'intero PC; nel caso di tensioni elevate, vi è rischio anche per l'incolumità delle persone e quindi sconsiglio questi circuiti a persone non adeguatamente qualificate. L'uso di interruttoriNello schema seguente sono illustrati tre modi per connettere un interruttore alla porta parallela. In tutte e tre i casi l'interruttore chiuso viene letto come tensione di 0 volt, l'interruttore aperto come tensione di circa 5 volt. Lo schema utilizzato con S1 richiede la presenza di una batteria esterna da 5V. Per conoscere lo stato dell'interruttore è sufficiente leggere lo stato del bit Error nel registro di controllo (oppure di un altro bit dello stesso registro se connesso ad un altro pin). È possibile la connessione con uno qualunque dei bit gestiti attraverso il registro di controllo. In certi casi questo circuito funziona anche senza batteria e senza resistenza ma l'estrema semplicità si paga con errori di lettura occasionali, soprattutto in ambiente rumoroso. Lo schema usato per S2 è simile al precedente, con la differenza che la tensione non viene prelevata da una batteria ma da uno dei pin della porta dati, posto preventivamente a uno logico. Lo svantaggio è quello di richiedere per ciascun interruttore due pin della porta parallela, oltre la massa. Lo schema utilizzato per S3 è il più semplice e funziona con i pin associati al registro di controllo; non richiede nessun componente oltre l'interruttore ma funziona esclusivamente a condizione che il pin a cui è connesso l'interruttore sia a collettore aperto e che la resistenza di pull-up sia montata internamente alla scheda. In questo caso, per leggere lo stato dell'interruttore occorre impostare a 1 il pin scrivendo nel registro di controllo e poi leggere il bit nello stesso registro. Attenzione: qualora la porta non fosse a collettore aperto (come succede in molte parallele moderne) si rischia la distruzione della porta. Pertanto sconsiglio l'uso di questo metodo. Lettura di otto bitIl seguente schema permette di leggere otto bit anche da porte non bidirezionali o senza uscite open collector. Viene utilizzato un multiplexer comandato dall'uscita di strobe, leggendo quattro bit alla volta. Il difetto di tale metodo deriva dalla relativa lentezza (richiede due letture e due scritture della porta) e dal fatto che è ovviamente richiesta un'alimentazione esterna per l'integrato (non disegnata nello schema). Il vantaggio è che funziona con qualunque tipo di porta senza modifiche. Il codice da eseguire, è il seguente: outportb(CONTROL, 0x01); // Seleziono il nibble basso dataLow = inportb(STATUS) & 0xF0; // Leggo il nibble basso dataLow = dataLow >> 4; // Allineo i quattro bit outportb(CONTROL, 0x00); // Seleziono il nibble alto dataHigh = inportb(STATUS) & 0xF0; // Leggo il nibble alto data = dataHigh | dataLow; // Unisco i due nibble data = data ^ 0x88; // Sistemo i due bit invertiti Ovviamente se la porta è bidirezionale oppure se è possibile usare in ingresso i quattro bit associati al registro di controllo vi sono minori problemi, pagati con una minore compatibilità. La soluzione più razionale nel caso in cui serva l'input di una quantità significativa di dati è però quella di adottare una porta EPP, sicuramente più standardizzata e veloce. Collegare una stampanteQuesto, non è un circuito applicativo, ma la schematizzazione dei segnali presenti sulla parallela durante il suo funzionamento più "naturale", cioè collegato ad una stampante secondo l'handshake centronics. Una premessa: le temporizzazioni mostrate sono solo indicative in quanto non ho trovato standard a cui riferirmi e le varie pubblicazioni riportano dettagli differenti; forse tale standard neppure esiste e quello mostrato è solo una consuetudine più o meno rispettata. Il controllo è completamente effettuato dal software, in genere dalla routine del BIOS o del sistema operativo invocata per la stampa. È ovviamente possibile scrivere da sé il software per la gestione, magari da usare su un microcontrollore. In rosso ho indicato i segnali che vanno dal PC alla stampante, in verde quelli che hanno percorso inverso. Non ho indicato i segnali non coinvolti nella comunicazione.
La linea Ack, proveniente dalla stampante in genere è ignorata dal PC (almeno in ambiente DOS e Windows). Alcuni documenti riportano la linea Ack ritardata rispetto al Busy (il fronte di discesa di Ack coincidente con quello di Busy) ma non mi risulta che ciò sia corretto. Risorse in reteUn altro tutorial sulla SPP che potrebbe esservi utile è quello di Craig Peacock, disponibile sul sito http://www.beyondlogic.org/, dove sono presenti anche documenti sui device driver in ambiente Windows e la gestione delle interruzioni in ambiente MS-DOS. Un'introduzione (in italiano) alla programmazione della alla LPT in ambiente Linux la potete trovare all'indirizzo http://linux.nuvoli.to.it/pj/pj9703/parallela.html Un software per il debug della LPT, utile per i primi esperimenti, lo trovate su http://www.vincenzov.net/. Analogamente nella sezione Tutorial ho inserito un tutorial sull'interfaccia parallela EPP sulle seriali RS232, e seriali RS485 e nella sezione Progetti diversi circuiti che utilizzano la porta parallela SPP, con esempi di programmazione in ambiente DOS e Windows. GNU Free Documentation LicenseLa licenza GNU FDL, disponibile sul sito http://www.gnu.org/ anche in versione italiana, è parte integrante di questo documento e ne contiene i termini di utilizzo. Questo tutorial è liberamente disponibile sul sito http://www.vincenzov.net/.
|
|
|
Lo staff di www.grix.it non si assume responsabilità sul contenuto di questa pagina. |
|
|
![]() |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
|
![]() |