Home
Accesso / Registrazione
 di 

Ethernet 3/7

L'interfaccia SPI

L'interfaccia SPI
La comunicazione tra ENC28J60 e PIC avviene tramite l'interfaccia SPI; questa supporta soltanto la modalità 0,0 ed il controller è uno slave, quindi è il PIC che fornisce il clock e gestisce la trasmissione.
La massima frequenza ammessa è di 10Mhz per le Rev. B1-B4, mentre è doppia per la B5. Inoltre, a causa di un problema nell'interfaccia (descritto nell'Errata), per le Rev. B1-B4 il clock deve necessariamente essere tra 8 e 10Mhz, oppure il clock del PIC deve essere prelevato dal pin CLKOUT del controller (max 25Mhz). Nel primo caso, dunque, il PIC deve lavorare ad una frequenza tra 32 e 40Mhz.

Comandi SPI
Tramite l'interfaccia è possibile inviare al chip 7 diversi comandi (di 8 bit), eventualmente seguiti da un byte di dati; questi sono:

Nome 1° byte 2° byte Descrizione
Read Control Register (RCR) 000 AAAAA serve a leggere il registro di controllo A
Write Control Register (WCR) 010 AAAAA DDDDDDDD per scrivere il byte D all'interno del registro A (nel banco selezionato)
Read Buffer Memory (RBM) 00111010 serve a leggere la memoria RAM del controller all'indirzzo corrente.
Write Buffer Memory (WBM) 01111010 DDDDDDDD scrive il byte D all'indirizzo corrente della memoria RAM.
Bit Field Set (BFS) 100 AAAAA DDDDDDDD setta nel registro A (solo ETH), i bit che in D sono a 1 (OR).
Bit Field Set (BFS) 101 AAAAA DDDDDDDD resetta nel registro A (solo ETH), i bit che in D sono a 1 (NOT AND).
System Reset Command (SRC) 11111111 resetta il controller.

Comandi SPI : implementazione
L'inizializzazione del modulo SPI verrà illustrata in seguito, intanto vediamo alcuni metodi di base.

#define spiWrite(x)	spiRW(x)
#define spiRead()	spiRW(0)
 ....
u8 spiRW(u8 data){
	SSPBUF = data;
	while(!PIR1bits.SSPIF);
	PIR1bits.SSPIF = 0;
	return SSPBUF;
}

Questo è il metodo che ci permette di leggere/scrivere nel bus SPI. Per quanto riguarda la scrittura, il byte da inviare (data) viene messo nel registro SSPBUF (che fa parte del modulo SPI), poi si attende che la trasmissione finisca osservando il bit SSPIF; per leggere un byte, invece, è necessario scrivere un zero nel registro SSPBUF (così vengono generati 8 impulsi di clock), si attende il termine dell'operazione ed il byte letto si troverà nello stesso registro SSPBUF.
In questo caso ho condensato in un unico metodo le operazioni di lettura e scrittura, poi, per chiarezza mentale, ho definito spiWrite e spiRead.

#define CS	PORTCbits.RC2 // Chip Select dell'ENC28J60

#define	WCR	(0b01000000)		// Write Control Register command
#define BFS	(0b10000000)		// Bit Field Set command
#define	BFC	(0b10100000)		// Bit Field Clear command
#define	RCR	(0b00000000)		// Read Control Register command
#define RBM	(0b00111010)		// Read Buffer Memory command
#define	WBM	(0b01111010) 		// Write Buffer Memory command
#define	SRC	(0b11111111)		// System Reset command
 ....
void writeReg(u8 reg, u8 data){
	CS = 0;
	spiWrite(WCR | reg);
	spiWrite(data);
	CS = 1;
}

u8 readMAC(u8 reg){
	u8 b;
	CS = 0;
	spiWrite(RCR | reg);
	spiRead();
	b = spiRead();
	CS = 1;
	return b;
}

u8 readETH(u8 reg){
	u8 b;
  	CS = 0;
	spiWrite(RCR | reg);
	b = spiRead();
  	CS = 1;
  	return b;
}

Con questi tre metodi possiamo leggere e scrivere i registri di controllo (per leggere i registri MII si usa readMAC).
Come si può notare la procedura di lettura di un registro MAC ed ETH è lievemente diversa, in quanto per il primo tipo bisogna inviare un bite zero prima di effettuare la lettura.

void BFCReg(u8 reg, u8 data){
  	CS = 0;
	spiWrite(BFC | reg);
	spiWrite(data);
  	CS = 1;
}

