Revision 0:ca476e0a1a28, committed 2018-08-20
- Comitter:
- hudakz
- Date:
- Mon Aug 20 11:37:35 2018 +0000
- Commit message:
- Initial release.
Changed in this revision
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