/********************************************************************* * * Medium Access Control (MAC) Layer for Microchip ENC28J60 * Module for Microchip TCP/IP Stack * -Provides access to ENC28J60 Ethernet controller * -Reference: ENC28J60 Data sheet, IEEE 802.3 Standard * ********************************************************************* * FileName: ENC28J60.c * Dependencies: ENC28J60.h * MAC.h * string.h * StackTsk.h * Helpers.h * Delay.h * Processor: PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F * Complier: Microchip C18 v3.02 or higher * Microchip C30 v2.01 or higher * Company: Microchip Technology, Inc. * * Software License Agreement * * Copyright © 2002-2007 Microchip Technology Inc. All rights * reserved. * * Microchip licenses to you the right to use, modify, copy, and * distribute: * (i) the Software when embedded on a Microchip microcontroller or * digital signal controller product (“Device”) which is * integrated into Licensee’s product; or * (ii) ONLY the Software driver source files ENC28J60.c and * ENC28J60.h ported to a non-Microchip device used in * conjunction with a Microchip ethernet controller for the * sole purpose of interfacing with the ethernet controller. * * You should refer to the license agreement accompanying this * Software for additional information regarding your rights and * obligations. * * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS” WITHOUT * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE. * * * Author Date Comment *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Howard Schlunder 6/28/04 Original * Howard Schlunder 10/8/04 Cleanup * Howard Schlunder 10/19/04 Small optimizations and more cleanup * Howard Schlunder 11/29/04 Added Set/GetCLKOUT * Howard Schlunder 12/23/05 Added B1 silicon errata workarounds * Howard Schlunder 1/09/06 Added comments and minor mods * Howard Schlunder 1/18/06 Added more silicon errata workarounds * Howard Schlunder 6/16/06 Synchronized with PIC18F97J60 code * Howard Schlunder 7/17/06 Updated TestMemory() for C30 * Howard Schlunder 8/07/06 Added SetRXHashTableEntry() function ********************************************************************/ #define __ENC28J60_C #include "TCPIP Stack/TCPIP.h" // Make sure that this hardware profile has an ENC28J60 in it #if defined(ENC_CS_TRIS) && !defined(STACK_USE_SLIP) /** D E F I N I T I O N S ****************************************************/ // IMPORTANT SPI NOTE: The code in this file expects that the SPI interrupt // flag (ENC_SPI_IF) be clear at all times. If the SPI is shared with // other hardware, the other code should clear the ENC_SPI_IF when it is // done using the SPI. // Since the ENC28J60 doesn't support auto-negotiation, full-duplex mode is // not compatible with most switches/routers. If a dedicated network is used // where the duplex of the remote node can be manually configured, you may // change this configuration. Otherwise, half duplex should always be used. #define HALF_DUPLEX //#define FULL_DUPLEX //#define LEDB_DUPLEX // Pseudo Functions #define LOW(a) (a & 0xFF) #define HIGH(a) ((a>>8) & 0xFF) // ENC28J60 Opcodes (to be ORed with a 5 bit address) #define WCR (0x2<<5) // Write Control Register command #define BFS (0x4<<5) // Bit Field Set command #define BFC (0x5<<5) // Bit Field Clear command #define RCR (0x0<<5) // Read Control Register command #define RBM ((0x1<<5) | 0x1A) // Read Buffer Memory command #define WBM ((0x3<<5) | 0x1A) // Write Buffer Memory command #define SR ((0x7<<5) | 0x1F) // System Reset command does not use an address. // It requires 0x1F, however. #define ETHER_IP (0x00u) #define ETHER_ARP (0x06u) // A header appended at the start of all RX frames by the hardware typedef struct _ENC_PREAMBLE { WORD NextPacketPointer; RXSTATUS StatusVector; MAC_ADDR DestMACAddr; MAC_ADDR SourceMACAddr; WORD_VAL Type; } ENC_PREAMBLE; // Prototypes of functions intended for MAC layer use only. static void BankSel(WORD Register); static REG ReadETHReg(BYTE Address); static REG ReadMACReg(BYTE Address); static void WriteReg(BYTE Address, BYTE Data); static void BFCReg(BYTE Address, BYTE Data); static void BFSReg(BYTE Address, BYTE Data); static void SendSystemReset(void); //static void GetRegs(void); //void Get8KBRAM(void); // Internal MAC level variables and flags. static WORD_VAL NextPacketLocation; static WORD_VAL CurrentPacketLocation; static BOOL WasDiscarded; static BYTE ENCRevID; //NOTE: All code in this module expect Bank 0 to be currently selected. If code ever changes the bank, it must restore it to Bank 0 before returning. /****************************************************************************** * Function: void MACInit(void) * * PreCondition: None * * Input: None * * Output: None * * Side Effects: None * * Overview: MACInit sets up the PIC's SPI module and all the * registers in the ENC28J60 so that normal operation can * begin. * * Note: None *****************************************************************************/ void MACInit(void) { BYTE i; // Set up the SPI module on the PIC for communications with the ENC28J60 ENC_CS_IO = 1; ENC_CS_TRIS = 0; // Make the Chip Select pin an output ENC_SCK_TRIS = 0; ENC_SDO_TRIS = 0; ENC_SDI_TRIS = 1; // Set up SPI #if defined(__18CXX) ENC_SPICON1 = 0x20; // SSPEN bit is set, SPI in master mode, FOSC/4, // IDLE state is low level ENC_SPI_IF = 0; ENC_SPISTATbits.CKE = 1; // Transmit data on rising edge of clock ENC_SPISTATbits.SMP = 0; // Input sampled at middle of data output time #else ENC_SPISTAT = 0; // clear SPI #if defined(__PIC24H__) || defined(__dsPIC33F__) ENC_SPICON1 = 0x0F; // 1:1 primary prescale, 5:1 secondary prescale (8MHz @ 40MIPS) // ENC_SPICON1 = 0x1E; // 4:1 primary prescale, 1:1 secondary prescale (10MHz @ 40MIPS, Doesn't work. CLKRDY is incorrectly reported as being clear. Problem caused by dsPIC33/PIC24H ES silicon bug.) #elif defined(__PIC24F__) // ENC_SPICON1 = 0x1F; // 1:1 prescale broken on PIC24F ES silicon (16MHz @ 16MIPS) ENC_SPICON1 = 0x1B; // 1:1 primary prescale, 2:1 secondary prescale (8MHz @ 16MIPS) #else // dsPIC30F ENC_SPICON1 = 0x17; // 1:1 primary prescale, 3:1 secondary prescale (10MHz @ 30MIPS) #endif ENC_SPICON2 = 0; ENC_SPICON1bits.CKE = 1; ENC_SPICON1bits.MSTEN = 1; ENC_SPISTATbits.SPIEN = 1; #endif // Wait for CLKRDY to become set. // Bit 3 in ESTAT is an unimplemented bit. If it reads out as '1' that // means the part is in RESET or there is something wrong with the SPI // connection. This loop makes sure that we can communicate with the // ENC28J60 before proceeding. do { i = ReadETHReg(ESTAT).Val; } while((i & 0x08) || (~i & ESTAT_CLKRDY)); // RESET the entire ENC28J60, clearing all registers SendSystemReset(); DelayMs(1); // Start up in Bank 0 and configure the receive buffer boundary pointers // and the buffer write protect pointer (receive buffer read pointer) WasDiscarded = TRUE; NextPacketLocation.Val = RXSTART; WriteReg(ERXSTL, LOW(RXSTART)); WriteReg(ERXSTH, HIGH(RXSTART)); WriteReg(ERXRDPTL, LOW(RXSTOP)); // Write low byte first WriteReg(ERXRDPTH, HIGH(RXSTOP)); // Write high byte last WriteReg(ERXNDL, LOW(RXSTOP)); WriteReg(ERXNDH, HIGH(RXSTOP)); WriteReg(ETXSTL, LOW(TXSTART)); WriteReg(ETXSTH, HIGH(TXSTART)); // Write a permanant per packet control byte of 0x00 WriteReg(EWRPTL, LOW(TXSTART)); WriteReg(EWRPTH, HIGH(TXSTART)); MACPut(0x00); // Enter Bank 1 and configure Receive Filters // (No need to reconfigure - Unicast OR Broadcast with CRC checking is // acceptable) // Write ERXFCON_CRCEN only to ERXFCON to enter promiscuous mode // Promiscious mode example: //BankSel(ERXFCON); //WriteReg((BYTE)ERXFCON, ERXFCON_CRCEN); // Enter Bank 2 and configure the MAC BankSel(MACON1); // Enable the receive portion of the MAC WriteReg((BYTE)MACON1, MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN); // Pad packets to 60 bytes, add CRC, and check Type/Length field. #if defined(FULL_DUPLEX) WriteReg((BYTE)MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX); WriteReg((BYTE)MABBIPG, 0x15); #else WriteReg((BYTE)MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); WriteReg((BYTE)MABBIPG, 0x12); #endif // Allow infinite deferals if the medium is continuously busy // (do not time out a transmission if the half duplex medium is // completely saturated with other people's data) WriteReg((BYTE)MACON4, MACON4_DEFER); // Late collisions occur beyond 63+8 bytes (8 bytes for preamble/start of frame delimiter) // 55 is all that is needed for IEEE 802.3, but ENC28J60 B5 errata for improper link pulse // collisions will occur less often with a larger number. WriteReg((BYTE)MACLCON2, 63); // Set non-back-to-back inter-packet gap to 9.6us. The back-to-back // inter-packet gap (MABBIPG) is set by MACSetDuplex() which is called // later. WriteReg((BYTE)MAIPGL, 0x12); WriteReg((BYTE)MAIPGH, 0x0C); // Set the maximum packet size which the controller will accept WriteReg((BYTE)MAMXFLL, LOW(6+6+2+1500+4)); // 1518 is the IEEE 802.3 specified limit WriteReg((BYTE)MAMXFLH, HIGH(6+6+2+1500+4)); // 1518 is the IEEE 802.3 specified limit // Enter Bank 3 and initialize physical MAC address registers BankSel(MAADR1); WriteReg((BYTE)MAADR1, AppConfig.MyMACAddr.v[0]); WriteReg((BYTE)MAADR2, AppConfig.MyMACAddr.v[1]); WriteReg((BYTE)MAADR3, AppConfig.MyMACAddr.v[2]); WriteReg((BYTE)MAADR4, AppConfig.MyMACAddr.v[3]); WriteReg((BYTE)MAADR5, AppConfig.MyMACAddr.v[4]); WriteReg((BYTE)MAADR6, AppConfig.MyMACAddr.v[5]); // Disable the CLKOUT output to reduce EMI generation WriteReg((BYTE)ECOCON, 0x00); // Output off (0V) //WriteReg((BYTE)ECOCON, 0x01); // 25.000MHz //WriteReg((BYTE)ECOCON, 0x03); // 8.3333MHz (*4 with PLL is 33.3333MHz) // Get the Rev ID so that we can implement the correct errata workarounds ENCRevID = ReadETHReg((BYTE)EREVID).Val; // Disable half duplex loopback in PHY. Bank bits changed to Bank 2 as a // side effect. WritePHYReg(PHCON2, PHCON2_HDLDIS); // Configure LEDA to display LINK status, LEDB to display TX/RX activity SetLEDConfig(0x3472); // Set the MAC and PHY into the proper duplex state #if defined(FULL_DUPLEX) WritePHYReg(PHCON1, PHCON1_PDPXMD); #elif defined(HALF_DUPLEX) WritePHYReg(PHCON1, 0x0000); #else // Use the external LEDB polarity to determine weather full or half duplex // communication mode should be set. { REG Register; PHYREG PhyReg; // Read the PHY duplex mode PhyReg = ReadPHYReg(PHCON1); DuplexState = PhyReg.PHCON1bits.PDPXMD; // Set the MAC to the proper duplex mode BankSel(MACON3); Register = ReadMACReg((BYTE)MACON3); Register.MACON3bits.FULDPX = PhyReg.PHCON1bits.PDPXMD; WriteReg((BYTE)MACON3, Register.Val); // 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 WriteReg((BYTE)MABBIPG, PhyReg.PHCON1bits.PDPXMD ? 0x15 : 0x12); } #endif BankSel(ERDPTL); // Return to default Bank 0 // Enable packet reception BFSReg(ECON1, ECON1_RXEN); }//end MACInit /****************************************************************************** * Function: BOOL MACIsLinked(void) * * PreCondition: None * * Input: None * * Output: TRUE: If the PHY reports that a link partner is present * and the link has been up continuously since the last * call to MACIsLinked() * FALSE: If the PHY reports no link partner, or the link went * down momentarily since the last call to MACIsLinked() * * Side Effects: None * * Overview: Returns the PHSTAT1.LLSTAT bit. * * Note: None *****************************************************************************/ BOOL MACIsLinked(void) { // LLSTAT is a latching low link status bit. Therefore, if the link // goes down and comes back up before a higher level stack program calls // MACIsLinked(), MACIsLinked() will still return FALSE. The next // call to MACIsLinked() will return TRUE (unless the link goes down // again). return ReadPHYReg(PHSTAT1).PHSTAT1bits.LLSTAT; } /****************************************************************************** * Function: BOOL MACIsTxReady(void) * * PreCondition: None * * Input: None * * Output: TRUE: If no Ethernet transmission is in progress * FALSE: If a previous transmission was started, and it has * not completed yet. While FALSE, the data in the * transmit buffer and the TXST/TXND pointers must not * be changed. * * Side Effects: None * * Overview: Returns the ECON1.TXRTS bit * * Note: None *****************************************************************************/ BOOL MACIsTxReady(void) { return !ReadETHReg(ECON1).ECON1bits.TXRTS; } /****************************************************************************** * Function: void MACDiscardRx(void) * * PreCondition: None * * Input: None * * Output: None * * Side Effects: None * * Overview: Marks the last received packet (obtained using * MACGetHeader())as being processed and frees the buffer * memory associated with it * * Note: None *****************************************************************************/ void MACDiscardRx(void) { WORD_VAL NewRXRDLocation; // Make sure the current packet was not already discarded if(WasDiscarded) return; WasDiscarded = TRUE; // Decrement the next packet pointer before writing it into // the ERXRDPT registers. This is a silicon errata workaround. // RX buffer wrapping must be taken into account if the // NextPacketLocation is precisely RXSTART. NewRXRDLocation.Val = NextPacketLocation.Val - 1; if(NewRXRDLocation.Val > RXSTOP) { NewRXRDLocation.Val = RXSTOP; } // Decrement the RX packet counter register, EPKTCNT BFSReg(ECON2, ECON2_PKTDEC); // Move the receive read pointer to unwrite-protect the memory used by the // last packet. The writing order is important: set the low byte first, // high byte last. WriteReg(ERXRDPTL, NewRXRDLocation.v[0]); WriteReg(ERXRDPTH, NewRXRDLocation.v[1]); } /****************************************************************************** * Function: WORD MACGetFreeRxSize(void) * * PreCondition: None * * Input: None * * Output: A WORD estimate of how much RX buffer space is free at * the present time. * * Side Effects: None * * Overview: None * * Note: None *****************************************************************************/ WORD MACGetFreeRxSize(void) { WORD_VAL ReadPT, WritePT; // Read the Ethernet hardware buffer write pointer. Because packets can be // received at any time, it can change between reading the low and high // bytes. A loop is necessary to make certain a proper low/high byte pair // is read. BankSel(EPKTCNT); do { // Save EPKTCNT in a temporary location ReadPT.v[0] = ReadETHReg((BYTE)EPKTCNT).Val; BankSel(ERXWRPTL); WritePT.v[0] = ReadETHReg(ERXWRPTL).Val; WritePT.v[1] = ReadETHReg(ERXWRPTH).Val; BankSel(EPKTCNT); } while(ReadETHReg((BYTE)EPKTCNT).Val != ReadPT.v[0]); // Determine where the write protection pointer is BankSel(ERXRDPTL); ReadPT.v[0] = ReadETHReg(ERXRDPTL).Val; ReadPT.v[1] = ReadETHReg(ERXRDPTH).Val; // Calculate the difference between the pointers, taking care to account // for buffer wrapping conditions if(WritePT.Val > ReadPT.Val) { return (RXSTOP - RXSTART) - (WritePT.Val - ReadPT.Val); } else if(WritePT.Val == ReadPT.Val) { return RXSIZE - 1; } else { return ReadPT.Val - WritePT.Val - 1; } } /****************************************************************************** * Function: BOOL MACGetHeader(MAC_ADDR *remote, BYTE* type) * * PreCondition: None * * Input: *remote: Location to store the Source MAC address of the * received frame. * *type: Location of a BYTE to store the constant * MAC_UNKNOWN, ETHER_IP, or ETHER_ARP, representing * the contents of the Ethernet type field. * * Output: TRUE: If a packet was waiting in the RX buffer. The * remote, and type values are updated. * FALSE: If a packet was not pending. remote and type are * not changed. * * Side Effects: Last packet is discarded if MACDiscardRx() hasn't already * been called. * * Overview: None * * Note: None *****************************************************************************/ BOOL MACGetHeader(MAC_ADDR *remote, BYTE* type) { ENC_PREAMBLE header; BYTE PacketCount; // Test if at least one packet has been received and is waiting BankSel(EPKTCNT); PacketCount = ReadETHReg((BYTE)EPKTCNT).Val; BankSel(ERDPTL); if(PacketCount == 0u) return FALSE; // Make absolutely certain that any previous packet was discarded if(WasDiscarded == FALSE) { MACDiscardRx(); return FALSE; } // Set the SPI read pointer to the beginning of the next unprocessed packet CurrentPacketLocation.Val = NextPacketLocation.Val; WriteReg(ERDPTL, CurrentPacketLocation.v[0]); WriteReg(ERDPTH, CurrentPacketLocation.v[1]); // Obtain the MAC header from the Ethernet buffer MACGetArray((BYTE*)&header, sizeof(header)); // The EtherType field, like most items transmitted on the Ethernet medium // are in big endian. header.Type.Val = swaps(header.Type.Val); // Validate the data returned from the ENC28J60. Random data corruption, // such as if a single SPI bit error occurs while communicating or a // momentary power glitch could cause this to occur in rare circumstances. if(header.NextPacketPointer > RXSTOP || ((BYTE_VAL*)(&header.NextPacketPointer))->bits.b0 || header.StatusVector.bits.Zero || header.StatusVector.bits.CRCError || header.StatusVector.bits.ByteCount > 1518u || !header.StatusVector.bits.ReceiveOk) { Reset(); } // Save the location where the hardware will write the next packet to NextPacketLocation.Val = header.NextPacketPointer; // Return the Ethernet frame's Source MAC address field to the caller // This parameter is useful for replying to requests without requiring an // ARP cycle. memcpy((void*)remote->v, (void*)header.SourceMACAddr.v, sizeof(*remote)); // Return a simplified version of the EtherType field to the caller *type = MAC_UNKNOWN; if( (header.Type.v[1] == 0x08u) && ((header.Type.v[0] == ETHER_IP) || (header.Type.v[0] == ETHER_ARP)) ) { *type = header.Type.v[0]; } // Mark this packet as discardable WasDiscarded = FALSE; return TRUE; } /****************************************************************************** * Function: void MACPutHeader(MAC_ADDR *remote, BYTE type, WORD dataLen) * * PreCondition: MACIsTxReady() must return TRUE. * * Input: *remote: Pointer to memory which contains the destination * MAC address (6 bytes) * type: The constant ETHER_ARP or ETHER_IP, defining which * value to write into the Ethernet header's type field. * dataLen: Length of the Ethernet data payload * * Output: None * * Side Effects: None * * Overview: None * * Note: Because of the dataLen parameter, it is probably * advantagous to call this function immediately before * transmitting a packet rather than initially when the * packet is first created. The order in which the packet * is constructed (header first or data first) is not * important. *****************************************************************************/ void MACPutHeader(MAC_ADDR *remote, BYTE type, WORD dataLen) { // Set the SPI write pointer to the beginning of the transmit buffer (post per packet control byte) WriteReg(EWRPTL, LOW(TXSTART+1)); WriteReg(EWRPTH, HIGH(TXSTART+1)); // Calculate where to put the TXND pointer dataLen += (WORD)sizeof(ETHER_HEADER) + TXSTART; // Write the TXND pointer into the registers, given the dataLen given WriteReg(ETXNDL, ((WORD_VAL*)&dataLen)->v[0]); WriteReg(ETXNDH, ((WORD_VAL*)&dataLen)->v[1]); // Set the per-packet control byte and write the Ethernet destination // address MACPutArray((BYTE*)remote, sizeof(*remote)); // Write our MAC address in the Ethernet source field MACPutArray((BYTE*)&AppConfig.MyMACAddr, sizeof(AppConfig.MyMACAddr)); // Write the appropriate Ethernet Type WORD for the protocol being used MACPut(0x08); MACPut((type == MAC_IP) ? ETHER_IP : ETHER_ARP); } /****************************************************************************** * Function: void MACFlush(void) * * PreCondition: A packet has been created by calling MACPut() and * MACPutHeader(). * * Input: None * * Output: None * * Side Effects: None * * Overview: MACFlush causes the current TX packet to be sent out on * the Ethernet medium. The hardware MAC will take control * and handle CRC generation, collision retransmission and * other details. * * Note: After transmission completes (MACIsTxReady() returns TRUE), * the packet can be modified and transmitted again by calling * MACFlush() again. Until MACPutHeader() or MACPut() is * called (in the TX data area), the data in the TX buffer * will not be corrupted. *****************************************************************************/ void MACFlush(void) { // Reset transmit logic if a TX Error has previously occured // This is a silicon errata workaround BFSReg(ECON1, ECON1_TXRST); BFCReg(ECON1, ECON1_TXRST); BFCReg(EIR, EIR_TXERIF | EIR_TXIF); // Start the transmission // After transmission completes (MACIsTxReady() returns TRUE), the packet // can be modified and transmitted again by calling MACFlush() again. // Until MACPutHeader() is called, the data in the TX buffer will not be // corrupted. BFSReg(ECON1, ECON1_TXRTS); // Revision B5 and B7 silicon errata workaround if(ENCRevID == 0x05u || ENCRevID == 0x06u) { WORD AttemptCounter = 0x0000; while(!(ReadETHReg(EIR).Val & (EIR_TXERIF | EIR_TXIF)) && (++AttemptCounter < 1000)); if(ReadETHReg(EIR).EIRbits.TXERIF || (AttemptCounter >= 1000)) { WORD_VAL ReadPtrSave; WORD_VAL TXEnd; TXSTATUS TXStatus; BYTE i; // Cancel the previous transmission if it has become stuck set BFCReg(ECON1, ECON1_TXRTS); // Save the current read pointer (controlled by application) ReadPtrSave.v[0] = ReadETHReg(ERDPTL).Val; ReadPtrSave.v[1] = ReadETHReg(ERDPTH).Val; // Get the location of the transmit status vector TXEnd.v[0] = ReadETHReg(ETXNDL).Val; TXEnd.v[1] = ReadETHReg(ETXNDH).Val; TXEnd.Val++; // Read the transmit status vector WriteReg(ERDPTL, TXEnd.v[0]); WriteReg(ERDPTH, TXEnd.v[1]); MACGetArray((BYTE*)&TXStatus, sizeof(TXStatus)); // Implement retransmission if a late collision occured (this can // happen on B5 when certain link pulses arrive at the same time // as the transmission) for(i = 0; i < 16u; i++) { if(ReadETHReg(EIR).EIRbits.TXERIF && TXStatus.bits.LateCollision) { // Reset the TX logic BFSReg(ECON1, ECON1_TXRST); BFCReg(ECON1, ECON1_TXRST); BFCReg(EIR, EIR_TXERIF | EIR_TXIF); // Transmit the packet again BFSReg(ECON1, ECON1_TXRTS); while(!(ReadETHReg(EIR).Val & (EIR_TXERIF | EIR_TXIF))); // Cancel the previous transmission if it has become stuck set BFCReg(ECON1, ECON1_TXRTS); // Read transmit status vector WriteReg(ERDPTL, TXEnd.v[0]); WriteReg(ERDPTH, TXEnd.v[1]); MACGetArray((BYTE*)&TXStatus, sizeof(TXStatus)); } else { break; } } // Restore the current read pointer WriteReg(ERDPTL, ReadPtrSave.v[0]); WriteReg(ERDPTH, ReadPtrSave.v[1]); } } } /****************************************************************************** * Function: void MACSetReadPtrInRx(WORD offset) * * PreCondition: A packet has been obtained by calling MACGetHeader() and * getting a TRUE result. * * Input: offset: WORD specifying how many bytes beyond the Ethernet * header's type field to relocate the SPI read * pointer. * * Output: None * * Side Effects: None * * Overview: SPI read pointer are updated. All calls to * MACGet() and MACGetArray() will use these new values. * * Note: RXSTOP must be statically defined as being > RXSTART for * this function to work correctly. In other words, do not * define an RX buffer which spans the 0x1FFF->0x0000 memory * boundary. *****************************************************************************/ void MACSetReadPtrInRx(WORD offset) { WORD_VAL ReadPT; // Determine the address of the beginning of the entire packet // and adjust the address to the desired location ReadPT.Val = CurrentPacketLocation.Val + sizeof(ENC_PREAMBLE) + offset; // Since the receive buffer is circular, adjust if a wraparound is needed if(ReadPT.Val > RXSTOP) ReadPT.Val -= RXSIZE; // Set the SPI read pointer to the new calculated value WriteReg(ERDPTL, ReadPT.v[0]); WriteReg(ERDPTH, ReadPT.v[1]); } /****************************************************************************** * Function: WORD MACSetWritePtr(WORD Address) * * PreCondition: None * * Input: Address: Address to seek to * * Output: WORD: Old EWRPT location * * Side Effects: None * * Overview: SPI write pointer is updated. All calls to * MACPut() and MACPutArray() will use this new value. * * Note: None *****************************************************************************/ WORD MACSetWritePtr(WORD address) { WORD_VAL oldVal; oldVal.v[0] = ReadETHReg(EWRPTL).Val; oldVal.v[1] = ReadETHReg(EWRPTH).Val; // Set the SPI write pointer to the new calculated value WriteReg(EWRPTL, ((WORD_VAL*)&address)->v[0]); WriteReg(EWRPTH, ((WORD_VAL*)&address)->v[1]); return oldVal.Val; } /****************************************************************************** * Function: WORD MACSetReadPtr(WORD Address) * * PreCondition: None * * Input: Address: Address to seek to * * Output: WORD: Old ERDPT value * * Side Effects: None * * Overview: SPI write pointer is updated. All calls to * MACPut() and MACPutArray() will use this new value. * * Note: None *****************************************************************************/ WORD MACSetReadPtr(WORD address) { WORD_VAL oldVal; oldVal.v[0] = ReadETHReg(ERDPTL).Val; oldVal.v[1] = ReadETHReg(ERDPTH).Val; // Set the SPI write pointer to the new calculated value WriteReg(ERDPTL, ((WORD_VAL*)&address)->v[0]); WriteReg(ERDPTH, ((WORD_VAL*)&address)->v[1]); return oldVal.Val; } /****************************************************************************** * Function: WORD MACCalcRxChecksum(WORD offset, WORD len) * * PreCondition: None * * Input: offset - Number of bytes beyond the beginning of the * Ethernet data (first byte after the type field) * where the checksum should begin * len - Total number of bytes to include in the checksum * * Output: 16-bit checksum as defined by RFC 793. * * Side Effects: None * * Overview: This function performs a checksum calculation in the MAC * buffer itself * * Note: None *****************************************************************************/ WORD MACCalcRxChecksum(WORD offset, WORD len) { WORD_VAL temp; WORD_VAL RDSave; // Add the offset requested by firmware plus the Ethernet header temp.Val = CurrentPacketLocation.Val + sizeof(ENC_PREAMBLE) + offset; if(temp.Val > RXSTOP) // Adjust value if a wrap is needed { temp.Val -= RXSIZE; } RDSave.v[0] = ReadETHReg(ERDPTL).Val; RDSave.v[1] = ReadETHReg(ERDPTH).Val; WriteReg(ERDPTL, temp.v[0]); WriteReg(ERDPTH, temp.v[1]); temp.Val = CalcIPBufferChecksum(len); WriteReg(ERDPTL, RDSave.v[0]); WriteReg(ERDPTH, RDSave.v[1]); return temp.Val; } /****************************************************************************** * Function: WORD CalcIPBufferChecksum(WORD len) * * PreCondition: Read buffer pointer set to starting of checksum data * * Input: len: Total number of bytes to calculate the checksum over. * The first byte included in the checksum is the byte * pointed to by ERDPT, which is updated by calls to * MACSetReadPtr(), MACGet(), MACGetArray(), * MACGetHeader(), etc. * * Output: 16-bit checksum as defined by RFC 793 * * Side Effects: None * * Overview: This function performs a checksum calculation in the MAC * buffer itself. The ENC28J60 has a hardware DMA module * which can calculate the checksum faster than software, so * this function replaces the CaclIPBufferChecksum() function * defined in the helpers.c file. Through the use of * preprocessor defines, this replacement is automatic. * * Note: This function works either in the RX buffer area or the TX * buffer area. No validation is done on the len parameter. *****************************************************************************/ WORD CalcIPBufferChecksum(WORD len) { WORD_VAL Start; DWORD_VAL Checksum = {0x00000000ul}; WORD ChunkLen; BYTE DataBuffer[20]; // Must be an even size WORD *DataPtr; // Save the SPI read pointer starting address Start.v[0] = ReadETHReg(ERDPTL).Val; Start.v[1] = ReadETHReg(ERDPTH).Val; while(len) { // Obtain a chunk of data (less SPI overhead compared // to requesting one byte at a time) ChunkLen = len > sizeof(DataBuffer) ? sizeof(DataBuffer) : len; MACGetArray(DataBuffer, ChunkLen); len -= ChunkLen; // Take care of a last odd numbered data byte if(((WORD_VAL*)&ChunkLen)->bits.b0) { DataBuffer[ChunkLen] = 0x00; ChunkLen++; } // Calculate the checksum over this chunk DataPtr = (WORD*)&DataBuffer[0]; while(ChunkLen) { Checksum.Val += *DataPtr++; ChunkLen -= 2; } } // Restore old read pointer location WriteReg(ERDPTL, Start.v[0]); WriteReg(ERDPTH, Start.v[1]); // Do an end-around carry (one's complement arrithmatic) Checksum.Val = (DWORD)Checksum.w[0] + (DWORD)Checksum.w[1]; // Do another end-around carry in case if the prior add // caused a carry out Checksum.w[0] += Checksum.w[1]; // Return the resulting checksum return ~Checksum.w[0]; } /****************************************************************************** * Function: void MACMemCopyAsync(WORD destAddr, WORD sourceAddr, WORD len) * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: destAddr: Destination address in the Ethernet memory to * copy to. If the MSb is set, the current EWRPT * value will be used instead. * sourceAddr: Source address to read from. If the MSb is * set, the current ERDPT value will be used * instead. * len: Number of bytes to copy * * Output: Byte read from the ENC28J60's RAM * * Side Effects: None * * Overview: Bytes are asynchrnously transfered within the buffer. Call * MACIsMemCopyDone() to see when the transfer is complete. * * Note: If a prior transfer is already in progress prior to * calling this function, this function will block until it * can start this transfer. * * If a negative value is used for the sourceAddr or destAddr * parameters, then that pointer will get updated with the * next address after the read or write. *****************************************************************************/ void MACMemCopyAsync(WORD destAddr, WORD sourceAddr, WORD len) { WORD_VAL ReadSave, WriteSave; BOOL UpdateWritePointer = FALSE; BOOL UpdateReadPointer = FALSE; if(((WORD_VAL*)&destAddr)->bits.b15) { UpdateWritePointer = TRUE; ((WORD_VAL*)&destAddr)->v[0] = ReadETHReg(EWRPTL).Val; ((WORD_VAL*)&destAddr)->v[1] = ReadETHReg(EWRPTH).Val; } if(((WORD_VAL*)&sourceAddr)->bits.b15) { UpdateReadPointer = TRUE; ((WORD_VAL*)&sourceAddr)->v[0] = ReadETHReg(ERDPTL).Val; ((WORD_VAL*)&sourceAddr)->v[1] = ReadETHReg(ERDPTH).Val; } // Handle special conditions where len == 0 or len == 1 // The DMA module is not capable of handling those corner cases if(len <= 1u) { if(!UpdateReadPointer) { ReadSave.v[0] = ReadETHReg(ERDPTL).Val; ReadSave.v[1] = ReadETHReg(ERDPTH).Val; } if(!UpdateWritePointer) { WriteSave.v[0] = ReadETHReg(EWRPTL).Val; WriteSave.v[1] = ReadETHReg(EWRPTH).Val; } WriteReg(ERDPTL, ((WORD_VAL*)&sourceAddr)->v[0]); WriteReg(ERDPTH, ((WORD_VAL*)&sourceAddr)->v[1]); WriteReg(EWRPTL, ((WORD_VAL*)&destAddr)->v[0]); WriteReg(EWRPTH, ((WORD_VAL*)&destAddr)->v[1]); while(len--) MACPut(MACGet()); if(!UpdateReadPointer) { WriteReg(ERDPTL, ReadSave.v[0]); WriteReg(ERDPTH, ReadSave.v[1]); } if(!UpdateWritePointer) { WriteReg(EWRPTL, WriteSave.v[0]); WriteReg(EWRPTH, WriteSave.v[1]); } } else { if(UpdateWritePointer) { WriteSave.Val = destAddr + len; WriteReg(EWRPTL, WriteSave.v[0]); WriteReg(EWRPTH, WriteSave.v[1]); } len += sourceAddr - 1; while(ReadETHReg(ECON1).ECON1bits.DMAST); WriteReg(EDMASTL, ((WORD_VAL*)&sourceAddr)->v[0]); WriteReg(EDMASTH, ((WORD_VAL*)&sourceAddr)->v[1]); WriteReg(EDMADSTL, ((WORD_VAL*)&destAddr)->v[0]); WriteReg(EDMADSTH, ((WORD_VAL*)&destAddr)->v[1]); if((sourceAddr <= RXSTOP) && (len > RXSTOP)) //&& (sourceAddr >= RXSTART)) len -= RXSIZE; WriteReg(EDMANDL, ((WORD_VAL*)&len)->v[0]); WriteReg(EDMANDH, ((WORD_VAL*)&len)->v[1]); BFCReg(ECON1, ECON1_CSUMEN); BFSReg(ECON1, ECON1_DMAST); if(UpdateReadPointer) { len++; if((sourceAddr <= RXSTOP) && (len > RXSTOP)) //&& (sourceAddr >= RXSTART)) len -= RXSIZE; WriteReg(ERDPTL, ((WORD_VAL*)&len)->v[0]); WriteReg(ERDPTH, ((WORD_VAL*)&len)->v[1]); } } } BOOL MACIsMemCopyDone(void) { return !ReadETHReg(ECON1).ECON1bits.DMAST; } /****************************************************************************** * Function: BYTE MACGet() * * PreCondition: SPI bus must be initialized (done in MACInit()). * ERDPT must point to the place to read from. * * Input: None * * Output: Byte read from the ENC28J60's RAM * * Side Effects: None * * Overview: MACGet returns the byte pointed to by ERDPT and * increments ERDPT so MACGet() can be called again. The * increment will follow the receive buffer wrapping boundary. * * Note: None *****************************************************************************/ BYTE MACGet() { BYTE Result; ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = RBM; while(!ENC_SPI_IF); // Wait until opcode/address is transmitted. Result = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_SSPBUF = 0; // Send a dummy byte to receive the register // contents. while(!ENC_SPI_IF); // Wait until register is received. Result = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_CS_IO = 1; return Result; }//end MACGet /****************************************************************************** * Function: WORD MACGetArray(BYTE *val, WORD len) * * PreCondition: SPI bus must be initialized (done in MACInit()). * ERDPT must point to the place to read from. * * Input: *val: Pointer to storage location * len: Number of bytes to read from the data buffer. * * Output: Byte(s) of data read from the data buffer. * * Side Effects: None * * Overview: Burst reads several sequential bytes from the data buffer * and places them into local memory. With SPI burst support, * it performs much faster than multiple MACGet() calls. * ERDPT is incremented after each byte, following the same * rules as MACGet(). * * Note: None *****************************************************************************/ WORD MACGetArray(BYTE *val, WORD len) { // Debug #if defined(__18F8722) || defined(_18F8722) || \ defined(__18F8627) || defined(_18F8627) || \ defined(__18F8622) || defined(_18F8622) || \ defined(__18F8572) || defined(_18F8572) || \ defined(__18F6722) || defined(_18F6722) || \ defined(__18F6627) || defined(_18F6627) || \ defined(__18F6622) || defined(_18F6622) || \ defined(__18F6527) || defined(_18F6527) WORD i; BYTE Dummy; i = len; Dummy = 0xFF; while(i--) { if(((BYTE_VAL*)&Dummy)->bits.b0) { // End bust operation ENC_CS_IO = 1; ((BYTE_VAL*)&Dummy)->bits.b0 = 0; // Start the burst operation ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = RBM; // Send the Read Buffer Memory opcode. while(!ENC_SPI_IF); // Wait until opcode/address is transmitted. } else Dummy = 0xFF; ENC_SPI_IF = 0; ENC_SSPBUF = 0; // Send a dummy byte to receive a byte if(val) { while(!ENC_SPI_IF); // Wait until byte is received. *val++ = ENC_SSPBUF; } else while(!ENC_SPI_IF); // Wait until byte is received. } ENC_CS_IO = 1; ENC_SPI_IF = 0; return len; #else WORD i; BYTE Dummy; // Start the burst operation ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = RBM; // Send the Read Buffer Memory opcode. i = 0; val--; while(!ENC_SPI_IF); // Wait until opcode/address is transmitted. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; // Read the data while(ibits.b0) { // End bust operation ENC_CS_IO = 1; ((BYTE_VAL*)&Dummy)->bits.b0 = 0; // Start the burst operation ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = WBM; // Send the Read Buffer Memory opcode. while(!ENC_SPI_IF); // Wait until opcode/address is transmitted. } else Dummy = 0xFF; ENC_SPI_IF = 0; ENC_SSPBUF = *val++; // Send byte while(!ENC_SPI_IF); // Wait until byte is sent } ENC_SPI_IF = 0; ENC_CS_IO = 1; return; #else BYTE Dummy; // Select the chip and send the proper opcode ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = WBM; // Send the Write Buffer Memory opcode while(!ENC_SPI_IF); // Wait until opcode/constant is transmitted. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; // Send the data while(len) { ENC_SSPBUF = *val; // Start sending the byte val++; // Increment after writing to ENC_SSPBUF to increase speed len--; // Decrement after writing to ENC_SSPBUF to increase speed while(!ENC_SPI_IF); // Wait until byte is transmitted Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; }; // Terminate the burst operation ENC_CS_IO = 1; #endif }//end MACPutArray /****************************************************************************** * Function: void MACPutROMArray(ROM BYTE *val, WORD len) * * PreCondition: SPI bus must be initialized (done in MACInit()). * EWRPT must point to the location to begin writing. * * Input: *val: Pointer to source of bytes to copy. * len: Number of bytes to write to the data buffer. * * Output: None * * Side Effects: None * * Overview: MACPutArray writes several sequential bytes to the * ENC28J60 RAM. It performs faster than multiple MACPut() * calls. EWRPT is incremented by len. * * Note: None *****************************************************************************/ void MACPutROMArray(ROM BYTE *val, WORD len) { // Debug #if defined(__18F8722) || defined(_18F8722) || \ defined(__18F8627) || defined(_18F8627) || \ defined(__18F8622) || defined(_18F8622) || \ defined(__18F8572) || defined(_18F8572) || \ defined(__18F6722) || defined(_18F6722) || \ defined(__18F6627) || defined(_18F6627) || \ defined(__18F6622) || defined(_18F6622) || \ defined(__18F6527) || defined(_18F6527) WORD i; BYTE Dummy; i = len; Dummy = 0xFF; while(i--) { if(((BYTE_VAL*)&Dummy)->bits.b0) { // End bust operation ENC_CS_IO = 1; ((BYTE_VAL*)&Dummy)->bits.b0 = 0; // Start the burst operation ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = WBM; // Send the Read Buffer Memory opcode. while(!ENC_SPI_IF); // Wait until opcode/address is transmitted. } else Dummy = 0xFF; ENC_SPI_IF = 0; ENC_SSPBUF = *val++; // Send byte while(!ENC_SPI_IF); // Wait until byte is sent } ENC_SPI_IF = 0; ENC_CS_IO = 1; return; #else BYTE Dummy; // Select the chip and send the proper opcode ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = WBM; // Send the Write Buffer Memory opcode while(!ENC_SPI_IF); // Wait until opcode/constant is transmitted. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; // Send the data while(len) { ENC_SSPBUF = *val; // Start sending the byte val++; // Increment after writing to ENC_SSPBUF to increase speed len--; // Decrement after writing to ENC_SSPBUF to increase speed while(!ENC_SPI_IF); // Wait until byte is transmitted Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; }; // Terminate the burst operation ENC_CS_IO = 1; #endif }//end MACPutROMArray /****************************************************************************** * Function: static void SendSystemReset(void) * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: None * * Output: None * * Side Effects: None * * Overview: SendSystemReset sends the System Reset SPI command to * the Ethernet controller. It resets all register contents * (except for ECOCON) and returns the device to the power * on default state. * * Note: None *****************************************************************************/ static void SendSystemReset(void) { BYTE Dummy; ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = SR; while(!ENC_SPI_IF); // Wait until the command is transmitted. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_CS_IO = 1; }//end SendSystemReset /****************************************************************************** * Function: REG ReadETHReg(BYTE Address) * * PreCondition: SPI bus must be initialized (done in MACInit()). * Bank select bits must be set corresponding to the register * to read from. * * Input: 5 bit address of the ETH control register to read from. * The top 3 bits must be 0. * * Output: Byte read from the Ethernet controller's ETH register. * * Side Effects: None * * Overview: ReadETHReg sends the 8 bit RCR opcode/Address byte over * the SPI and then retrives the register contents in the * next 8 SPI clocks. * * Note: This routine cannot be used to access MAC/MII or PHY * registers. Use ReadMACReg() or ReadPHYReg() for that * purpose. *****************************************************************************/ static REG ReadETHReg(BYTE Address) { REG r; // Select the chip and send the Read Control Register opcode/address ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = RCR | Address; while(!ENC_SPI_IF); // Wait until the opcode/address is transmitted r.Val = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_SSPBUF = 0; // Send a dummy byte to receive the register // contents while(!ENC_SPI_IF); // Wait until the register is received r.Val = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_CS_IO = 1; return r; }//end ReadETHReg /****************************************************************************** * Function: REG ReadMACReg(BYTE Address) * * PreCondition: SPI bus must be initialized (done in MACInit()). * Bank select bits must be set corresponding to the register * to read from. * * Input: 5 bit address of the MAC or MII register to read from. * The top 3 bits must be 0. * * Output: Byte read from the Ethernet controller's MAC/MII register. * * Side Effects: None * * Overview: ReadMACReg sends the 8 bit RCR opcode/Address byte as well * as a dummy byte over the SPI and then retrives the * register contents in the last 8 SPI clocks. * * Note: This routine cannot be used to access ETH or PHY * registers. Use ReadETHReg() or ReadPHYReg() for that * purpose. *****************************************************************************/ static REG ReadMACReg(BYTE Address) { REG r; ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = RCR | Address; // Send the Read Control Register opcode and // address. while(!ENC_SPI_IF); // Wait until opcode/address is transmitted. r.Val = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_SSPBUF = 0; // Send a dummy byte while(!ENC_SPI_IF); // Wait for the dummy byte to be transmitted r.Val = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_SSPBUF = 0; // Send another dummy byte to receive the register // contents. while(!ENC_SPI_IF); // Wait until register is received. r.Val = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_CS_IO = 1; return r; }//end ReadMACReg /****************************************************************************** * Function: ReadPHYReg * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: Address of the PHY register to read from. * * Output: 16 bits of data read from the PHY register. * * Side Effects: None * * Overview: ReadPHYReg performs an MII read operation. While in * progress, it simply polls the MII BUSY bit wasting time * (10.24us). * * Note: None *****************************************************************************/ PHYREG ReadPHYReg(BYTE Register) { PHYREG Result; // Set the right address and start the register read operation BankSel(MIREGADR); WriteReg((BYTE)MIREGADR, Register); WriteReg((BYTE)MICMD, MICMD_MIIRD); // Loop to wait until the PHY register has been read through the MII // This requires 10.24us BankSel(MISTAT); while(ReadMACReg((BYTE)MISTAT).MISTATbits.BUSY); // Stop reading BankSel(MIREGADR); WriteReg((BYTE)MICMD, 0x00); // Obtain results and return Result.VAL.v[0] = ReadMACReg((BYTE)MIRDL).Val; Result.VAL.v[1] = ReadMACReg((BYTE)MIRDH).Val; BankSel(ERDPTL); // Return to Bank 0 return Result; }//end ReadPHYReg /****************************************************************************** * Function: void WriteReg(BYTE Address, BYTE Data) * * PreCondition: SPI bus must be initialized (done in MACInit()). * Bank select bits must be set corresponding to the register * to modify. * * Input: 5 bit address of the ETH, MAC, or MII register to modify. * The top 3 bits must be 0. * Byte to be written into the register. * * Output: None * * Side Effects: None * * Overview: WriteReg sends the 8 bit WCR opcode/Address byte over the * SPI and then sends the data to write in the next 8 SPI * clocks. * * Note: This routine is almost identical to the BFCReg() and * BFSReg() functions. It is seperate to maximize speed. * Unlike the ReadETHReg/ReadMACReg functions, WriteReg() * can write to any ETH or MAC register. Writing to PHY * registers must be accomplished with WritePHYReg(). *****************************************************************************/ static void WriteReg(BYTE Address, BYTE Data) { BYTE Dummy; ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = WCR | Address; // Send the opcode and address. while(!ENC_SPI_IF); // Wait until opcode/address is transmitted. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_SSPBUF = Data; // Send the byte to be writen. while(!ENC_SPI_IF); // Wait until register is written. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_CS_IO = 1; }//end WriteReg /****************************************************************************** * Function: void BFCReg(BYTE Address, BYTE Data) * * PreCondition: SPI bus must be initialized (done in MACInit()). * Bank select bits must be set corresponding to the register * to modify. * * Input: 5 bit address of the register to modify. The top 3 bits * must be 0. * Byte to be used with the Bit Field Clear operation. * * Output: None * * Side Effects: None * * Overview: BFCReg sends the 8 bit BFC opcode/Address byte over the * SPI and then sends the data in the next 8 SPI clocks. * * Note: This routine is almost identical to the WriteReg() and * BFSReg() functions. It is separate to maximize speed. * BFCReg() must only be used on ETH registers. *****************************************************************************/ static void BFCReg(BYTE Address, BYTE Data) { BYTE Dummy; ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = BFC | Address; // Send the opcode and address. while(!ENC_SPI_IF); // Wait until opcode/address is transmitted. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_SSPBUF = Data; // Send the byte to be writen. while(!ENC_SPI_IF); // Wait until register is written. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_CS_IO = 1; }//end BFCReg /****************************************************************************** * Function: void BFSReg(BYTE Address, BYTE Data) * * PreCondition: SPI bus must be initialized (done in MACInit()). * Bank select bits must be set corresponding to the register * to modify. * * Input: 5 bit address of the register to modify. The top 3 bits * must be 0. * Byte to be used with the Bit Field Set operation. * * Output: None * * Side Effects: None * * Overview: BFSReg sends the 8 bit BFC opcode/Address byte over the * SPI and then sends the data in the next 8 SPI clocks. * * Note: This routine is almost identical to the WriteReg() and * BFCReg() functions. It is separate to maximize speed. * BFSReg() must only be used on ETH registers. *****************************************************************************/ static void BFSReg(BYTE Address, BYTE Data) { BYTE Dummy; ENC_CS_IO = 0; ENC_SPI_IF = 0; ENC_SSPBUF = BFS | Address; // Send the opcode and address. while(!ENC_SPI_IF); // Wait until opcode/address is transmitted. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_SSPBUF = Data; // Send the byte to be writen. while(!ENC_SPI_IF); // Wait until register is written. Dummy = ENC_SSPBUF; ENC_SPI_IF = 0; ENC_CS_IO = 1; }//end BFSReg /****************************************************************************** * Function: WritePHYReg * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: Address of the PHY register to write to. * 16 bits of data to write to PHY register. * * Output: None * * Side Effects: Alters bank bits to point to Bank 3 * * Overview: WritePHYReg performs an MII write operation. While in * progress, it simply polls the MII BUSY bit wasting time. * * Note: None *****************************************************************************/ void WritePHYReg(BYTE Register, WORD Data) { // Write the register address BankSel(MIREGADR); WriteReg((BYTE)MIREGADR, Register); // Write the data // Order is important: write low byte first, high byte last WriteReg((BYTE)MIWRL, ((WORD_VAL*)&Data)->v[0]); WriteReg((BYTE)MIWRH, ((WORD_VAL*)&Data)->v[1]); // Wait until the PHY register has been written BankSel(MISTAT); while(ReadMACReg((BYTE)MISTAT).MISTATbits.BUSY); BankSel(ERDPTL); // Return to Bank 0 }//end WritePHYReg /****************************************************************************** * Function: BankSel * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: Register address with the high byte containing the 2 bank * select 2 bits. * * Output: None * * Side Effects: None * * Overview: BankSel takes the high byte of a register address and * changes the bank select bits in ETHCON1 to match. * * Note: None *****************************************************************************/ static void BankSel(WORD Register) { BFCReg(ECON1, ECON1_BSEL1 | ECON1_BSEL0); BFSReg(ECON1, ((WORD_VAL*)&Register)->v[1]); }//end BankSel /****************************************************************************** * Function: void MACPowerDown(void) * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: None * * Output: None * * Side Effects: None * * Overview: MACPowerDown puts the ENC28J60 in low power sleep mode. In * sleep mode, no packets can be transmitted or received. * All MAC and PHY registers should not be accessed. * * Note: If a packet is being transmitted while this function is * called, this function will block until it is it complete. * If anything is being received, it will be completed. *****************************************************************************/ void MACPowerDown(void) { // Disable packet reception BFCReg(ECON1, ECON1_RXEN); // Make sure any last packet which was in-progress when RXEN was cleared // is completed while(ReadETHReg(ESTAT).ESTATbits.RXBUSY); // If a packet is being transmitted, wait for it to finish while(ReadETHReg(ECON1).ECON1bits.TXRTS); // Enter sleep mode BFSReg(ECON2, ECON2_PWRSV); }//end MACPowerDown /****************************************************************************** * Function: void MACPowerUp(void) * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: None * * Output: None * * Side Effects: None * * Overview: MACPowerUp returns the ENC28J60 back to normal operation * after a previous call to MACPowerDown(). Calling this * function when already powered up will have no effect. * * Note: If a link partner is present, it will take 10s of * milliseconds before a new link will be established after * waking up. While not linked, packets which are * transmitted will most likely be lost. MACIsLinked() can * be called to determine if a link is established. *****************************************************************************/ void MACPowerUp(void) { // Leave power down mode BFCReg(ECON2, ECON2_PWRSV); // Wait for the 300us Oscillator Startup Timer (OST) to time out. This // delay is required for the PHY module to return to an operational state. while(!ReadETHReg(ESTAT).ESTATbits.CLKRDY); // Enable packet reception BFSReg(ECON1, ECON1_RXEN); }//end MACPowerUp /****************************************************************************** * Function: void SetCLKOUT(BYTE NewConfig) * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: NewConfig - 0x00: CLKOUT disabled (pin driven low) * 0x01: Divide by 1 (25 MHz) * 0x02: Divide by 2 (12.5 MHz) * 0x03: Divide by 3 (8.333333 MHz) * 0x04: Divide by 4 (6.25 MHz, POR default) * 0x05: Divide by 8 (3.125 MHz) * * Output: None * * Side Effects: None * * Overview: Writes the value of NewConfig into the ECOCON register. * The CLKOUT pin will beginning outputting the new frequency * immediately. * * Note: *****************************************************************************/ void SetCLKOUT(BYTE NewConfig) { BankSel(ECOCON); WriteReg((BYTE)ECOCON, NewConfig); BankSel(ERDPTL); }//end SetCLKOUT /****************************************************************************** * Function: BYTE GetCLKOUT(void) * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: None * * Output: BYTE - 0x00: CLKOUT disabled (pin driven low) * 0x01: Divide by 1 (25 MHz) * 0x02: Divide by 2 (12.5 MHz) * 0x03: Divide by 3 (8.333333 MHz) * 0x04: Divide by 4 (6.25 MHz, POR default) * 0x05: Divide by 8 (3.125 MHz) * 0x06: Reserved * 0x07: Reserved * * Side Effects: None * * Overview: Returns the current value of the ECOCON register. * * Note: None *****************************************************************************/ BYTE GetCLKOUT(void) { BYTE i; BankSel(ECOCON); i = ReadETHReg((BYTE)ECOCON).Val; BankSel(ERDPTL); return i; }//end GetCLKOUT /****************************************************************************** * Function: void SetRXHashTableEntry(MAC_ADDR DestMACAddr) * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: DestMACAddr: 6 byte group destination MAC address to allow * through the Hash Table Filter * * Output: Sets the appropriate bit in the EHT* registers to allow * packets sent to DestMACAddr to be received if the Hash * Table receive filter is enabled * * Side Effects: None * * Overview: Calculates a CRC-32 using polynomial 0x4C11DB7 and then, * using bits 28:23 of the CRC, sets the appropriate bit in * the EHT* registers * * Note: This code is commented out to save code space on systems * that do not need this function. Change the "#if 0" line * to "#if 1" to uncomment it. *****************************************************************************/ #if 0 void SetRXHashTableEntry(MAC_ADDR DestMACAddr) { DWORD_VAL CRC = {0xFFFFFFFF}; BYTE HTRegister; BYTE i, j; // Calculate a CRC-32 over the 6 byte MAC address // using polynomial 0x4C11DB7 for(i = 0; i < sizeof(MAC_ADDR); i++) { BYTE crcnext; // shift in 8 bits for(j = 0; j < 8; j++) { crcnext = 0; if(((BYTE_VAL*)&(CRC.v[3]))->bits.b7) crcnext = 1; crcnext ^= (((BYTE_VAL*)&DestMACAddr.v[i])->bits.b0); CRC.Val <<= 1; if(crcnext) CRC.Val ^= 0x4C11DB7; // next bit DestMACAddr.v[i] >>= 1; } } // CRC-32 calculated, now extract bits 28:23 // Bits 25:23 define where within the Hash Table byte the bit needs to be set // Bits 28:26 define which of the 8 Hash Table bytes that bits 25:23 apply to i = CRC.v[3] & 0x1F; HTRegister = (i >> 2) + (BYTE)EHT0; i = (i << 1) & 0x06; ((BYTE_VAL*)&i)->bits.b0 = ((BYTE_VAL*)&CRC.v[2])->bits.b7; // Set the proper bit in the Hash Table BankSel(EHT0); BFSReg(HTRegister, 1<