void BFSReg(u8 reg, u8 data){
  	CS = 0;
	spiWrite(BFS | reg);
	spiWrite(data);
  	CS = 1;
}

void setBank(u8 bank){
  	BFCReg(ECON1, 0b11);
  	BFSReg(ECON1, bank);
}

Qui vediamo l'implementazione dei comandi Bit Set e Bit Clear; poi il metodo setBank, con il quale possiamo selezionare il banco di memoria dei registri di controllo, agendo sul registro ECON1 (comune a tutti i banchi).

u16  bufSize;
  ....
void encPut(u8 b){
	CS = 0;
	spiWrite(WBM);
	spiWrite(b);
	CS = 1;	
	bufSize++;
}

u8 encGet(){
	u8 b;
	CS = 0;
	spiWrite(RBM);
	b = spiRead();
	CS = 1;	
	return b;
}

void sendReset(){
	CS = 0;
	spiWrite(SRC);
	CS = 1;
}

I rimanenti tre comandi sono eseguiti da questi tre metodi: lettura e scrittura della RAM, e reset. La variaible statica bufSize serve
a tener traccia del numero di byte scritti nella RAM (servirà poi).

I registri PHY
I registri PHY sono a 16bit e sono accessibili per mezzo dei registri MII.
Per leggere un registro PHY:

  • si mette l'indirizzo nel registro MIREGADR;
  • settando il bit MIIRD (1) del registro MICMD, inizia la lettura;
  • si attende il termine della lettura osservando il bit BUSY (1) del registro MISTAT;
  • si resetta il bit MIIRD;
  • il dato sarà presente nei registri MIRDL e MIRDH;

Per scrivere in un registro PHY:

  • si pone l'indirizzo del registro in MIREGADR;
  • prima si scrive il byte meno significativo in MIWRL, poi scrivendo il byte più significativo in MIWRH inizia la scrittura.
  • si attende che il modulo PHY termini l'operazione.
u16 readPHY(u8 reg){
	setBank(2);
	writeReg(MIREGADR, reg);
	writeReg(MICMD, 0x01);	

	setBank(3);
	while(readMAC(MISTAT) & 1);

	setBank(2);
	writeReg(MICMD, 0x00);	
	
	return readMAC(MIRDL) | (readMAC(MIRDH) << 8 );
}

void writePHY(u8 reg, u16 data){
	setBank(2);
	writeReg(MIREGADR, reg);
	writeReg(MIWRL, LOW(data));
	writeReg(MIWRH, HIGH(data));

	setBank(3);
	while (readMAC(MISTAT) & 1);
}

Altri metodi
I seguenti servono a scrivere/leggere più byte dalla RAM del controller; l'indirizzo da cui vengono letti è contenuto nei registri ERDPT, mentre l'indirizzo di scrittura è contenuto nei registri EWRPT (sono autoincrementanti).

void encGetArray(u8* buf, u16 len){
	CS = 0;
	spiWrite(RBM);
	while(len--)
		*buf++ = spiRead();
	CS = 1;
}

void encPutArray(u8* buf,u16 len){
	bufSize += len;
	CS = 0;
	spiWrite(WBM);
	while(len--)
		spiWrite(*buf++);
	CS = 1;
}	

void encPutString(const rom u8 *str){
	CS = 0;
	spiWrite(WBM);
	while(*str) {
    		spiWrite(*str++);
		bufSize++;
	}
	CS = 1;
}

