Enables fast realtime communication over Ethernet by using plain Ethernet frames (overhead of TCP/IP network layers is stripped away).
Diff: RawEthernet.cpp
- 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); +} +