ENC28J60 with lwIP: DHCP Client

Hardware and Software Requirements

To complete the project successfully, you need the below items:

The STM32F401RE Nucleo Board shown in figure below was used for this demonstration.

STM32F401RE Nucleo
STM32F401RE Nucleo Board

The ENC28J60 board was used for Ethernet connection.

ENC28J60 DEV Board
ENC28J60 Breakout module

I also used KiCad to design an own interface module to avoid using a breadboard or having wires all over the place. The KiCad files can be shared upon request.

ENC28J60 Interface Board
ENC28J60 Interface Board

Hardware Setup

SPI Connection: NUCLEO-STM32F401RE ↔ ENC28J60
Signal Connection
3.3VNUCLEO 3V3 → ENC28J60 VCC
GNDNUCLEO GND → ENC28J60 GND
MOSIPA7 (SPI1 MOSI) → SI
MISOPA6 (SPI1 MISO) → SO
SCKPA5 (SPI1 SCK) → SCK
CSPB6 (GPIO Output) → CS
INTPC7 (EXTI7) → INT

ENC28J60 Driver Development


void enc28j60_initDr(enc28j60Drv * dev, spiChipSel cs, spiChipDSl dCS, slaveRead rd, slaveWrite wr, intHandler hdle, delayMs delay);
void enc28j60_strtDr(enc28j60Drv * dev);
void enc28j60_sftRst(enc28j60Drv * dev);
bool enc28j60_intPnd(enc28j60Drv * dev);
void enc28j60_intSet(enc28j60Drv * dev);
void enc28j60_intCls(enc28j60Drv * dev);
bool enc28j60_etherReceive(enc28j60Drv * dev, uint8_t * data, uint16_t length);
bool enc28j60_etherTransmit(enc28j60Drv * dev, uint8_t * data, uint16_t length);
ENC28J60 publicly accessible driver functions

lwIP Integration



err_t enc28j60_translate(struct netif *netif, struct pbuf *p) {
	if(bTxBusy != true) {
		dMesgPrint(DEBUG_INFO, "TX started!\r\n");
		struct pbuf *q;
	    uint16_t total_len = p->tot_len;
	    uint16_t offset = 0;

	    /* Copy the full chained pbuf into linear ENC buffer */
	    for (q = p; q != NULL; q = q->next)
	    {
	        memcpy(&dev.txPkt.data[offset], q->payload, q->len);
	        offset += q->len;
	    }

	    /* Safety: offset must equal total length */
	    if (offset != total_len) {
	        return ERR_BUF;
	    }

	    (void) enc28j60_etherTransmit(&dev, dev.txPkt.data, total_len);

	    bTxBusy = true;
	    return ERR_OK;
	}else {
		dMesgPrint(DEBUG_ERROR, "TX busy!\r\n");
		return ERR_INPROGRESS;
	}
}

void ethernet_do_translation_to_pbub(enc28j60Drv * dev, struct pbuf *p) {
	uint16_t len = dev->rxPkt.rxPktLen.u16PktLen;
    if (len > p->tot_len) len = p->tot_len;
	pbuf_take(p, dev->rxPkt.data, len);
}

ENC28J60 TX and RX glue functions

DHCP Client Specific Code


enc28j60_initDr(&dev, spi1ChipSelect, spi1ChipDeSelect,
                spi1Read, spi1Write, NULL, delayMsFunction);
enc28j60_strtDr(&dev);

lwip_init();
netif_add_noaddr(&my_netif, NULL, ethernet_init, ethernet_input);
netif_set_default(&my_netif);
netif_set_up(&my_netif);

dhcp_set_struct(&my_netif, &myDhcpClient);
dhcp_start(&my_netif);

while(true)
{
    if(u32FinerTimer >= 1)
    {
        u32FinerTimer -= 1;
        dhcp_fine_tmr();
        HAL_GPIO_TogglePin(GreenLED1_GPIO_Port, GreenLED1_Pin);
    }

    if(u32CoarseTimer >= 120)
    {
        u32CoarseTimer -= 120;
        dhcp_coarse_tmr();
    }
}
DHCP client integration code

Ping Terminal Outputs


EIR REG --> 64
6) Receive Packet Pending Interrupt Flag bit
Next Point address --> 5030
ETH --> Length out of range
ETH --> RX OK
PKT number 147413
PKT length 98
ip_input: iphdr->dest 0xca08a8c0 netif->ip_addr 0xca08a8c0 (0x8a8c0, 0x8a8c0, 0xca000000)
ip4_input: packet accepted on interface en
ip4_input: 
IP header:
+-------------------------------+
| 4 | 5 |  0x00 |        84     | (v, hl, tos, len)
+-------------------------------+
|     1324      |010|       0   | (id, flags, offset)
+-------------------------------+
|   64  |    1  |    0xa28b     | (ttl, proto, chksum)
+-------------------------------+
|  192  |  168  |    8  |  215  | (src)
+-------------------------------+
|  192  |  168  |    8  |  202  | (dest)
+-------------------------------+
ip4_input: p->len 84 p->tot_len 84
ip4_output_if: en0
IP header:
+-------------------------------+
| 4 | 5 |  0x00 |        84     | (v, hl, tos, len)
+-------------------------------+
|     1324      |010|       0   | (id, flags, offset)
+-------------------------------+
|  255  |    1  |    0xe38a     | (ttl, proto, chksum)
+-------------------------------+
|  192  |  168  |    8  |  202  | (src)
+-------------------------------+
|  192  |  168  |    8  |  215  | (dest)
+-------------------------------+
ip4_output_if: call netif->output()

Ping message output obtained from minicom on Linux

Source Code

The source code for the project can be found here — checkout the dhcpClient tag.