Ethernet 4/7

Ethernet

Leggere il RevID

A questo punto abbiamo tutto il codice necessario per scambiare dati con il controller, possiamo quindi leggere il registro EREVID, situato nel banco 3, il quale contiene il numero di revisione del Chip.
Nell’esempio proposto, il valore del registro viene messo su PORTB, quindi, rispetto lo schema, il pin RB0 dovrebbe restare scollegato da INT, e TRISB settatto a zero.
Il alternativa, lasciando int collegato, si può shiftare PORTB a sinistra.

void main() {
	encInit();

	setBank(3);
	PORTB = readETH(EREVID);
	
	while (1);	
}

Il codice completo è allegato a questo articolo.

Trasmissione
L’ENC28J60, a livello MAC, si occupa di generare i campi Preamble, SFD, l’eventuale padding ed il FCS; tutto il resto deve essere inserito dal software nel buffer di trasmissione. Inoltre, il byte che si trova nella prima locazione di memoria di tale buffer, è un byte di controllo e non viene effettivamente inviato. Utilizzando zero come valore di questo byte, vengono usate, per la trasmissione, le opzioni già impostate in MACON3.
Abbiamo definito il buffer di trasmissione in modo tale che cominci all’indirizzo TX_BUF_START, quindi le operazioni da seguire
per la preparazione dell’intestazione MAC sono:

  • Si impostano i registri EWRPT in modo tale che puntino all’inizio del buffer TX.
  • Inviando il comando WBM si può iniziare ad scrivere i dati nel buffer (tramite spiWrite).
  • Si scrive il byte di controllo (ad esempio zero).
  • Seguono l’indirizzo MAC del destinatario, quello del mittente, infine il campo Type/Length.

La funzione che svolge questa operazione è MACPutHeader:

void MACPutHeader(MACAddr target, u16 type){
	u8 i;
	bufSize = sizeof(MAC_Header);		
	setBank(0);
	writeReg(EWRPTL, LOW(TX_BUF_START));
	writeReg(EWRPTH, HIGH(TX_BUF_START));
	CS = 0;
	spiWrite(WBM);
	spiWrite(0x00);	// usa MACON3

	for (i=0;i<6;i++)
		spiWrite(target.b[i]);
	spiWrite(MY_MAC1);
	spiWrite(MY_MAC2);
	spiWrite(MY_MAC3);
	spiWrite(MY_MAC4);
	spiWrite(MY_MAC5);
	spiWrite(MY_MAC6);

	spiWrite(HIGH(type));
	spiWrite(LOW(type));
	CS = 1;
}

Le strutture MAC_Header e MACAddr sono definite nel file MAC.h:

#define TYPE_ARP    0x0806
#define TYPE_IP     0x0800


typedef struct _MAC_Addr {
  u8    b[6];
} MACAddr;

typedef struct _MAC_Header {
  MACAddr       destMAC;
  MACAddr       sourceMAC;
  u16           type;
} MAC_Header;

Quindi l’intestazione MAC è pronta; ora si possono inviare al controller i dati del livello superiore.
Dopo quest’ultima operazione, il pacchetto è pronto per essere effettivamente inviato; a questo risultato si giunge con poche istruzioni:

  • Si attende che il controller sia pronto a trasmettere osservando il bit TXRTS (3) del registro ECON1.
  • I registri ETXND vengono caricati con l’indirizzo dell’ultimo byte da inviare. Questo indirizzo sarà TX_BUF_START + bufSize.
  • Settando il bit TXRTS inizia la trasmissione.

A causa di un problema descritto nell’Errata, se si verificano errori (bit EIR.TXERIF), è necessario resettare la logica di trasmissione, con il bit TXRST (7) di ECON1.

void MACSend(){
	setBank(0);
	if (readETH(EIR) & 0b10) {		// se si è verificato un errore
		BFSReg(ECON1, 0b10000000);	//
		BFCReg(ECON1, 0b10000000);	// resetta il TX
	}
	while(readETH(ECON1) & 0b1000);		// attende che sia pronto ad inviare
	writeReg(ETXNDL, LOW(TX_BUF_START + bufSize));
	writeReg(ETXNDH, HIGH(TX_BUF_START + bufSize));
	BFSReg(ECON1, 0b1000); 			// invia
}

Ricezione
Innanzi tutto, per verificare se è stato ricevuto un pacchetto, si controlla che il registro EPKTCNT sia diverso da zero; infatti questo registro mantiene il conto dei pacchetti ricevuti, e, una volta elaborato il pacchetto, va decrementato.
L’avvenuta ricezione può essere vista attraverso il bit EIR.RXIF, ma secondo l’Errata, il valore di questo bit non è affidabile.
I dati ricevuti vengono scritti dal controller nel buffer di ricezione a partire dall’indirizzo ERXWRPT, che in fase di inizializzazione è automaticamente settato a zero.
A tale indirizzo i primi sei byte non appartengono al pacchetto, ma sono byte di controllo: i primi due contengono l’indirizzo dove verrà scritto il prossimo pacchetto; gli altri non sono molto importanti (per approfondimenti, consultare il datasheet).
definiamo due nuove variabili, la prima delle quali va inizializzata a zero in encInit:

u16  RdPt;
u16  packetStart;

RdPt contiene l’indirizzo del prossimo pacchetto, mentre packetStart l’indirizzo del pacchetto corrente.
Detto ciò vediamo il metodo MACGetHeader:

void MACGetHeader(MAC_Header* header){
	u8 buf[6];

	packetStart = RdPt;					// salva RdPt in packetStart
	setBank(0);
	writeReg(ERDPTL, LOW(RdPt));				//
	writeReg(ERDPTH, HIGH(RdPt));				// ERDPT = RdPt
	encGetArray(&buf[0], 6);				// legge i 6 byte di controllo
	RdPt = (u16)((u16)buf[0] | ((u16)buf[1] << 8 )); 	// puntatore prossimo pacchetto

	encGetArray((u8*)header, sizeof(MAC_Header));		// legge l'intestazione MAC

	header->type = htons(header->type);			// swappa il campo type
}

Come si può vedere, come prima cosa si carica in ERDPT l’indirizzo del pacchetto da leggere (salvato in RdPt, inizialmente zero), poi vengono letti i byte di controllo e si salva il nuovo RdPt; infine viene letta l’intestazione MAC e salvata in header.

Leggi anche:
Ethernet 1/7

Ethernet 2/7

Ethernet 3/7

Leave a Reply