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

Dependents:   RawEthernet_Hello

Files at this revision

API Documentation at this revision

Comitter:
hudakz
Date:
Mon Aug 20 11:37:35 2018 +0000
Commit message:
Initial release.

Changed in this revision

RawEthernet.cpp Show annotated file Show diff for this revision Revisions of this file
RawEthernet.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r ca476e0a1a28 RawEthernet.cpp
--- /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);
+}
+
diff -r 000000000000 -r ca476e0a1a28 RawEthernet.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RawEthernet.h	Mon Aug 20 11:37:35 2018 +0000
@@ -0,0 +1,79 @@
+#ifndef RAW_ETHERNET_H
+#define RAW_ETHERNET_H
+
+#include "mbed.h"
+
+//------------------------------------------------------------------------------
+// Functions for using an ENC28J60 ethernet controller, optimized for 
+// communication speed.
+//
+// Written by Rogier Schouten for AVR micro controllers 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
+// 
+
+class RawEthernet
+{
+public:
+    /**
+     * Constructor
+     *
+     * \param mosi mbed pin to use SPI
+     * \param miso mbed pin to use SPI
+     * \param sclk mbed pin to use SPI
+     * \param cs pin to select the ENC28J60 chip
+     */
+    RawEthernet(PinName mosi, PinName miso, PinName sclk, PinName cs, uint8_t myMac[6], uint8_t myIp[4]);
+    void     linkTo(uint8_t remoteMac[6]);
+    void     packetSend(uint16_t len, uint8_t* packet);
+    bool     isLinkUp(void);
+    uint8_t  receive(uint8_t* buf, uint8_t maxlen);
+    void     gratuitousArp();
+    void     send(uint8_t* buf, uint8_t len);
+    uint8_t  getRev(void);
+protected:
+    uint8_t  readOp(uint8_t op, uint8_t address);
+    void     writeOp(uint8_t op, uint8_t address, uint8_t data);
+    uint8_t  readReg(uint8_t address);
+    void     writeReg(uint8_t address, uint8_t data);
+    void     setBank(uint8_t address);
+    void     readBuffer(uint16_t len, uint8_t* data);
+    void     writeBuffer(uint16_t len, uint8_t* data);
+    uint16_t phyReadH(uint8_t address);
+    void     phyWrite(uint8_t address, uint16_t data);
+private:
+    SPI         _spi;
+    DigitalOut  _cs;
+    uint8_t     _bank;
+    int16_t     _nextPacketPtr;
+    uint8_t     _lastTransmitPayloadLen;
+    uint8_t     _mac[6];
+    uint8_t     _ip[4];
+    uint8_t     _remoteMac[6];
+    static char _arpReqHdr[10];
+};
+#endif // RAW_ETHERNET_H