Mbed library for ENC28J60 Ethernet modules. Full support for TCP/IP and UDP Server, Client and HTTP server (webserver). DHCP and DNS is included.

Dependents:   mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more

Library for ENC28J60 Ethernet modules.

/media/uploads/hudakz/enc28j60_module01.jpg

Ported to mbed from Norbert Truchsess's UIPEthernet library for Arduino. Thank you Norbert!

  • Full support for persistent (streaming) TCP/IP and UDP connections Client and Server each, ARP, ICMP, DHCP and DNS.
  • Works with both Mbed OS 2 and Mbed OS 5.

Usage:

  • Import the library into your project.
  • Add #include "UipEthernet.h" to main.cpp
  • Create one instance of the UipEthernet class initialized with the MAC address you'd like to use and SPI pins of the connected Mbed board.

Example programs:

Import programWebSwitch_ENC28J60

HTTP Server serving a simple webpage which enables to remotely turn a digital output on/off. Compile, download, run and type 'IP_address/secret/' (don't forget the last '/') into your web browser and hit ENTER.

Import programHTTPServer_Echo_ENC28J60

A simple HTTP server echoing received requests. Ethernet connection is over an ENC28J60 board. Usage: Type the server's IP address into you web browser and hit <ENTER>.

Import programTcpServer_ENC28J60

Simple TCP/IP Server using the UIPEthernet library for ENC28J60 Ethernet boards.

Import programTcpClient_ENC28J60

Simple TCP/IP Client using the UIPEthernet library for ENC28J60 Ethernet boards.

Import programUdpServer_ENC28J60

Simple UDP Server using the UIPEthernet library for ENC28J60 Ethernet boards.

Import programUdpClient_ENC28J60

Simple UDP Client using the UIPEthernet library for ENC28J60 Ethernet boards.

Import programMQTT_Hello_ENC28J60

MQTT Client example program. Ethernet connection is via an ENC28J60 module.

Revision:
11:647d53d146f1
Parent:
9:a156d3de5647
Child:
14:7648334eb41b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utility/Enc28j60Eth.cpp	Fri Aug 30 08:11:40 2019 +0000
@@ -0,0 +1,734 @@
+/*
+ Enc28J60Network.cpp
+ 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/
+
+ Modified (ported to mbed) by Zoltan Hudak <hudakz@inbox.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 "Enc28j60Eth.h"
+#include "mbed.h"
+
+extern "C"
+{
+#include "enc28j60.h"
+#include "uip.h"
+}
+
+// Static member initialization
+uint16_t    Enc28j60Eth::nextPacketPtr;
+uint8_t     Enc28j60Eth::bank = 0xff;
+struct      memblock Enc28j60Eth::receivePkt;
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+Enc28j60Eth::Enc28j60Eth(PinName mosi, PinName miso, PinName sclk, PinName cs) :
+    MemPool(),
+    _spi(mosi, miso, sclk),
+    _cs(cs)
+{ }
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::init(uint8_t* macaddr)
+{
+    MemPool::init();           // 1 byte in between RX_STOP_INIT and pool to allow prepending of controlbyte
+    // initialize SPI interface
+    _spi.format(8, 0);          // 8-bit, mode 0
+    _spi.frequency(20000000);   // 20 Mbit/s
+    wait_ms(1000);              // 1 second for stable state
+    // Release SPI
+    _cs = 1;
+
+    // perform system reset
+    writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
+
+    // check CLKRDY bit to see if reset is complete
+    //while(!(readReg(ESTAT) & ESTAT_CLKRDY));
+    // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait.
+    wait_ms(50);
+
+    // 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
+    writeRegPair(ERXSTL, RXSTART_INIT);
+
+    // set receive pointer address
+    writeRegPair(ERXRDPTL, RXSTART_INIT);
+
+    // RX end
+    writeRegPair(ERXNDL, RXEND_INIT);
+
+    //All memory which is not used by the receive buffer is considered the transmission buffer.
+    // No explicit action is required to initialize the transmission buffer.
+    // However, he host controller should leave at least seven bytes between each
+    // packet and the beginning of the receive buffer.
+    // TX start
+    //writeRegPair(ETXSTL, TXSTART_INIT);
+    // TX end
+    //writeRegPair(ETXNDL, TXEND_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!
+    writeReg(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN | ERXFCON_BCEN);
+    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);
+
+    // 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
+    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);
+
+    //Configure leds
+    phyWrite(PHLCON, 0x476);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+memhandle Enc28j60Eth::receivePacket()
+{
+    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 (readReg(EPKTCNT) != 0) {
+        uint16_t    readPtr = nextPacketPtr +
+            6 > RXEND_INIT ? nextPacketPtr +
+            6 -
+            RXEND_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;
+#ifdef ENC28J60DEBUG
+        printf
+        (
+            "receivePacket [%d-%d], next: %d, stat: %d, count: %d -> ",
+            readPtr,
+            (readPtr + len) % (RXEND_INIT + 1),
+            nextPacketPtr,
+            rxstat,
+            readReg(EPKTCNT)
+        );
+        (rxstat & 0x80) != 0 ? printf("OK") : printf("failed");
+        printf("\r\n");
+#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) {
+            receivePkt.begin = readPtr;
+            receivePkt.size = len;
+            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();
+    }
+
+    return(NOBLOCK);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::setERXRDPT()
+{
+    writeRegPair(ERXRDPTL, nextPacketPtr == RXSTART_INIT ? RXEND_INIT : nextPacketPtr - 1);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+memaddress Enc28j60Eth::blockSize(memhandle handle)
+{
+    return handle == NOBLOCK ? 0 : handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::sendPacket(memhandle handle)
+{
+    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);
+
+#ifdef ENC28J60DEBUG
+    printf("sendPacket(%d) [%d-%d]: ", handle, start, end);
+    for (uint16_t i = start; i <= end; i++) {
+        printf("%d ", readByte(i));
+    }
+
+    printf("\r\n");
+#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);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t Enc28j60Eth::setReadPtr(memhandle handle, memaddress position, uint16_t len)
+{
+    memblock*   packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle];
+    memaddress  start = handle == UIP_RECEIVEBUFFERHANDLE &&
+        packet->begin +
+        position > RXEND_INIT ? packet->begin +
+        position -
+        RXEND_INIT +
+        RXSTART_INIT : packet->begin +
+        position;
+
+    writeRegPair(ERDPTL, start);
+
+    if (len > packet->size - position)
+        len = packet->size - position;
+    return len;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t Enc28j60Eth::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len)
+{
+    len = setReadPtr(handle, position, len);
+    readBuffer(len, buffer);
+    return len;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t Enc28j60Eth::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len)
+{
+    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;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint8_t Enc28j60Eth::readByte(uint16_t addr)
+{
+    uint8_t result;
+
+    writeRegPair(ERDPTL, addr);
+
+    _cs = 0;
+
+    // issue read command
+    _spi.write(ENC28J60_READ_BUF_MEM);
+
+    // read data
+    result = _spi.write(0x00);
+    _cs = 1;
+    return(result);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::writeByte(uint16_t addr, uint8_t data)
+{
+    writeRegPair(EWRPTL, addr);
+
+    _cs = 0;
+
+    // issue write command
+    _spi.write(ENC28J60_WRITE_BUF_MEM);
+
+    // write data
+    _spi.write(data);
+    _cs = 1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::copyPacket
+(
+    memhandle   dest_pkt,
+    memaddress  dest_pos,
+    memhandle   src_pkt,
+    memaddress  src_pos,
+    uint16_t    len
+)
+{
+    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 > RXEND_INIT ? src->begin +
+        src_pos -
+        RXEND_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();
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::freePacket()
+{
+    setERXRDPT();
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint8_t Enc28j60Eth::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
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::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 Enc28j60Eth::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 Enc28j60Eth::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
+ */
+void Enc28j60Eth::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 Enc28j60Eth::readReg(uint8_t address)
+{
+    // set the bank
+    setBank(address);
+
+    // do the read
+    return readOp(ENC28J60_READ_CTRL_REG, address);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::writeReg(uint8_t address, uint8_t data)
+{
+    // set the bank
+    setBank(address);
+
+    // do the write
+    writeOp(ENC28J60_WRITE_CTRL_REG, address, data);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::writeRegPair(uint8_t address, uint16_t data)
+{
+    // 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);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::phyWrite(uint8_t address, uint16_t data)
+{
+    // 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) {
+        wait_us(15);
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t Enc28j60Eth::phyRead(uint8_t address)
+{
+    writeReg(MIREGADR, address);
+    writeReg(MICMD, MICMD_MIIRD);
+
+    // wait until the PHY read completes
+    while (readReg(MISTAT) & MISTAT_BUSY) {
+        wait_us(15);
+    }   //and MIRDH
+
+    writeReg(MICMD, 0);
+    return(readReg(MIRDL) | readReg(MIRDH) << 8);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::clkout(uint8_t clk)
+{
+    //setup clkout: 2 is 12.5MHz:
+    writeReg(ECOCON, clk & 0x7);
+}
+
+// read the revision of the chip:
+uint8_t Enc28j60Eth::getrev()
+{
+    return(readReg(EREVID));
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint16_t Enc28j60Eth::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len)
+{
+    uint8_t     spdr;
+    uint16_t    t;
+    uint16_t    i;
+
+    len = setReadPtr(handle, pos, len) - 1;
+    _cs = 0;
+
+    // issue read command
+    spdr = _spi.write(ENC28J60_READ_BUF_MEM);
+    for (i = 0; i < len; i += 2) {
+        // read data
+        spdr = _spi.write(0x00);
+        t = spdr << 8;
+        spdr = _spi.write(0x00);
+        t += spdr;
+        sum += t;
+        if (sum < t) {
+            sum++;  /* carry */
+        }
+    }
+
+    if (i == len) {
+        spdr = _spi.write(0x00);
+        t = (spdr << 8) + 0;
+        sum += t;
+        if (sum < t) {
+            sum++;  /* carry */
+        }
+    }
+
+    _cs = 1;
+
+    /* Return sum in host byte order. */
+    return sum;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::powerOff()
+{
+    writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
+    wait_ms(50);
+    writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS);
+    wait_ms(50);
+    writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void Enc28j60Eth::powerOn()
+{
+    writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
+    wait_ms(50);
+    writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
+    wait_ms(50);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+bool Enc28j60Eth::linkStatus()
+{
+    return(phyRead(PHSTAT2) & 0x0400) > 0;
+}