官网的驱动需要多个文件,为了提取ENC28J60驱动,改写如下:
原数据类型定义:
typedef unsigned char BYTE; /* 8-bit unsigned */ typedef unsigned short int WORD; /* 16-bit unsigned */
现数据类型定义(types.h):
typedef unsigned char INT8U; /* 8-bit unsigned */ typedef unsigned short int INT16U; /* 16-bit unsigned */
bsp_ENC28J60.h
/********************************************************************* * * ENC28J60 registers/bits * ********************************************************************* * FileName: ENC28J60.h * Dependencies: None * Processor: PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32 * Compiler: Microchip C32 v1.05 or higher * Microchip C30 v3.12 or higher * Microchip C18 v3.30 or higher * HI-TECH PICC-18 PRO 9.63PL2 or higher * Company: Microchip Technology, Inc. * * Software License Agreement * * Copyright (C) 2002-2009 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, ENC28J60.h, * ENCX24J600.c and ENCX24J600.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 06/01/04 Original * Howard Schlunder 06/29/04 Fixed byte boundary problems on a * couple of PHY register structs. * Howard Schlunder 09/29/04 Matched with data sheet * Howard Schlunder 01/04/06 Matched with new data sheet * Howard Schlunder 06/29/06 Changed MACON3.PHDRLEN to PHDREN * Howard Schlunder 07/21/06 Several bits removed to match now * reserved bits in rev. B data sheet * (DS39662B) ********************************************************************/ #ifndef __BSP_ENC28J60_H #define __BSP_ENC28J60_H #if !defined(__PACKED) #define __PACKED #endif //#include "GenericTypeDefs.h" #include <types.h> typedef union { INT8U v[7]; struct { INT16U ByteCount; unsigned char CollisionCount:4; unsigned char CRCError:1; unsigned char LengthCheckError:1; unsigned char LengthOutOfRange:1; unsigned char Done:1; unsigned char Multicast:1; unsigned char Broadcast:1; unsigned char PacketDefer:1; unsigned char ExcessiveDefer:1; unsigned char MaximumCollisions:1; unsigned char LateCollision:1; unsigned char Giant:1; unsigned char Underrun:1; INT16U BytesTransmittedOnWire; unsigned char ControlFrame:1; unsigned char PAUSEControlFrame:1; unsigned char BackpressureApplied:1; unsigned char VLANTaggedFrame:1; unsigned char Zeros:4; } bits; } TXSTATUS; typedef union { INT8U v[4]; struct { INT16U ByteCount; unsigned char PreviouslyIgnored:1; unsigned char RXDCPreviouslySeen:1; unsigned char CarrierPreviouslySeen:1; unsigned char CodeViolation:1; unsigned char CRCError:1; unsigned char LengthCheckError:1; unsigned char LengthOutOfRange:1; unsigned char ReceiveOk:1; unsigned char Multicast:1; unsigned char Broadcast:1; unsigned char DribbleNibble:1; unsigned char ControlFrame:1; unsigned char PauseControlFrame:1; unsigned char UnsupportedOpcode:1; unsigned char VLANType:1; unsigned char Zero:1; } bits; } RXSTATUS; /****************************************************************************** * Register locations ******************************************************************************/ // Bank 0 registers -------- #define ERDPTL 0x00 #define ERDPTH 0x01 #define EWRPTL 0x02 #define EWRPTH 0x03 #define ETXSTL 0x04 #define ETXSTH 0x05 #define ETXNDL 0x06 #define ETXNDH 0x07 #define ERXSTL 0x08 #define ERXSTH 0x09 #define ERXNDL 0x0A #define ERXNDH 0x0B #define ERXRDPTL 0x0C #define ERXRDPTH 0x0D #define ERXWRPTL 0x0E #define ERXWRPTH 0x0F #define EDMASTL 0x10 #define EDMASTH 0x11 #define EDMANDL 0x12 #define EDMANDH 0x13 #define EDMADSTL 0x14 #define EDMADSTH 0x15 #define EDMACSL 0x16 #define EDMACSH 0x17 //#define 0x18 //#define 0x19 //#define r 0x1A #define EIE 0x1B #define EIR 0x1C #define ESTAT 0x1D #define ECON2 0x1E #define ECON1 0x1F // Bank 1 registers ----- #define EHT0 0x100 #define EHT1 0x101 #define EHT2 0x102 #define EHT3 0x103 #define EHT4 0x104 #define EHT5 0x105 #define EHT6 0x106 #define EHT7 0x107 #define EPMM0 0x108 #define EPMM1 0x109 #define EPMM2 0x10A #define EPMM3 0x10B #define EPMM4 0x10C #define EPMM5 0x10D #define EPMM6 0x10E #define EPMM7 0x10F #define EPMCSL 0x110 #define EPMCSH 0x111 //#define 0x112 //#define 0x113 #define EPMOL 0x114 #define EPMOH 0x115 //#define r 0x116 //#define r 0x117 #define ERXFCON 0x118 #define EPKTCNT 0x119 //#define r 0x11A //#define EIE 0x11B //#define EIR 0x11C //#define ESTAT 0x11D //#define ECON2 0x11E //#define ECON1 0x11F // Bank 2 registers ----- #define MACON1 0x200 //#define r 0x201 #define MACON3 0x202 #define MACON4 0x203 #define MABBIPG 0x204 //#define 0x205 #define MAIPGL 0x206 #define MAIPGH 0x207 #define MACLCON1 0x208 #define MACLCON2 0x209 #define MAMXFLL 0x20A #define MAMXFLH 0x20B //#define r 0x20C //#define r 0x20D //#define r 0x20E //#define 0x20F //#define r 0x210 //#define r 0x211 #define MICMD 0x212 //#define r 0x213 #define MIREGADR 0x214 //#define r 0x215 #define MIWRL 0x216 #define MIWRH 0x217 #define MIRDL 0x218 #define MIRDH 0x219 //#define r 0x21A //#define EIE 0x21B //#define EIR 0x21C //#define ESTAT 0x21D //#define ECON2 0x21E //#define ECON1 0x21F // Bank 3 registers ----- #define MAADR5 0x300 #define MAADR6 0x301 #define MAADR3 0x302 #define MAADR4 0x303 #define MAADR1 0x304 #define MAADR2 0x305 #define EBSTSD 0x306 #define EBSTCON 0x307 #define EBSTCSL 0x308 #define EBSTCSH 0x309 #define MISTAT 0x30A //#define 0x30B //#define 0x30C //#define 0x30D //#define 0x30E //#define 0x30F //#define 0x310 //#define 0x311 #define EREVID 0x312 //#define 0x313 //#define 0x314 #define ECOCON 0x315 //#define 0x316 #define EFLOCON 0x317 #define EPAUSL 0x318 #define EPAUSH 0x319 //#define r 0x31A //#define EIE 0x31B //#define EIR 0x31C //#define ESTAT 0x31D //#define ECON2 0x31E //#define ECON1 0x31F // Structures typedef union _REG { INT8U Val; // EIE bits ---------- struct { unsigned char RXERIE:1; unsigned char TXERIE:1; unsigned char :1; unsigned char TXIE:1; unsigned char LINKIE:1; unsigned char DMAIE:1; unsigned char PKTIE:1; unsigned char INTIE:1; } EIEbits; // EIR bits ---------- struct { unsigned char RXERIF:1; unsigned char TXERIF:1; unsigned char :1; unsigned char TXIF:1; unsigned char LINKIF:1; unsigned char DMAIF:1; unsigned char PKTIF:1; unsigned char :1; } EIRbits; // ESTAT bits --------- struct { unsigned char CLKRDY:1; unsigned char TXABRT:1; unsigned char RXBUSY:1; unsigned char :1; unsigned char LATECOL:1; unsigned char :1; unsigned char BUFER:1; unsigned char INT:1; } ESTATbits; // ECON2 bits -------- struct { unsigned char :3; unsigned char VRPS:1; unsigned char :1; unsigned char PWRSV:1; unsigned char PKTDEC:1; unsigned char AUTOINC:1; } ECON2bits; // ECON1 bits -------- struct { unsigned char BSEL0:1; unsigned char BSEL1:1; unsigned char RXEN:1; unsigned char TXRTS:1; unsigned char CSUMEN:1; unsigned char DMAST:1; unsigned char RXRST:1; unsigned char TXRST:1; } ECON1bits; // ERXFCON bits ------ struct { unsigned char BCEN:1; unsigned char MCEN:1; unsigned char HTEN:1; unsigned char MPEN:1; unsigned char PMEN:1; unsigned char CRCEN:1; unsigned char ANDOR:1; unsigned char UCEN:1; } ERXFCONbits; // MACON1 bits -------- struct { unsigned char MARXEN:1; unsigned char PASSALL:1; unsigned char RXPAUS:1; unsigned char TXPAUS:1; unsigned char :4; } MACON1bits; // MACON3 bits -------- struct { unsigned char FULDPX:1; unsigned char FRMLNEN:1; unsigned char HFRMEN:1; unsigned char PHDREN:1; unsigned char TXCRCEN:1; unsigned char PADCFG0:1; unsigned char PADCFG1:1; unsigned char PADCFG2:1; } MACON3bits; struct { unsigned char FULDPX:1; unsigned char FRMLNEN:1; unsigned char HFRMEN:1; unsigned char PHDREN:1; unsigned char TXCRCEN:1; unsigned char PADCFG:3; } MACON3bits2; // MACON4 bits -------- struct { unsigned char :4; unsigned char NOBKOFF:1; unsigned char BPEN:1; unsigned char DEFER:1; unsigned char :1; } MACON4bits; // MICMD bits --------- struct { unsigned char MIIRD:1; unsigned char MIISCAN:1; unsigned char :6; } MICMDbits; // EBSTCON bits ----- struct { unsigned char BISTST:1; unsigned char TME:1; unsigned char TMSEL0:1; unsigned char TMSEL1:1; unsigned char PSEL:1; unsigned char PSV0:1; unsigned char PSV1:1; unsigned char PSV2:1; } EBSTCONbits; struct { unsigned char BISTST:1; unsigned char TME:1; unsigned char TMSEL:2; unsigned char PSEL:1; unsigned char PSV:3; } EBSTCONbits2; // MISTAT bits -------- struct { unsigned char BUSY:1; unsigned char SCAN:1; unsigned char NVALID:1; unsigned char :5; } MISTATbits; // ECOCON bits ------- struct { unsigned char COCON0:1; unsigned char COCON1:1; unsigned char COCON2:1; unsigned char :5; } ECOCONbits; struct { unsigned char COCON:3; unsigned char :5; } ECOCONbits2; // EFLOCON bits ----- struct { unsigned char FCEN0:1; unsigned char FCEN1:1; unsigned char FULDPXS:1; unsigned char :5; } EFLOCONbits; struct { unsigned char FCEN:2; unsigned char FULDPXS:1; unsigned char :5; } EFLOCONbits2; } REG; /****************************************************************************** * PH Register Locations ******************************************************************************/ #define PHCON1 0x00 #define PHSTAT1 0x01 #define PHID1 0x02 #define PHID2 0x03 #define PHCON2 0x10 #define PHSTAT2 0x11 #define PHIE 0x12 #define PHIR 0x13 #define PHLCON 0x14 typedef union { INT16U Val; INT8U v[2] __PACKED; struct __PACKED { INT8U LB; INT8U HB; } byte; struct __PACKED { INT8U b0:1; INT8U b1:1; INT8U b2:1; INT8U b3:1; INT8U b4:1; INT8U b5:1; INT8U b6:1; INT8U b7:1; INT8U b8:1; INT8U b9:1; INT8U b10:1; INT8U b11:1; INT8U b12:1; INT8U b13:1; INT8U b14:1; INT8U b15:1; } bits; } WORD_VAL, WORD_BITS; typedef union { INT16U Val; WORD_VAL VAL; // PHCON1 bits ---------- struct { unsigned :8; unsigned PDPXMD:1; unsigned :2; unsigned PPWRSV:1; unsigned :2; unsigned PLOOPBK:1; unsigned PRST:1; } PHCON1bits; // PHSTAT1 bits -------- struct { unsigned :1; unsigned JBSTAT:1; unsigned LLSTAT:1; unsigned :5; unsigned :3; unsigned PHDPX:1; unsigned PFDPX:1; unsigned :3; } PHSTAT1bits; // PHID2 bits ---------- struct { unsigned PREV0:1; unsigned PREV1:1; unsigned PREV2:1; unsigned PREV3:1; unsigned PPN0:1; unsigned PPN1:1; unsigned PPN2:1; unsigned PPN3:1; unsigned PPN4:1; unsigned PPN5:1; unsigned PID19:1; unsigned PID20:1; unsigned PID21:1; unsigned PID22:1; unsigned PID23:1; unsigned PID24:1; } PHID2bits; struct { unsigned PREV:4; unsigned PPNL:4; unsigned PPNH:2; unsigned PID:6; } PHID2bits2; // PHCON2 bits ---------- struct { unsigned :8; unsigned HDLDIS:1; unsigned :1; unsigned JABBER:1; unsigned :2; unsigned TXDIS:1; unsigned FRCLNK:1; unsigned :1; } PHCON2bits; // PHSTAT2 bits -------- struct { unsigned :5; unsigned PLRITY:1; unsigned :2; unsigned :1; unsigned DPXSTAT:1; unsigned LSTAT:1; unsigned COLSTAT:1; unsigned RXSTAT:1; unsigned TXSTAT:1; unsigned :2; } PHSTAT2bits; // PHIE bits ----------- struct { unsigned :1; unsigned PGEIE:1; unsigned :2; unsigned PLNKIE:1; unsigned :3; unsigned :8; } PHIEbits; // PHIR bits ----------- struct { unsigned :2; unsigned PGIF:1; unsigned :1; unsigned PLNKIF:1; unsigned :3; unsigned :8; } PHIRbits; // PHLCON bits ------- struct { unsigned :1; unsigned STRCH:1; unsigned LFRQ0:1; unsigned LFRQ1:1; unsigned LBCFG0:1; unsigned LBCFG1:1; unsigned LBCFG2:1; unsigned LBCFG3:1; unsigned LACFG0:1; unsigned LACFG1:1; unsigned LACFG2:1; unsigned LACFG3:1; unsigned :4; } PHLCONbits; struct { unsigned :1; unsigned STRCH:1; unsigned LFRQ:2; unsigned LBCFG:4; unsigned LACFG:4; unsigned :4; } PHLCONbits2; } PHYREG; /****************************************************************************** * Individual Register Bits ******************************************************************************/ // ETH/MAC/MII bits // EIE bits ---------- #define EIE_INTIE (1<<7) #define EIE_PKTIE (1<<6) #define EIE_DMAIE (1<<5) #define EIE_LINKIE (1<<4) #define EIE_TXIE (1<<3) #define EIE_TXERIE (1<<1) #define EIE_RXERIE (1) // EIR bits ---------- #define EIR_PKTIF (1<<6) #define EIR_DMAIF (1<<5) #define EIR_LINKIF (1<<4) #define EIR_TXIF (1<<3) #define EIR_TXERIF (1<<1) #define EIR_RXERIF (1) // ESTAT bits --------- #define ESTAT_INT (1<<7) #define ESTAT_BUFER (1<<6) #define ESTAT_LATECOL (1<<4) #define ESTAT_RXBUSY (1<<2) #define ESTAT_TXABRT (1<<1) #define ESTAT_CLKRDY (1) // ECON2 bits -------- #define ECON2_AUTOINC (1<<7) #define ECON2_PKTDEC (1<<6) #define ECON2_PWRSV (1<<5) #define ECON2_VRPS (1<<3) // ECON1 bits -------- #define ECON1_TXRST (1<<7) #define ECON1_RXRST (1<<6) #define ECON1_DMAST (1<<5) #define ECON1_CSUMEN (1<<4) #define ECON1_TXRTS (1<<3) #define ECON1_RXEN (1<<2) #define ECON1_BSEL1 (1<<1) #define ECON1_BSEL0 (1) // ERXFCON bits ------ #define ERXFCON_UCEN (1<<7) #define ERXFCON_ANDOR (1<<6) #define ERXFCON_CRCEN (1<<5) #define ERXFCON_PMEN (1<<4) #define ERXFCON_MPEN (1<<3) #define ERXFCON_HTEN (1<<2) #define ERXFCON_MCEN (1<<1) #define ERXFCON_BCEN (1) // MACON1 bits -------- #define MACON1_TXPAUS (1<<3) #define MACON1_RXPAUS (1<<2) #define MACON1_PASSALL (1<<1) #define MACON1_MARXEN (1) // MACON3 bits -------- #define MACON3_PADCFG2 (1<<7) #define MACON3_PADCFG1 (1<<6) #define MACON3_PADCFG0 (1<<5) #define MACON3_TXCRCEN (1<<4) #define MACON3_PHDREN (1<<3) #define MACON3_HFRMEN (1<<2) #define MACON3_FRMLNEN (1<<1) #define MACON3_FULDPX (1) // MACON4 bits -------- #define MACON4_DEFER (1<<6) #define MACON4_BPEN (1<<5) #define MACON4_NOBKOFF (1<<4) // MICMD bits --------- #define MICMD_MIISCAN (1<<1) #define MICMD_MIIRD (1) // EBSTCON bits ----- #define EBSTCON_PSV2 (1<<7) #define EBSTCON_PSV1 (1<<6) #define EBSTCON_PSV0 (1<<5) #define EBSTCON_PSEL (1<<4) #define EBSTCON_TMSEL1 (1<<3) #define EBSTCON_TMSEL0 (1<<2) #define EBSTCON_TME (1<<1) #define EBSTCON_BISTST (1) // MISTAT bits -------- #define MISTAT_NVALID (1<<2) #define MISTAT_SCAN (1<<1) #define MISTAT_BUSY (1) // ECOCON bits ------- #define ECOCON_COCON2 (1<<2) #define ECOCON_COCON1 (1<<1) #define ECOCON_COCON0 (1) // EFLOCON bits ----- #define EFLOCON_FULDPXS (1<<2) #define EFLOCON_FCEN1 (1<<1) #define EFLOCON_FCEN0 (1) // PHY bits // PHCON1 bits ---------- #define PHCON1_PRST (1ul<<15) #define PHCON1_PLOOPBK (1ul<<14) #define PHCON1_PPWRSV (1ul<<11) #define PHCON1_PDPXMD (1ul<<8) // PHSTAT1 bits -------- #define PHSTAT1_PFDPX (1ul<<12) #define PHSTAT1_PHDPX (1ul<<11) #define PHSTAT1_LLSTAT (1ul<<2) #define PHSTAT1_JBSTAT (1ul<<1) // PHID2 bits -------- #define PHID2_PID24 (1ul<<15) #define PHID2_PID23 (1ul<<14) #define PHID2_PID22 (1ul<<13) #define PHID2_PID21 (1ul<<12) #define PHID2_PID20 (1ul<<11) #define PHID2_PID19 (1ul<<10) #define PHID2_PPN5 (1ul<<9) #define PHID2_PPN4 (1ul<<8) #define PHID2_PPN3 (1ul<<7) #define PHID2_PPN2 (1ul<<6) #define PHID2_PPN1 (1ul<<5) #define PHID2_PPN0 (1ul<<4) #define PHID2_PREV3 (1ul<<3) #define PHID2_PREV2 (1ul<<2) #define PHID2_PREV1 (1ul<<1) #define PHID2_PREV0 (1ul) // PHCON2 bits ---------- #define PHCON2_FRCLNK (1ul<<14) #define PHCON2_TXDIS (1ul<<13) #define PHCON2_JABBER (1ul<<10) #define PHCON2_HDLDIS (1ul<<8) // PHSTAT2 bits -------- #define PHSTAT2_TXSTAT (1ul<<13) #define PHSTAT2_RXSTAT (1ul<<12) #define PHSTAT2_COLSTAT (1ul<<11) #define PHSTAT2_LSTAT (1ul<<10) #define PHSTAT2_DPXSTAT (1ul<<9) #define PHSTAT2_PLRITY (1ul<<5) // PHIE bits ----------- #define PHIE_PLNKIE (1ul<<4) #define PHIE_PGEIE (1ul<<1) // PHIR bits ----------- #define PHIR_PLNKIF (1ul<<4) #define PHIR_PGIF (1ul<<2) // PHLCON bits ------- #define PHLCON_LACFG3 (1ul<<11) #define PHLCON_LACFG2 (1ul<<10) #define PHLCON_LACFG1 (1ul<<9) #define PHLCON_LACFG0 (1ul<<8) #define PHLCON_LBCFG3 (1ul<<7) #define PHLCON_LBCFG2 (1ul<<6) #define PHLCON_LBCFG1 (1ul<<5) #define PHLCON_LBCFG0 (1ul<<4) #define PHLCON_LFRQ1 (1ul<<3) #define PHLCON_LFRQ0 (1ul<<2) #define PHLCON_STRCH (1ul<<1) #endif
bsp_ENC28J60.h
/********************************************************************* * * 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, PIC32 * Compiler: Microchip C32 v1.05 or higher * Microchip C30 v3.12 or higher * Microchip C18 v3.30 or higher * HI-TECH PICC-18 PRO 9.63PL2 or higher * Company: Microchip Technology, Inc. * * Software License Agreement * * Copyright (C) 2002-2009 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, ENC28J60.h, * ENCX24J600.c and ENCX24J600.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 __BSP_ENC28J60_C //#include "HardwareProfile.h" #include "bsp_ENC28J60.h" // Make sure that this hardware profile has an ENC28J60 in it #if defined(ENC_CS_TRIS) //#include "TCPIP Stack/TCPIP.h" /** 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. // Maximum SPI frequency specified in data sheet #define ENC_MAX_SPI_FREQ (20000000ul) // Hz #define ETHER_IP (0x00u) #define ETHER_ARP (0x06u) // A header appended at the start of all RX frames by the hardware typedef struct __attribute__((aligned(2), packed)) { INT16U NextPacketPointer; RXSTATUS StatusVector; MAC_ADDR DestMACAddr; MAC_ADDR SourceMACAddr; WORD_VAL Type; } ENC_PREAMBLE; #if defined (__18CXX) #define ClearSPIDoneFlag() {ENC_SPI_IF = 0;} #define WaitForDataByte() {while(!ENC_SPI_IF); ENC_SPI_IF = 0;} #define SPI_ON_BIT (ENC_SPICON1bits.SSPEN) #elif defined(__C30__) #define ClearSPIDoneFlag() static inline __attribute__((__always_inline__)) void WaitForDataByte( void ) { while ((ENC_SPISTATbits.SPITBF == 1) || (ENC_SPISTATbits.SPIRBF == 0)); } #define SPI_ON_BIT (ENC_SPISTATbits.SPIEN) #elif defined( __PIC32MX__ ) #define ClearSPIDoneFlag() static inline __attribute__((__always_inline__)) void WaitForDataByte( void ) { while (!ENC_SPISTATbits.SPITBE || !ENC_SPISTATbits.SPIRBF); } #define SPI_ON_BIT (ENC_SPICON1bits.ON) #else #error Determine SPI flag mechanism #endif // Prototypes of functions intended for MAC layer use only. static void BankSel(INT16U Register); static REG ReadETHReg(INT8U Address); static REG ReadMACReg(INT8U Address); static void WriteReg(INT8U Address, INT8U Data); static void BFCReg(INT8U Address, INT8U Data); static void BFSReg(INT8U Address, INT8U 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 INT8U ENCRevID; //NOTE: All code in this module expects 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) { INT8U 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 #if defined(__18CXX) ENC_SCK_TRIS = 0; ENC_SDO_TRIS = 0; ENC_SDI_TRIS = 1; #endif // If the RESET pin is connected, take the chip out of reset #if defined(ENC_RST_IO) ENC_RST_IO = 1; ENC_RST_TRIS = 0; #endif // Set up SPI ClearSPIDoneFlag(); #if defined(__18CXX) ENC_SPICON1 = 0x20; // SSPEN bit is set, SPI in master mode, FOSC/4, // IDLE state is low level ENC_SPISTATbits.CKE = 1;// Transmit data on rising edge of clock ENC_SPISTATbits.SMP = 0;// Input sampled at middle of data output time #elif defined(__C30__) 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; #elif defined(__C32__) ENC_SPIBRG = (GetPeripheralClock()-1ul)/2ul/ENC_MAX_SPI_FREQ; ENC_SPICON1bits.SMP = 1; // Delay SDI input sampling (PIC perspective) by 1/2 SPI clock ENC_SPICON1bits.CKE = 1; ENC_SPICON1bits.MSTEN = 1; ENC_SPICON1bits.ON = 1; #endif // RESET the entire ENC28J60, clearing all registers // Also 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 { SendSystemReset(); i = ReadETHReg(ESTAT).Val; } while((i & 0x08) || (~i & ESTAT_CLKRDY)); // 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((INT8U)ERXFCON, ERXFCON_CRCEN); // Enter Bank 2 and configure the MAC BankSel(MACON1); // Enable the receive portion of the MAC WriteReg((INT8U)MACON1, MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN); // Pad packets to 60 bytes, add CRC, and check Type/Length field. #if defined(FULL_DUPLEX) WriteReg((INT8U)MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX); WriteReg((INT8U)MABBIPG, 0x15); #else WriteReg((INT8U)MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); WriteReg((INT8U)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((INT8U)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((INT8U)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((INT8U)MAIPGL, 0x12); WriteReg((INT8U)MAIPGH, 0x0C); // Set the maximum packet size which the controller will accept WriteReg((INT8U)MAMXFLL, LOW(6+6+2+1500+4)); // 1518 is the IEEE 802.3 specified limit WriteReg((INT8U)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((INT8U)MAADR1, AppConfig.MyMACAddr.v[0]); WriteReg((INT8U)MAADR2, AppConfig.MyMACAddr.v[1]); WriteReg((INT8U)MAADR3, AppConfig.MyMACAddr.v[2]); WriteReg((INT8U)MAADR4, AppConfig.MyMACAddr.v[3]); WriteReg((INT8U)MAADR5, AppConfig.MyMACAddr.v[4]); WriteReg((INT8U)MAADR6, AppConfig.MyMACAddr.v[5]); // Disable the CLKOUT output to reduce EMI generation WriteReg((INT8U)ECOCON, 0x00); // Output off (0V) //WriteReg((INT8U)ECOCON, 0x01); // 25.000MHz //WriteReg((INT8U)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((INT8U)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((INT8U)MACON3); Register.MACON3bits.FULDPX = PhyReg.PHCON1bits.PDPXMD; WriteReg((INT8U)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((INT8U)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: Is is safe to call this function multiple times between * MACGetHeader() calls. Extra packets won't be thrown away * until MACGetHeader() makes it available. *****************************************************************************/ 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: INT16U MACGetFreeRxSize(void) * * PreCondition: None * * Input: None * * Output: A INT16U estimate of how much RX buffer space is free at * the present time. * * Side Effects: None * * Overview: None * * Note: None *****************************************************************************/ INT16U 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((INT8U)EPKTCNT).Val; BankSel(ERXWRPTL); WritePT.v[0] = ReadETHReg(ERXWRPTL).Val; WritePT.v[1] = ReadETHReg(ERXWRPTH).Val; BankSel(EPKTCNT); } while(ReadETHReg((INT8U)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, INT8U* type) * * PreCondition: None * * Input: *remote: Location to store the Source MAC address of the * received frame. * *type: Location of a INT8U 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, INT8U* type) { ENC_PREAMBLE header; INT8U PacketCount; // Test if at least one packet has been received and is waiting BankSel(EPKTCNT); PacketCount = ReadETHReg((INT8U)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((INT8U*)&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, INT8U type, INT16U 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, INT8U type, INT16U 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 += (INT16U)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((INT8U*)remote, sizeof(*remote)); // Write our MAC address in the Ethernet source field MACPutArray((INT8U*)&AppConfig.MyMACAddr, sizeof(AppConfig.MyMACAddr)); // Write the appropriate Ethernet Type INT16U 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) { INT16U AttemptCounter = 0x0000; while(!(ReadETHReg(EIR).Val & (EIR_TXERIF | EIR_TXIF)) && (++AttemptCounter < 1000u)); if(ReadETHReg(EIR).EIRbits.TXERIF || (AttemptCounter >= 1000u)) { WORD_VAL ReadPtrSave; WORD_VAL TXEnd; TXSTATUS TXStatus; INT8U 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((INT8U*)&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((INT8U*)&TXStatus, sizeof(TXStatus)); } else { break; } } // Restore the current read pointer WriteReg(ERDPTL, ReadPtrSave.v[0]); WriteReg(ERDPTH, ReadPtrSave.v[1]); } } } /****************************************************************************** * Function: void MACSetReadPtrInRx(INT16U offset) * * PreCondition: A packet has been obtained by calling MACGetHeader() and * getting a TRUE result. * * Input: offset: INT16U 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(INT16U 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: PTR_BASE MACSetWritePtr(PTR_BASE Address) * * PreCondition: None * * Input: Address: Address to seek to * * Output: INT16U: 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 *****************************************************************************/ PTR_BASE MACSetWritePtr(PTR_BASE 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: PTR_BASE MACSetReadPtr(PTR_BASE Address) * * PreCondition: None * * Input: Address: Address to seek to * * Output: INT16U: 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 *****************************************************************************/ PTR_BASE MACSetReadPtr(PTR_BASE 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: INT16U MACCalcRxChecksum(INT16U offset, INT16U 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 *****************************************************************************/ INT16U MACCalcRxChecksum(INT16U offset, INT16U 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: INT16U CalcIPBufferChecksum(INT16U 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. *****************************************************************************/ INT16U CalcIPBufferChecksum(INT16U len) { WORD_VAL Start; DWORD_VAL Checksum = {0x00000000ul}; INT16U ChunkLen; INT16U DataBuffer[10]; INT16U *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((INT8U*)DataBuffer, ChunkLen); len -= ChunkLen; // Take care of a last odd numbered data byte if(((WORD_VAL*)&ChunkLen)->bits.b0) { ((INT8U*)DataBuffer)[ChunkLen] = 0x00; ChunkLen++; } // Calculate the checksum over this chunk DataPtr = DataBuffer; 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(PTR_BASE destAddr, PTR_BASE sourceAddr, INT16U len) * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: destAddr: Destination address in the Ethernet memory to * copy to. If (PTR_BASE)-1 is specified, the * current EWRPT value will be used instead. * sourceAddr: Source address to read from. If (PTR_BASE)-1 is * specified, 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 (PTR_BASE)-1 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(PTR_BASE destAddr, PTR_BASE sourceAddr, INT16U len) { WORD_VAL ReadSave, WriteSave; BOOL UpdateWritePointer = FALSE; BOOL UpdateReadPointer = FALSE; if(destAddr == (PTR_BASE)-1) { UpdateWritePointer = TRUE; destAddr = ReadETHReg(EWRPTL).Val; ((INT8U*)&destAddr)[1] = ReadETHReg(EWRPTH).Val; } if(sourceAddr == (PTR_BASE)-1) { UpdateReadPointer = TRUE; sourceAddr = ReadETHReg(ERDPTL).Val; ((INT8U*)&sourceAddr)[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, ((INT8U*)&sourceAddr)[0]); WriteReg(ERDPTH, ((INT8U*)&sourceAddr)[1]); WriteReg(EWRPTL, ((INT8U*)&destAddr)[0]); WriteReg(EWRPTH, ((INT8U*)&destAddr)[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, ((INT8U*)&sourceAddr)[0]); WriteReg(EDMASTH, ((INT8U*)&sourceAddr)[1]); WriteReg(EDMADSTL, ((INT8U*)&destAddr)[0]); WriteReg(EDMADSTH, ((INT8U*)&destAddr)[1]); if((sourceAddr <= RXSTOP) && (len > RXSTOP)) //&& (sourceAddr >= RXSTART)) len -= RXSIZE; WriteReg(EDMANDL, ((INT8U*)&len)[0]); WriteReg(EDMANDH, ((INT8U*)&len)[1]); BFCReg(ECON1, ECON1_CSUMEN); BFSReg(ECON1, ECON1_DMAST); if(UpdateReadPointer) { len++; if((sourceAddr <= RXSTOP) && (len > RXSTOP)) //&& (sourceAddr >= RXSTART)) len -= RXSIZE; WriteReg(ERDPTL, ((INT8U*)&len)[0]); WriteReg(ERDPTH, ((INT8U*)&len)[1]); } } } BOOL MACIsMemCopyDone(void) { return !ReadETHReg(ECON1).ECON1bits.DMAST; } /****************************************************************************** * Function: INT8U 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 *****************************************************************************/ INT8U MACGet() { INT8U Result; ENC_CS_IO = 0; ClearSPIDoneFlag(); #if defined(__C32__) { // Send the opcode and read a byte in one 16-bit operation ENC_SPICON1bits.MODE16 = 1; ENC_SSPBUF = RBM<<8 | 0x00; // Send Read Buffer Memory command plus 8 dummy bits to generate clocks for the return result WaitForDataByte(); // Wait until INT8U is transmitted ENC_SPICON1bits.MODE16 = 0; } #elif defined(__C30__) { // Send the opcode and read a byte in one 16-bit operation ENC_SPISTATbits.SPIEN = 0; ENC_SPICON1bits.MODE16 = 1; ENC_SPISTATbits.SPIEN = 1; ENC_SSPBUF = RBM<<8 | 0x00; // Send Read Buffer Memory command plus 8 dummy bits to generate clocks for the return result WaitForDataByte(); // Wait until INT8U is transmitted ENC_SPISTATbits.SPIEN = 0; ENC_SPICON1bits.MODE16 = 0; ENC_SPISTATbits.SPIEN = 1; } #else { // Send the opcode and read a byte in two 8-bit operations ENC_SSPBUF = RBM; WaitForDataByte(); // Wait until opcode/address is transmitted. Result = ENC_SSPBUF; ENC_SSPBUF = 0; // Send a dummy byte to receive the register // contents. WaitForDataByte(); // Wait until register is received. } #endif Result = ENC_SSPBUF; ENC_CS_IO = 1; return Result; }//end MACGet /****************************************************************************** * Function: INT16U MACGetArray(INT8U *val, INT16U 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 *****************************************************************************/ INT16U MACGetArray(INT8U *val, INT16U len) { // Workaround needed on HPC Explorer (classic) board to prevent interference // with I2C temperature sensor on the same SPI wires #if defined(__18F8722) || defined(_18F8722) || defined(__18F8723) || defined(_18F8723) INT16U i; volatile INT8U Dummy; i = len; Dummy = 0xFF; ClearSPIDoneFlag(); 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_SSPBUF = RBM; // Send the Read Buffer Memory opcode. WaitForDataByte(); // Wait until opcode/address is transmitted. } else Dummy = 0xFF; ENC_SSPBUF = 0; // Send a dummy byte to receive a byte if(val) { WaitForDataByte(); // Wait until byte is received. *val++ = ENC_SSPBUF; } else { WaitForDataByte(); // Wait until byte is received. } } ENC_CS_IO = 1; return len; #else INT16U i; volatile INT8U Dummy; // Start the burst operation ENC_CS_IO = 0; ClearSPIDoneFlag(); ENC_SSPBUF = RBM; // Send the Read Buffer Memory opcode. i = 0; if(val) val--; WaitForDataByte(); // Wait until opcode/address is transmitted. Dummy = ENC_SSPBUF; #if defined(__C32__) { DWORD_VAL dwv; // Read the data, 4 bytes at a time, for as long as possible if(len >= 4) { ENC_SPICON1bits.MODE32 = 1; while(1) { ENC_SSPBUF = 0x00000000; // Send a dummy DWORD to generate 32 clocks i += 4; WaitForDataByte(); // Wait until DWORD is transmitted dwv.Val = ENC_SSPBUF; if(val) { *(++val) = dwv.v[3]; *(++val) = dwv.v[2]; *(++val) = dwv.v[1]; *(++val) = dwv.v[0]; } if(len - i < 4) break; }; ENC_SPICON1bits.MODE32 = 0; } } #elif defined(__C30__) { WORD_VAL wv; // Read the data, 2 bytes at a time, for as long as possible if(len >= 2) { ENC_SPISTATbits.SPIEN = 0; ENC_SPICON1bits.MODE16 = 1; ENC_SPISTATbits.SPIEN = 1; while(1) { ENC_SSPBUF = 0x0000; // Send a dummy INT8U to generate 32 clocks i += 2; WaitForDataByte(); // Wait until INT8U is transmitted wv.Val = ENC_SSPBUF; if(val) { *(++val) = wv.v[1]; *(++val) = wv.v[0]; } if(len - i < 2) break; }; ENC_SPISTATbits.SPIEN = 0; ENC_SPICON1bits.MODE16 = 0; ENC_SPISTATbits.SPIEN = 1; } } #endif // Read the data while(i<len) { ENC_SSPBUF = 0; // Send a dummy byte to receive a byte i++; if(val) { val++; WaitForDataByte(); // Wait until byte is received. *val = ENC_SSPBUF; } else { WaitForDataByte(); // Wait until byte is received. Dummy = ENC_SSPBUF; } }; // Terminate the burst operation ENC_CS_IO = 1; return i; #endif }//end MACGetArray /****************************************************************************** * Function: void MACPut(INT8U val) * * PreCondition: SPI bus must be initialized (done in MACInit()). * EWRPT must point to the location to begin writing. * * Input: Byte to write into the ENC28J60 buffer memory * * Output: None * * Side Effects: None * * Overview: MACPut outputs the Write Buffer Memory opcode/constant * (8 bits) and data to write (8 bits) over the SPI. * EWRPT is incremented after the write. * * Note: None *****************************************************************************/ void MACPut(INT8U val) { volatile INT8U Dummy; ENC_CS_IO = 0; ClearSPIDoneFlag(); #if defined(__C32__) { // Send the Write Buffer Memory and data, in on 16-bit write ENC_SPICON1bits.MODE16 = 1; ENC_SSPBUF = (WBM<<8) | (INT16U)val; // Start sending the INT8U WaitForDataByte(); // Wait until INT8U is transmitted ENC_SPICON1bits.MODE16 = 0; } #elif defined(__C30__) { // Send the Write Buffer Memory and data, in on 16-bit write ENC_SPISTATbits.SPIEN = 0; ENC_SPICON1bits.MODE16 = 1; ENC_SPISTATbits.SPIEN = 1; ENC_SSPBUF = (WBM<<8) | (INT16U)val; // Start sending the INT8U WaitForDataByte(); // Wait until INT8U is transmitted ENC_SPISTATbits.SPIEN = 0; ENC_SPICON1bits.MODE16 = 0; ENC_SPISTATbits.SPIEN = 1; } #else { ENC_SSPBUF = WBM; // Send the opcode and constant. WaitForDataByte(); // Wait until opcode/constant is transmitted. Dummy = ENC_SSPBUF; ENC_SSPBUF = val; // Send the byte to be writen. WaitForDataByte(); // Wait until finished transmitting } #endif Dummy = ENC_SSPBUF; ENC_CS_IO = 1; }//end MACPut /****************************************************************************** * Function: void MACPutArray(INT8U *val, INT16U 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 MACPutArray(INT8U *val, INT16U len) { // Workaround needed on HPC Explorer (classic) board to prevent interference // with I2C temperature sensor on the same SPI wires #if defined(__18F8722) || defined(_18F8722) || defined(__18F8723) || defined(_18F8723) INT16U i; volatile INT8U Dummy; i = len; Dummy = 0xFF; ClearSPIDoneFlag(); 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_SSPBUF = WBM; // Send the Read Buffer Memory opcode. WaitForDataByte(); // Wait until opcode/address is transmitted. } else Dummy = 0xFF; ENC_SSPBUF = *val++; // Send byte WaitForDataByte(); // Wait until byte is sent } ENC_CS_IO = 1; return; #else volatile INT8U Dummy; // Select the chip and send the proper opcode ENC_CS_IO = 0; ClearSPIDoneFlag(); ENC_SSPBUF = WBM; // Send the Write Buffer Memory opcode WaitForDataByte(); // Wait until opcode/constant is transmitted. Dummy = ENC_SSPBUF; #if defined(__C32__) { DWORD_VAL dwv; // Send the data, 4 bytes at a time, for as long as possible if(len >= 4) { dwv.v[3] = *val++; dwv.v[2] = *val++; dwv.v[1] = *val++; dwv.v[0] = *val++; ENC_SPICON1bits.MODE32 = 1; while(1) { ENC_SSPBUF = dwv.Val; // Start sending the DWORD len -= 4; if(len < 4) break; dwv.v[3] = *val++; dwv.v[2] = *val++; dwv.v[1] = *val++; dwv.v[0] = *val++; WaitForDataByte(); // Wait until DWORD is transmitted Dummy = ENC_SSPBUF; }; WaitForDataByte(); // Wait until DWORD is transmitted Dummy = ENC_SSPBUF; ENC_SPICON1bits.MODE32 = 0; } } #elif defined(__C30__) { WORD_VAL wv; // Send the data, 2 bytes at a time, for as long as possible if(len >= 2) { wv.v[1] = *val++; wv.v[0] = *val++; ENC_SPISTATbits.SPIEN = 0; ENC_SPICON1bits.MODE16 = 1; ENC_SPISTATbits.SPIEN = 1; while(1) { ENC_SSPBUF = wv.Val; // Start sending the INT8U len -= 2; if(len < 2) break; wv.v[1] = *val++; wv.v[0] = *val++; WaitForDataByte(); // Wait until INT8U is transmitted Dummy = ENC_SSPBUF; }; WaitForDataByte(); // Wait until INT8U is transmitted Dummy = ENC_SSPBUF; ENC_SPISTATbits.SPIEN = 0; ENC_SPICON1bits.MODE16 = 0; ENC_SPISTATbits.SPIEN = 1; } } #endif // Send the data, one byte at a time 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 WaitForDataByte(); // Wait until byte is transmitted Dummy = ENC_SSPBUF; }; // Terminate the burst operation ENC_CS_IO = 1; #endif }//end MACPutArray #if defined(__18CXX) /****************************************************************************** * Function: void MACPutROMArray(ROM INT8U *val, INT16U 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 INT8U *val, INT16U len) { // Workaround needed on HPC Explorer (classic) board to prevent interference // with I2C temperature sensor on the same SPI wires #if defined(__18F8722) || defined(_18F8722) || defined(__18F8723) || defined(_18F8723) INT16U i; volatile INT8U Dummy; i = len; Dummy = 0xFF; ClearSPIDoneFlag(); 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_SSPBUF = WBM; // Send the Read Buffer Memory opcode. WaitForDataByte(); // Wait until opcode/address is transmitted. } else Dummy = 0xFF; ENC_SSPBUF = *val++; // Send byte WaitForDataByte(); // Wait until byte is sent } ENC_CS_IO = 1; return; #else volatile INT8U Dummy; // Select the chip and send the proper opcode ENC_CS_IO = 0; ClearSPIDoneFlag(); ENC_SSPBUF = WBM; // Send the Write Buffer Memory opcode WaitForDataByte(); // Wait until opcode/constant is transmitted. Dummy = ENC_SSPBUF; // 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 WaitForDataByte(); // Wait until byte is transmitted Dummy = ENC_SSPBUF; }; // Terminate the burst operation ENC_CS_IO = 1; #endif }//end MACPutROMArray #endif /****************************************************************************** * 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) { volatile INT8U Dummy; // Note: The power save feature may prevent the reset from executing, so // we must make sure that the device is not in power save before issuing // a reset. BFCReg(ECON2, ECON2_PWRSV); // Give some opportunity for the regulator to reach normal regulation and // have all clocks running DelayMs(1); // Execute the System Reset command ENC_CS_IO = 0; ClearSPIDoneFlag(); ENC_SSPBUF = SR; WaitForDataByte(); // Wait until the command is transmitted. Dummy = ENC_SSPBUF; ENC_CS_IO = 1; // Wait for the oscillator start up timer and PHY to become ready DelayMs(1); }//end SendSystemReset /****************************************************************************** * Function: REG ReadETHReg(INT8U 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(INT8U Address) { REG r; // Select the chip and send the Read Control Register opcode/address ENC_CS_IO = 0; ClearSPIDoneFlag(); ENC_SSPBUF = RCR | Address; WaitForDataByte(); // Wait until the opcode/address is transmitted r.Val = ENC_SSPBUF; ENC_SSPBUF = 0; // Send a dummy byte to receive the register // contents WaitForDataByte(); // Wait until the register is received r.Val = ENC_SSPBUF; ENC_CS_IO = 1; return r; }//end ReadETHReg /****************************************************************************** * Function: REG ReadMACReg(INT8U 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(INT8U Address) { REG r; ENC_CS_IO = 0; ClearSPIDoneFlag(); ENC_SSPBUF = RCR | Address; // Send the Read Control Register opcode and // address. WaitForDataByte(); // Wait until opcode/address is transmitted. r.Val = ENC_SSPBUF; ENC_SSPBUF = 0; // Send a dummy byte WaitForDataByte(); // Wait for the dummy byte to be transmitted r.Val = ENC_SSPBUF; ENC_SSPBUF = 0; // Send another dummy byte to receive the register // contents. WaitForDataByte(); // Wait until register is received. r.Val = ENC_SSPBUF; 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(INT8U Register) { PHYREG Result; // Set the right address and start the register read operation BankSel(MIREGADR); WriteReg((INT8U)MIREGADR, Register); WriteReg((INT8U)MICMD, MICMD_MIIRD); // Loop to wait until the PHY register has been read through the MII // This requires 10.24us BankSel(MISTAT); while(ReadMACReg((INT8U)MISTAT).MISTATbits.BUSY); // Stop reading BankSel(MIREGADR); WriteReg((INT8U)MICMD, 0x00); // Obtain results and return Result.VAL.v[0] = ReadMACReg((INT8U)MIRDL).Val; Result.VAL.v[1] = ReadMACReg((INT8U)MIRDH).Val; BankSel(ERDPTL); // Return to Bank 0 return Result; }//end ReadPHYReg /****************************************************************************** * Function: void WriteReg(INT8U Address, INT8U 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. * INT8U 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(INT8U Address, INT8U Data) { volatile INT8U Dummy; ENC_CS_IO = 0; ClearSPIDoneFlag(); #if defined(__C32__) { // Send the Write Buffer Memory and data, in on 16-bit write ENC_SPICON1bits.MODE16 = 1; ENC_SSPBUF = ((WCR | Address)<<8) | (INT16U)Data; // Start sending the INT8U WaitForDataByte(); // Wait until INT8U is transmitted ENC_SPICON1bits.MODE16 = 0; } #else { ENC_SSPBUF = WCR | Address; // Send the opcode and address. WaitForDataByte(); // Wait until opcode/constant is transmitted. Dummy = ENC_SSPBUF; ENC_SSPBUF = Data; // Send the byte to be writen. WaitForDataByte(); // Wait until finished transmitting } #endif Dummy = ENC_SSPBUF; ENC_CS_IO = 1; }//end WriteReg /****************************************************************************** * Function: void BFCReg(INT8U Address, INT8U 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(INT8U Address, INT8U Data) { volatile INT8U Dummy; ENC_CS_IO = 0; ClearSPIDoneFlag(); ENC_SSPBUF = BFC | Address; // Send the opcode and address. WaitForDataByte(); // Wait until opcode/address is transmitted. Dummy = ENC_SSPBUF; ENC_SSPBUF = Data; // Send the byte to be writen. WaitForDataByte(); // Wait until register is written. Dummy = ENC_SSPBUF; ENC_CS_IO = 1; }//end BFCReg /****************************************************************************** * Function: void BFSReg(INT8U Address, INT8U 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(INT8U Address, INT8U Data) { volatile INT8U Dummy; ENC_CS_IO = 0; ClearSPIDoneFlag(); ENC_SSPBUF = BFS | Address; // Send the opcode and address. WaitForDataByte(); // Wait until opcode/address is transmitted. Dummy = ENC_SSPBUF; ENC_SSPBUF = Data; // Send the byte to be writen. WaitForDataByte(); // Wait until register is written. Dummy = ENC_SSPBUF; 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(INT8U Register, INT16U Data) { // Write the register address BankSel(MIREGADR); WriteReg((INT8U)MIREGADR, Register); // Write the data // Order is important: write low byte first, high byte last WriteReg((INT8U)MIWRL, ((WORD_VAL*)&Data)->v[0]); WriteReg((INT8U)MIWRH, ((WORD_VAL*)&Data)->v[1]); // Wait until the PHY register has been written BankSel(MISTAT); while(ReadMACReg((INT8U)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(INT16U 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(INT8U 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(INT8U NewConfig) { BankSel(ECOCON); WriteReg((INT8U)ECOCON, NewConfig); BankSel(ERDPTL); }//end SetCLKOUT /****************************************************************************** * Function: INT8U GetCLKOUT(void) * * PreCondition: SPI bus must be initialized (done in MACInit()). * * Input: None * * Output: INT8U - 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 *****************************************************************************/ INT8U GetCLKOUT(void) { INT8U i; BankSel(ECOCON); i = ReadETHReg((INT8U)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}; INT8U HTRegister; INT8U i, j; // Calculate a CRC-32 over the 6 byte MAC address // using polynomial 0x4C11DB7 for(i = 0; i < sizeof(MAC_ADDR); i++) { INT8U 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) + (INT8U)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<<i); BankSel(ERDPTL); // Return to Bank 0 } #endif //// GetRegs is a function for debugging purposes only. It will read all //// registers and store them in the PIC's RAM so they can be viewed with //// the ICD2. //REG Regs[4][32]; //void GetRegs(void) //{ // INT8U i; // // BankSel(0x000); // for(i=0; i<0x1A; i++) // Regs[0][i] = ReadETHReg(i); // for(i=0x1B; i<32; i++) // Regs[0][i] = ReadETHReg(i); // // BankSel(0x100); // for(i=0; i<0x1A; i++) // Regs[1][i] = ReadETHReg(i); // for(i=0x1B; i<32; i++) // Regs[1][i] = ReadETHReg(i); // // BankSel(0x200); // for(i=0; i<5; i++) // Regs[2][i] = ReadMACReg(i); // Regs[2][5] = ReadETHReg(i); // for(i=6; i<0x0F; i++) // Regs[2][i] = ReadMACReg(i); // Regs[2][0x0F] = ReadETHReg(i); // for(i=0x10; i<0x13; i++) // Regs[2][i] = ReadMACReg(i); // Regs[2][0x13] = ReadETHReg(i); // for(i=0x14; i<0x1A; i++) // Regs[2][i] = ReadMACReg(i); // for(i=0x1B; i<32; i++) // Regs[2][i] = ReadETHReg(i); // // BankSel(0x300); // for(i=0; i<0x06; i++) // Regs[3][i] = ReadMACReg(i); // for(i=6; i<0x0A; i++) // Regs[3][i] = ReadETHReg(i); // Regs[3][0x0A] = ReadMACReg(i); // for(i=0x0B; i<0x1A; i++) // Regs[3][i] = ReadETHReg(i); // for(i=0x1B; i<32; i++) // Regs[3][i] = ReadETHReg(i); // // Regs[0][0x1A].Val = 0; // Regs[1][0x1A].Val = 0; // Regs[2][0x1A].Val = 0; // Regs[3][0x1A].Val = 0; // // BankSel(ERDPTL); // // return; //} //// Get8KBMem is a function intended for debugging purposes. It will read all //// Ethernet RAM and output it in hex out the UART //void Get8KBMem(void) //{ // WORD_VAL i; // INT8U v; // WORD_VAL RDSave; // // RDSave.v[0] = ReadETHReg(ERDPTL).Val; // RDSave.v[1] = ReadETHReg(ERDPTH).Val; // // for(i.Val = 0; i.Val < 8192; i.Val++) // { // WriteReg(ERDPTL, i.v[0]); // WriteReg(ERDPTH, i.v[1]); // v = MACGet(); // // putcUART('0'); // while(BusyUART()); // putcUART('x'); // while(BusyUART()); // putcUART(btohexa_high(v)); // while(BusyUART()); // putcUART(btohexa_low(v)); // while(BusyUART()); // } // // WriteReg(ERDPTL, RDSave.v[0]); // WriteReg(ERDPTH, RDSave.v[1]); // //} #endif //#if defined(ENC_CS_TRIS)
驱动enc28j60芯片
时间: 2024-10-13 06:53:13