Enables fast realtime communication over Ethernet by using plain Ethernet frames (overhead of TCP/IP network layers is stripped away).

Dependents:   RawEthernet_Hello

Revision:
0:ca476e0a1a28
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RawEthernet.cpp	Mon Aug 20 11:37:35 2018 +0000
@@ -0,0 +1,884 @@
+//------------------------------------------------------------------------------
+// Functions for using an ENC28J60 ethernet controller, optimized for 
+// communication speed.
+//
+// Written by Rogier Schouten http://www.rogiershikes.tk
+// Based on code by Guido Socher http://www.tuxgraphics.org
+// Idea modified and further updated by Guido Socher
+// Ported to MBED by Zoltan Hudak hudakz@outlook.com
+// License: GPL V2
+//
+// Enables to read sensor data or control IO-ports with less than a millisecond delay. 
+// The trick is in removing the TCP/IP overhead and use raw Ethernet frames. 
+// A Linux application using raw network sockets is controlling this slave device.
+//
+// Assumptions:
+// - Max. payload data: 255 bytes per packet
+// - The network consists of a master PC and one or more slave devices. 
+//   Only plain hubs and switches are allowed between PC and ethernet device.
+//   Note that some wlan routers have internal switches which switch only
+//   IP packets. They will discard plain ethernet frames. A normal 100Mbit 
+//   office/workgroup switch will however work.
+// 
+// Based on these assumptions, we can optimize the protocol for faster communication:
+// - Master and slave send unicast packets.
+// - We use raw ethernet and no higher-level protocol such as UDP or TCP/IP
+// - We use the EtherType field of a packet as a length field. Actually, we only
+//   use one byte of it since we have max 255 length packets.
+//  
+// Furthermore, there are a few code optimizations:
+// - Minimize communication between ENC and MCU.
+// - No unnecessary memory bank checks
+// 
+#include "RawEthernet.h"
+
+// 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
+// All-bank registers
+#define EIE     0x1B
+#define EIR     0x1C
+#define ESTAT   0x1D
+#define ECON2   0x1E
+#define ECON1   0x1F
+// Bank 0 registers
+#define ERDPTL      (0x00 | 0x00)
+#define ERDPTH      (0x01 | 0x00)
+#define EWRPTL      (0x02 | 0x00)
+#define EWRPTH      (0x03 | 0x00)
+#define ETXSTL      (0x04 | 0x00)
+#define ETXSTH      (0x05 | 0x00)
+#define ETXNDL      (0x06 | 0x00)
+#define ETXNDH      (0x07 | 0x00)
+#define ERXSTL      (0x08 | 0x00)
+#define ERXSTH      (0x09 | 0x00)
+#define ERXNDL      (0x0A | 0x00)
+#define ERXNDH      (0x0B | 0x00)
+#define ERXRDPTL    (0x0C | 0x00)
+#define ERXRDPTH    (0x0D | 0x00)
+#define ERXWRPTL    (0x0E | 0x00)
+#define ERXWRPTH    (0x0F | 0x00)
+#define EDMASTL     (0x10 | 0x00)
+#define EDMASTH     (0x11 | 0x00)
+#define EDMANDL     (0x12 | 0x00)
+#define EDMANDH     (0x13 | 0x00)
+#define EDMADSTL    (0x14 | 0x00)
+#define EDMADSTH    (0x15 | 0x00)
+#define EDMACSL     (0x16 | 0x00)
+#define EDMACSH     (0x17 | 0x00)
+// Bank 1 registers
+#define EHT0    (0x00 | 0x20)
+#define EHT1    (0x01 | 0x20)
+#define EHT2    (0x02 | 0x20)
+#define EHT3    (0x03 | 0x20)
+#define EHT4    (0x04 | 0x20)
+#define EHT5    (0x05 | 0x20)
+#define EHT6    (0x06 | 0x20)
+#define EHT7    (0x07 | 0x20)
+#define EPMM0   (0x08 | 0x20)
+#define EPMM1   (0x09 | 0x20)
+#define EPMM2   (0x0A | 0x20)
+#define EPMM3   (0x0B | 0x20)
+#define EPMM4   (0x0C | 0x20)
+#define EPMM5   (0x0D | 0x20)
+#define EPMM6   (0x0E | 0x20)
+#define EPMM7   (0x0F | 0x20)
+#define EPMCSL  (0x10 | 0x20)
+#define EPMCSH  (0x11 | 0x20)
+#define EPMOL   (0x14 | 0x20)
+#define EPMOH   (0x15 | 0x20)
+#define EWOLIE  (0x16 | 0x20)
+#define EWOLIR  (0x17 | 0x20)
+#define ERXFCON (0x18 | 0x20)
+#define EPKTCNT (0x19 | 0x20)
+// Bank 2 registers
+#define MACON1      (0x00 | 0x40 | 0x80)
+#define MACON2      (0x01 | 0x40 | 0x80)
+#define MACON3      (0x02 | 0x40 | 0x80)
+#define MACON4      (0x03 | 0x40 | 0x80)
+#define MABBIPG     (0x04 | 0x40 | 0x80)
+#define MAIPGL      (0x06 | 0x40 | 0x80)
+#define MAIPGH      (0x07 | 0x40 | 0x80)
+#define MACLCON1    (0x08 | 0x40 | 0x80)
+#define MACLCON2    (0x09 | 0x40 | 0x80)
+#define MAMXFLL     (0x0A | 0x40 | 0x80)
+#define MAMXFLH     (0x0B | 0x40 | 0x80)
+#define MAPHSUP     (0x0D | 0x40 | 0x80)
+#define MICON       (0x11 | 0x40 | 0x80)
+#define MICMD       (0x12 | 0x40 | 0x80)
+#define MIREGADR    (0x14 | 0x40 | 0x80)
+#define MIWRL       (0x16 | 0x40 | 0x80)
+#define MIWRH       (0x17 | 0x40 | 0x80)
+#define MIRDL       (0x18 | 0x40 | 0x80)
+#define MIRDH       (0x19 | 0x40 | 0x80)
+// Bank 3 registers
+#define MAADR1  (0x00 | 0x60 | 0x80)
+#define MAADR0  (0x01 | 0x60 | 0x80)
+#define MAADR3  (0x02 | 0x60 | 0x80)
+#define MAADR2  (0x03 | 0x60 | 0x80)
+#define MAADR5  (0x04 | 0x60 | 0x80)
+#define MAADR4  (0x05 | 0x60 | 0x80)
+#define EBSTSD  (0x06 | 0x60)
+#define EBSTCON (0x07 | 0x60)
+#define EBSTCSL (0x08 | 0x60)
+#define EBSTCSH (0x09 | 0x60)
+#define MISTAT  (0x0A | 0x60 | 0x80)
+#define EREVID  (0x12 | 0x60)
+#define ECOCON  (0x15 | 0x60)
+#define EFLOCON (0x17 | 0x60)
+#define EPAUSL  (0x18 | 0x60)
+#define EPAUSH  (0x19 | 0x60)
+// PHY registers
+#define PHCON1  0x00
+#define PHSTAT1 0x01
+#define PHHID1  0x02
+#define PHHID2  0x03
+#define PHCON2  0x10
+#define PHSTAT2 0x11
+#define PHIE    0x12
+#define PHIR    0x13
+#define PHLCON  0x14
+// ENC28J60 ERXFCON Register Bit Definitions
+#define ERXFCON_UCEN    0x80
+#define ERXFCON_ANDOR   0x40
+#define ERXFCON_CRCEN   0x20
+#define ERXFCON_PMEN    0x10
+#define ERXFCON_MPEN    0x08
+#define ERXFCON_HTEN    0x04
+#define ERXFCON_MCEN    0x02
+#define ERXFCON_BCEN    0x01
+// ENC28J60 EIE Register Bit Definitions
+#define EIE_INTIE   0x80
+#define EIE_PKTIE   0x40
+#define EIE_DMAIE   0x20
+#define EIE_LINKIE  0x10
+#define EIE_TXIE    0x08
+#define EIE_WOLIE   0x04
+#define EIE_TXERIE  0x02
+#define EIE_RXERIE  0x01
+// ENC28J60 EIR Register Bit Definitions
+#define EIR_PKTIF   0x40
+#define EIR_DMAIF   0x20
+#define EIR_LINKIF  0x10
+#define EIR_TXIF    0x08
+#define EIR_WOLIF   0x04
+#define EIR_TXERIF  0x02
+#define EIR_RXERIF  0x01
+// ENC28J60 ESTAT Register Bit Definitions
+#define ESTAT_INT       0x80
+#define ESTAT_LATECOL   0x10
+#define ESTAT_RXBUSY    0x04
+#define ESTAT_TXABRT    0x02
+#define ESTAT_CLKRDY    0x01
+// ENC28J60 ECON2 Register Bit Definitions
+#define ECON2_AUTOINC   0x80
+#define ECON2_PKTDEC    0x40
+#define ECON2_PWRSV     0x20
+#define ECON2_VRPS      0x08
+// ENC28J60 ECON1 Register Bit Definitions
+#define ECON1_TXRST     0x80
+#define ECON1_RXRST     0x40
+#define ECON1_DMAST     0x20
+#define ECON1_CSUMEN    0x10
+#define ECON1_TXRTS     0x08
+#define ECON1_RXEN      0x04
+#define ECON1_BSEL1     0x02
+#define ECON1_BSEL0     0x01
+// ENC28J60 MACON1 Register Bit Definitions
+#define MACON1_LOOPBK   0x10
+#define MACON1_TXPAUS   0x08
+#define MACON1_RXPAUS   0x04
+#define MACON1_PASSALL  0x02
+#define MACON1_MARXEN   0x01
+// ENC28J60 MACON2 Register Bit Definitions
+#define MACON2_MARST    0x80
+#define MACON2_RNDRST   0x40
+#define MACON2_MARXRST  0x08
+#define MACON2_RFUNRST  0x04
+#define MACON2_MATXRST  0x02
+#define MACON2_TFUNRST  0x01
+// ENC28J60 MACON3 Register Bit Definitions
+#define MACON3_PADCFG2  0x80
+#define MACON3_PADCFG1  0x40
+#define MACON3_PADCFG0  0x20
+#define MACON3_TXCRCEN  0x10
+#define MACON3_PHDRLEN  0x08
+#define MACON3_HFRMLEN  0x04
+#define MACON3_FRMLNEN  0x02
+#define MACON3_FULDPX   0x01
+// ENC28J60 MACON4 Register Bit Definitions
+#define MACON4_DEFER    0x40
+#define MACON4_BPEN     0x20
+#define MACON4_NOBKOFF  0x10
+// ENC28J60 MICMD Register Bit Definitions
+#define MICMD_MIISCAN   0x02
+#define MICMD_MIIRD     0x01
+// ENC28J60 MISTAT Register Bit Definitions
+#define MISTAT_NVALID   0x04
+#define MISTAT_SCAN     0x02
+#define MISTAT_BUSY     0x01
+// ENC28J60 PHY PHCON1 Register Bit Definitions
+#define PHCON1_PRST     0x8000
+#define PHCON1_PLOOPBK  0x4000
+#define PHCON1_PPWRSV   0x0800
+#define PHCON1_PDPXMD   0x0100
+// ENC28J60 PHY PHSTAT1 Register Bit Definitions
+#define PHSTAT1_PFDPX   0x1000
+#define PHSTAT1_PHDPX   0x0800
+#define PHSTAT1_LLSTAT  0x0004
+#define PHSTAT1_JBSTAT  0x0002
+// ENC28J60 PHY PHSTAT2H Register Bit Definitions
+#define PHSTAT2H_LSTAT  0x04
+// ENC28J60 PHY PHCON2 Register Bit Definitions
+#define PHCON2_FRCLINK  0x4000
+#define PHCON2_TXDIS    0x2000
+#define PHCON2_JABBER   0x0400
+#define PHCON2_HDLDIS   0x0100
+// ENC28J60 Packet Control Byte Bit Definitions
+#define PKTCTRL_PHUGEEN     0x08
+#define PKTCTRL_PPADEN      0x04
+#define PKTCTRL_PCRCEN      0x02
+#define PKTCTRL_POVERRIDE   0x01
+// SPI operation codes
+#define ENC28J60_READ_CTRL_REG  0x00
+#define ENC28J60_READ_BUF_MEM   0x3A
+#define ENC28J60_WRITE_CTRL_REG 0x40
+#define ENC28J60_WRITE_BUF_MEM  0x7A
+#define ENC28J60_BIT_FIELD_SET  0x80
+#define ENC28J60_BIT_FIELD_CLR  0xA0
+#define ENC28J60_SOFT_RESET     0xFF
+// Buffer memory allocation within controlller.
+// Assuming the controller is slower than the network, we allocate one transmit
+// packet and leave the rest for receiving.
+// The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata
+#define RXSTART_INIT    0x0
+#define RXSTOP_INIT     (0x1FFF - 0x0600 - 2)   // note: MUST be odd (see errata point 13)
+#define TXSTART_INIT    (0x1FFF - 0x0600)
+// Maximum packet length: this software has a limit of 255 payload data bytes
+// and then there is some ethernet overhead (srd, dst, len, fcs)
+#define ENC28J60_MAX_PACKET_LEN ((uint16_t) 273)
+// field or packet lengths
+#define ETH_HEADER_LEN      14
+#define ETH_CHECKSUM_LEN    4
+#define ETH_ENVELOPE_LEN    (ETH_HEADER_LEN + ETH_CHECKSUM_LEN)
+#define ETH_PAYLOAD_MIN     46
+#define ETH_PAYLOAD_MAX     1500
+#define ETH_PACKET_MIN      64
+#define ETH_PACKET_MAX      1518
+// field locations in ethernet packet
+#define ETH_DST_MAC_P   0
+#define ETH_SRC_MAC_P   6
+#define ETH_TYPE_H_P    12
+#define ETH_TYPE_L_P    13
+#define ENC28J60_HAS_PENDING_TRANSMIT_ON_TRANSMIT
+
+/* Static member initialization */
+char RawEthernet::_arpReqHdr[10] = { 8, 6, 0, 1, 8, 0, 6, 4, 0, 1 };
+
+/**
+  * @brief   Constructor
+  * @note
+  * @param   mosi SPI master-out slave-in pin #
+  * @param   miso SPI master-in slave-out pin #
+  * @param   sclk SPI serial clock pin #
+  * @param   cs SPI chip select pin #
+  * @retval
+  */
+RawEthernet::RawEthernet(PinName mosi, PinName miso, PinName sclk, PinName cs, uint8_t myMac[6], uint8_t myIp[4]) :
+    _spi(mosi, miso, sclk),
+    _cs(cs)
+{
+    int i;
+
+    for (i = 0; i < 6; i++)
+        _mac[i] = myMac[i];
+
+    for (i = 0; i < 4; i++)
+        _ip[i] = myIp[i];
+}
+
+/**
+  * @brief   Read operation
+  * @note    Reads from ENC28J60 register
+  * @param   op operation code
+  * @param   address register address
+  * @retval  Register value
+  */
+uint8_t RawEthernet::readOp(uint8_t op, uint8_t address)
+{
+    uint8_t result;
+
+    _cs = 0;
+
+    // issue read command
+    _spi.write(op | (address & ADDR_MASK));
+
+    // read data
+    result = _spi.write(0x00);
+
+    // do dummy read if needed (for mac and mii, see datasheet page 29)
+    if (address & 0x80)
+        result = _spi.write(0x00);
+
+    // release CS
+    _cs = 1;
+    return(result);
+}
+
+/**
+ * @brief   Write operation
+ * @note    Writes to ENC28J60 register
+ * @param   op operation code
+ * @param   address register address
+ * @retval  data data to be written
+ */
+void RawEthernet::writeOp(uint8_t op, uint8_t address, uint8_t data)
+{
+    _cs = 0;
+
+    // issue write command
+    _spi.write(op | (address & ADDR_MASK));
+
+    // write data
+    _spi.write(data);
+    _cs = 1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void RawEthernet::setBank(uint8_t address)
+{
+    // set the bank (if needed)
+    if ((address & BANK_MASK) != _bank) {
+        // set the bank
+        writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1 | ECON1_BSEL0));
+        writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK) >> 5);
+        _bank = (address & BANK_MASK);
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint8_t RawEthernet::readReg(uint8_t address)
+{
+    setBank(address);
+    readOp(ENC28J60_READ_CTRL_REG, address);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void RawEthernet::writeReg(uint8_t address, uint8_t data)
+{
+    setBank(address);
+    writeOp(ENC28J60_WRITE_CTRL_REG, address, data);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void RawEthernet::readBuffer(uint16_t len, uint8_t* data)
+{
+    _cs = 0;
+
+    // issue read command
+    _spi.write(ENC28J60_READ_BUF_MEM);
+    while (len) {
+        len--;
+
+        // read data
+        *data = _spi.write(0x00);
+        data++;
+    }
+
+    //*data = '\0';
+    _cs = 1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void RawEthernet::writeBuffer(uint16_t len, uint8_t* data)
+{
+    _cs = 0;
+
+    // issue write command
+    _spi.write(ENC28J60_WRITE_BUF_MEM);
+    while (len) {
+        len--;
+
+        // write data
+        _spi.write(*data);
+        data++;
+    }
+
+    _cs = 1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t RawEthernet::phyReadH(uint8_t address)
+{
+    // set the right address and start the register read operation
+    writeReg(MIREGADR, address);
+    writeReg(MICMD, MICMD_MIIRD);
+    wait_us(15);
+
+    // wait until the PHY read completes
+    while (readReg(MISTAT) & MISTAT_BUSY);
+
+    // reset reading bit
+    setBank(2);
+    writeReg(MICMD, 0x00);
+
+    return(readReg(MIRDH));
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void RawEthernet::phyWrite(uint8_t address, uint16_t data)
+{
+    setBank(2);
+
+    // set the PHY register address
+    writeReg(MIREGADR, address);
+
+    // write the PHY data
+    writeReg(MIWRL, data);
+    writeReg(MIWRH, data >> 8);
+
+    // wait until the PHY write completes
+    setBank(3);
+    while (readReg(MISTAT) & MISTAT_BUSY) {
+        wait_us(15);
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void RawEthernet::linkTo(uint8_t remoteMac[6])
+{
+    for (int i = 0; i < 6; i++)
+        _remoteMac[i] = remoteMac[i];
+
+    _spi.format(8, 0);          // 8bit, mode 0
+    _spi.frequency(7000000);    // 7MHz
+    wait(1);                    // 1 second for stable state
+    // initialize I/O
+    _cs = 1;
+    // perform system reset
+    writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
+    wait_ms(50);
+    // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait.
+    //while(!(read(ESTAT) & ESTAT_CLKRDY));
+    // do bank 0 stuff
+    // initialize receive buffer
+    // 16-bit transfers, must write low byte first
+    // set receive buffer start address
+    _nextPacketPtr = RXSTART_INIT;
+
+    // Rx start
+    writeReg(ERXSTL, RXSTART_INIT & 0xFF);
+    writeReg(ERXSTH, RXSTART_INIT >> 8);
+
+    // set receive pointer address
+    writeReg(ERXRDPTL, RXSTART_INIT & 0xFF);
+    writeReg(ERXRDPTH, RXSTART_INIT >> 8);
+
+    // RX end
+    writeReg(ERXNDL, RXSTOP_INIT & 0xFF);
+    writeReg(ERXNDH, RXSTOP_INIT >> 8);
+
+    // TX start
+    writeReg(ETXSTL, TXSTART_INIT & 0xFF);
+    writeReg(ETXSTH, TXSTART_INIT >> 8);
+
+    // TX end (initialize for a packet with a payload of 1 byte)
+    uint16_t    address = (TXSTART_INIT + ETH_HEADER_LEN + 1);
+    writeReg(ETXNDL, address & 0xFF);
+    writeReg(ETXNDH, address >> 8);
+
+    // prepare the parts of the transmit packet that never change
+    // write per-packet control byte (0x00 means use macon3 settings)
+    writeReg(EWRPTL, (TXSTART_INIT) & 0xFF);
+    writeReg(EWRPTH, (TXSTART_INIT) >> 8);
+    writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
+
+    // write broadcast address as DST MAC
+    uint8_t i = 0;
+    while (i < 6) {
+        writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0xFF);
+        i++;
+    }
+
+    // set our MAC address as the SRC MAC into the transmit buffer
+    // set the write pointer to start of transmit buffer area
+    writeBuffer(6, const_cast<uint8_t*>(_mac));
+    // First EtherType/length byte is always 0, initialize second byte to 1
+    writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
+    writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x01);
+
+    // do bank 1 stuff, packet filter:
+    setBank(1);
+    // only allow unicast packets destined for us and that have a correct CRC
+    writeReg(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN);
+
+    // do bank 2 stuff
+    setBank(2);
+    // enable MAC receive, disable flow control (only needed in full-duplex)
+    writeReg(MACON1, MACON1_MARXEN);
+    // bring MAC out of reset
+    writeReg(MACON2, 0x00);
+    // enable automatic padding to 60bytes and CRC operations
+    // also, force half-duplex operation
+    writeReg(MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN);
+    // half-duplex only: back-off settings
+    writeReg(MACON4, MACON4_DEFER | MACON4_BPEN | MACON4_NOBKOFF);
+    // set the maximum packet size which the controller will accept
+    // do not send packets longer than MAX_FRAMELEN:
+    writeReg(MAMXFLL, ENC28J60_MAX_PACKET_LEN & 0xFF);
+    writeReg(MAMXFLH, ENC28J60_MAX_PACKET_LEN >> 8);
+    // set inter-frame gap (non-back-to-back)
+    writeReg(MAIPGL, 0x12);
+    writeReg(MAIPGH, 0x0C);
+    // set inter-frame gap (back-to-back)
+    writeReg(MABBIPG, 0x12);
+
+    // do bank 3 stuff
+    // write MAC address
+    // NOTE: MAC address in ENC28J60 is byte-backward
+    writeReg(MAADR5, _mac[0]);
+    writeReg(MAADR4, _mac[1]);
+    writeReg(MAADR3, _mac[2]);
+    writeReg(MAADR2, _mac[3]);
+    writeReg(MAADR1, _mac[4]);
+    writeReg(MAADR0, _mac[5]);
+
+    // no loopback of transmitted frames
+    phyWrite(PHCON2, PHCON2_HDLDIS);
+
+    // switch to bank 0
+    setBank(ECON1);
+
+    // enable interrutps
+    writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE | EIE_PKTIE);
+
+    // enable packet reception
+    writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
+
+    // change clkout from 6.25MHz to 12.5MHz
+    writeReg(ECOCON, 2 & 0x7);
+    wait_us(60);
+
+    /* magjack leds configuration, see enc28j60 datasheet, page 11 */
+    // LEDA=green,        LEDB=yellow,
+    // LEDA=links status, LEDB=receive/transmit
+    // phyWrite(PHLCON,0b0000 0100 0111 0110);
+    phyWrite(PHLCON, 0x476);
+
+    // wait until the link is up, then send a gratuitous ARP request
+    // to inform any conected switch about my existence
+    while (!isLinkUp());
+    wait_ms(50);
+    // send gratuitous ARP request
+    gratuitousArp();
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+bool RawEthernet::isLinkUp(void)
+{
+    return(phyReadH(PHSTAT2) & PHSTAT2H_LSTAT);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint8_t RawEthernet::receive(uint8_t* buf, uint8_t maxlen)
+{
+    uint16_t    len;
+    uint16_t    currentPacketPtr = _nextPacketPtr;
+    uint16_t    address;
+    uint16_t    framelen;
+
+    // check if a packet has been received and buffered
+    setBank(1);
+    if (readReg(EPKTCNT) == 0)
+        return(0);
+
+    setBank(0);
+
+    // Somehow, the read pointer is NOT already at the start of the next packet
+    // even though we leave it in that state
+    writeReg(ERDPTL, (_nextPacketPtr & 0xff));
+    writeReg(ERDPTH, (_nextPacketPtr) >> 8);
+    // Read the next packet pointer 
+    _nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0);
+    _nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8;
+    // read the frame length
+    framelen = readOp(ENC28J60_READ_BUF_MEM, 0);
+    framelen |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8;
+    if (maxlen > framelen - 14) {
+        // subtract eth source, dest and length fields
+        maxlen = framelen - 14;
+    }
+
+    // Read EtherType (we use this as a length field) (note +6 for receive vectors)
+    // Set read pointer (taking care of wrap-around)
+    address = currentPacketPtr + ETH_TYPE_H_P + 6;
+    if (address > RXSTOP_INIT) {
+        address -= (RXSTOP_INIT - RXSTART_INIT + 1);
+    }
+
+    writeReg(ERDPTL, (address & 0xff));
+    writeReg(ERDPTH, (address) >> 8);
+    readBuffer(6, _remoteMac);
+
+    // A value of less than 0x05dc in the EtherType has to be interpreted as length.
+    // This is what we do here.
+    // The length is 16 bit. The upper 8 bits must be zero
+    // otherwise it is not our packet.
+    len = readOp(ENC28J60_READ_BUF_MEM, 0);
+    if (len == 0) {
+        // read the lower byte of the length field
+        len = readOp(ENC28J60_READ_BUF_MEM, 0);
+        // limit retrieve length to maxlen, ignoring anything else
+        if (len > maxlen) {
+            len = maxlen;
+        }
+
+        // copy payload data from the receive buffer
+        readBuffer(len, buf);
+    }
+    else
+        len = 0;
+
+    // Move the RX read pointer to the start of the next received packet
+    // This frees the memory we just read out.
+    // However, compensate for the errata point 13, rev B4: enver write an even address!
+    if ((_nextPacketPtr - 1 < RXSTART_INIT) || (_nextPacketPtr - 1 > RXSTOP_INIT)) {
+        writeReg(ERXRDPTL, (RXSTOP_INIT) & 0xFF);
+        writeReg(ERXRDPTH, (RXSTOP_INIT) >> 8);
+    }
+    else {
+        writeReg(ERXRDPTL, (_nextPacketPtr - 1) & 0xFF);
+        writeReg(ERXRDPTH, (_nextPacketPtr - 1) >> 8);
+    }
+
+    // Decrement the packet counter indicate we are done with this packet
+    writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
+    return(len);
+}
+
+// sends gratuitous ARP request (spontaneous arp request) to teach
+
+// switches what our mac is.
+void RawEthernet::gratuitousArp()
+{
+    uint8_t     i = 0;
+    uint16_t    address;
+    setBank(0);
+
+    // (control byte, and SRC MAC have already been set during init)
+#ifdef ENC28J60_HAS_PENDING_TRANSMIT_ON_TRANSMIT
+    // Check no transmit in progress
+
+    while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS) {
+        // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12.
+        if ((readReg(EIR) & EIR_TXERIF)) {
+            writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
+            writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
+        }
+    }
+
+#else
+    // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12.
+
+    if ((readReg(EIR) & EIR_TXERIF)) {
+        writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
+        writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
+    }
+#endif
+    // Set the write pointer to start of transmit buffer area
+
+    // +1 to skip the per packet control byte and write directly the mac
+    // The control byte was set to zero during initialisation and remains like that.
+    writeReg(EWRPTL, (TXSTART_INIT + 1) & 0xFF);
+    writeReg(EWRPTH, (TXSTART_INIT + 1) >> 8);
+    // write a broadcase destination mac (all ff):
+    while (i < 6) {
+        writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0xFF);
+        i++;
+    }
+
+    // The mac in the ethernet field does not need to be changed.
+    // Set the write pointer to the first byte of the EtherType field
+    address = TXSTART_INIT + 1 + ETH_TYPE_H_P;
+    writeReg(EWRPTL, address & 0xFF);
+    writeReg(EWRPTH, address >> 8);
+    // there are 10 fixed bytes in the arp request
+    i = 0;
+    while (i < 10) {
+        writeOp(ENC28J60_WRITE_BUF_MEM, 0, (_arpReqHdr[i]));
+        i++;
+    }
+
+    i = 0;
+    while (i < 6) {
+        writeOp(ENC28J60_WRITE_BUF_MEM, 0, _mac[i]);
+        i++;
+    }
+
+    i = 0;
+    while (i < 4) {
+        writeOp(ENC28J60_WRITE_BUF_MEM, 0, _ip[i]);
+        i++;
+    }
+
+    // target data:
+    i = 0;
+    while (i < 6) {
+        writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0xff);
+        i++;
+    }
+
+    // to self, for gratuitous arp:
+    i = 0;
+    while (i < 4) {
+        writeOp(ENC28J60_WRITE_BUF_MEM, 0, _ip[i]);
+        i++;
+    }
+
+    // Set the TXND pointer to correspond to the payload size given
+    address = (TXSTART_INIT + 42);
+    writeReg(ETXNDL, address & 0xFF);
+    writeReg(ETXNDH, address >> 8);
+
+    // Send the contents of the transmit buffer onto the network
+    writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void RawEthernet::send(uint8_t* buf, uint8_t len)
+{
+    uint16_t    address;
+    setBank(0);
+
+    // (control byte, and SRC MAC have already been set during init)
+#ifdef ENC28J60_HAS_PENDING_TRANSMIT_ON_TRANSMIT
+    // Check no transmit in progress
+
+    while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS) {
+        // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12.
+        if ((readReg(EIR) & EIR_TXERIF)) {
+            writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
+            writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
+        }
+    }
+
+#else
+    // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12.
+
+    if ((readReg(EIR) & EIR_TXERIF)) {
+        writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
+        writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
+    }
+#endif
+    // Set the write pointer to start of transmit buffer area
+
+    // +1 to skip the per packet control byte and write directly the mac
+    // The control byte was set to zero during initialisation and remains like that.
+    writeReg(EWRPTL, (TXSTART_INIT + 1) & 0xFF);
+    writeReg(EWRPTH, (TXSTART_INIT + 1) >> 8);
+    writeBuffer(6, _remoteMac);
+
+    // Set the write pointer to the first byte of the EtherType field
+    // (field after the mac address). This is the 802.3 length field.
+    address = TXSTART_INIT + 1 + ETH_TYPE_H_P;
+    writeReg(EWRPTL, address & 0xFF);
+    writeReg(EWRPTH, address >> 8);
+    // write the length of the data in the ethernet type field.
+    // The type field to be interpreted by the receiver as ieee802.3 length if
+    // the value is less than 0x05dc (see e.g. http://www.cavebear.com/archive/cavebear/Ethernet/type.html ):
+    writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0);
+    writeOp(ENC28J60_WRITE_BUF_MEM, 0, len);
+    // Copy the payload into the transmit buffer
+    writeBuffer(len, buf);  // remove dest mac and write the rest
+    // Set the TXND pointer to correspond to the payload size given
+    address = (TXSTART_INIT + ETH_HEADER_LEN + len);
+    writeReg(ETXNDL, address & 0xFF);
+    writeReg(ETXNDH, address >> 8);
+
+    // Send the contents of the transmit buffer onto the network
+    writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint8_t RawEthernet::getRev(void)
+{
+    uint8_t rev;
+
+    rev = readReg(EREVID);
+
+    // microchip forgott to step the number on the silcon when they
+    // released the revision B7. 6 is now rev B7. We still have
+    // to see what they do when they release B8. At the moment
+    // there is no B8 out yet
+    if (rev > 5)
+        rev++;
+    return(rev);
+}
+