UIPEthernet library for Arduino IDE, Eclipse with arduino plugin and MBED/SMeshStudio (AVR,STM32F,ESP8266,Intel ARC32,Nordic nRF51,Teensy boards,Realtek Ameba(RTL8195A,RTL8710)), ENC28j60 network chip. Compatible with Wiznet W5100 Ethernet library API. Compiled and tested on Nucleo-F302R8. Master repository is: https://github.com/UIPEthernet/UIPEthernet/
Diff: utility/Enc28J60Network.cpp
- Revision:
- 0:e3fb1267e3c3
- Child:
- 16:66225c1d660c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/utility/Enc28J60Network.cpp Wed Dec 21 16:58:10 2016 +0100 @@ -0,0 +1,1122 @@ +/* + Enc28J60NetworkClass.h + UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. + + Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de> + All rights reserved. + + based on enc28j60.c file from the AVRlib library by Pascal Stang. + For AVRlib See http://www.procyonengineering.com/ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Enc28J60Network.h" +#if defined(ARDUINO) + #include "Arduino.h" +#endif +#if defined(__MBED__) + #include <mbed.h> + #define delay(x) wait_ms(x) +#endif +#include "logging.h" + +#if ENC28J60_USE_SPILIB + #include <SPI.h> + #if defined(ARDUINO) + extern SPIClass SPI; + #endif + #if defined(__MBED__) + SPI _spi(SPI_MOSI,SPI_MISO,SPI_SCK); + DigitalOut _cs(ENC28J60_CONTROL_CS); + Serial LogObject(SERIAL_TX,SERIAL_RX); + #endif +#endif + +extern "C" { + #if defined(ARDUINO_ARCH_AVR) + // AVR-specific code + #include <avr/io.h> + #elif defined(ARDUINO_ARCH_SAM) + // SAM-specific code + #else + // generic, non-platform specific code + #endif +#include "enc28j60.h" +#include "uip.h" +} + +#if defined(ARDUINO) + // set CS to 0 = active + #define CSACTIVE digitalWrite(ENC28J60_CONTROL_CS, LOW) + // set CS to 1 = passive + #define CSPASSIVE digitalWrite(ENC28J60_CONTROL_CS, HIGH) +#endif +#if defined(__MBED__) + // set CS to 0 = active + #define CSACTIVE _cs=0 + // set CS to 1 = passive + #define CSPASSIVE _cs=1 +#endif + +// +#if defined(ARDUINO_ARCH_AVR) +#define waitspi() while(!(SPSR&(1<<SPIF))) +#elif defined(ARDUINO_ARCH_SAM) +#if ENC28J60_CONTROL_CS==BOARD_SPI_SS0 or ENC28J60_CONTROL_CS==BOARD_SPI_SS1 or ENC28J60_CONTROL_CS==BOARD_SPI_SS2 or ENC28J60_CONTROL_CS==BOARD_SPI_SS3 +#define ENC28J60_USE_SPILIB_EXT 1 +#endif +#endif + +uint16_t Enc28J60Network::nextPacketPtr; +uint8_t Enc28J60Network::bank=0xff; +uint8_t Enc28J60Network::erevid=0; + +struct memblock Enc28J60Network::receivePkt; + +bool Enc28J60Network::broadcast_enabled = false; + + +void Enc28J60Network::init(uint8_t* macaddr) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::init(uint8_t* macaddr) DEBUG_V3:Function started")); + #endif + receivePkt.begin = 0; + receivePkt.size = 0; + + unsigned int timeout = 15; + MemoryPool::init(); // 1 byte in between RX_STOP_INIT and pool to allow prepending of controlbyte + // initialize I/O + // ss as output: + #if defined(ARDUINO) + pinMode(ENC28J60_CONTROL_CS, OUTPUT); + #endif + CSPASSIVE; // ss=0 + // + + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("ENC28J60::init DEBUG:csPin = ")); + LogObject.uart_send_decln(ENC28J60_CONTROL_CS); + LogObject.uart_send_str(F("ENC28J60::init DEBUG:miso = ")); + LogObject.uart_send_decln(SPI_MISO); + LogObject.uart_send_str(F("ENC28J60::init DEBUG:mosi = ")); + LogObject.uart_send_decln(SPI_MOSI); + LogObject.uart_send_str(F("ENC28J60::init DEBUG:sck = ")); + LogObject.uart_send_decln(SPI_SCK); + #endif +#if ENC28J60_USE_SPILIB + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_strln(F("ENC28J60::init DEBUG:Use SPI lib SPI.begin()")); + #endif + #if defined(ARDUINO) + SPI.begin(); + #endif + #if defined(ARDUINO_ARCH_AVR) + // AVR-specific code + SPI.setClockDivider(SPI_CLOCK_DIV2); //results in 8MHZ at 16MHZ system clock. + #elif defined(ARDUINO_ARCH_SAM) + // SAM-specific code + SPI.setClockDivider(10); //defaults to 21 which results in aprox. 4MHZ. A 10 should result in a little more than 8MHZ. + #elif defined(__STM32F1__) || defined(__STM32F3__) || defined(__STM32F4__) + // generic, non-platform specific code + #define USE_STM32F1_DMAC 1 //on STM32 + // BOARD_NR_SPI >= 1 BOARD_SPI1_NSS_PIN, BOARD_SPI1_SCK_PIN, BOARD_SPI1_MISO_PIN, BOARD_SPI1_MOSI_PIN + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); + SPI.setClockDivider(SPI_CLOCK_DIV8); //value 8 the result is 9MHz at 72MHz clock. + #else + #if defined(ARDUINO) + SPI.setBitOrder(MSBFIRST); + //SPI.setDataMode(SPI_MODE0); + //SPI.setClockDivider(SPI_CLOCK_DIV16); + #endif + #if defined(__MBED__) + _spi.format(8, 0); // 8bit, mode 0 + _spi.frequency(7000000); // 7MHz + #endif + #endif +#else + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_strln(F("ENC28J60::init DEBUG:Use Native hardware SPI")); + #endif + pinMode(SPI_MOSI, OUTPUT); + pinMode(SPI_SCK, OUTPUT); + pinMode(SPI_MISO, INPUT); + //Hardware SS must be configured as OUTPUT to enable SPI-master (regardless of which pin is configured as ENC28J60_CONTROL_CS) + //pinMode(ENC28J60_CONTROL_CS, OUTPUT); + + digitalWrite(SPI_MOSI, LOW); + digitalWrite(SPI_SCK, LOW); + + // initialize SPI interface + // master mode and Fosc/2 clock: + SPCR = (1<<SPE)|(1<<MSTR); + SPSR |= (1<<SPI2X); +#endif + + // perform system reset + writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); + delay(2); // errata B7/2 + delay(50); + // check CLKRDY bit to see if reset is complete + // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait. + //while(!(readReg(ESTAT) & ESTAT_CLKRDY)); + // do bank 0 stuff + // initialize receive buffer + // 16-bit transfers, must write low byte first + // set receive buffer start address + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:Before readOp(ENC28J60_READ_CTRL_REG, ESTAT)")); + #endif + nextPacketPtr = RXSTART_INIT; + while ((!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) && (timeout>0)) + { + timeout=timeout-1; + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + } + #if ACTLOGLEVEL>=LOG_ERR + if (timeout==0) {LogObject.uart_send_strln(F("ENC28J60::init ERROR:TIMEOUT !!!"));} + #endif + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After readOp(ENC28J60_READ_CTRL_REG, ESTAT)")); + #endif + // Rx start + writeRegPair(ERXSTL, RXSTART_INIT); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After writeRegPair(ERXSTL, RXSTART_INIT)")); + #endif + // set receive pointer address + writeRegPair(ERXRDPTL, RXSTART_INIT); + // RX end + writeRegPair(ERXNDL, RXSTOP_INIT); + // TX start + //writeRegPair(ETXSTL, TXSTART_INIT); + // TX end + //writeRegPair(ETXNDL, TXSTOP_INIT); + // do bank 1 stuff, packet filter: + // For broadcast packets we allow only ARP packtets + // All other packets should be unicast only for our mac (MAADR) + // + // The pattern to match on is therefore + // Type ETH.DST + // ARP BROADCAST + // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9 + // in binary these poitions are:11 0000 0011 1111 + // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 + //TODO define specific pattern to receive dhcp-broadcast packages instead of setting ERFCON_BCEN! +// enableBroadcast(); // change to add ERXFCON_BCEN recommended by epam + writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After writeReg(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN)")); + #endif + writeRegPair(EPMM0, 0x303f); + writeRegPair(EPMCSL, 0xf7f9); + // + // + // do bank 2 stuff + // enable MAC receive + // and bring MAC out of reset (writes 0x00 to MACON2) + writeRegPair(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); + // enable automatic padding to 60bytes and CRC operations + writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN)")); + #endif + // set inter-frame gap (non-back-to-back) + writeRegPair(MAIPGL, 0x0C12); + // set inter-frame gap (back-to-back) + writeReg(MABBIPG, 0x12); + // Set the maximum packet size which the controller will accept + // Do not send packets longer than MAX_FRAMELEN: + writeRegPair(MAMXFLL, MAX_FRAMELEN); + // do bank 3 stuff + // write MAC address + // NOTE: MAC address in ENC28J60 is byte-backward + writeReg(MAADR5, macaddr[0]); + writeReg(MAADR4, macaddr[1]); + writeReg(MAADR3, macaddr[2]); + writeReg(MAADR2, macaddr[3]); + writeReg(MAADR1, macaddr[4]); + writeReg(MAADR0, macaddr[5]); + // no loopback of transmitted frames + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:Before phyWrite(PHCON2, PHCON2_HDLDIS)")); + #endif + phyWrite(PHCON2, PHCON2_HDLDIS); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After phyWrite(PHCON2, PHCON2_HDLDIS)")); + #endif + // switch to bank 0 + setBank(ECON1); + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:After setBank(ECON1)")); + #endif + // enable interrutps + writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); + // enable packet reception + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + //Configure leds + phyWrite(PHLCON,0x476); + + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("ENC28J60::init DEBUG_V3:Before readReg(EREVID);")); + #endif + erevid=readReg(EREVID); + if (erevid==0xFF) {erevid=0;} + // microchip forgot 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 (erevid > 5) ++erevid; + #if ACTLOGLEVEL>=LOG_INFO + LogObject.uart_send_str(F("ENC28J60::init INFO: Chip erevid=")); + LogObject.uart_send_dec(erevid); + LogObject.uart_send_strln(F(" initialization completed.")); + #endif + +// return Enc28J60Network::erevid; +} + +memhandle +Enc28J60Network::receivePacket(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::receivePacket(void) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + uint8_t rxstat; + uint16_t len; + // check if a packet has been received and buffered + //if( !(readReg(EIR) & EIR_PKTIF) ){ + // The above does not work. See Rev. B4 Silicon Errata point 6. + #if ACTLOGLEVEL>=LOG_ERR + if (erevid==0) + { + LogObject.uart_send_strln(F("Enc28J60Network::receivePacket(void) ERROR:ENC28j50 Device not found !!! Bypass receivePacket function !!!")); + } + #endif + uint8_t epktcnt=readReg(EPKTCNT); + if ((erevid!=0) && (epktcnt!=0) && (epktcnt==readReg(EPKTCNT))) + { + uint16_t readPtr = nextPacketPtr+6 > RXSTOP_INIT ? nextPacketPtr+6-RXSTOP_INIT+RXSTART_INIT : nextPacketPtr+6; + // Set the read pointer to the start of the received packet + writeRegPair(ERDPTL, nextPacketPtr); + // read the next packet pointer + nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); + nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + // read the packet length (see datasheet page 43) + len = readOp(ENC28J60_READ_BUF_MEM, 0); + len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + len -= 4; //remove the CRC count + // read the receive status (see datasheet page 43) + rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); + //rxstat |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::receivePacket(void) DEBUG:receivePacket [")); + LogObject.uart_send_hex(readPtr); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_hex((readPtr+len) % (RXSTOP_INIT+1)); + LogObject.uart_send_str(F("], next: ")); + LogObject.uart_send_hex(nextPacketPtr); + LogObject.uart_send_str(F(", stat: ")); + LogObject.uart_send_hex(rxstat); + LogObject.uart_send_str(F(", Packet count: ")); + LogObject.uart_send_dec(epktcnt); + LogObject.uart_send_str(F(" -> ")); + LogObject.uart_send_strln((rxstat & 0x80)!=0 ? "OK" : "failed"); + #endif + // decrement the packet counter indicate we are done with this packet + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); + // check CRC and symbol errors (see datasheet page 44, table 7-3): + // The ERXFCON.CRCEN is set by default. Normally we should not + // need to check this. + if (((rxstat & 0x80) != 0) && (nextPacketPtr<=RXSTOP_INIT)) + { + receivePkt.begin = readPtr; + receivePkt.size = len; + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::receivePacket(void) DEBUG: rxstat OK. receivePkt.size=")); + LogObject.uart_send_decln(len); + #endif + return UIP_RECEIVEBUFFERHANDLE; + } + // Move the RX read pointer to the start of the next received packet + // This frees the memory we just read out + setERXRDPT(); + } + else if (epktcnt!=readReg(EPKTCNT)) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("Enc28J60Network::receivePacket(void) ERROR:Packet count readed differ values. SPI bus ERROR!!!")); + #endif + } + + return (NOBLOCK); +} + +void +Enc28J60Network::setERXRDPT(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::setERXRDPT(void) DEBUG_V3:Function started")); + #endif + nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr-1; + if (nextPacketPtr>RXSTOP_INIT) {nextPacketPtr=RXSTART_INIT;} + if ((nextPacketPtr&1)!=0) {nextPacketPtr--;} + #if ACTLOGLEVEL>=LOG_LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::setERXRDPT(void) DEBUG:Set nextPacketPtr:")); + LogObject.uart_send_hexln(nextPacketPtr); + #endif + writeRegPair(ERXRDPTL, nextPacketPtr); +} + +memaddress +Enc28J60Network::blockSize(memhandle handle) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::blockSize(memhandle handle) DEBUG_V3:Function started")); + #endif + return ((handle == NOBLOCK) || (erevid==0)) ? 0 : handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; +} + +void +Enc28J60Network::sendPacket(memhandle handle) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::sendPacket(memhandle handle) INFO:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + memblock *packet = &blocks[handle]; + uint16_t start = packet->begin-1; + uint16_t end = start + packet->size; + + // backup data at control-byte position + uint8_t data = readByte(start); + // write control-byte (if not 0 anyway) + if (data) + writeByte(start, 0); + + #if ACTLOGLEVEL>=LOG_DEBUG + LogObject.uart_send_str(F("Enc28J60Network::sendPacket(memhandle handle) DEBUG:sendPacket(")); + LogObject.uart_send_dec(handle); + LogObject.uart_send_str(F(") [")); + LogObject.uart_send_hex(start); + LogObject.uart_send_str(F("-")); + LogObject.uart_send_hex(end); + LogObject.uart_send_str(F("]: ")); + for (uint16_t i=start; i<=end; i++) + { + LogObject.uart_send_hex(readByte(i)); + LogObject.uart_send_str(F(" ")); + } + LogObject.uart_send_strln(F("")); + #endif + + // TX start + writeRegPair(ETXSTL, start); + // Set the TXND pointer to correspond to the packet size given + writeRegPair(ETXNDL, end); + // send the contents of the transmit buffer onto the network + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); + // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. + if( (readReg(EIR) & EIR_TXERIF) ) + { + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); + } + + //restore data on control-byte position + if (data) + writeByte(start, data); +} + +uint16_t +Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) DEBUG_V3:Function started")); + #endif + memblock *packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; + memaddress start = handle == UIP_RECEIVEBUFFERHANDLE && packet->begin + position > RXSTOP_INIT ? packet->begin + position-RXSTOP_INIT+RXSTART_INIT : packet->begin + position; + + writeRegPair(ERDPTL, start); + + if (len > packet->size - position) + len = packet->size - position; + return len; +} + +uint16_t +Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + len = setReadPtr(handle, position, len); + readBuffer(len, buffer); + return len; +} + +uint16_t +Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_str(F("Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) DEBUG_V3:Function started with len:")); + LogObject.uart_send_decln(len); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + memblock *packet = &blocks[handle]; + uint16_t start = packet->begin + position; + + writeRegPair(EWRPTL, start); + + if (len > packet->size - position) + len = packet->size - position; + writeBuffer(len, buffer); + return len; +} + + +void Enc28J60Network::enableBroadcast (bool temporary) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::enableBroadcast (bool temporary) DEBUG_V3:Function started")); + #endif + writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_BCEN); + if(!temporary) + broadcast_enabled = true; +} + +void Enc28J60Network::disableBroadcast (bool temporary) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::disableBroadcast (bool temporary) DEBUG_V3:Function started")); + #endif + if(!temporary) + broadcast_enabled = false; + if(!broadcast_enabled) + writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_BCEN); +} + +void Enc28J60Network::enableMulticast (void) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::enableMulticast (void) DEBUG_V3:Function started")); + #endif + writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_MCEN); +} + +void Enc28J60Network::disableMulticast (void) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::disableMulticast (void) DEBUG_V3:Function started")); + #endif + writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_MCEN); +} + +uint8_t Enc28J60Network::readRegByte (uint8_t address) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readRegByte (uint8_t address) DEBUG_V3:Function started")); + #endif + setBank(address); + return readOp(ENC28J60_READ_CTRL_REG, address); +} + +void Enc28J60Network::writeRegByte (uint8_t address, uint8_t data) { + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeRegByte (uint8_t address, uint8_t data) DEBUG_V3:Function started")); + #endif + setBank(address); + writeOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + + +uint8_t Enc28J60Network::readByte(uint16_t addr) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readByte(uint16_t addr) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + writeRegPair(ERDPTL, addr); + + CSACTIVE; + #if ENC28J60_USE_SPILIB + // issue read command + #if defined(ARDUINO) + SPI.transfer(ENC28J60_READ_BUF_MEM); + // read data + uint8_t c = SPI.transfer(0x00); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_READ_BUF_MEM); + // read data + uint8_t c = _spi.write(0x00); + #endif + CSPASSIVE; + return (c); + #else + // issue read command + SPDR = ENC28J60_READ_BUF_MEM; + waitspi(); + // read data + SPDR = 0x00; + waitspi(); + CSPASSIVE; + return (SPDR); + #endif +} + +void Enc28J60Network::writeByte(uint16_t addr, uint8_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeByte(uint16_t addr, uint8_t data) DEBUG_V3:Function started")); + #endif + #if defined(ESP8266) + wdt_reset(); + #endif + writeRegPair(EWRPTL, addr); + + CSACTIVE; + #if ENC28J60_USE_SPILIB + // issue write command + #if defined(ARDUINO) + SPI.transfer(ENC28J60_WRITE_BUF_MEM); + // write data + SPI.transfer(data); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_WRITE_BUF_MEM); + // write data + _spi.write(data); + #endif + #else + // issue write command + SPDR = ENC28J60_WRITE_BUF_MEM; + waitspi(); + // write data + SPDR = data; + waitspi(); + #endif + CSPASSIVE; +} + +void +Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::copyPacket(memhandle dest_pkt, memaddress dest_pos, memhandle src_pkt, memaddress src_pos, uint16_t len) DEBUG_V3:Function started")); + #endif + memblock *dest = &blocks[dest_pkt]; + memblock *src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; + memaddress start = src_pkt == UIP_RECEIVEBUFFERHANDLE && src->begin + src_pos > RXSTOP_INIT ? src->begin + src_pos-RXSTOP_INIT+RXSTART_INIT : src->begin + src_pos; + enc28J60_mempool_block_move_callback(dest->begin+dest_pos,start,len); + // Move the RX read pointer to the start of the next received packet + // This frees the memory we just read out + setERXRDPT(); +} + +void +enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) DEBUG_V3:Function started")); + #endif +//void +//Enc28J60Network::memblock_mv_cb(uint16_t dest, uint16_t src, uint16_t len) +//{ + //as ENC28J60 DMA is unable to copy single bytes: + if (len == 1) + { + Enc28J60Network::writeByte(dest,Enc28J60Network::readByte(src)); + } + else + { + // calculate address of last byte + len += src - 1; + + /* 1. Appropriately program the EDMAST, EDMAND + and EDMADST register pairs. The EDMAST + registers should point to the first byte to copy + from, the EDMAND registers should point to the + last byte to copy and the EDMADST registers + should point to the first byte in the destination + range. The destination range will always be + linear, never wrapping at any values except from + 8191 to 0 (the 8-Kbyte memory boundary). + Extreme care should be taken when + programming the start and end pointers to + prevent a never ending DMA operation which + would overwrite the entire 8-Kbyte buffer. + */ + Enc28J60Network::writeRegPair(EDMASTL, src); + Enc28J60Network::writeRegPair(EDMADSTL, dest); + + if ((src <= RXSTOP_INIT)&& (len > RXSTOP_INIT))len -= (RXSTOP_INIT-RXSTART_INIT); + Enc28J60Network::writeRegPair(EDMANDL, len); + + /* + 2. If an interrupt at the end of the copy process is + desired, set EIE.DMAIE and EIE.INTIE and + clear EIR.DMAIF. + + 3. Verify that ECON1.CSUMEN is clear. */ + Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN); + + /* 4. Start the DMA copy by setting ECON1.DMAST. */ + Enc28J60Network::writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); + + // wait until runnig DMA is completed + while (Enc28J60Network::readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST) + { + delay(1); + } + } +} + +void +Enc28J60Network::freePacket(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::freePacket(void) DEBUG_V3:Function started")); + #endif + setERXRDPT(); +} + +uint8_t +Enc28J60Network::readOp(uint8_t op, uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readOp(uint8_t op, uint8_t address) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue read command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(op | (address & ADDR_MASK)); + // read data + if(address & 0x80) + { + // do dummy read if needed (for mac and mii, see datasheet page 29) + SPI.transfer(0x00); + } + uint8_t c = SPI.transfer(0x00); + #endif + #if defined(__MBED__) + _spi.write(op | (address & ADDR_MASK)); + // read data + if(address & 0x80) + { + // do dummy read if needed (for mac and mii, see datasheet page 29) + _spi.write(0x00); + } + uint8_t c = _spi.write(0x00); + #endif + // release CS + CSPASSIVE; + return(c); + #else + // issue read command + SPDR = op | (address & ADDR_MASK); + waitspi(); + // read data + SPDR = 0x00; + waitspi(); + // do dummy read if needed (for mac and mii, see datasheet page 29) + if(address & 0x80) + { + SPDR = 0x00; + waitspi(); + } + // release CS + CSPASSIVE; + return(SPDR); + #endif +} + +void +Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue write command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(op | (address & ADDR_MASK)); + // write data + SPI.transfer(data); + #endif + #if defined(__MBED__) + _spi.write(op | (address & ADDR_MASK)); + // write data + _spi.write(data); + #endif + #else + // issue write command + SPDR = op | (address & ADDR_MASK); + waitspi(); + // write data + SPDR = data; + waitspi(); + #endif + CSPASSIVE; +} + +void +Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue read command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(ENC28J60_READ_BUF_MEM); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_READ_BUF_MEM); + #endif + #else + SPDR = ENC28J60_READ_BUF_MEM; + waitspi(); + #endif + while(len) + { + len--; + // read data + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + *data = SPI.transfer(0x00); + #endif + #if defined(__MBED__) + *data = _spi.write(0x00); + #endif + #else + SPDR = 0x00; + waitspi(); + *data = SPDR; + #endif + data++; + } + //*data='\0'; + CSPASSIVE; +} + +void +Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) DEBUG_V3:Function started")); + #endif + CSACTIVE; + // issue write command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(ENC28J60_WRITE_BUF_MEM); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_WRITE_BUF_MEM); + #endif + #else + SPDR = ENC28J60_WRITE_BUF_MEM; + waitspi(); + #endif + while(len) + { + len--; + // write data + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(*data); + #endif + #if defined(__MBED__) + _spi.write(*data); + #endif + data++; + #else + SPDR = *data; + data++; + waitspi(); + #endif + } + CSPASSIVE; +} + +void +Enc28J60Network::setBank(uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::setBank(uint8_t address) DEBUG_V3:Function started")); + #endif + // 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); + } +} + +uint8_t +Enc28J60Network::readReg(uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::readReg(uint8_t address) DEBUG_V3:Function started")); + #endif + // set the bank + setBank(address); + // do the read + return readOp(ENC28J60_READ_CTRL_REG, address); +} + +void +Enc28J60Network::writeReg(uint8_t address, uint8_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeReg(uint8_t address, uint8_t data) DEBUG_V3:Function started")); + #endif + // set the bank + setBank(address); + // do the write + writeOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + +void +Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) DEBUG_V3:Function started")); + #endif + // set the bank + setBank(address); + // do the write + writeOp(ENC28J60_WRITE_CTRL_REG, address, (data&0xFF)); + writeOp(ENC28J60_WRITE_CTRL_REG, address+1, (data) >> 8); +} + +void +Enc28J60Network::phyWrite(uint8_t address, uint16_t data) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::phyWrite(uint8_t address, uint16_t data) DEBUG_V3:Function started")); + #endif + unsigned int timeout = 15; + // set the PHY register address + writeReg(MIREGADR, address); + // write the PHY data + writeRegPair(MIWRL, data); + // wait until the PHY write completes + while (readReg(MISTAT) & MISTAT_BUSY) + { + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + if (--timeout == 0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("Enc28J60Network::phyWrite ERROR:TIMEOUT !!!")); + #endif + return; + } + } +} + +uint16_t +Enc28J60Network::phyRead(uint8_t address) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::phyRead(uint8_t address) DEBUG_V3:Function started")); + #endif + unsigned int timeout = 15; + writeReg(MIREGADR,address); + writeReg(MICMD, MICMD_MIIRD); + // wait until the PHY read completes + while(readReg(MISTAT) & MISTAT_BUSY) + { + delay(10); + #if defined(ESP8266) + wdt_reset(); + #endif + if (--timeout == 0) + { + #if ACTLOGLEVEL>=LOG_ERR + LogObject.uart_send_strln(F("Enc28J60Network::phyRead ERROR:TIMEOUT !!!")); + #endif + return 0; + } + } + writeReg(MICMD, 0); + return (readReg(MIRDL) | readReg(MIRDH) << 8); +} + +void +Enc28J60Network::clkout(uint8_t clk) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::clkout(uint8_t clk) DEBUG_V3:Function started")); + #endif + //setup clkout: 2 is 12.5MHz: + writeReg(ECOCON, clk & 0x7); +} + +uint16_t +Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) DEBUG_V3:Function started")); + #endif + uint16_t t; + len = setReadPtr(handle, pos, len)-1; + CSACTIVE; + // issue read command + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + SPI.transfer(ENC28J60_READ_BUF_MEM); + #endif + #if defined(__MBED__) + _spi.write(ENC28J60_READ_BUF_MEM); + #endif + #else + SPDR = ENC28J60_READ_BUF_MEM; + waitspi(); + #endif + uint16_t i; + for (i = 0; i < len; i+=2) + { + // read data + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + t = SPI.transfer(0x00) << 8; + t += SPI.transfer(0x00); + #endif + #if defined(__MBED__) + t = _spi.write(0x00) << 8; + t += _spi.write(0x00); + #endif + #else + SPDR = 0x00; + waitspi(); + t = SPDR << 8; + SPDR = 0x00; + waitspi(); + t += SPDR; + #endif + sum += t; + if(sum < t) + { + sum++; /* carry */ + } + } + if(i == len) + { + #if ENC28J60_USE_SPILIB + #if defined(ARDUINO) + t = (SPI.transfer(0x00) << 8) + 0; + #endif + #if defined(__MBED__) + t = (_spi.write(0x00) << 8) + 0; + #endif + #else + SPDR = 0x00; + waitspi(); + t = (SPDR << 8) + 0; + #endif + sum += t; + if(sum < t) + { + sum++; /* carry */ + } + } + CSPASSIVE; + + /* Return sum in host byte order. */ + return sum; +} + +void +Enc28J60Network::powerOff(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::powerOff(void) DEBUG_V3:Function started")); + #endif + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); + delay(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); + delay(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); +} + +void +Enc28J60Network::powerOn(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::powerOn(void) DEBUG_V3:Function started")); + #endif + writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); + delay(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + delay(50); +} + +// read erevid from object: +uint8_t +Enc28J60Network::geterevid(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_str(F("Enc28J60Network::geterevid(void) DEBUG_V3:Function started and return:")); + LogObject.uart_send_decln(erevid); + #endif + return(erevid); +} + +// read the phstat2 of the chip: +uint16_t +Enc28J60Network::PhyStatus(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_str(F("Enc28J60Network::PhyStatus(void) DEBUG_V3:Function started")); + LogObject.uart_send_decln(erevid); + #endif + uint16_t phstat2; + phstat2=phyRead(PHSTAT2); + if ((phstat2 & 0x20) > 0) {phstat2=phstat2 &0x100;} + phstat2=(phstat2 & 0xFF00) | erevid; + if ((phstat2 & 0x8000) > 0) {phstat2=0;} + return phstat2; +} + +bool +Enc28J60Network::linkStatus(void) +{ + #if ACTLOGLEVEL>=LOG_DEBUG_V3 + LogObject.uart_send_strln(F("Enc28J60Network::linkStatus(void) DEBUG_V3:Function started")); + #endif + return (phyRead(PHSTAT2) & 0x0400) > 0; +} + +Enc28J60Network Enc28J60;