Ethernet 3/7
The SPI interface
The communication between ENC28J60 and PIC it happens through the interface SPI; this only supports the 0,0 modality and the controller is a slave, so the PIC provides the clock and manages the transmission.
The maximum admitted frequency is of 10Mhz for the Rev. B1-B4, while is double for the B5. Also, because of a problem in the interface (described in Errata), for the Rev. B1-B4 the clock must necessarily be between 8 and 10Mhz, or the PIC clock must be draw from the pin CLKOUT of the controller (max 25Mhz). So, in the first case the PIC must work at a frequency between 32 and 40Mhz.
SPI Commands
Through the interface is possible to send to the chip 7 various commands (of 8bit), eventuality followed from a dates byte; there are:
| Nome | 1° byte | 2° byte | Description |
| Read Control Register (RCR) | 000 AAAAA | is useful to read the control register A | |
| Write Control Register (WCR) | 010 AAAAA | DDDDDDDD | to write the byte D inside the registry A (in the selected bank) |
| Read Buffer Memory (RBM) | 00111010 | is useful to read the controller RAM memory to the current address. | |
| Write Buffer Memory (WBM) | 01111010 | DDDDDDDD | writes the byte D to the current address of the RAM memory. |
| Bit Field Set (BFS) | 100 AAAAA | DDDDDDDD | sets in the registry A (just ETH), the bit which in D are at 1 (OR). |
| Bit Field Set (BFS) | 101 AAAAA | DDDDDDDD | resets in the registry A (just ETH), the bit which in D are at 1 (NOT AND). |
| System Reset Command (SRC) | 11111111 | reset the controller. |
SPI Commands: implementation
The SPI initialization module is illustrated, let's see some base methods.
#define spiWrite(x) spiRW(x)
#define spiRead() spiRW(0)
....
u8 spiRW(u8 data){
SSPBUF = data;
while(!PIR1bits.SSPIF);
PIR1bits.SSPIF = 0;
return SSPBUF;
}This is a method which permits to read/write in the bus SPI. As far as the writing, the byte for the sending (data) is put in the SSPBUF registry (is part of SPI module), then attend that the transmission is finished observing the SSPIF bit; to read a byte, is necessary to write a zero in the registry SSPBUF (like this are generated 8 impulses of clock), attend the operation end and the read byte it will be in the same SSPBUF registry .
In this case I I have condensed in an only method the reading operations and writing, then, for mental clarity, I have defined spiWrite and spiRead.
#define CS PORTCbits.RC2 // Chip Select dell'ENC28J60
#define WCR (0b01000000) // Write Control Register command
#define BFS (0b10000000) // Bit Field Set command
#define BFC (0b10100000) // Bit Field Clear command
#define RCR (0b00000000) // Read Control Register command
#define RBM (0b00111010) // Read Buffer Memory command
#define WBM (0b01111010) // Write Buffer Memory command
#define SRC (0b11111111) // System Reset command
....
void writeReg(u8 reg, u8 data){
CS = 0;
spiWrite(WCR | reg);
spiWrite(data);
CS = 1;
}
u8 readMAC(u8 reg){
u8 b;
CS = 0;
spiWrite(RCR | reg);
spiRead();
b = spiRead();
CS = 1;
return b;
}
u8 readETH(u8 reg){
u8 b;
CS = 0;
spiWrite(RCR | reg);
b = spiRead();
CS = 1;
return b;
}With these three methods we can read and write the control registries ( to read registries MII is used readMAC).
Like you can notice the reading procedure of a MAC and ETH registry is slightly diverse, for the first type must send a bite zero before make the reading.
void BFCReg(u8 reg, u8 data){
CS = 0;
spiWrite(BFC | reg);
spiWrite(data);
CS = 1;
}
void BFSReg(u8 reg, u8 data){
CS = 0;
spiWrite(BFS | reg);
spiWrite(data);
CS = 1;
}
void setBank(u8 bank){
BFCReg(ECON1, 0b11);
BFSReg(ECON1, bank);
}Here we see the Bit Set and Bit Clear commands implementation; then the setBank method, with which we can select the memory bank of the control registries, acting on the ECON1 registry (commune at all the baks).
u16 bufSize;
....
void encPut(u8 b){
CS = 0;
spiWrite(WBM);
spiWrite(b);
CS = 1;
bufSize++;
}
u8 encGet(){
u8 b;
CS = 0;
spiWrite(RBM);
b = spiRead();
CS = 1;
return b;
}
void sendReset(){
CS = 0;
spiWrite(SRC);
CS = 1;
}The remaining three commands are performed by these three methods: the reading and the writing of the RAM, and reset. The variable static bufSize is useful to keep track of the RAM byte number (it will be useful then).
The PHY registers
The PHY registers are a 16bit and are accessible the way of MII registers.
To read a PHY register:
- it is put the the address in the MIREGADR register;
- setting the bit MIIRD (1) of the MICMD register, starts the reading;
- attend the end of the reading observing the BUSY bit of the MISTAT register;
- resetting the MIIRD bit;
- the data will be present in the MIRDL and MIRDH registers;
To write in a PHY register:
- put the register address in MIREGADR;
- first write the byte less important in MIWRL, then writing the byte more important in MIWRH starts the writing.
- attend that the module PHY finishes the operation.
u16 readPHY(u8 reg){
setBank(2);
writeReg(MIREGADR, reg);
writeReg(MICMD, 0x01);
setBank(3);
while(readMAC(MISTAT) & 1);
setBank(2);
writeReg(MICMD, 0x00);
return readMAC(MIRDL) | (readMAC(MIRDH) << 8 );
}
void writePHY(u8 reg, u16 data){
setBank(2);
writeReg(MIREGADR, reg);
writeReg(MIWRL, LOW(data));
writeReg(MIWRH, HIGH(data));
setBank(3);
while (readMAC(MISTAT) & 1);
}
Others methods
The following are useful to writing/reading more byte from the controller RAM; the address from where are read is contained in the ERDPT registers, while the writing address is contained in the EWRPT registers (are auto increasing).
void encGetArray(u8* buf, u16 len){
CS = 0;
spiWrite(RBM);
while(len--)
*buf++ = spiRead();
CS = 1;
}
void encPutArray(u8* buf,u16 len){
bufSize += len;
CS = 0;
spiWrite(WBM);
while(len--)
spiWrite(*buf++);
CS = 1;
}
void encPutString(const rom u8 *str){
CS = 0;
spiWrite(WBM);
while(*str) {
spiWrite(*str++);
bufSize++;
}
CS = 1;
}
The SPI Initialization
The SPI module initialization it's happen in these two simples instructions:
void encInit(){
TRISB = 0xFF; // configuration I/O di PORTB
TRISC = 0xD1; // configuration I/O di PORTC
PORTC = 0x00;
SSPSTAT = 0x40;
SSPCON1 = 0x20;In particular the module MSSP is enabled and configured in "0,0"modality, with clock equivalent at Fosc/4.
ENC28J60 Initialization
The controller initialization predicts the various registers configurations, as well as the qualification to the reception.
#define RX_BUF_START 0 #define RX_BUF_END 6499 #define TX_BUF_START 6500 .... setBank(0); writeReg(ERXSTL, LOW(RX_BUF_START)); // writeReg(ERXSTH, HIGH(RX_BUF_START)); // start buffer of the reading writeReg(ERXRDPTL, LOW(RX_BUF_END)); // writeReg(ERXRDPTH, HIGH(RX_BUF_END)); // buffer punter of the reading writeReg(ERXNDL, LOW(RX_BUF_END)); // writeReg(ERXNDH, HIGH(RX_BUF_END)); // fine buffer of the reading writeReg(ETXSTL, LOW(TX_BUF_START)); // writeReg(ETXSTH, HIGH(TX_BUF_START)); // start buffer of the reading
The ENC28J60 buffer can be divided between the transmission memory and the reception.
To do that already si configurano i puntatori del buffer di ricezione; the memory remaining will be the transmission buffer.
The registers ERXST contain the first byte address of the reception buffer, while the ERXND registers the last byte.
In ERXRDPT, instead, resides the reading punter of RX memory, or rather check off a zone (together with ERXWRPT) which must be elaborated from the PIC and so can't be wrote; initially the value of this address is the same with ERXND (must be odd aster a problem described in Errata). The ERXWRPT register is zero at reset and is updated automatically to a packet reception.
setBank(2); writeReg(MACON1, 0b01101); // MARXEN, TXPAUS, RXPAUS writeReg(MACON3, 0b00110000); // Half Duplex, Padding 60byte, CRC writeReg(MAIPGL, 0x12); // writeReg(MAIPGH, 0x0C); // writeReg(MABBIPG,0x12); // Inter-Packet Gap
These registers configure the MAC module.Through the MACON1 register is enabled the MAC module and the pause plot reception/transmission.
With the MACON3 register is choice the Duplex (Half or Full) modality of the MAC module that must be imposted in the same mode in the PHY module too; also in this register there are presentes some configurations about Padding automatic and the CRC calculation.
The MAIPG and MABBIPG registers contain the pause values between the packets; those presents in the code are the standard values.
writeReg(MAMXFLL, LOW(1500)); writeReg(MAMXFLH, HIGH(1500));
In the MAXFL (Max Frame Length) registers is salved the maximum dimension allowed for a packet; the controller can be configured that refuses to send a packet which pass this limit.
#define MY_MAC1 0x00 #define MY_MAC2 0x04 #define MY_MAC3 0xA3 #define MY_MAC4 0x00 #define MY_MAC5 0x00 #define MY_MAC6 0x00 .... setBank(3); writeReg(MAADR1, MY_MAC1); writeReg(MAADR2, MY_MAC2); writeReg(MAADR3, MY_MAC3); writeReg(MAADR4, MY_MAC4); writeReg(MAADR5, MY_MAC5); writeReg(MAADR6, MY_MAC6);
The MAC address of our device is saved in the MACADR registers; these are used from the filter to reject the packets which aren't destined to the controller, so the address isn't insert automatically in the send packets.
writePHY(PHCON2, 0b0000000100000000); // disability the loopback writePHY(PHCON1, 0); // ability the PHY setBank(1); writeReg(ERXFCON, 0b10100001); // impost the reception filters BFSReg(ECON1, 0b100); // ability the reception
With this last fragment ends the controller initialization. The PHY module hasn't many options to configure, the only options to configure are the LoopBack disabling (used to make the test) and the module qualification.
The filters are setts to accept just the packets for the configured MAC address and broadcast.
- Chris's blog
- 1935 reads





Post new comment