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.
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"
tomain.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.
Diff: utility/Enc28j60Py.cpp
- Revision:
- 9:a156d3de5647
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/utility/Enc28j60Py.cpp Tue Aug 27 15:01:10 2019 +0000 @@ -0,0 +1,729 @@ +/* + 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 "Enc28j60Py.h" +#include "mbed.h" + +extern "C" +{ +#include "enc28j60.h" +#include "uip.h" +} + +// Static member initialization +uint16_t Enc28j60Phy::nextPacketPtr; +uint8_t Enc28j60Phy::bank = 0xff; +struct memblock Enc28j60Phy::receivePkt; + +/** + * @brief + * @note + * @param + * @retval + */ +Enc28j60Phy::Enc28j60Phy(PinName mosi, PinName miso, PinName sclk, PinName cs) : + MemPool(), + _spi(mosi, miso, sclk), + _cs(cs) +{ } + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28j60Phy::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(10000000); // 10 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, 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! + 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 Enc28j60Phy::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 > 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; +#ifdef ENC28J60DEBUG + printf + ( + "receivePacket [%d-%d], next: %d, stat: %d, count: %d -> ", + readPtr, + (readPtr + len) % (RXSTOP_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 Enc28j60Phy::setERXRDPT() +{ + writeRegPair(ERXRDPTL, nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr - 1); +} + +/** + * @brief + * @note + * @param + * @retval + */ +memaddress Enc28j60Phy::blockSize(memhandle handle) +{ + return handle == NOBLOCK ? 0 : handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28j60Phy::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 Enc28j60Phy::setReadPtr(memhandle handle, memaddress position, uint16_t len) +{ + 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; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint16_t Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::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 > 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(); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28j60Phy::freePacket() +{ + setERXRDPT(); +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::readReg(uint8_t address) +{ + // set the bank + setBank(address); + + // do the read + return readOp(ENC28J60_READ_CTRL_REG, address); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::clkout(uint8_t clk) +{ + //setup clkout: 2 is 12.5MHz: + writeReg(ECOCON, clk & 0x7); +} + +// read the revision of the chip: +uint8_t Enc28j60Phy::getrev() +{ + return(readReg(EREVID)); +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint16_t Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::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 Enc28j60Phy::linkStatus() +{ + return(phyRead(PHSTAT2) & 0x0400) > 0; +}