How to keep an ENC28J60 Module from Freezing

The ENC28J60 module is good for attaching an Arduino to a LAN or the Internet. The one real problem in doing so is how to keep an ENC28J60 module from freezing. No, not temperature wise ! 🙄  (‘Freezing’ in the sense of locking up, stop working, etc.)

How to keep an ENC28J60 Module from Freezing

ENC28J60 LAN Module

There are many Ethernet  arduino libraries for the ENC28J60 module, however,  I am using the UIPEthernet library from HERE. It is updated from the original Ethernet and follow on UIPEthernet libraries. Documentation on the Ethernet library can be found HERE along with sample code. Note: The UIPEthernet library I am using fully supports all the documented functions from the original library.

There are various ERRATA on the ENC28J60, however, the current versions of the UIPEthernet  library address those issues. This library also supports the STM32F103C8T6. Usually one would think that such ‘lockup’ or ‘freezing’ problems are related to power problems. That problem is not an issue here, it is taken care of by testing with different good power sources and lots of capacitance.

I have the most trouble when I attach an STM32F103C8T6  to the ENC28J60 module.

ARM Cortex M3 STM32F103C8T6

STM32F103C8T6

I seem to have the least problems when I attach an Arduino UNO to it.

Arduino UNO

Arduino UNO

I think that perhaps the extra speed of the STM32F103C8T6 enhances the problem. In the main loop() code, the library function checks on the ENC28J60 and perhaps due to the faster speed of the STM32F103C8T6 there is something going on that affects the ENC28J60.

Another, less know issue, is the need to occasionally call the library function maintain(). If one looks at the documentation, it seems maintain() is only used to renew a DHCP lease. Further research will show that this function call is needed, periodically, if connected to an ENC28J60.  I found that there are fewer ‘freezing’ problems when calling that function in the main loop() code, however, they still occasionally happen.

The best approach to fully solving the issue is to somehow check the status of the ENC28J60 and reset it if it is ‘frozen’. Fortunately, the code for that is available HERE.

This is my sample WEB server code which shows how to keep an ENC28J60 module from freezing by implementing that checking of the ENC28J60.  It also shows the use of the maintain() function and the use of the STM32F103C8T6 watch dog timer. I use the watch dog timer to make sure the mictocontroller running the code to control the ENC28J60 is not somehow locked up (‘frozen’).

Here is the code:

/*
  Web Server
 
 A simple web server that shows the value of the analog input pins.
 or blinks the built in LED (check the comments)

 From: earl@microcontrollerelectronics.com
 Demonstrates checking the ENC28J60 and keeping it from freezing.

ENC28J60  STM32F103
SCK        PA5
SO         PA6
SI         PA7
CS         PA4
RST        PA0

VCC - 5V
GND - GND

--ENC28J60--
CLK  INT
WOL  SO
SI   SCK
CS   RESET
VCC  GND

             +-----------------[USB]-----------------+
  [SS2|PB12] | [31]                            [Gnd] |
 [SCK2|PB13] | [30]                  +---+     [Gnd] |
[MISO2|PB14] | [29]    +-----+       |0 0|     [3V3] |
[MOSI2|PB15] | [28]    |Reset|       |x x|   [Reset] | 
       [PA8] | [27]    +-----+       |1 1|      [ 0] | [PB11|SDA2|RX3] 
   [TX1|PA9] | [26]                  +---+      [ 1] | [PB10|SCL2|TX3]
  [RX1|PA10] | [25]                   ^ ^       [33] | [PB1]
 [USB-|PA11] | [24]            Boot1--+ |       [ 3] | [PB0|A0]
 [USB+|PA12] | [23]            Boot0----+       [ 4] | [PA7|A1|MOSI1]
      [PA15] | [20]                             [ 5] | [PA6|A2|MISO1]
       [PB3] | [19]        +-----------+        [ 6] | [PA5|A3|SCK1]
       [PB4] | [18]        | STM32F103 |        [ 7] | [PA4|A4|SS1]
       [PB5] | [17]        | Blue Pill |        [ 8] | [PA3|A5|RX2]
  [SCL1|PB6] | [16]        +-----------+        [ 9] | [PA2|A6|TX2]
  [SDA1|PB7] | [15]                             [10] | [PA1|A7]
       [PB8] | [32]                             [11] | [PA0|A8]
       [PB9] | [PB9]                            [12] | [PC15]
             | [5V]      +---------------+      [13] | [PC14]
             | [Gnd]     |    ST-Link    |      [14] | [PC13|LED]
             | [3V3]     |3V3 DIO CLK GND|     [Vbat]| 
             +-------------+---+---+---+-------------+
                           |   |   |   |
*/

#include <libmaple/iwdg.h>
#include <UIPEthernet.h>

// ..utility\Enc28J60Network.h file - 
//move readReg() subroutine def from private to public 

#define NET_ENC28J60_EIR          0x1C
#define NET_ENC28J60_ESTAT        0x1D
#define NET_ENC28J60_ECON1        0x1F
#define NET_ENC28J60_EIR_RXERIF   0x01
#define NET_ENC28J60_ESTAT_BUFFER 0x40
#define NET_ENC28J60_ECON1_RXEN   0x04
#define NET_ENC28J60_CHECK_PERIOD 5000UL

#include <SPI.h>
#define iwdg_init_ms(N) iwdg_init(IWDG_PRE_256,((N)/5))

