friendev EtherDune TCP/IP library
/home/jander/temp/etherdune/ENC28J60.cpp
Go to the documentation of this file.
1 // EtherDune ENC28J60 hardware driver
2 // Author: Javier Peletier <jm@friendev.com>
3 // Credits: Initially based off EtherCard's own enc28j60.cpp file, but heavily modified afterwards.
4 // Credits: Jean-Claude Wippler, Guido Socher and Pascal Stang
5 // Summary: Encapsulates access to the ENC28J60 hardware
6 //
7 // Copyright (c) 2015 All Rights Reserved, http://friendev.com
8 //
9 // This source is subject to the GPLv2 license.
10 // Please see the License.txt file for more information.
11 // All other rights reserved.
12 //
13 // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
14 // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
15 // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
16 // PARTICULAR PURPOSE.
17 
18 
19 #include <ACross.h>
20 #include "ENC28J60.h"
21 #include <stdarg.h>
22 #include "Checksum.h"
23 #include "NetworkService.h"
24 
25 #include <ACSPI.h>
26 
27 #define AC_LOGLEVEL 2
28 #include <ACLog.h>
29 ACROSS_MODULE("ENC28J60");
30 
31 static uint8_t selectPin;
32 static byte Enc28j60Bank;
33 static uint16_t nextPacketPtr;
34 
35 static uint16_t remainingPacketSize;
36 static byte* chunkPtr;
37 
38 void initSPI()
39 {
40  ACross::SPI::init();
41 }
42 
43 
44 static byte readOp(byte op, byte address)
45 {
46 
47  uint8_t b[3];
48  b[0] = op | (address & ADDR_MASK);
49  b[1] = 0;
50  b[2] = 0;
51 
52  uint16_t sendLength = (address & 0x80) ? 3 : 2;
53 
54  ACross::SPI::sendReceive(selectPin, sendLength, b, 1, b);
55 
56  return b[0];
57 
58 }
59 
60 
61 
62 static void writeOp(byte op, byte address, byte data)
63 {
64 
65  op |= address & ADDR_MASK;
66  ACross::SPI::send(selectPin, 1, &op, 1, &data);
67 }
68 
69 static void SetBank(byte address)
70 {
71  if ((address & BANK_MASK) != Enc28j60Bank) {
73  Enc28j60Bank = address & BANK_MASK;
75  }
76 }
77 
78 static byte readRegByte(byte address)
79 {
80  SetBank(address);
81  return readOp(ENC28J60_READ_CTRL_REG, address);
82 }
83 
84 static uint16_t readReg(byte address)
85 {
86  return readRegByte(address) + (readRegByte(address + 1) << 8);
87 }
88 
89 
90 static void writeRegByte(byte address, byte data)
91 {
92  SetBank(address);
93  writeOp(ENC28J60_WRITE_CTRL_REG, address, data);
94 }
95 
96 static void writeReg(byte address, uint16_t data)
97 {
98  writeRegByte(address, (uint8_t) data);
99  writeRegByte(address + 1, data >> 8);
100 }
101 
102 void ENC28J60::readBuf(uint16_t len, byte* data)
103 {
104 
105  uint8_t b = ENC28J60_READ_BUF_MEM;
106  ACross::SPI::sendReceive(selectPin, 1, &b, len, data);
107 }
108 
109 void ENC28J60::readBuf(uint16_t src, uint16_t len, byte* data)
110 {
111  writeReg(ERDPT, src);
112  readBuf(len, data);
113 }
114 
115 void ENC28J60::writeBuf(uint16_t len, const byte* data)
116 {
117 
118  uint8_t b = ENC28J60_WRITE_BUF_MEM;
119  ACross::SPI::send(selectPin, 1, &b, len, (uint8_t*)data);
120 
121 }
122 
123 void ENC28J60::writeBuf(uint16_t dst, uint16_t len, const byte* data)
124 {
125  writeReg(EWRPT, dst);
126  writeBuf(len, data);
127 
128 }
129 
131 {
132  writeBuf(1, &b);
133 }
134 void ENC28J60::writeByte(uint16_t dst, byte b)
135 {
136  writeBuf(dst, 1, &b);
137 }
138 
139 byte ENC28J60::readByte(uint16_t src)
140 {
141  byte b;
142  readBuf(src, 1, &b);
143  return b;
144 }
145 
146 #if ENABLE_HW_CHECKSUM
147 uint16_t ENC28J60::hardwareChecksumRxOffset(uint16_t offset, uint16_t len)
148 {
149  uint16_t src = incRxPtr(currentPacketPtr, offset);
150  return hardwareChecksum(src, len);
151 }
152 
153 uint16_t ENC28J60::hardwareChecksum(uint16_t src, uint16_t len)
154 {
155  if (len == 0)
156  return 0;
157 
158  // calculate address of last byte
159  uint16_t last = len + src - 1;
160 
161  writeReg(EDMASTL, src);
162 
163  if ((src <= RXSTOP_INIT) && (last > RXSTOP_INIT))
164  last -= (RXSTOP_INIT - RXSTART_INIT);
165 
166  writeReg(EDMANDL, last);
167 
168  //According to ENC28J60 Silicon Errata, when calculating hardware checksums
169  //packets may be lost.
170 
171  //try to mitigate packet loss by waiting for the controller to free up
172  //of course a packet may arrive precisely when we're calculating
173  //a checksum, so this attempt may be futile anyway
175  ;
176 
177  /* 4. Start the DMA copy by setting ECON1.DMAST. */
178  SetBank(EDMACS);
180 
181  // wait until runnig DMA is completed
183 
184  return readReg(EDMACS);
185 }
186 
187 #endif
188 
189 inline uint16_t incRxPtr(uint16_t ptr, uint16_t len)
190 {
191  ptr += len;
192  if (ptr > RXSTOP_INIT + 1)
193  ptr -= (RXSTOP_INIT + 1);
194 
195  return ptr;
196 }
197 
198 void ENC28J60::moveMem(uint16_t dest, uint16_t src, uint16_t len)
199 {
200 
201  //as ENC28J60 DMA is unable to copy single bytes:
202 
203  if (len == 0)
204  return;
205 
206  if (len == 1)
207  {
208  writeByte(dest, readByte(src));
209  }
210  else
211  {
212  // calculate address of last byte
213  uint16_t last= len+ src - 1;
214 
215  /* 1. Appropriately program the EDMAST, EDMAND
216  and EDMADST register pairs. The EDMAST
217  registers should point to the first byte to copy
218  from, the EDMAND registers should point to the
219  last byte to copy and the EDMADST registers
220  should point to the first byte in the destination
221  range. The destination range will always be
222  linear, never wrapping at any values except from
223  8191 to 0 (the 8-Kbyte memory boundary).
224  Extreme care should be taken when
225  programming the start and end pointers to
226  prevent a never ending DMA operation which
227  would overwrite the entire 8-Kbyte buffer.
228  */
229  writeReg(EDMASTL, src);
230  writeReg(EDMADSTL, dest);
231 
232  if ((src <= RXSTOP_INIT) && (last > RXSTOP_INIT))
233  last -= (RXSTOP_INIT - RXSTART_INIT);
234 
235  writeReg(EDMANDL, last);
236 
237  /*
238  2. If an interrupt at the end of the copy process is
239  desired, set EIE.DMAIE and EIE.INTIE and
240  clear EIR.DMAIF.
241 
242  3. Verify that ECON1.CSUMEN is clear. */
244 
245  /* 4. Start the DMA copy by setting ECON1.DMAST. */
247 
248  // wait until runnig DMA is completed
250  }
251 }
252 
253 
254 
255 
256 void ENC28J60::packetSend(uint16_t len)
257 {
258  // see http://forum.mysensors.org/topic/536/
259  // while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS)
260  if (readRegByte(EIR) & EIR_TXERIF) {
263  writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF);
264  }
265 
266  writeByte(TXSTART_INIT, 0x00); // set the control byte to zero.
267 
268  writeReg(ETXST, TXSTART_INIT);
269  writeReg(ETXND, TXSTART_INIT + len - 1 + 1); // to include the control byte.
270 
272 
273 }
274 
275 void ENC28J60::packetSend(uint16_t len, const byte* data)
276 {
277  writeBuf(TXSTART_INIT_DATA, len, data);
278  packetSend(len);
279 }
280 
282 {
284 }
285 
286 static void writePhy(byte address, uint16_t data) {
287  writeRegByte(MIREGADR, address);
288  writeReg(MIWR, data);
289  while (readRegByte(MISTAT) & MISTAT_BUSY)
290  ;
291 }
292 
293 static uint16_t readPhyByte(byte address) {
294  writeRegByte(MIREGADR, address);
296  while (readRegByte(MISTAT) & MISTAT_BUSY)
297  ;
298  writeRegByte(MICMD, 0x00);
299  return readRegByte(MIRD + 1);
300 }
301 
307  return (readPhyByte(PHSTAT2) >> 2) & 1;
308 }
309 
310 uint8_t ENC28J60::begin(uint8_t cspin)
311 {
312 
313  initSPI();
314  selectPin = cspin;
315  pinMode(selectPin, OUTPUT);
316  pinMode(selectPin, HIGH);
317 
318 
320  delay(2); // errata B7/2
322  ;
323 
324  nextPacketPtr = RXSTART_INIT;
325  writeReg(ERXST, RXSTART_INIT);
326  writeReg(ERXRDPT, RXSTART_INIT);
327  writeReg(ERXND, RXSTOP_INIT);
328  writeReg(ETXST, TXSTART_INIT);
329  writeReg(ETXND, TXSTOP_INIT);
330  enableBroadcast(); // change to add ERXFCON_BCEN recommended by epam
331  writeReg(EPMM0, 0x303f);
332  writeReg(EPMCS, 0xf7f9);
334  writeRegByte(MACON2, 0x00);
337  writeReg(MAIPG, 0x0C12);
338  writeRegByte(MABBIPG, 0x12);
347  SetBank(ECON1);
350 
351  //SetBank(ECON2);
352  //writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_AUTOINC); //enable read auto wrapping/increment
353 
354  byte rev = readRegByte(EREVID);
355  // microchip forgot to step the number on the silcon when they
356  // released the revision B7. 6 is now rev B7. We still have
357  // to see what they do when they release B8. At the moment
358  // there is no B8 out yet
359  if (rev > 5) ++rev;
360  return rev;
361 
362 
363 
364 }
365 
366 
368 {
369  if (readRegByte(EPKTCNT) > 0)
370  {
372 
373  struct
374  {
375  uint16_t nextPacket;
376  uint16_t byteCount;
377  uint16_t status;
378  } header;
379 
380  readBuf(sizeof header, (byte*)&header);
381 
382  nextPacketPtr = header.nextPacket;
383 
384  if ((header.status & 0x80) != 0)
385  {
386  remainingPacketSize = header.byteCount - 4; //remove the CRC count
387  uint16_t len = min(remainingPacketSize, EtherDune_SAMPLE_SIZE);
389  readBuf(len, chunkPtr);
390  remainingPacketSize -= len;
391  chunkPtr += len;
392  NetworkService::processIncomingPacket();
393  }
394  release();
395 
396  }
397 
398 }
399 
401 {
402 #if (EtherDune_SAMPLE_SIZE < EtherDune_BUFFER_SIZE)
404 #endif
406 }
407 
408 
409 
411 {
412  if (nextPacketPtr - 1 > RXSTOP_INIT)
413  writeReg(ERXRDPT, RXSTOP_INIT);
414  else
417 }
#define ENC28J60_READ_BUF_MEM
#define ECON2
#define MACON2
#define MIRD
#define EDMADSTL
#define PHCON2_HDLDIS
#define MACON3_PADCFG0
#define MACON1_MARXEN
#define ESTAT_CLKRDY
#define ENC28J60_BIT_FIELD_CLR
#define ENC28J60_SOFT_RESET
#define ESTAT
static void moveMem(uint16_t dest, uint16_t src, uint16_t len)
Definition: ENC28J60.cpp:198
#define ECON1_CSUMEN
#define ERXND
#define EDMASTL
static void writePhy(byte address, uint16_t data)
Definition: ENC28J60.cpp:286
#define MABBIPG
static byte readRegByte(byte address)
Definition: ENC28J60.cpp:78
static byte * chunkPtr
Definition: ENC28J60.cpp:36
#define ENC28J60_WRITE_CTRL_REG
static void release()
Definition: ENC28J60.cpp:410
static byte readByte(uint16_t src)
Definition: ENC28J60.cpp:139
#define PHCON2
#define MISTAT
#define MACON1_RXPAUS
#define ECON2_PKTDEC
static byte Enc28j60Bank
Definition: ENC28J60.cpp:32
#define EPKTCNT
#define MAADR3
static uint16_t nextPacketPtr
Definition: ENC28J60.cpp:33
static void writeOp(byte op, byte address, byte data)
Definition: ENC28J60.cpp:62
static void writeRegByte(byte address, byte data)
Definition: ENC28J60.cpp:90
static uint8_t begin(uint8_t cspin)
Definition: ENC28J60.cpp:310
#define MAX_FRAMELEN
max frame length which the conroller will accept: (note: maximum ethernet frame length would be 1518)...
Definition: config.h:128
#define ERXST
static EthBuffer packet
in-memory packet buffer currently being processed.
#define MAIPG
#define ESTAT_RXBUSY
#define EIE_PKTIE
static void enableBroadcast()
Definition: ENC28J60.cpp:281
#define ECON1_RXEN
#define MACON1
#define MACON3_TXCRCEN
#define EPMCS
#define ETXST
#define EtherDune_SAMPLE_SIZE
Minimum amount of bytes to read to decide whether we want the rest of the packet or not...
Definition: config.h:75
static void writeByte(byte b)
Definition: ENC28J60.cpp:130
#define EIE_INTIE
#define EREVID
static void SetBank(byte address)
Definition: ENC28J60.cpp:69
ACROSS_MODULE("ENC28J60")
uint16_t incRxPtr(uint16_t ptr, uint16_t len)
Definition: ENC28J60.cpp:189
#define ERXFCON_BCEN
#define ENC28J60_BIT_FIELD_SET
#define MAADR5
static void packetSend(uint16_t len)
Definition: ENC28J60.cpp:256
void initSPI()
Definition: ENC28J60.cpp:38
#define ERDPT
#define MAADR0
#define ECON1_TXRST
static uint8_t selectPin
Definition: ENC28J60.cpp:31
#define EIR
static bool isLinkUp()
Determines whether the network link is ready
Definition: ENC28J60.cpp:306
#define ENC28J60_READ_CTRL_REG
#define ECON1_TXRTS
#define ERXRDPT
#define EDMACS
static byte readOp(byte op, byte address)
Definition: ENC28J60.cpp:44
#define ECON1_BSEL0
#define ETXND
#define ECON1_DMAST
static MACAddress localMAC
Ethernet MAC address.
static uint16_t hardwareChecksumRxOffset(uint16_t offset, uint16_t len)
#define EWRPT
#define ERXFCON
#define PHSTAT2
#define MIREGADR
static uint16_t hardwareChecksum(uint16_t src, uint16_t len)
static uint16_t remainingPacketSize
Definition: ENC28J60.cpp:35
static void readBuf(uint16_t src, uint16_t len, byte *data)
Definition: ENC28J60.cpp:109
#define MIWR
static void writeReg(byte address, uint16_t data)
Definition: ENC28J60.cpp:96
#define EPMM0
static uint16_t readPhyByte(byte address)
Definition: ENC28J60.cpp:293
#define MACON1_TXPAUS
static void loadAll()
Definition: ENC28J60.cpp:400
static void writeBuf(uint16_t dst, uint16_t len, const byte *data)
Definition: ENC28J60.cpp:123
#define MAADR2
#define EIE
#define EIR_TXERIF
#define ECON1_BSEL1
#define MACON3
#define MICMD
#define MAMXFL
#define EDMANDL
#define ECON1
#define ENC28J60_WRITE_BUF_MEM
#define MICMD_MIIRD
static uint16_t readReg(byte address)
Definition: ENC28J60.cpp:84
static void loadSample()
Definition: ENC28J60.cpp:367
#define BANK_MASK
#define MAADR4
#define MAADR1
#define ADDR_MASK
#define MACON3_FRMLNEN
#define MISTAT_BUSY