Microchip ENC28J60 Ethernet Controller

Aus TippvomTibb
Zur Navigation springen Zur Suche springen

Allgemeines

Der Integrierte Schaltkreis ENC28J60 ist ein Stand-Alone Ethernet Controller mit SPI Interface. Diese Seite ist eine Kurzfassung mit Erläuterungen des Datenblattes und dient mir als Hilfe bei der Kontrolle der verschiedenen Implementierungen (uIPEnthernet, EthernetENC, FlowCode, ...).

Er bietet folgende Funktionsblöcke

  • Physical Layer Block (PHY) mit Anschluss zum Ethernet
  • Media Access Control Block (MAC)
  • TX/RX Data Handling Block (Filter, FlowControl, DMA, Checksum, Arbiter, ...)
  • 8192 Byte SRAM als FIFO organisiert
  • SPI zur Kommunikation mit dem uC

Zum Steuern der Blöcke gibt es 2 Registerblöcke

  • Control Registers in 4 Bänken organisiert. Jede Bank hat 32 (0x00 - 0x1F) Register, von denen allerdings 6 Common Register (0x1A-0x1F) in jeder Bank gleich sind.
  • PHY Registers 0x00 - 0x1F (32) von denen allerdings nur 9 benutzt werden

Control Register

Die Control Register bilden die Hauptschnittstelle bei der Kommunikation zwischen dem Mikrocontroller (o.ä.) und dem Ethernet Controller. Die Register in den 4 Bänken sind nach den Blöcken ETH (E), MAC (MA) und MII (MI) gruppiert, so dass man nicht ständig zwischen den Bänken hin und her schalten muss, wenn man einen Block 'bedient'. Jedes Register beginnt somit entweder mit einem E, MA oder MI.

Common Register

Dies haben eine zentrale Bedeutung, da sie 'immer' erreichbar sind und mit ihnen der Zugriff auf die anderen Register, die auf 4 Bänke aufgeteilt sind steuern.

Es sind:

  • Reserved (0x1A)
  • EIE (0x1B)
  • EIR (0x1C)
  • ESTAT ETHERNET CONTROL REGISTER 1 (0x1D)
  • ECON2 ETHERNET CONTROL REGISTER 2 (0x1E)
  • ECON1

Ethernet Buffer

Der gesamte Buffer 8192 Bytes ist in 2 Teile geteilt. In einen Bereich in den die ankommenden Daten eingeschrieben werden und in einen Teil, der die abgehenden Daten enthält. Die Größe der Bereiche ist programmierbar. Der Eingangspuffer ist als HardwareFIFO organisiert. Dieser FIFO-Bereich wird durch die Register ERXSTH:ERXSTL (Start) and ERXNDH:ERXNDL (End) bestimmt. Die Start- und End-Bytes gehören zum Pufferbereich. Zum Festlegen des FIFO-Puffers muss die Receive Logic (ECON1.RXEN 0) ausgeschaltet sein. Der Pointer (ERXWRPTH:ERXWRPTL readonly) zeigt auf die aktuelle Stelle an die gerade ein empfangenes Byte geschrieben wurde (sucessfully received). Über ihn kann man herausfinden wie viel Platz noch im Empfangspuffer ist. Der Pointer ERXRDPT stellt einen Art Deckel da. Neu ankommende Daten werden nur bis unterhalb dieses Pointer geschrieben und dann verworfen. Das ist i.d.R. nicht erwünscht. Da der Pointer von dem Hostcontroller geschrieben wird kann, das eigentlich nur bedeuten, dass die Daten schneller am PHY ankommen wie sie am SPI abgeholt werden. Der Bereich des Puufers , der nicht als Empfangspuffer definiert ist ist automatisch der Sendepuffer. Gefährlich ist weiterhin, dass die Sendepointer (ETXST, ETXND) nicht auf Kollission mit dem Empfangspuffer geprüft werden. Das Programm im Hostcontroller muss dafür Sorge tragen, dass die Sendepointer nicht im, oder zu nahe am Empfangspuffer liegen. Die LeseSchreibPointer (ERDPT and EWRPT) dien den SPI Kommandos zum Lesen und Schreiben des Puffers.

PHY Register

Die 32 Physical Layer Register sind 16 Bit breit. Es sind allerdings nur 9 davon belegt. Die Register MII (Media Independent Interface Managemant)sind NICHT direkt über das SPI Control Interface erreichbar. Man muss den Umweg über spezielle MAC Register gehen.