Inizializzazione SPI
L'inizializzazione del modulo SPI avviene con queste due semplici istruzioni:

 void encInit(){
  	TRISB = 0xFF;		// configurazione I/O di PORTB
  	TRISC = 0xD1;		// configurazione I/O di PORTC
  	PORTC = 0x00;
	
	SSPSTAT = 0x40;	
	SSPCON1 = 0x20;

In particolare il modulo MSSP viene abilitato e configurato in modalità "0,0", con clock pari a Fosc/4.

Inizializzazione ENC28J60
L'inizializzazione del controller prevede la configurazione di diversi registri, nonché l'abilitazione alla ricezione.

#define RX_BUF_START	0
#define RX_BUF_END	6499	
#define	TX_BUF_START	6500
 ....
setBank(0);
writeReg(ERXSTL,   LOW(RX_BUF_START));		//
writeReg(ERXSTH,   HIGH(RX_BUF_START));		// inizio buffer di lettura
writeReg(ERXRDPTL, LOW(RX_BUF_END));		//
writeReg(ERXRDPTH, HIGH(RX_BUF_END));		// puntatore del buffer di lettura
writeReg(ERXNDL,   LOW(RX_BUF_END));		//
writeReg(ERXNDH,   HIGH(RX_BUF_END));		// fine buffer di lettura
writeReg(ETXSTL,   LOW(TX_BUF_START));		//
writeReg(ETXSTH,   HIGH(TX_BUF_START));		// inizio buffer di scrittura

Come già detto in precedenza, il buffer dell'ENC28J60 può essere diviso a piacere tra memoria di trasmissione e di ricezione.
Per fare cio si configurano i puntatori del buffer di ricezione; la memoria rimanente sarà il buffer di trasmissione.
I registri ERXST contengono l'indirizzo del primo byte del buffer di ricezione, mentre i registri ERXND l'ultimo byte.
In ERXRDPT, invece, risiede il puntatore di lettura della memoria RX, ovvero contrassegna una zona (insieme a ERXWRPT) che deve ancora essere elaborata dal PIC e quindi non può essere scritta; inizialmente il valore di questo indirizzo è uguale a ERXND (deve essere dispari secondo un problema descritto nell'Errata). Il registro ERXWRPT vale zero al reset ed è aggiornato automaticamente alla ricezione di un pacchetto.

setBank(2);
writeReg(MACON1, 0b01101);  		// MARXEN,  TXPAUS,  RXPAUS
writeReg(MACON3, 0b00110000); 		// Half Duplex, Padding 60byte, CRC
writeReg(MAIPGL, 0x12); 		//
writeReg(MAIPGH, 0x0C); 		//
writeReg(MABBIPG,0x12); 		// Inter-Packet Gap

Questi registri configurano il modulo MAC. Attraverso il registro MACON1 si abilitano il modulo MAC e la ricezione/trasmissione di trame di pausa.
Con il registro MACON3 si sceglie la modalità Duplex (Half o Full) del modulo MAC che deve essere impostata nello stesso modo anche nel modulo PHY; inoltre in questo registro sono presenti alcune configurazioni sul Padding automatico ed il calcolo del CRC.
I registri MAIPG e MABBIPG contengono i valori delle pause tra pacchetti; quelli presenti nel codice sono i valori standard.

writeReg(MAMXFLL, LOW(1500));
writeReg(MAMXFLH, HIGH(1500));

Nei registri MAXFL (Max Frame Length) viene salvata la massima dimensione consentita per un pacchetto; il controller può essere configurato in modo tale che si rifiuti di inviare un pacchetto che supera questo limite.

#define MY_MAC1     0x00
#define MY_MAC2     0x04
#define MY_MAC3     0xA3
#define MY_MAC4     0x00
#define MY_MAC5     0x00
#define MY_MAC6     0x00
  ....
setBank(3);
writeReg(MAADR1, MY_MAC1);
writeReg(MAADR2, MY_MAC2);
writeReg(MAADR3, MY_MAC3);
writeReg(MAADR4, MY_MAC4);
writeReg(MAADR5, MY_MAC5);
writeReg(MAADR6, MY_MAC6);

L'indirizzo MAC del nostro dispositivo viene salvato nei registri MACADR; quest'ultimi vengono solamente utilizzati dal filtro per respingere pacchetti non destinati al controller, quindi l'indirizzo non viene inserito automaticamente nei pacchetti da inviare.

writePHY(PHCON2, 0b0000000100000000);	// disabilita il loopback
writePHY(PHCON1, 0);			// abilita il PHY

setBank(1);
writeReg(ERXFCON, 0b10100001); 		// imposta i filtri di ricezione
BFSReg(ECON1, 0b100);         		// abilita la ricezione

Con quest'ultimo spezzone termina l'inizializzazione del controller. Il modulo PHY non ha molte opzioni da configurare, le uniche operazioni che vengono effettuate sono la disabilitazione del LoopBack (usato per fare test) e l'abilitazione del modulo.
I filtri sono settati in modo tale da accettare solo pacchetti destinati all'indirizzo MAC configurato e pacchetti di broadcast.

Leggi anche:
Ethernet
Ethernet 2/7
Ethernet 4/7

 

 

Scrivi un commento all'articolo esprimendo la tua opinione sul tema, chiedendo eventuali spiegazioni e/o approfondimenti e contribuendo allo sviluppo dell'argomento proposto. Verranno accettati solo commenti a tema con l'argomento dell'articolo stesso. Commenti NON a tema dovranno essere necessariamente inseriti nel Forum creando un "nuovo argomento di discussione". Per commentare devi accedere al Blog

 

 

Login   
 Twitter Facebook LinkedIn Youtube Google RSS

Chi è online

Ci sono attualmente 6 utenti e 86 visitatori collegati.

Ultimi Commenti