How to integrate ENC28J60 with lwIP using STM32 Microcontroller

Hardware and Software Requirements

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

The STM32F401RE Nucleo Board 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 used KiCad to design my 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


static uint8_t enc28j60_buffer[1500];

err_t enc28j60_translate(struct netif *netif, struct pbuf *p)
{
    uint16_t length = p->len;
    memcpy(enc28j60_buffer, (uint8_t *)p->payload, length);
    enc28j60_etherTransmit(&dev, enc28j60_buffer, length);
    return ERR_OK;
}

void ethernet_do_translation_to_pbub(enc28j60Drv * dev, struct pbuf *p)
{
    p->next = NULL;
    p->len = dev->rxPkt.rxPktLen.u16PktLen;
    p->payload = dev->rxPkt.data;
    memcpy((uint8_t *)p->payload,
           dev->rxPkt.data,
           dev->rxPkt.rxPktLen.u16PktLen);
    p->ref = 1;
}
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


[... your ping output remains unchanged ...]
Ping message output obtained from minicom on Linux

Source Code

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