enc28j60 library
1.0
platform agnostic enc28j60 driver with asynchronous IO support and fully compliant with errata
|
This is ENC28J60 driver practically platform agnostic, yet targetting mainly baremetal systems. Supports asynchronous writting and reading from enc28j60's internal buffer (fails back to synchronous transfers if the platform does not support asynchronous SPI IO), and is compliant with errata sheet for all versions. Comes with supplied examples and Doxygen documentation. Licensed under MIT license. To learn how to correctly use this library to comply with the errata sheet, see below.
When working with enc28j60, keep in mind following tips in order for your code to comply with errata sheet:
enc28j60_init_rxbuffer()
, make sure that the RX buffer starts at 0000h
(errata #5)enc28j60_readrxframescount()
instead of checking for PKTIF
flag (errata #6)enc28j60_aftertx()
, enc28j60_readtxstatus()
and enc28j60_shouldretransmit()
with additional code to retransmit the frame in case of late collision (errata #12, #14, #15; also note that LATECOL
flag is unreliable, this is why it's important to call enc28j60_readtxstatus()
and check for late collision there using txstatus.status & ENC28J60_TXSTATUS_LATE_COLLISION
- which is done inside enc28j60_shouldretransmit()
): Module | Feature | # | Summary | B1 | B4 | B5 | B7 | Implemented in/by |
---|---|---|---|---|---|---|---|---|
MAC Interface | - | 1. | MAC registers unreliable with slow asynchronous SPI clock | X | X | user by setting correct SPI speed | ||
Reset | - | 2. | CLKRDY set early | X | X | X | X | enc28j60_softreset() |
Core | Operating Specifications | 3. | Industrial (-40°C to +85°C) temperature range unsupported | X | X | - | ||
Oscillator | CLKOUT pin | 4. | CLKOUT unavailable in Power Save mode | X | X | X | X | - |
Memory | Ethernet Buffer | 5. | Receive buffer must start at 0000h | X | X | X | X | user when using enc28j60_init_rxbuffer() |
Interrupts | - | 6. | Receive Packet Pending Interrupt Flag (PKTIF) unreliable | X | X | X | X | user by using enc28j60_readrxframescount() instead of checking for PKTIF |
PHY | - | 7. | TPIN+/- automatic polarity detection and correction unreliable | X | X | X | X | user by avoiding incorrect RX polarity |
PHY | - | 8. | RBIAS resistor value differs between silicon revisions | X | X | user when designing board | ||
PHY | - | 9. | Internal loopback in half-duplex unreliable | X | X | X | X | enc28j60_init() |
PHY | - | 10. | Internal loopback in full-duplex unreliable | X | X | X | X | user |
PHY LEDs | - | 11. | Combined Collision and Duplex Status mode unavailable | X | X | X | X | user |
Transmit logic | - | 12. | Transmit abort may stall transmit logic | X | X | X | X | enc28j60_aftertx() |
PHY | - | 13. | Received link pulses potentially cause collisions | X | X | enc28j60_shouldretransmit() | ||
Memory | Ethernet Buffer | 14. | Even values in ERXRDPT may corrupt receive buffer | X | X | X | X | enc28j60_advancerxreadpointer() or enc28j60_finishreadframe() |
Transmit logic | - | 15. | LATECOL Status bit unreliable | X | X | X | X | enc28j60_readtxstatus() |
PHY LEDs | - | 16. | LED auto-polarity detection unreliable | X | X | X | X | user |
DMA | - | 17. | DMA checksum calculations will abort receive packets | X | X | X | X | user by doing checksum in software |
Receive Filter | - | 18. | Pattern match filter allows reception of extra packets | X | X | X | X | user by discarding unwanted packets |
SPI | - | 19. | Reset command unavailable in Power Save mode | X | X | X | X | enc28j60_softreset() |
First copy enc28j60-struct.example.h
into enc28j60-struct.h
and make changes into the code, structures and macros if you wish. You have to choose whenever your system supports advanced spi driver (that is a driver that can transceive to/from two different buffers while keeping SS
low all the time). In this guide, we assume that you have advanced driver, but you can find code examples even for the basic driver in /examples/*_nonadv.c
.
After customizing the enc28j60-struct.h
, implement the functions defined there (enc28j60_spi_transfer()
if you don't have advanced SPI driver, enc28j60_spi_advanced_transfer()
otherwise; enc28j60_spi_transfer_join()
, enc28j60_spi_error_handler()
, enc28j60_delay()
and enc28j60_getticks()
).
Choose whenever to use full duplex (if available - otherwise half-duplex will be used even if is_full_duplex
is true) and whenever to discard frames with invalid CRC and finally chose MAC address. Reset the device and initialize it with chosen parameters.
Note that you can modify the struct enc28j60
and add for example a pointer to the SPI device you wish to use. Then the first lines will look like this:
It's possible to setup interrupts to handle reception of frames, however to keep things simple we start with simple blocking architecture. First you can choose, whenever you wish to accept broadcast and multicast packets, set where RX buffer starts and ends, and then finally enable the receiver and wait for frames.
or, if you want to prototype faster, you can just use built-in function enc28j60_receiveframeblocking()
which either waits at most given number of ticks, or waits infinitely if 0
is supplied as timeout
argument until a frame is received (if you don't want this function to block, simply check the number of received frames yourself before calling this function):
But note that this function blocks - if you don't want it to block, you can simply check the amount of received frames, and if at least one was received, the call to enc28j60_receciveframeblocking()
won't wait, but immediately start reading that frame from enc28j60's memory.
The transmission is simple as well, and the code below can be divided into two pieces, one part of which gets executed somewhere in the application, while the other part might get executed from interrupt.
This code may seem non-trivial, but without the extra effort it would not be compliant with errata #12, #13, #15, and the transceiver could freeze. The last few lines can be however simplified by using built-in function enc28j60_transmitframeblocking()
: