1 | /*! \file enc28j60.c \brief Microchip ENC28J60 Ethernet Interface Driver. */
|
2 | //*****************************************************************************
|
3 | //
|
4 | // File Name : 'enc28j60.c'
|
5 | // Title : Microchip ENC28J60 Ethernet Interface Driver
|
6 | // Author : Pascal Stang (c)2005
|
7 | // Created : 9/22/2005
|
8 | // Revised : 9/22/2005
|
9 | // Version : 0.1
|
10 | // Target MCU : Atmel AVR series
|
11 | // Editor Tabs : 4
|
12 | //
|
13 | // Description : This driver provides initialization and transmit/receive
|
14 | // functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY.
|
15 | // This chip is novel in that it is a full MAC+PHY interface all in a 28-pin
|
16 | // chip, using an SPI interface to the host processor.
|
17 | //
|
18 | //*****************************************************************************
|
19 |
|
20 | #include "global.h"
|
21 | //#include "timer.h"
|
22 | //#include "rprintf.h"
|
23 |
|
24 | #include "enc28j60.h"
|
25 | #include <avr/io.h>
|
26 |
|
27 |
|
28 | u08 Enc28j60Bank;
|
29 | u16 NextPacketPtr;
|
30 | #define ENC28J60_CONTROL_PORT PORTD
|
31 | #define ENC28J60_CONTROL_DDR DDRD
|
32 | #define ENC28J60_CONTROL_CS 4
|
33 |
|
34 | #define F_CPU 8000000 /* Processor Speed */
|
35 | #define CYCLES_PER_US ((F_CPU+500000)/1000000) /* cpu cycles per microsecond */
|
36 |
|
37 | void delay(unsigned short us)
|
38 | {
|
39 | unsigned short delay_loops;
|
40 | register unsigned short i;
|
41 |
|
42 | delay_loops = (us+3)/5*CYCLES_PER_US; // +3 for rounding up (dirty)
|
43 |
|
44 | // one loop takes 5 cpu cycles
|
45 | for (i=0; i < delay_loops; i++) {};
|
46 | }
|
47 |
|
48 | void nicSetMacAddress(u08* macaddr)
|
49 | {
|
50 | // write MAC address
|
51 | // NOTE: MAC address in ENC28J60 is byte-backward
|
52 | enc28j60Write(MAADR5, *macaddr++);
|
53 | enc28j60Write(MAADR4, *macaddr++);
|
54 | enc28j60Write(MAADR3, *macaddr++);
|
55 | enc28j60Write(MAADR2, *macaddr++);
|
56 | enc28j60Write(MAADR1, *macaddr++);
|
57 | enc28j60Write(MAADR0, *macaddr++);
|
58 | }
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 | u08 enc28j60ReadOp(u08 op, u08 address)
|
65 | {
|
66 | u08 data;
|
67 |
|
68 | // assert CS
|
69 | ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS);
|
70 |
|
71 | // issue read command
|
72 | SPDR = op | (address & ADDR_MASK);
|
73 | while(!(SPSR & (1<<SPIF)));
|
74 | // read data
|
75 | SPDR = 0x00;
|
76 | while(!(SPSR & (1<<SPIF)));
|
77 | // do dummy read if needed
|
78 | if(address & 0x80)
|
79 | {
|
80 | SPDR = 0x00;
|
81 | while(!(inb(SPSR) & (1<<SPIF)));
|
82 | }
|
83 | data = SPDR;
|
84 |
|
85 | // release CS
|
86 | ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS);
|
87 |
|
88 | return data;
|
89 | }
|
90 |
|
91 | void enc28j60WriteOp(u08 op, u08 address, u08 data)
|
92 | {
|
93 | // assert CS
|
94 | ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS);
|
95 |
|
96 | // issue write command
|
97 | SPDR = op | (address & ADDR_MASK);
|
98 | while(!(SPSR & (1<<SPIF)));
|
99 | // write data
|
100 | SPDR = data;
|
101 | while(!(SPSR & (1<<SPIF)));
|
102 |
|
103 | // release CS
|
104 | ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS);
|
105 | }
|
106 |
|
107 | void enc28j60ReadBuffer(u16 len, u08* data)
|
108 | {
|
109 | // assert CS
|
110 | ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS);
|
111 |
|
112 | // issue read command
|
113 | SPDR = ENC28J60_READ_BUF_MEM;
|
114 | while(!(SPSR & (1<<SPIF)));
|
115 | while(len--)
|
116 | {
|
117 | // read data
|
118 | SPDR = 0x00;
|
119 | while(!(SPSR & (1<<SPIF)));
|
120 | *data++ = SPDR;
|
121 | }
|
122 | // release CS
|
123 | ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS);
|
124 | }
|
125 |
|
126 | void enc28j60WriteBuffer(u16 len, u08* data)
|
127 | {
|
128 | // assert CS
|
129 | ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS);
|
130 |
|
131 | // issue write command
|
132 | SPDR = ENC28J60_WRITE_BUF_MEM;
|
133 | while(!(SPSR & (1<<SPIF)));
|
134 | while(len--)
|
135 | {
|
136 | // write data
|
137 | SPDR = *data++;
|
138 | while(!(SPSR & (1<<SPIF)));
|
139 | }
|
140 | // release CS
|
141 | ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS);
|
142 | }
|
143 |
|
144 | void enc28j60SetBank(u08 address)
|
145 | {
|
146 | // set the bank (if needed)
|
147 | if((address & BANK_MASK) != Enc28j60Bank)
|
148 | {
|
149 | // set the bank
|
150 | enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0));
|
151 | enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5);
|
152 | Enc28j60Bank = (address & BANK_MASK);
|
153 | }
|
154 | }
|
155 |
|
156 | u08 enc28j60Read(u08 address)
|
157 | {
|
158 | // set the bank
|
159 | enc28j60SetBank(address);
|
160 | // do the read
|
161 | return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address);
|
162 | }
|
163 |
|
164 | void enc28j60Write(u08 address, u08 data)
|
165 | {
|
166 | // set the bank
|
167 | enc28j60SetBank(address);
|
168 | // do the write
|
169 | enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data);
|
170 | }
|
171 |
|
172 |
|
173 |
|
174 | void enc28j60PhyWrite(u08 address, u16 data)
|
175 | {
|
176 | // set the PHY register address
|
177 | enc28j60Write(MIREGADR, address);
|
178 |
|
179 | // write the PHY data
|
180 | enc28j60Write(MIWRL, data);
|
181 | enc28j60Write(MIWRH, data>>8);
|
182 |
|
183 | // wait until the PHY write completes
|
184 | while(enc28j60Read(MISTAT) & MISTAT_BUSY);
|
185 | }
|
186 |
|
187 | void enc28j60Init(void)
|
188 | {
|
189 | // initialize I/O
|
190 |
|
191 | ENC28J60_CONTROL_DDR |= 1<<ENC28J60_CONTROL_CS;
|
192 | ENC28J60_CONTROL_PORT |= 1<<ENC28J60_CONTROL_CS;
|
193 |
|
194 |
|
195 | /*
|
196 | PORTB |= 1<<PB5; //SCK HI
|
197 | DDRB |= 1<<PB2 |1<<PB3 | 1<<PB5; // mosi, sck, ss output
|
198 | DDRB &= ~(1<<PB4); // miso input
|
199 | */
|
200 |
|
201 | PORTB |= 1<<PB7; //SCK HI
|
202 | DDRB |= 1<<PB4 |1<<PB5 | 1<<PB7; // mosi, sck, ss output
|
203 | DDRB &= ~(1<<PB6); // miso input
|
204 |
|
205 | //SS muss out sein, damit nicht auf Slave gewechselt wird
|
206 |
|
207 | // initialize SPI interface
|
208 | // master mode und /2x takt (klappt auch wenn der mega8 mit 16mhz läuft !!)
|
209 | SPCR |= 1<<MSTR
|
210 | SPCR |= 1<<SPE;
|
211 | SPSR |= 1<<SPI2X
|
212 |
|
213 |
|
214 |
|
215 | // perform system reset
|
216 | enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
|
217 | // check CLKRDY bit to see if reset is complete
|
218 | delay(50);
|
219 | while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY));
|
220 |
|
221 | // do bank 0 stuff
|
222 | // initialize receive buffer
|
223 | // 16-bit transfers, must write low byte first
|
224 | // set receive buffer start address
|
225 | NextPacketPtr = RXSTART_INIT;
|
226 | enc28j60Write(ERXSTL, RXSTART_INIT&0xFF);
|
227 | enc28j60Write(ERXSTH, RXSTART_INIT>>8);
|
228 | // set receive pointer address
|
229 | enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF);
|
230 | enc28j60Write(ERXRDPTH, RXSTART_INIT>>8);
|
231 | // set receive buffer end
|
232 | // ERXND defaults to 0x1FFF (end of ram)
|
233 | enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF);
|
234 | enc28j60Write(ERXNDH, RXSTOP_INIT>>8);
|
235 | // set transmit buffer start
|
236 | // ETXST defaults to 0x0000 (beginnging of ram)
|
237 | enc28j60Write(ETXSTL, TXSTART_INIT&0xFF);
|
238 | enc28j60Write(ETXSTH, TXSTART_INIT>>8);
|
239 |
|
240 | // do bank 2 stuff
|
241 | // enable MAC receive
|
242 | enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
|
243 | // bring MAC out of reset
|
244 | enc28j60Write(MACON2, 0x00);
|
245 | // enable automatic padding and CRC operations
|
246 | enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
|
247 |
|
248 | // set inter-frame gap (non-back-to-back)
|
249 | enc28j60Write(MAIPGL, 0x12);
|
250 | enc28j60Write(MAIPGH, 0x0C);
|
251 | // set inter-frame gap (back-to-back)
|
252 | enc28j60Write(MABBIPG, 0x12);
|
253 | // Set the maximum packet size which the controller will accept
|
254 | enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF);
|
255 | enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8);
|
256 |
|
257 | // do bank 3 stuff
|
258 | // write MAC address
|
259 | // NOTE: MAC address in ENC28J60 is byte-backward
|
260 | enc28j60Write(MAADR5, ENC28J60_MAC0);
|
261 | enc28j60Write(MAADR4, ENC28J60_MAC1);
|
262 | enc28j60Write(MAADR3, ENC28J60_MAC2);
|
263 | enc28j60Write(MAADR2, ENC28J60_MAC3);
|
264 | enc28j60Write(MAADR1, ENC28J60_MAC4);
|
265 | enc28j60Write(MAADR0, ENC28J60_MAC5);
|
266 |
|
267 | // no loopback of transmitted frames
|
268 | enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS);
|
269 |
|
270 | // switch to bank 0
|
271 | enc28j60SetBank(ECON1);
|
272 | // enable interrutps
|
273 | enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);
|
274 | // enable packet reception
|
275 | enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
|
276 |
|
277 | }
|
278 |
|
279 | void enc28j60PacketSend(unsigned int len, unsigned char* packet)
|
280 | {
|
281 | // Set the write pointer to start of transmit buffer area
|
282 | enc28j60Write(EWRPTL, TXSTART_INIT);
|
283 | enc28j60Write(EWRPTH, TXSTART_INIT>>8);
|
284 | // Set the TXND pointer to correspond to the packet size given
|
285 | enc28j60Write(ETXNDL, (TXSTART_INIT+len));
|
286 | enc28j60Write(ETXNDH, (TXSTART_INIT+len)>>8);
|
287 |
|
288 | // write per-packet control byte
|
289 | enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
|
290 |
|
291 | // copy the packet into the transmit buffer
|
292 | enc28j60WriteBuffer(len, packet);
|
293 |
|
294 | // send the contents of the transmit buffer onto the network
|
295 | enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
|
296 | }
|
297 |
|
298 | unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet)
|
299 | {
|
300 | u16 rxstat;
|
301 | u16 len;
|
302 |
|
303 | // check if a packet has been received and buffered
|
304 | if( !(enc28j60Read(EIR) & EIR_PKTIF) )
|
305 | return 0;
|
306 |
|
307 | // Make absolutely certain that any previous packet was discarded
|
308 | //if( WasDiscarded == FALSE)
|
309 | // MACDiscardRx();
|
310 |
|
311 | // Set the read pointer to the start of the received packet
|
312 | enc28j60Write(ERDPTL, (NextPacketPtr));
|
313 | enc28j60Write(ERDPTH, (NextPacketPtr)>>8);
|
314 | // read the next packet pointer
|
315 | NextPacketPtr = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
|
316 | NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
|
317 | // read the packet length
|
318 | len = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
|
319 | len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
|
320 | // read the receive status
|
321 | rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
|
322 | rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
|
323 |
|
324 | // limit retrieve length
|
325 | // (we reduce the MAC-reported length by 4 to remove the CRC)
|
326 | len = MIN(len, maxlen);
|
327 |
|
328 | // copy the packet from the receive buffer
|
329 | enc28j60ReadBuffer(len, packet);
|
330 |
|
331 | // Move the RX read pointer to the start of the next received packet
|
332 | // This frees the memory we just read out
|
333 | enc28j60Write(ERXRDPTL, (NextPacketPtr));
|
334 | enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8);
|
335 |
|
336 | // decrement the packet counter indicate we are done with this packet
|
337 | enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
|
338 |
|
339 | return len;
|
340 | }
|