#define pinLED PC13
#define ETH_RS_PIN PA0

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

IPAddress   ipdns(  8,   8,   8, 8);
IPAddress      ip(192, 168,   2, 50);
IPAddress gateway(192, 168,   2, 1);
IPAddress  subnet(255, 255, 255, 0);

EthernetServer server = EthernetServer(80);

String cdata = "";
unsigned long timer;

void setup() {
  Serial.begin(115200);
  pinMode(pinLED, OUTPUT);
  digitalWrite(pinLED,HIGH);
  eth_reset();
  iwdg_init_ms(4000);
  timer = millis();
}

void eth_reset() { 
  pinMode(ETH_RS_PIN, OUTPUT);
  digitalWrite(ETH_RS_PIN, LOW);
  delay(100);
  digitalWrite(ETH_RS_PIN, HIGH);
  pinMode(ETH_RS_PIN, INPUT);
  
  Ethernet.begin(mac, ip, ipdns, gateway, subnet);
  server.begin();
  Serial.print(F("WEB server is at "));
  Serial.println(Ethernet.localIP());

  Serial.print(F("DNS server is at ")); 
  Serial.println(Ethernet.dnsServerIP()); 
}

void loop() {
  iwdg_feed();
  Ethernet.maintain();
   
  if ((millis() - timer) > NET_ENC28J60_CHECK_PERIOD) {    

       // Enc28J60 is Enc28J60Network class that is defined in Enc28J60Network.h
       // readReg() subroutine must be moved from private to public members area in utility\Enc28J60Network.h
       // ENC28J60 ignore all incoming packets if ECON1.RXEN is not set
       uint8_t stateEconRxen = Enc28J60.readReg((uint8_t) NET_ENC28J60_ECON1) & NET_ENC28J60_ECON1_RXEN;
       // ESTAT.BUFFER rised on TX or RX error
       // I think the test of this register is not necessary - EIR.RXERIF state checking may be enough
       uint8_t stateEstatBuffer = Enc28J60.readReg((uint8_t) NET_ENC28J60_ESTAT) & NET_ENC28J60_ESTAT_BUFFER;
       // EIR.RXERIF set on RX error
       uint8_t stateEirRxerif = Enc28J60.readReg((uint8_t) NET_ENC28J60_EIR) & NET_ENC28J60_EIR_RXERIF;
       Serial.println("---REGS---");
       Serial.println(stateEconRxen,HEX);
       Serial.println(stateEstatBuffer,HEX);
       Serial.println(stateEirRxerif,HEX);
       if (!stateEconRxen || (stateEstatBuffer && stateEirRxerif)) {
         Serial.println ("ENC28J60 reinit");
//          Enc28J60.init(netConfig->macAddress);
         eth_reset();
       }
    timer = millis();
  }
  
  EthernetClient client = server.available();
  
  if (client) {    
    Serial.println(F("\n--Client Connected--\n"));
    cdata = "";
    Serial.println(cdata);
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        cdata.concat(c);
        if (cdata.indexOf("\r\n\r\n") > 0) {
          Serial.print(F("Buffer Length: "));
          Serial.println(cdata.length());
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println(F("Connection: close")); 
          if(cdata.indexOf("Analog") > 0) client.println(F("Refresh: 5"));
          client.println();
          client.println(F("<!DOCTYPE HTML>"));
          client.println(F("<html>"));
          client.println(F("<body>"));
          client.println(F("<br><br><center><H1>STM32F103C8T6 WEB Server</H1></center>"));

          if(cdata.indexOf("LED") > 0) {
            digitalWrite(pinLED,!digitalRead(pinLED));
            if (digitalRead(pinLED)) client.println(F("<center><br><br><br><br><H1>LED OFF!</H1></center>"));
            else                     client.println(F("<center><br><br><br><br><H1>LED ON!</H1></center>")); 
            client.println(F("</body>"));
            client.println(F("</html>"));
            break;
          }
          
          if(cdata.indexOf("Analog") > 0) {
            client.println(F("<table border=1 cellspacing=4 cellpadding=4>"));
            client.println(F("<tr><th>Analog</th><th>Value</th></tr>"));
            for (int analogChannel = 0; analogChannel < 4; analogChannel++) {
              int sensorReading = analogRead(analogChannel);
              client.print(F("<tr><td>"));
              client.print(analogChannel);
              client.print(F("</td><td>"));
              client.print(sensorReading);
              client.println(F("</td></tr>"));       
            }
            client.println(F("</table>"));
            client.println(F("</body>"));
            client.println(F("</html>"));
            break;
          }         
          
          client.println(F("<center><br><br><br><br><H1>Unknown Command!</H1></center>"));
          client.println(F("</body>"));
          client.println(F("</html>"));
          break;
        }
      }
    }
    client.stop();
    Serial.println(F("\n--Client Disconnected--"));
    timer = millis();
  }  
}

From the ASCII chart in the comments at the top of the code, one can see how the STM32F103C8T6 is wired to the ENC28J60.  One thing to note, is that the RESET of the ENC28J60 is accomplished via its RESET pin rather than by calling the Enc28J60.init() function.  This does a hardware reset and seems to work in all lockup cases.

1 comments

    • Sokkou on March 13, 2018 at 7:31 am
    • Reply

    Thank you for posting this. It is the exact situation I am working on at this moment!

Leave a Reply

Your email address will not be published.