Reading

Während der MIIM operation (>10,24 us bis MISTAT.BUSY clear) dürfen keine MIISCAN Operationen durchgeführt werden und auch nicht in das MIWRH geschrieben werden. Ablauf:

  • Schreibe die Adresse des gewünschten PHY-Registers in das MIREGADR Register
  • Setze das MICMD.MIIRD bit -> es wird darauf hin das MISTAT.BUSY Bit gesetzt
  • Warte 10,24 us
  • Abfrageschleife bis das MISTAT.BUSY wieder gelöscht wird. Dann ist die Operation abgeschlossen.
  • Lösche das MICMD.MIIRD Bit
  • Lesen des gewünschten Inhalts aus den MIRDL und MIRDH Register

Writing

Man kann immer nur alle 16 Bit als Ganzes schreiben (MIIM Operation Dauer 10,24 us). Also erst lesen, Bit manipulieren und zurückschreiben. Während der MIIM operation (>10,24 us bis MISTAT.BUSY clear) dürfen keine MIISCAN Operationen durchgeführt werden und auch nicht in das MIWRH geschrieben werden.

  • Die gewünschte Adresse in das MIREGADR Register schreiben.
  • Die unteren 8 Bit in das MIWRL Register schreiben.
  • Die oberen 8 Bit in das MIWRH Register schreiben. Damit starten die Übertragung ins PHY Register automatisch. Das MISTAT.BUSY wird zu 1.
  • Sobald die MIIM Operation abgeschlossen ist wird das MISTAT.BUSY Bit gelöscht.

Scanning

Der MAC Block kann so konfiguriert werden, dass er automatische und unmittelbare (back-to-back) Lese-Operationen auf PHY Register ausführt. Dies ist z. B. sehr hilfreich wenn man regelmäßig/ständig Statusinformationen einliest. Während der MISCAN operation (>10,24 us bis MISTAT.BUSY clear) dürfen keine MIIRD Operationen durchgeführt werden und auch nicht in das MIWRH geschrieben werden.

Ablauf:

  • Schreibe das gewünschte PHY Register in das MIREGADR Register.
  • Setze das MICMD.MIISCAN Bit auf 1. Das MISAT.BUSY wird automatisch gesetzt.
  • Warte 10,24 us und abhängig von MSTAT.NVALID
  • Wiederhole den Lesevorgang auf MIRDL and MIRDH alle 10,24 us
  • Beende SCAN durch MICMD.MIISCAN Bit auf 0 und warte bis das MISTAT.BUSY Bit gelöscht ist.

SPI Instraction Set

Es gibt 7 Befehle mit deren Hilfe der Hostcontroller auf die Control Register und den Puffer zugreifen kann.

  • Control Register Read/Write (2)
  • Buffer Memory Read/Write (2)
  • Bit Field Set/Clear (2)
  • Soft Reset

Die Implementierung des SPI behandle ich an dieser Stelle nicht und setze voraus, dass die entsprechende Library wie gewünscht funktioniert.

Implementierung

In dem Teilprogramm "Enc28J60Network.cpp" der uIPEthernet(ENC) Library wird der CS (ChipSelect) Pin mit csPin bezeichnet und mit SS verbunden. Im Programm werden die Adressen der Register auf 8 Bit erweitert.

// ENC28J60 Control Registers
// Control register definitions are a combination of address,
// bank number, and Ethernet/MAC/PHY indicator bits.
// - Register address         (bits 0-4)
// - Bank number              (bits 5-6)
// - MAC/PHY indicator        (bit 7)
#define ADDR_MASK        0x1F
#define BANK_MASK        0x60
#define SPRD_MASK        0x80

Weiterhin werden in diesem Teilprogramm, besser als EnC28J60_Base bezeichnet, die Routinen:

  • init
  • receivePacket
  • sendPacket
  • readPacket
  • writePacket
  • copyPacket
  • freePacket -> setERXRDPT
  • readOp
  • writeOp
  • readBuffer
  • writeBuffer
  • setBank
  • readReg
  • writeReg
  • writeRegPair
  • phyWrite
  • phyRead
  • clkout
  • getrev
  • chksum
  • powerOff
  • powerOn
  • linkStatus
  • readByte
  • writeByte
  • setERXRDPT
  • setReadPtr
  • blockSize

LinkStatus

  • SPI Transaction
  • Rückgabewert: Bool
  • res = (phyRead(PHSTAT2) & 0x0400) > 0;

phyRead

