Introduction
I’ve been putting off learning about interfacing for a while now. It isn’t because I don’t want to learn it though; In fact it’s probably the part of embedded systems that I was looking most forward to. No; the reason I’ve been putting it off is purely due to the difficulty I’ve had trying to find a good, concise explanation of how it all works. I’m not content just loading in the interfacing libraries and accepting that as complete; I would really like to understand what is actually happening on each pin that I’m interfacing with and maybe even understand what the driver is doing.
The Module
So lets get started! The module I purchased to work with is a Duinotech
board sporting a HanRun HR911105A RJ45 connector
and the ENC28J60 Ethernet controller
.
The first thing I did was look up the datasheet for the ENC28J60 Ethernet controller. I was looking for detailed information on what each of the Ten interface pins on the board did and was happy to find that almost all the information I needed was available in the document provided.
Some of the key features the controller offers that I’m interested in are:
-
Fully Compatible with 10/100/1000Base-T Networks
- The interface can provide full gigabit speeds. -
Supports Full and Half-Duplex modes
- Bi-directional simultaneous communication can occur on the same port, though I don’t think the Arduino won’t be-able to utilize this due to its single core/one operation at a time limitation. -
SPI Interface with Clock Speeds up to 20 MHz
- SPI (Serial Peripheral Interface) will be the main way I communicate with the module. I already know there’s a very extensive SPI library available that I can learn with. -
8-Kbyte Transmit/Receive Packet Dual Port SRAM
- Allows for latching (in SRAM // Static random-access memory) and queuing of packets and data when up to 8-Kbytes. Again, I don’t think this will be relevant to me but it’s still nice to know about. -
Supports Unicast, Multicast and Broadcast Packets
- Self explanatory and expected on most interfaces these days. -
WOL or Magic Packet
- Wake on LAN, another neat feature I’d love to explore on the board.
Pin Description
Below is the schematic provided for the ENC28J60 Ethernet controller
. There are only a couple pins that we have direct access to via the Pin headers, I’ll try to explain as best I can what each of these pins are and what they do.
CLKOUT
- The Programmable clock output pin is provided for use as the host controller clock or as a clock source for other devices in the system.- Has an internal
Prescaler
which can divide the output by 1, 2, 3, 4 or 8. APrescaler
is used to reduce a high frequency electrical signal to a lower frequency by integer division.
- Has an internal
-
INT
- Interrupt output pin signals the occurrence of an interrupt event to the host controller. -
WOL
- Wake on Lan pin which can be used to push a magic packet through to the Ethernet interface connected. There’s some interesting examples of this here. -
SO
- Also defined asMISO (Master In Slave Out)
and is used as the Slave line for sending data to the master. The master in this case is normally the Arduino or microcontroller. -
SI
- Also defined asMOSI (Master Out Slave In)
and is used as the Master line for sending data to the peripherals. -
SCK
- The Serial clock which synchronizes the data transmission generated by the master. -
CS
- Chip select the input pin for the SPI interface. It is held low while any operation is performed and returned high when finished. -
RESET
- Active low device that resets the input. -
VCC
- 3.3V supply input. GND
- Grounding pin.
If you’re interested in the full block diagram for the controller, it can be seen below. Take note of the various pin labels we just discussed and use them to work out how the system all comes together.
Serial Peripheral Interface (SPI)
Commands and data are sent to the device via the SI pin
, with data being clocked in on the rising edge of SCK
. Data is driven out on the SO line
on the falling edge of SCK
. The CS pin
must be held low while any operation is performed and returned high when finished.
Below is a timeline displaying the Pin states for both input and output.
These interfaces are mapped to the following ports on the Arduino Uno. You can find the correct pins for other boards under Connections here. Note that the ENC28J60 controller uses a different pin for CS than the normal pin 10 (SS).
-
SI
Pin -> DigitalPin 11
ORICSP-4
-
SO
Pin -> DigitalPin 12
ORICSP-1
-
SCK
Pin -> DigitalPin 13
ORICSP-3
-
CS
Pin -> DigitalPin 8
NOTE
You can also use the Reset, Ground and 3.3V pins on the ICSP interface. Else just use the normal pins. You can safely ignore the RESET pin
for most examples.
Ethercard Libraries + Examples
In order to get our device communicating we’ll need to use a library called Ethercard
. It can be downloaded from here:
Once you’ve downloaded the zip, place the contents in your Arduino libraries
folder. The contents of the ethercard
folder comes with a couple examples that we’ll be using to first confirm that our interfacing is correct.
Go ahead and open up the testDHCP sketch
under examples and upload the code to your Arduino. Connect a networked Ethernet cable to your Ethernet module and fire up the serial monitor. You will also need to set the baud rate to 57600
(as specified in the code).
If all went well you should receive the following serial output indicating that you’ve successfully been assigned a DHCP address (your IP values will vary obviously).
Yay! We got it working. That’s a really simple example though, and we still don’t really know how it’s working. Next we’ll take a look at the code and try to build our own code from scratch.
Build a Webserver
Lets open a new project and begin. Start by adding the appropriate code to include the EtherCard library:
Next we’ll setup a place where we can specify static IP information. We’ll do that by creating and populating the following byte arrays with relevant IP network information
Add a another byte array to store the mac address of the interface. You can either make up one yourself or obtain the interfaces actual address from the board itself. If you can’t be bothered with either of these things feel free to use mine.
I would also recommend adding the following line to specify the TCP/IP buffer size
. We will be using this later during the interface initialization.
Now lets get started on the setup()
code. We’ll start by opening up a serial connection for debugging with the baud rate of 57600
Now we need to initialize the Ethernet interface using the ether.begin()
function. If we have a look at the functions definition in the EtherCard.h
file we can see what values it accepts.
We can use the information above to construct the ether.begin() function using the Ethernet buffer size and our Ethernet mac address. We also want to wrap this initialization in an if statement
to confirm the interface is constructed correctly. Use the following syntax to get that done:
Next we need to setup the interface with either DHCP or the static values we declared earlier on. You can choose to do either by implementing one of the following solutions:
DHCP
implementation
Static
implementation is defined in EtherCard.h
And therefore can be implemented as follows:
At this point, we should have our interface setup. Just to make sure lets write out the IP values to serial:
Now that we have the interface up and running we can work on publishing a HTML page to the interface when someone tries to connect to the web server. We’ll be storing our test html page within the Arduino code itself. If you have an SD card
and an SD adapter
on your Arduino you can also tell the microcontroller to serve the page from the SD directory. In my case however I am simply going to build the HTML directly onto the Arduino chip.
Back at the top of your program add the following as a constant char array. It will store your text in the Arduino program memory
(PROGMEM)
The only thing left to do is to setup some code in the loop()
to serve the page whenever someone connects. This is done via the following code block:
Lets explore what each of the functions in the loop() block do:
packetLoop()
- Waits for and accepts a TCP/IP connection from the Ethernet interface. In this case thepacketReceive()
function is the parameter this function takes.
packetReceive()
- Defined in theenc28j60.h
file, this function copies the packet data from the Ethernet controllers memory and passes it to thepacketLoop()
function
memcpy_P
- Takes three arguments:destination
- ether.tcpOffset() will receive our bytessource
- The declared page array we declared and stored in PROGMEMnum
- The number of bytes we want to copy to destination, in this case it’s thesizeof
(returns size in bytes) thepage array
tcpOffset()
- Initializes the SPI interface and serves as a input for the TCP payload
httpServerReply()
- advises the interface of how many bytes make up the TCP payload. In this case it’s the byte size of the page array minus 1
And that is it!, if you run your code and navigate to the IP address either set statically or assigned to you via DHCP in a web browser, you should be greeted with the HTML page you defined in the page array.
Conclusion
That was fun! Difficult, but very fun once I’d found the datasheet for the Ethernet controller. Something I’ve noticed is that I’ve become a lot better at tracking down information myself instead of relying on other peoples description to explain things to me. I think it’s important to do things like what I did in this post; instead of relying on something else’s knowledge to solve a problem, try to understand why everything happens rather than simply accepting that it does.
Below is a full copy of the code I used during the implementation. Also be sure to check the github link and have a browse of the libraries that come with EtherCard.