Posted: December 14, 2019


A few years ago I got a SparkFun breakout board for a Microchip ENC28J60 Ethernet chip. My goal for it was just to send a UDP packet from an MSP430G2553 microcontroller to my Linux desktop. I actually built the circuit a few years ago, but was having trouble with the SPI so I put it away for a while. A couple days ago I took the board out and realized I had a couple mistakes in my code, fix it, and got it working.

I included the source code in the samples/msp430 directory of naken_asm git repository (linked to below). There's also an include file for all the definitions in the include/ethernet directory.


Microchip ENC28J60 board next to MSP430 launchpad debugger.

Here's a picture of the Ethernet interface board soldered to the circuit built around the MSP430G2553. The whole circuit is connected by the TEST, /RST, GND, and Vcc wires to an MSP430G Launchpad to power, program the flash, and debug it.


Probably the start of this project is creating a IP/UDP packet to test with. I wrote a C program to make it a little easier and originally had the checksum for the IP header hardcoded. I also decided to not include a UDP checksum (by setting that 16 bit field to 0). The current MSP430 assembly code computes and sets the IP header checksum. The ENC28J60 chip also doesn't send an Ethernet frame header on its own so that had to be hardeded too, along with a control byte the ENC28J60 chip uses to know if it needs to add an Ethernet checksum and such. The hardcoded Ethernet frame looks like this:

; IP packet is: ; Version = 4, IHL = 5, Total Length = 0x24 (9 * 4 = 36 bytes) ; Identification = 0, Flags = 0, Fragment Offset = 0 ; Time To Live = 15, Protocol = 0x11 (17 UDP), Header Checksum = 0x2805 ; Source IP Address = (0xc0 0xa8, 0x02, 0x0f) ; Destination IP Address = (0xc0 0xa8, 0x02, 0x10) ; UDP part is: ; Source Port = 0x22b8 (8888), Destination Port = 0x1e61 (7777) ; Data = "HELLO!!\n" (0x48 0x45 0x4c 04c 0x4f 0x21 0x21 0x0a) packet_pad: db 0x00 ; Needed to align the ethernet frame on 16 bit boundary packet: db 0x0e ; Control byte ethernet_frame: db 0x30, 0x85, 0xa9, 0x00, 0x00, 0x00 ; Destination MAC address db 0xde, 0xad, 0xba, 0xdd, 0x00, 0xd4 ; Source MAC address db 0x08, 0x00 ; EtherType = IPv4 ip_packet: db 0x45, 0x00, 0x00, 0x24, db 0x00, 0x00, 0x00, 0x00, db 0x0f, 0x11, 0x00, 0x00, db 0xc0, 0xa8, 0x02, 0x0f, ; Source IP Address ( db 0xc0, 0xa8, 0x02, 0x10, ; Destination IP Address ( udp_packet: db 0x22, 0xb8, 0x1e, 0x61, db 0x00, 0x10, 0x00, 0x00, db 0x48, 0x45, 0x4c, 0x4c, ; H E L L db 0x4f, 0x21, 0x21, '\n', ; O ! ! <CR> packet_end:

The destination MAC address is the address of my desktop and the source MAC address is just something made up. When making up the MAC address, it seems the first byte has to be an even number or it's treated as a multicast.

To test the firmware, on a Linux desktop netcat is run with:

nc -u -l 7777

I also used Wireshark for some debugging when things weren't working, but in the end netcat was enough. If it appeared in Wireshark, it showed up from netcat.

Communication with the chip is actually not too bad... once getting past all the awkward stuff. There are multiple sets of registers that are accessed in different ways. When reading from a MAC / MII register there needs to be a dummy byte read in before the real data shows up. Also the PHY registers are accessed through some M based registers in an odd way. The chip's registers are also "banked" so in order to access the correct register, the bank has to be set. Kind of reminds me of Microchip's 8 bit PIC chips.

To send an Ethernet frame, first the frame needs to be transferred to the 8k buffer in the chip. A pointer to where to write the next byte to is set to where the packet should be placed. To transmit the packet, a pointer to the start packet must be set along with a pointer to the last byte in the packet.

So after getting the SPI working and proving that after I wrote to a register that I read the same value back that I wrote to it, I was having trouble seeing the packet being received in Wireshark. I thought maybe (although it didn't make sense) that I might have needed to set up an arp mapping on the desktop, so I did the arp -s thing to add the MAC address of When that didn't work, I tried a delay between setup and transmitting a packet and that seemed to fix it. What the issue was is the PHY needs to be polled to know if it's in a ready state. I added code to do that and it appears to fix it. Now I kind of don't know if the arp -s thing was really needed. I'd put money down that it's not. It seems that arp should be more for trying to figure out the MAC address for a specific IP address. I already knew the destination MAC address so arp seems pointless.

Source code

Copyright 1997-2024 - Michael Kohn