Dient zum lesen ein PHY Registers. Siehe oben. Ablauf: uint16_t // Zurückgelifert wird ein 16 Bit Wert Enc28J60Network::phyRead(uint8_t address) {

 writeReg(MIREGADR,address); // Es wird der 5 Bit Adresswert in das MIREGADR (0x14 Bank 2) Register geschrieben
 writeReg(MICMD, MICMD_MIIRD); // Es MICMD.MIIRD auf 1 gesetzt MIIRD Media Independent Interface Read Bit im MICMD (0x12 Bank 2)
 // wait until the PHY read completes
 while(readReg(MISTAT) & MISTAT_BUSY){
   delayMicroseconds(15); // Hier wird immer wieder 15 us gewartet. So habe ich es nicht verstanden. Nicht optimal? delay VOR das while
 }  //and MIRDH // Diesen Kommentar kann ich nicht deuten
 writeReg(MICMD, 0);
 return (readReg(MIRDL) | readReg(MIRDH) << 8);

} Soft Reset writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);

readReg/writeReg

Die beiden Funktion lösen ein readOp/writeOp nachdem die passende Bank eingestellt ist. Die readReg liefert den 8 Bit Wert eines Control Registers. Es wird die 'passende' Bank (dazu dient die Bankmask Bit 5-6) gesetzt und ein readOp durchgeführt.


readOp/writeOP

Der SoftReset wäre somit eine LeseFunktion. Die Read-Funktion liefert einen 8 Bit Wert. Die passende Bank muss gesetzt sein. Der erste Parameter ist der SPI OP CODE und die Adresse. Im Falle Write kommt noch ein 8 Bit Wert als Data dazu. SPI.transfer ist ein gleichzeitiger Lese-/Schreibzugriff. uint8_t Enc28J60Network::readOp(uint8_t op, uint8_t address) {

 CSACTIVE;
 // issue read command
 SPI.transfer(op | (address & ADDR_MASK));  // https://www.arduino.cc/en/Reference/SPITransfer
 // read data
 if(address & 0x80)
   {
   // do dummy read if needed (for mac and mii, see datasheet page 29)
   SPI.transfer(0x00);
   }
 uint8_t c = SPI.transfer(0x00);
 CSPASSIVE;
 return c;

}

void Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) {

 CSACTIVE;
 // issue write command
 SPI.transfer(op | (address & ADDR_MASK));
 // write data
 SPI.transfer(data);
 CSPASSIVE;

}

Software Bibliotheken

EthernetENC

EthernetENC.h greift auf Ethernet.h zu , was wiederum eigentlich UIPEthernet.h ist.

UIPEthernet is a TCP/IP stack that can be used with a enc28j60 based Ethernet-shield.
UIPEthernet uses the fine uIP stack by Adam Dunkels <adam@sics.se>

Eigentlich sollte es besser ueberall uIP heiszen.

Die UIPEthernetClass wird mit Ethernet angesprochen und beinhaltet folgende Methoden:

init(CS Pin)

Es wird nur die private Variable csPin gesetzt.

begin (MAC, ....)

5 Varianten: 1 DHCP Variante und 4 Statische Varianten

Wenn nur die IP-Adresse (V4) uebergeben wird, werden dns, gateway zu 1 angenommen und subnet per default auf 255.255.255.0 gesetzt.

maintain()

EthernetLinkStatus linkStatus()

EthernetHardwareStatus hardwareStatus()

IPAddress localIP()

IPAddress subnetMask()

IPAddress gatewayIP()

IPAddress dnsServerIP()

Beispiel: https://github.com/UIPEthernet/UIPEthernet/blob/master/examples/EchoServer/EchoServer.ino

This Hello World example sets up a server at 192.168.1.6 on port 1000.
Telnet here to access the service.  The uIP stack will also respond to
pings to test if you have successfully established a TCP connection to
the Arduino.
This example was based upon uIP hello-world by Adam Dunkels <adam@sics.se>
Ported to the Arduino IDE by Adam Nielsen <malvineous@shikadi.net>
Adaption to Enc28J60 by Norbert Truchsess <norbert.truchsess@t-online.de>

Reduziert auf das Wesentliche ergibt sich folgende Befehlsabfolge: Setup EthernetServer server = EthernetServer(LISTENPORT); Ethernet.begin(mac,myIP,myDNS,myGW,myMASK); server.begin(); // start listening for clients Loop EthernetClient client = server.available() size = client.available() size = client.read(msg,size); client.write(msg,size);