enc28j60.c

来自「本附件为嵌入式Web的相关资料」· C语言 代码 · 共 1,923 行 · 第 1/4 页

C
1,923
字号
/*********************************************************************
 *
 *	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
 *
 * This software is owned by Microchip Technology Inc. ("Microchip") 
 * and is supplied to you for use exclusively as described in the 
 * associated software agreement.  This software is protected by 
 * software and other intellectual property laws.  Any use in 
 * violation of the software license may subject the user to criminal 
 * sanctions as well as civil liability.  Copyright 2006 Microchip
 * Technology Inc.  All rights reserved.
 *
 * This software is provided "AS IS."  MICROCHIP DISCLAIMS ALL 
 * WARRANTIES, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, NOT LIMITED 
 * TO MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND 
 * INFRINGEMENT.  Microchip shall in no event be liable for special, 
 * incidental, or consequential damages.
 *
 *
 * 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 "mchp_tcp_ip\tcpip.h"
#include <plib.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 (0x02 << 5)			// Write Control Register command
#define BFS (0x04 << 5)			// Bit Field Set command
#define	BFC (0x05 << 5)			// Bit Field Clear command
#define	RCR (0x00 << 5)			// Read Control Register command
#define RBM ((0x01 << 5) | 0x1A)	// Read Buffer Memory command
#define	WBM ((0x03 << 5) | 0x1A) // Write Buffer Memory command
#define	SR  ((0x07 << 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;
} __attribute__((packed)) 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;



/*************************************************************
 * EN28J60 SPI functions
 *************************************************************/
#if defined(__18CXX)
#define _ENC_SPICON             0x20
#elif defined(__PIC24H__) || defined(__dsPIC33F__)
#define _ENC_SPICON             0x0F
#elif defined(__PIC24F__)
#define _ENC_SPICON             0x1B
#else
#define _ENC_SPICON             0x17
#endif

/*************************************************************
 * EN28J60 SPI Open
 *************************************************************/
inline void SPIOpen(void)
{
#ifdef EXPLORER_16
    mSetSPILatch();
#endif
    // Set up SPI
#ifdef __PIC32MX__

#ifdef _ENC_USE_SPI_1
    OpenSPI1(0x013F, 0x8000);
#else
    OpenSPI2(0x013F, 0x8000);
#endif

#elif defined(__18CXX)
	ENC_SPICON1         = _ENC_SPICON;	
	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
	ENC_SPICON1             = _ENC_SPICON;	
    ENC_SPICON1bits.CKE     = 1;
    ENC_SPICON1bits.MSTEN   = 1;
    ENC_SPISTATbits.SPIEN   = 1;
#endif

}
/*************************************************************
 * EN28J60 SPI Byte Transfer
 *************************************************************/
inline BYTE ByteTransfer(BYTE data)
{
    BYTE result;

#ifdef EXPLORER_16
    mSetSPILatch();
#endif

#ifdef __PIC32MX__

    #ifdef _ENC_USE_SPI_1
    putcSPI1((unsigned int)data);
    result = (BYTE)getcSPI1();
    #else
    putcSPI2((unsigned int)data);
    result = (BYTE)getcSPI2();
    #endif
#else
	ENC_SPI_IF = 0;
    ENC_SSPBUF = data;
	while(!ENC_SPI_IF);		// Wait until opcode/address is transmitted.
	result = ENC_SSPBUF;
    ENC_SPI_IF = 0;
#endif
	return result;

}

//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;

    SPIOpen();

	// 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

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?