Enables fast realtime communication over Ethernet by using plain Ethernet frames (overhead of TCP/IP network layers is stripped away).
RawEthernet.cpp
- Committer:
- hudakz
- Date:
- 2018-08-20
- Revision:
- 0:ca476e0a1a28
File content as of revision 0:ca476e0a1a28:
//------------------------------------------------------------------------------
// 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);
}