/*! \file enc28j60.c \brief Microchip ENC28J60 Ethernet Interface Driver. */ //***************************************************************************** // // File Name : 'enc28j60.c' // Title : Microchip ENC28J60 Ethernet Interface Driver // Author : Pascal Stang (c)2005 // Created : 9/22/2005 // Revised : 9/22/2005 // Version : 0.1 // Target MCU : Atmel AVR series // Editor Tabs : 4 // // Description : This driver provides initialization and transmit/receive // functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. // This chip is novel in that it is a full MAC+PHY interface all in a 28-pin // chip, using an SPI interface to the host processor. // //***************************************************************************** #include "wiring.h" #include #include "avr/io.h" #include "avrlibdefs.h" #include "avrlibtypes.h" #include "HardwareSerial.h" #include "programStrings.h" // include configuration #include "nic.h" #include "net.h" #include "enc28j60conf.h" #include "enc28j60.h" #ifdef SPDR0 #define SPDR SPDR0 #define SPCR SPCR0 #define SPSR SPSR0 #define SPIF SPIF0 #define MSTR MSTR0 #define CPOL CPOL0 #define DORD DORD0 #define SPR0 SPR00 #define SPR1 SPR01 #define SPI2X SPI2X0 #define SPE SPE0 #endif u08 Enc28j60Bank; u16 NextPacketPtr; char nicInit(void) { return enc28j60Init(); } void nicSend(unsigned int len, unsigned char* packet) { enc28j60PacketSend(len, packet); } unsigned int nicPoll(unsigned int maxlen, unsigned char* packet) { return enc28j60PacketReceive(maxlen, packet); } void nicGetMacAddress(uint8_t* macaddr) { // read MAC address registers // NOTE: MAC address in ENC28J60 is byte-backward *macaddr++ = enc28j60Read(MAADR5); *macaddr++ = enc28j60Read(MAADR4); *macaddr++ = enc28j60Read(MAADR3); *macaddr++ = enc28j60Read(MAADR2); *macaddr++ = enc28j60Read(MAADR1); *macaddr++ = enc28j60Read(MAADR0); } void nicSetMacAddress(uint8_t* macaddr) { // write MAC address // NOTE: MAC address in ENC28J60 is byte-backward enc28j60Write(MAADR5, *macaddr++); enc28j60Write(MAADR4, *macaddr++); enc28j60Write(MAADR3, *macaddr++); enc28j60Write(MAADR2, *macaddr++); enc28j60Write(MAADR1, *macaddr++); enc28j60Write(MAADR0, *macaddr++); } #if defined(NET_DEBUG) || defined(NIC_DEBUG) void nicRegDump(void) { enc28j60RegDump(); } #endif void nicHardReset(void) { #ifndef ENC28J60_HARD_RESET_DISABLED enc28j60hardReset(); #endif } void nicSoftReset(void) { enc28j60softReset(); } void nicReboot(void) { enc28j60Reboot(); } uint16_t nicGetChecksum(uint8_t* packet, uint16_t len) { #ifdef ENC28J60_CHECKSUM_ENABLED enc28j60getChecksum(packet, len); #else return 0x0000; #endif } void enc28j60Reboot(void) { #ifndef ENC28J60_HARD_RESET_DISABLED enc28j60hardReset(); #else delayMicroseconds(10); #endif enc28j60softReset(); } void enc28j60softReset(void) { // perform system reset enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); delayMicroseconds(50); // check CLKRDY bit to see if reset is complete //while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY)); // Errata workaround #1, CLKRDY check is unreliable, delay 1 mS instead delay(1); } #ifndef ENC28J60_HARD_RESET_DISABLED void enc28j60hardReset(void) { // HW reset sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_HRESET); cbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_HRESET); // Down delayMicroseconds(10); sbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_HRESET); // Up delay(1); } #endif uint8_t enc28j60ReadOp(uint8_t op, uint8_t address) { uint8_t data; // assert CS cbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); // issue read command SPDR = op | (address & ADDR_MASK); while(!(SPSR & (1<>8); // Set the TXND pointer to correspond to the packet size given enc28j60Write(ETXNDL, (TXSTART_INIT+len)); enc28j60Write(ETXNDH, (TXSTART_INIT+len)>>8); // write per-packet control byte enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); // copy the packet into the transmit buffer enc28j60WriteBuffer(len, packet); // return the calculated checksum return enc28j60doHardwareChecksum(len); } uint16_t enc28j60doHardwareChecksum(uint16_t len) { // Set EDMASTL to the beggining of the used memory enc28j60Write(EDMASTL, TXSTART_INIT); enc28j60Write(EDMASTH, TXSTART_INIT>>8); // Set EDMAND to the end of the memory used enc28j60Write(EDMANDL, (TXSTART_INIT+len)); enc28j60Write(EDMANDH, (TXSTART_INIT+len)>>8); // Set EDMADST to the beggining of the DMA buffer enc28j60Write(EDMADSTL, TXSTART_INIT); enc28j60Write(EDMADSTH, TXSTART_INIT>>8); // Enable DMA copy mode enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN); enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); // Wait for the DMA copy to complete while(!(enc28j60Read(EIR) & EIR_DMAIF)); // Start the checksum calculation enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_CSUMEN); enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); // Wait for the checksum calculation to complete while(!(enc28j60Read(ECON1) & ~ECON1_DMAST && enc28j60Read(EIR) & EIR_DMAIF)); // Read back and return the calculated checksum return (uint16_t)(enc28j60Read(EDMACSL) | (uint16_t)enc28j60Read(EDMACSH)<<8); } void enc28j60WriteBuffer(uint16_t len, uint8_t* data) { // assert CS cbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); // issue write command SPDR = ENC28J60_WRITE_BUF_MEM; while(!(SPSR & (1<>5); Enc28j60Bank = (address & BANK_MASK); } } uint8_t enc28j60Read(uint8_t address) { // set the bank enc28j60SetBank(address); // do the read return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); } void enc28j60Write(uint8_t address, uint8_t data) { // set the bank enc28j60SetBank(address); // do the write enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); } uint16_t enc28j60PhyRead(uint8_t address) { uint16_t data; // Set the right address and start the register read operation enc28j60Write(MIREGADR, address); enc28j60Write(MICMD, MICMD_MIIRD); // wait until the PHY read completes while(enc28j60Read(MISTAT) & MISTAT_BUSY); // quit reading enc28j60Write(MICMD, 0x00); // get data value data = enc28j60Read(MIRDH); data <<= 8; data |= enc28j60Read(MIRDL); // return the data return data; } void enc28j60PhyWrite(uint8_t address, uint16_t data) { // set the PHY register address enc28j60Write(MIREGADR, address); // write the PHY data enc28j60Write(MIWRL, data); enc28j60Write(MIWRH, data>>8); // wait until the PHY write completes while(enc28j60Read(MISTAT) & MISTAT_BUSY); } char enc28j60Init(void) { /** * Enable ENC28J560 Control ports **/ // Enable CS and pull it high as CS is inverted sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_CS); // CS as output sbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); // CS high // Enable Hard reset and pull it high as RESET is inverted #ifndef ENC28J60_HARD_RESET_DISABLED sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_HRESET); // HR as output cbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_HRESET); // HR low #endif /** * setup SPI I/O pins **/ sbi(ENC28J60_CONTROL_DDR, ENC28J60_SPI_SCK); // set SCK as output cbi(ENC28J60_CONTROL_PORT, ENC28J60_SPI_SCK); // set SCK lo sbi(ENC28J60_CONTROL_DDR, ENC28J60_SPI_MOSI); // set MOSI as output cbi(ENC28J60_CONTROL_DDR, ENC28J60_SPI_MISO); // set MISO as input sbi(ENC28J60_CONTROL_PORT, ENC28J60_SPI_SS); // SS must be output for Master mode to work // master mode sbi(SPCR, MSTR); // select clock phase positive-going in middle of data cbi(SPCR, CPOL); // Data order MSB first cbi(SPCR,DORD); // switch to f/4 2X = f/2 bitrate cbi(SPCR, SPR0); cbi(SPCR, SPR1); sbi(SPSR, SPI2X); // enable SPI sbi(SPCR, SPE); delay(1); #ifdef DEBUG_ENC_INIT SPrintln("SPI Enabled"); #endif // Reboot the ENC28J60 enc28j60Reboot(); #ifdef DEBUG_ENC_INIT SPrintln("PHY reboot done"); #endif #ifdef ENC28J60_LAMPS_MODE #ifdef DEBUG_ENC_INIT SPrintln("Custom lamps"); #endif enc28j60PhyWrite(PHLCON, ENC28J60_LAMPS_MODE); #else // Errata #9 correction if (enc28j60Read(MACON3) & MACON3_FULDPX) { #ifdef DEBUG_ENC_INIT SPrintln("Full duplex lamps"); #endif enc28j60PhyWrite(PHLCON, PHLCON_DEFAULT); } else { #ifdef DEBUG_ENC_INIT SPrintln("Half duplex lamps"); #endif enc28j60PhyWrite(PHLCON, PHLCON_DEFAULT_HD); } #endif #ifdef DEBUG_ENC_INIT SPrintln("Lamps set"); #endif // do bank 0 stuff // initialize receive buffer // 16-bit transfers, must write low byte first // set receive buffer start address NextPacketPtr = RXSTART_INIT; enc28j60Write(ERXSTL, RXSTART_INIT&0xFF); enc28j60Write(ERXSTH, RXSTART_INIT>>8); // set receive pointer address enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF); enc28j60Write(ERXRDPTH, RXSTART_INIT>>8); // set receive buffer end // ERXND defaults to 0x1FFF (end of ram) enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF); enc28j60Write(ERXNDH, RXSTOP_INIT>>8); // set transmit buffer start // ETXST defaults to 0x0000 (beginnging of ram) enc28j60Write(ETXSTL, TXSTART_INIT&0xFF); enc28j60Write(ETXSTH, TXSTART_INIT>>8); // Needs register implementation #ifdef ENC28J60_PACKET_FILTER_ENABLED // 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 //enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN); enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_PMEN); enc28j60Write(EPMM0, 0x3f); enc28j60Write(EPMM1, 0x30); enc28j60Write(EPMCSL, 0xf9); enc28j60Write(EPMCSH, 0xf7); #endif // do bank 2 stuff // enable MAC receive enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); // bring MAC out of reset enc28j60Write(MACON2, 0x00); // enable automatic padding and CRC operations enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); // enc28j60Write(MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); // set inter-frame gap (non-back-to-back) enc28j60Write(MAIPGL, 0x12); enc28j60Write(MAIPGH, 0x0C); // set inter-frame gap (back-to-back) enc28j60Write(MABBIPG, 0x12); // Set the maximum packet size which the controller will accept enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF); enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8); // do bank 3 stuff // write MAC address // NOTE: MAC address in ENC28J60 is byte-backward enc28j60Write(MAADR5, ENC28J60_MAC0); enc28j60Write(MAADR4, ENC28J60_MAC1); enc28j60Write(MAADR3, ENC28J60_MAC2); enc28j60Write(MAADR2, ENC28J60_MAC3); enc28j60Write(MAADR1, ENC28J60_MAC4); enc28j60Write(MAADR0, ENC28J60_MAC5); // no loopback of transmitted frames enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); // switch to bank 0 enc28j60SetBank(ECON1); // enable interrutps enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); // enable packet reception enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); #ifdef DEBUG_ENC_INIT SPrintln("Banks robbed"); #endif /* enc28j60PhyWrite(PHLCON, 0x0AA2); // setup duplex ---------------------- // Disable receive logic and abort any packets currently being transmitted enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS|ECON1_RXEN); { uint16_t temp; // Set the PHY to the proper duplex mode temp = enc28j60PhyRead(PHCON1); temp &= ~PHCON1_PDPXMD; enc28j60PhyWrite(PHCON1, temp); // Set the MAC to the proper duplex mode temp = enc28j60Read(MACON3); temp &= ~MACON3_FULDPX; enc28j60Write(MACON3, temp); } // Set the back-to-back inter-packet gap time to IEEE specified // requirements. The meaning of the MABBIPG value changes with the duplex // state, so it must be updated in this function. // In full duplex, 0x15 represents 9.6us; 0x12 is 9.6us in half duplex //enc28j60Write(MABBIPG, DuplexState ? 0x15 : 0x12); // Reenable receive logic enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); // setup duplex ---------------------- */ return 0; } #define ETHERNET_MIN_PACKET_LENGTH 0x3C #define ETHERNET_HEADER_LENGTH 0x0E #define IP_TCP_HEADER_LENGTH 40 #define TOTAL_HEADER_LENGTH (IP_TCP_HEADER_LENGTH+ETHERNET_HEADER_LENGTH) void enc28j60PacketSend(uint16_t len, uint8_t* packet) { /* // dump packet int i,j; for (i=0;i>8); // Set the TXND pointer to correspond to the packet size given enc28j60Write(ETXNDL, (TXSTART_INIT+len)); enc28j60Write(ETXNDH, (TXSTART_INIT+len)>>8); // write per-packet control byte enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); // TODO, fix this up /* if( uip_len <= TOTAL_HEADER_LENGTH ) { // copy the packet into the transmit buffer enc28j60WriteBuffer(len, packet); } else { len -= TOTAL_HEADER_LENGTH; enc28j60WriteBuffer(TOTAL_HEADER_LENGTH, packet); enc28j60WriteBuffer(len, (unsigned char *)uip_appdata); } */ // copy the packet into the transmit buffer enc28j60WriteBuffer(len, packet); // send the contents of the transmit buffer onto the network enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); // From errata Rev B.4 // if( (enc28j60Read(EIR) & EIR_TXERIF) ){ // enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); // } } #ifndef enc28j60ReadNextPtr #define enc28j60ReadNextPtr(ptr) do { ptr = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); \ ptr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; \ } while(0) #endif uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet) { uint16_t rxstat; uint16_t len; // check if a packet has been received and buffered if( !(enc28j60Read(EIR) & EIR_PKTIF) ) { // Errata workaround #4, PKTIF is not reliable // double check by looking at EPKTCNT if (enc28j60Read(EPKTCNT) == 0) return 0; } // Set the read pointer to the start of the received packet enc28j60Write(ERDPTL, (NextPacketPtr)); enc28j60Write(ERDPTH, (NextPacketPtr)>>8); // read the next packet pointer enc28j60ReadNextPtr(NextPacketPtr); // read the packet length enc28j60ReadNextPtr(len); // read the receive status enc28j60ReadNextPtr(rxstat); // limit retrieve length // (we reduce the MAC-reported length by 4 to remove the CRC) len = MIN(len, maxlen); // copy the packet from the receive buffer enc28j60ReadBuffer(len, packet); // Move the RX read pointer to the start of the next received packet // This frees the memory we just read out // Errata workaround #11. Make sure ERXRDPT is odd // { uint16_t rs,re; rs = enc28j60Read(ERXSTH); rs <<= 8; rs |= enc28j60Read(ERXSTL); re = enc28j60Read(ERXNDH); re <<= 8; re |= enc28j60Read(ERXNDL); if (NextPacketPtr - 1 < rs || NextPacketPtr - 1 > re) { enc28j60Write(ERXRDPTL, (re)); enc28j60Write(ERXRDPTH, (re)>>8); } else { enc28j60Write(ERXRDPTL, (NextPacketPtr-1)); enc28j60Write(ERXRDPTH, (NextPacketPtr-1)>>8); } } // decrement the packet counter indicate we are done with this packet enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); return len; } void enc28j60ReceiveOverflowRecover(void) { // receive buffer overflow handling procedure // recovery completed } #if defined(NET_DEBUG) || defined(NIC_DEBUG) #define SPrint_D(str) SPrint(str); delayMicroseconds(100); void enc28j60RegDump(void) { // unsigned char macaddr[6]; // result = ax88796Read(TR); // SPrint("Media State: "); // if(!(result & AUTOD)) // SPrint("Autonegotiation\r\n"); // else if(result & RST_B) // SPrint("PHY in Reset \r\n"); // else if(!(result & RST_10B)) // SPrint("10BASE-T \r\n"); // else if(!(result & RST_TXB)) // SPrint("100BASE-T \r\n"); SPrint_D("RevID: "); Serial.println(enc28j60Read(EREVID), HEX); SPrint_D("Cntrl: ECON1 ECON2 ESTAT EIR EIE\r\n"); SPrint_D(" "); Serial.print(enc28j60Read(ECON1), HEX); SPrint_D(" "); Serial.print(enc28j60Read(ECON2), HEX); SPrint_D(" "); Serial.print(enc28j60Read(ESTAT), HEX); SPrint_D(" "); Serial.print(enc28j60Read(EIR), HEX); SPrint_D(" "); Serial.print(enc28j60Read(EIE), HEX); Serial.println(); SPrint_D("MAC : MACON1 MACON2 MACON3 MACON4 MAC-Address\r\n"); SPrint_D(" "); Serial.print(enc28j60Read(MACON1), HEX); SPrint_D(" "); Serial.print(enc28j60Read(MACON2), HEX); SPrint_D(" "); Serial.print(enc28j60Read(MACON3), HEX); SPrint_D(" "); Serial.print(enc28j60Read(MACON4), HEX); SPrint_D(" "); Serial.print(enc28j60Read(MAADR5), HEX); Serial.print(enc28j60Read(MAADR4), HEX); Serial.print(enc28j60Read(MAADR3), HEX); Serial.print(enc28j60Read(MAADR2), HEX); Serial.print(enc28j60Read(MAADR1), HEX); Serial.print(enc28j60Read(MAADR0), HEX); Serial.println(); SPrint_D("Rx : ERXST ERXND ERXWRPT ERXRDPT ERXFCON EPKTCNT MAMXFL\r\n"); SPrint_D(" "); Serial.print(enc28j60Read(ERXSTH), HEX); Serial.print(enc28j60Read(ERXSTL), HEX); SPrint_D(" "); Serial.print(enc28j60Read(ERXNDH), HEX); Serial.print(enc28j60Read(ERXNDL), HEX); SPrint_D(" "); Serial.print(enc28j60Read(ERXWRPTH), HEX); Serial.print(enc28j60Read(ERXWRPTL), HEX); SPrint_D(" "); Serial.print(enc28j60Read(ERXRDPTH), HEX); Serial.print(enc28j60Read(ERXRDPTL), HEX); SPrint_D(" "); Serial.print(enc28j60Read(ERXFCON), HEX); SPrint_D(" "); Serial.print(enc28j60Read(EPKTCNT), HEX); SPrint_D(" "); Serial.print(enc28j60Read(MAMXFLH), HEX); Serial.print(enc28j60Read(MAMXFLL), HEX); Serial.println(); SPrint_D("Tx : ETXST ETXND MACLCON1 MACLCON2 MAPHSUP\r\n"); SPrint_D(" "); Serial.print(enc28j60Read(ETXSTH), HEX); Serial.print(enc28j60Read(ETXSTL), HEX); SPrint_D(" "); Serial.print(enc28j60Read(ETXNDH), HEX); Serial.print(enc28j60Read(ETXNDL), HEX); SPrint_D(" "); Serial.print(enc28j60Read(MACLCON1), HEX); SPrint_D(" "); Serial.print(enc28j60Read(MACLCON2), HEX); SPrint_D(" "); Serial.print(enc28j60Read(MAPHSUP), HEX); Serial.println(); SPrint_D("PHLCON: 00"); Serial.println((long int)(enc28j60PhyRead(PHLCON)), BIN); delay(1); } #endif