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 + -
显示快捷键?