Here is the code to write / read from the ENC chip over SPI. This layer sits below the MAC layer (Part 3) and utilized functions from the SPI library (Part 1).
ENC28J60.h
//ENC28J60.h #ifndef H_ENC28J60 #define H_ENC28J60 #include #include "globals.h" #include "static.h" #include "controlboard.h" #include "ENCDefs.h" #include "protocol/IPSuite.h" #define ERXST (0x1000) // Receive Buffer Start #define ERXND (0x1FFF) // Receive Buffer End #define MAMXFL (0x05EE) // Maximum Frame Length (in bytes) #define BBIPG (0x15) // Back to Back Inter-packet Gap #define TX_BASEADDR (0x0100) // Base Address for Packet Development // Write Buffer Memory States #define WBM_OFF (0) #define WBM_ON (1) // Read Buffer Memory States #define RBM_OFF (0) #define RBM_ON (1) // Pointer Types #define TX_ST (0) #define TX_ND (1) #define RD (2) #define WR (3) void ENC_Enable(void); unsigned char ENC_ReadControlRegister(unsigned int, unsigned char); unsigned char ENC_ReadControlRegisterETH(unsigned char); unsigned char ENC_ReadControlRegisterMACMII(unsigned char); unsigned int ENC_ReadControlRegisterPHY(unsigned char); void ENC_RBM_Open(void); void ENC_RBM_Close(void); unsigned char ENC_ReadBufferMemory(void); void ENC_WriteControlRegister(unsigned int, unsigned char); void ENC_BFS(unsigned int, unsigned char); void ENC_BFC(unsigned int, unsigned char); void ENC_WBM_Open(void); void ENC_WBM_Close(void); void ENC_WriteBufferMemory(unsigned char); void ENC_SoftReset(void); void ENC_SetBank(unsigned char); void ENC_Init(unsigned char); void ENC_InitMAC(unsigned char); void ENC_InitPHY(unsigned char); unsigned char rtoc(REG); void ENC_SetTxPointer(unsigned char, unsigned int); void ENC_HandleInterrupts(void); void ENC_TxPrepare(void); void ENC_TxSend(void); void ENC_HandleRx(unsigned char); unsigned char ENC_RxBufferSize(void); #endif
ENC28J60.c
//ENC28J60.c
#include "ENC28J60.h"
unsigned char WBM_STATE;
unsigned char RBM_STATE;
unsigned int TxEnd; // Needed for Transmit Counter
unsigned char rx_buffer[4][2048];
unsigned char rx_buffer_wr;
unsigned char rx_buffer_rd;
extern unsigned char HW_ADDRESS[6];
//! Enables the ENC Module (asserts the /RST line)
void ENC_Enable(void)
{
// Raise CS
E_CS = 1;
// Enable Ethernet
E_RST = 1;
}
//! Reads a Control Register from ENC Module
/*!
\param loc register location
\param bankset explicitly change bank before read (0=no, 1=yes (recommended))
\return the byte stored in the register
*/
unsigned char ENC_ReadControlRegister(unsigned int loc, unsigned char bankset)
{
unsigned char bank, addr, byte;
// Pull Bank From Address
bank = ((loc >>
& 0x0F);
// Trim Address
addr = (loc & 0xFF);
// Check for out of range
if(addr > 0x1F){ return; }
if(bankset)
{
ENC_SetBank(bank);
}
//printf("B:0x%02x, A:0x%02x\r\n", bank, addr);
//return;
byte = -1;
if((bank == 0) || (bank == 1))
{
byte = ENC_ReadControlRegisterETH(addr);
}
else if(bank == 2)
{
byte = ENC_ReadControlRegisterMACMII(addr);
}
else if((bank == 3) && (addr < 0x06))
{
byte = ENC_ReadControlRegisterMACMII(addr);
}
else if((bank == 3) && (addr >= 0x06))
{
byte = ENC_ReadControlRegisterETH(addr);
}
else
{
printf("Failed Address Parsing.\r\n");
}
#ifdef DEBUG_ENC
printf("ENC: [%02x] = %02x\r\n", addr, byte);
#endif
return byte;
}
//! Reads a Control Register from ENC Module's ETH Registers (should not be called explicitly.)
/*!
\param loc register location
\return the byte stored in the register
\sa ENC_ReadControlRegister()
*/
unsigned char ENC_ReadControlRegisterETH(unsigned char address)
{
unsigned char byte;
E_CS = 0;
SPI_Put(2, address);
byte = SPI_Get(2);
E_CS = 1;
return byte;
}
//! Reads a Control Register from ENC Module's MAC or MII Registers (should not be called explicitly.)
/*!
\param loc register location
\return the byte stored in the register
\sa ENC_ReadControlRegister()
*/
unsigned char ENC_ReadControlRegisterMACMII(unsigned char address)
{
unsigned char dummy, byte;
E_CS = 0;
SPI_Put(2, address);
dummy = SPI_Get(2);
byte = SPI_Get(2);
E_CS = 1;
return byte;
}
//! Reads a Control Register from ENC Module's PHY Register
/*!
\param loc register location
\return the value (16-bits) stored in the register
*/
unsigned int ENC_ReadControlRegisterPHY(unsigned char addr)
{
unsigned char byte, temp, hb, lb;
unsigned int i, val;
// Check for out of bounds
if(addr > 0x14){ return; }
// 1. Write the address of the PHY register to read from into the MIREGADR register.
ENC_WriteControlRegister(MIREGADR, addr);
// 2. Set the MICMD.MIIRD bit. The read operation begins and the MISTAT.BUSY bit is set.
ENC_BFS(MICMD, MICMD_MIIRD);
// 3. Wait 10.24 µs. Poll the MISTAT.BUSY bit to be certain that the operation is complete.
// While busy, the host controller should not start any MIISCAN operations or write to the MIWRH register.
// When the MAC has obtained the register contents, the BUSY bit will clear itself.
do{
for(i = 0; i<1024; i++){ ; }
temp = ENC_ReadControlRegister(MISTAT, 1);
} while ((temp & 0x01) == 0x01);
// 4. Clear the MICMD.MIIRD bit.
ENC_BFC(MICMD, 0x01);
// 5. Read the desired data from the MIRDL and MIRDH registers. The order that these bytes are
// accessed is unimportant.
hb = ENC_ReadControlRegister(MIRDH, 1);
lb = ENC_ReadControlRegister(MIRDL, 1);
val = hb;
val = (val << 8);
val+= lb;
#ifdef DEBUG_ENC
printf("ENC: [PHY 0x%02x] = %04x\r\n", addr, val);
#endif
return val;
}
//! Begins the process of a multi-byte read from the read-buffer
/*!
\sa ENC_RBM_Close()
*/
void ENC_RBM_Open(void)
{
E_CS = 0;
SPI_Put(2, 0x3A);
RBM_STATE = RBM_ON;
return;
}
//! Ends the process of a multi-byte read from the read-buffer
/*!
\sa ENC_RBM_Open()
*/
void ENC_RBM_Close(void)
{
E_CS = 1;
RBM_STATE = RBM_OFF;
return;
}
//! Reads a single byte from read-buffer (register location where read pointer is)
/*!
\return byte byte value read (value at buffer read pointer)
*/
unsigned char ENC_ReadBufferMemory(void)
{
unsigned char byte;
if(RBM_STATE != RBM_ON){ printf("Trying a RBM Read when RBM_STATE != RBM_ON!\r\n"); return ;}
byte = SPI_Get(2);
#ifdef DEBUG_ENC
printf("ENC: [BM] = %02x\r\n", byte);
#endif
return byte;
}
//! Writes a byte to a control register location
/*!
\param loc 16-bit register location (bits 11-8 are bank, 7-0 are register)
\param byte 8-bit value to be written
*/
void ENC_WriteControlRegister(unsigned int loc, unsigned char byte)
{
unsigned char opcode = 0x02;
unsigned char bank, addr;
// Pull Bank From Address
bank = ((loc >>
& 0x0F);
// Trim Address
addr = (loc & 0xFF);
ENC_SetBank(bank);
if(addr > 0x1F){ return; }
E_CS = 0;
SPI_Put(2, ((opcode << 5) | addr));
SPI_Put(2, byte);
E_CS = 1;
#ifdef DEBUG_ENC
printf("ENC: [%02x] <= %02x\r\n", addr, byte);
#endif
return;
}
//! Sets certain bits in a register
/*!
byte is not the bitval, but rather a mask
(ie: not 0, 1, 2 but rather 0x01, 0x02, 0x04)
\param loc 16-bit register location (bits 11-8 are bank, 7-0 are register)
\param byte 8-bit mask to be set
*/
void ENC_BFS(unsigned int loc, unsigned char byte)
{
unsigned char opcode = 0x04;
unsigned char bank, addr;
// Pull Bank From Address
bank = ((loc >>
& 0x0F);
// Trim Address
addr = (loc & 0xFF);
if(addr > 0x1F){ return; }
// Prevent Recursive Lock
if(addr <= 0x1A){
ENC_SetBank(bank);
}
E_CS = 0;
SPI_Put(2, ((opcode << 5) | addr));
SPI_Put(2, byte);
E_CS = 1;
#ifdef DEBUG_ENC
printf("ENC: [%02x] <| %02x\r\n", addr, byte);
#endif
}
//! Clears certain bits in a register
/*!
byte is not the bitval, but rather a mask
(ie: not 0, 1, 2 but rather 0x01, 0x02, 0x04)
\param loc 16-bit register location (bits 11-8 are bank, 7-0 are register)
\param byte 8-bit mask to be set
*/
void ENC_BFC(unsigned int loc, unsigned char byte)
{
unsigned char opcode = 0x05;
unsigned char bank, addr;
// Pull Bank From Address
bank = ((loc >>
& 0x0F);
// Trim Address
addr = (loc & 0xFF);
if(addr > 0x1F){ return; }
// Prevent Recursive Lock
if(addr <= 0x1A){
ENC_SetBank(bank);
}
E_CS = 0;
SPI_Put(2, ((opcode << 5) | addr));
SPI_Put(2, byte);
E_CS = 1;
#ifdef DEBUG_ENC
printf("ENC: [%02x] <: %02x\r\n", addr, byte);
#endif
}
//! Starts the process of a multi-byte write to the write-buffer
/*!
\sa ENC_RBM_Close()
*/
void ENC_WBM_Open(void)
{
E_CS = 0;
SPI_Put(2, 0x7A);
WBM_STATE = WBM_ON;
return;
}
//! Ends the process of a multi-byte write to the write-buffer
/*!
\sa ENC_RBM_Open()
*/
void ENC_WBM_Close(void)
{
E_CS = 1;
WBM_STATE = WBM_OFF;
return;
}
//! Writes a single byte to the write buffer (register location where write pointer is)
/*!
\return byte byte to be written (value at buffer write pointer)
*/
void ENC_WriteBufferMemory(unsigned char byte)
{
if(WBM_STATE != WBM_ON){ printf("Trying a WBM Write when WBM_STATE != WBM_ON!\r\n"); return ;}
SPI_Put(2, byte);
// Advance End Counter
TxEnd++;
#ifdef DEBUG_ENC
printf("ENC: [BM] <= %02x\r\n", byte);
#endif
return;
}
//! Issues a reset command to the ENC Module
void ENC_SoftReset(void)
{
E_CS = 0;
SPI_Put(2, 0xFF);
SPI_Get(2);
E_CS = 1;
#ifdef DEBUG_ENC
printf("ENC: Soft Reset Issued\r\n");
#endif
return;
}
//! Changes the ENC module's current bank
/*!
\param byte bank to switch to (only used bottom 2 bits)
*/
void ENC_SetBank(unsigned char byte)
{
unsigned char bank = (byte & 0x03);
ENC_BFC(ECON1, 0x03);
ENC_BFS(ECON1, bank);
#ifdef DEBUG_ENC
printf("ENC: Bank Switched to %1d\r\n", bank);
#endif
return;
}
//! Initializes ENC Module
/*!
\param echo prints spew as it goes along (0=no, 1=yes)
*/
void ENC_Init(unsigned char echo)
{
unsigned char byte;
unsigned char byteval;
REG RXF;
if(echo){ printf("Initializing ENC Controller...\r\n"); }
ENC_SoftReset();
if(echo){ printf(" Controller Reset.\r\n"); }
// 6.4 OST Timer
if(echo){ printf(" Waiting for CLKOK..."); }
do{
byte = ENC_ReadControlRegister(ESTAT, 1);
} while((byte & 0x01) != 0x01);
if(echo){ printf(" OK\r\n"); }
// 6.1 Receive Buffer
ENC_WriteControlRegister(ERXSTL, make8(ERXST,0));
ENC_WriteControlRegister(ERXSTH, make8(ERXST,1));
ENC_WriteControlRegister(ERXNDL, make8(ERXND,0));
ENC_WriteControlRegister(ERXNDH, make8(ERXND,1));
if(echo){ printf(" Receive Buffer Initialized [0x%04x:0x%04x]\r\n", ERXST, ERXND); }
ENC_WriteControlRegister(ERXRDPTL, make8(ERXST,0));
ENC_WriteControlRegister(ERXRDPTH, make8(ERXST,1));
if(echo){ printf(" Receive Read Pointer Initialized [@0x%04x]\r\n", ERXST); }
// 6.2 Transmit Buffer
// No initialization necessary
// 6.3 Receive Filters
/*
RXF.ERXFCONbits.ANDOR = 1; // Packets will be rejected unless all enabled filters accept the packet (1)
RXF.ERXFCONbits.UCEN = 1; // Packets not having a dest. addr. matching the local MAC will be discarded (1)
RXF.ERXFCONbits.CRCEN = 1; // Enable CRC Filter (1)
RXF.ERXFCONbits.PMEN = 0; // Pattern Match Filter Disabled (0)
RXF.ERXFCONbits.MPEN = 0; // Magic Packet Filter Disabled (0)
RXF.ERXFCONbits.HTEN = 0; // Hast Table Filter Disabled (0)
RXF.ERXFCONbits.MCEN = 0; // Multicast Filter Disabled (0)
RXF.ERXFCONbits.BCEN = 0; // Broadcast Filter Disabled (0)
*/
ENC_WriteControlRegister(ERXFCON, RXF.Val);
if(echo){ printf(" Receive Filters Configured [0x%02x]\r\n", RXF.Val); }
// 6.5 MAC Initialization Settings
ENC_InitMAC(echo);
// 6.6 PHY Initialization Settings
ENC_InitPHY(echo);
ENC_BFS(EIE, EIE_PKTIE | EIE_INTIE);
if(echo){ printf(" Packet Receive Interrupts Enabled.\r\n"); }
ENC_BFS(ECON1, ECON1_RXEN);
if(echo){ printf(" Packet Receiver Enabled.\r\n"); }
rx_buffer_rd = 0;
rx_buffer_wr = 0;
if(echo){ printf(" Receive Buffers Enabled.\r\n"); }
if(echo){ printf("ENC Controller Initialization Complete.\r\n"); }
return;
}
//! Initializes ENC Module's MAC
/*!
Should be called from ENC_Init()
\param echo prints spew as it goes along (0=no, 1=yes)
\sa ENC_Init()
*/
void ENC_InitMAC(unsigned char echo)
{
unsigned char i;
if(echo){ printf(" Begin MAC Initialization...\r\n"); }
// 1
ENC_BFS(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
// 2
// Pad to 64 bytes, Enable Add. of CRC, Enable Full Duplex
ENC_BFS(MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FULDPX);
// 3
// Leave MACON4 as default (0x00)
// 4
ENC_WriteControlRegister(MAMXFLL, make8(MAMXFL, 0));
ENC_WriteControlRegister(MAMXFLH, make8(MAMXFL, 1));
if(echo){ printf(" MAC Max Frame Length Set [0x%04x]\r\n", MAMXFL); }
// 5
ENC_WriteControlRegister(MABBIPG, BBIPG);
if(echo){ printf(" MAC Back-to-back Inter-packet Gap Set [0x%02x]\r\n", BBIPG); }
// 6
ENC_WriteControlRegister(MAIPGL, 0x12);
// 7
// 8
// 9
ENC_WriteControlRegister(MAADR1, HW_ADDRESS[0]);
ENC_WriteControlRegister(MAADR2, HW_ADDRESS[1]);
ENC_WriteControlRegister(MAADR3, HW_ADDRESS[2]);
ENC_WriteControlRegister(MAADR4, HW_ADDRESS[3]);
ENC_WriteControlRegister(MAADR5, HW_ADDRESS[4]);
ENC_WriteControlRegister(MAADR6, HW_ADDRESS[5]);
if(echo)
{
printf(" MAC Address Programmed: ");
for(i=0; i<6; i++)
{
printf("%02x", HW_ADDRESS[i]);
if(i != 5){ printf("-"); }
}
printf("\r\n");
}
if(echo){ printf(" MAC Initialization Complete.\r\n"); }
return;
}
//! Initializes ENC Module's PHY
/*!
Should be called from ENC_Init()
\param echo prints spew as it goes along (0=no, 1=yes)
\sa ENC_Init()
*/
void ENC_InitPHY(unsigned char echo)
{
if(echo){ printf(" Begin PHY Initialization...\r\n"); }
if(echo){ printf(" PHY Initialization Complete.\r\n"); }
return;
}
//! Sets a ENC Module Tx Pointer
/*!
\param type which pointer to set (see .h files for type enumeration)
\param loc 16-bit register address to set it to
*/
void ENC_SetTxPointer(unsigned char type, unsigned int loc)
{
if(type == TX_ST)
{
ENC_WriteControlRegister(ETXSTL, make8(loc, 0));
ENC_WriteControlRegister(ETXSTH, make8(loc, 1));
}
else if(type == TX_ND)
{
ENC_WriteControlRegister(ETXNDL, make8(loc, 0));
ENC_WriteControlRegister(ETXNDH, make8(loc, 1));
}
else if(type == RD)
{
ENC_WriteControlRegister(ERDPTL, make8(loc, 0));
ENC_WriteControlRegister(ERDPTH, make8(loc, 1));
}
else if(type == WR)
{
ENC_WriteControlRegister(EWRPTL, make8(loc, 0));
ENC_WriteControlRegister(EWRPTH, make8(loc, 1));
}
else
{
printf("Error: Incorrect Pointer Type!\r\n");
}
}
//! ISR for interrupts received from ENC
void ENC_HandleInterrupts(void)
{
unsigned char flags, PKTCNT;
flags = ENC_ReadControlRegister(EIR, 1);
#ifdef DEBUG_ISR
printf("Interrupt Flags: 0x%02x\r\n", flags);
#endif
if(flags)
{
// Disable Global IE while handling
ENC_BFC(EIE, EIE_INTIE);
if(flags & (1<<6))
{
do{
PKTCNT = ENC_ReadControlRegister(EPKTCNT, 1);
if(PKTCNT)
{
#ifdef DEBUG_ISR
printf("- PKTIF\r\n");
#endif
ENC_BFC(EIR, EIR_PKTIF);
ENC_HandleRx(PKTCNT);
// Have any more packets arrived?
PKTCNT = ENC_ReadControlRegister(EPKTCNT, 1);
}
} while(PKTCNT > 0);
}
if(flags & (1<<3))
{
#ifdef DEBUG_ISR
printf("- TXIF\r\n");
#endif
ENC_BFC(EIR, EIR_TXIF);
}
// Re-enable Global IE
ENC_BFS(EIE, EIE_INTIE);
}
}
//! Prepares the ENC Module to accept an incoming packet
/*!
Sets Pointers Correctly, Resets Length Counter, Opens WBM
*/
void ENC_TxPrepare(void)
{
unsigned int TxStart;
TxStart = TX_BASEADDR;
// Set Tranmit Start Pointer
ENC_SetTxPointer(TX_ST, TxStart);
#ifdef DEBUG_TXPTR
printf("[TX_ST = %04x]\r\n", TxStart);
#endif
// Set WBM Pointer
ENC_SetTxPointer(WR, TxStart);
TxEnd = TxStart;
// Open WBM
ENC_WBM_Open();
// Packet Control Byte
ENC_WriteBufferMemory(0x00);
}
//! Sends a packet that was loaded through ENC_TxPrepare and ENC_WBM
/*!
Closes WBM, Sets Pointer, Configures Interrupts, Initiates Transfer
*/
void ENC_TxSend(void)
{
// Close WBM
ENC_WBM_Close();
// Set Transmit End Pointer
ENC_SetTxPointer(TX_ND, --TxEnd);
#ifdef DEBUG_TXPTR
printf("[TX_ND = %04x]\r\n", TxEnd);
#endif
// Configure Interrupts
if(1)
{
ENC_BFC(EIR, EIR_TXIF);
ENC_BFS(EIE, EIE_TXIE | EIE_INTIE);
}
// Begin Transmission
ENC_BFS(ECON1, ECON1_TXRTS);
}
//! Prepares the ENC Module to accept an incoming packet
/*!
Called from ISR, Handles an RX interrupt
\param PKTCNT PKTCNT register value (# of unread packets in buffer)
*/
void ENC_HandleRx(unsigned char PKTCNT)
{
unsigned char RDHB, RDLB, NPHB, NPLB;
unsigned int RXRD, RXNP;
unsigned int i;
unsigned char byte;
RDLB = ENC_ReadControlRegister(ERXRDPTL, 1);
RDHB = ENC_ReadControlRegister(ERXRDPTH, 1);
RXRD = form16(RDHB, RDLB);
// Set Read Pointer
ENC_WriteControlRegister(ERDPTL, RDLB);
ENC_WriteControlRegister(ERDPTH, RDHB);
ENC_RBM_Open();
// Find out where next pointer is
NPLB = ENC_ReadBufferMemory();
NPHB = ENC_ReadBufferMemory();
RXNP = form16(NPHB, NPLB);
#ifdef DEBUG_RAWRX
printf("Packet Rec'd [0x%04x:0x%04x]\r\n", RXRD, RXNP);
#endif
i = 0;
while((RXRD + i + 2) < RXNP)
{
byte = ENC_ReadBufferMemory();
rx_buffer[rx_buffer_wr][i+2] = byte;
#ifdef DEBUG_RAWRX
printf("%02x", byte);
if(((i) % 16) == 15){ printf("\r\n"); }
else { printf(" "); }
#endif
i++;
}
#ifdef DEBUG_RAWRX
printf("\r\n");
#endif
#ifdef DEBUG_RXRING
printf("Stored to Buffer %d\r\n", rx_buffer_wr);
#endif
// Load Length
rx_buffer[rx_buffer_wr][0] = make8(i,1);
rx_buffer[rx_buffer_wr][1] = make8(i,0);
// Advance rx_buffer_wr
rx_buffer_wr++;
if(rx_buffer_wr >= 4){ rx_buffer_wr = 0; }
ENC_RBM_Close();
// Decrement Packet Counter
ENC_BFS(ECON2, ECON2_PKTDEC);
// Advance Rx Read Pointer
ENC_WriteControlRegister(ERXRDPTL, NPLB);
ENC_WriteControlRegister(ERXRDPTH, NPHB);
}
//! Calculates and returns size of Rx ring buffer
/*!
\return The value between 0(empty) and buffer-size-1, inclusive.
*/
unsigned char ENC_RxBufferSize(void)
{
if(rx_buffer_wr == rx_buffer_rd)
{
return 0;
}
if(rx_buffer_wr > rx_buffer_rd)
{
return (rx_buffer_wr - rx_buffer_rd);
}
else
{
return (4 - rx_buffer_rd - rx_buffer_wr);
}
}
RSS Feed