Link LED starts flashing when ethernet cable connected, but no network activity

I designed a prototype board based on this Wiznet example. I also used the Arduino Ethernet Shield 2.0 schematic as a reference to design the other parts that interface with the microcontroller.

(Schematic in next post because I can’t post more than 2 links)

When I switch on the board, the LINK LED is off. When I plug a cable into the RJ45 connector, the LINK LED starts to flash on and off at a fixed rate (at about 0.5 Hz). In this time, nothing happens in terms of network communication as far as I can tell: on my network switch - which the RJ45 cable is connected to - the “ACT” LED does not come on, indicating no activity. The ACT LED on the RJ45 connector also never comes on. However, since the LINK LED starts to flash when I plug the cable in, I figure something is happening, but is not fully working.

Since having made the board, I have discovered three potential issues:

  • I did not use a 12k4 resistor at EXRES1 (pin 10 of the W5500), but rather a 10k resistor. I am not sure if this matters.
  • The SCSn chip select pin on the W5500 is not pulled up to Vcc, but instead is connected directly to the microcontroller (an Atmel ATmega32U4). I notice in the Arduino schematic that this is pulled up via a 10k resistor. However, I hacked a 10k resistor on to the board but this doesn’t seem to fix it. (The SPI bus is not shared with any other device).
  • I have not added 10k resistors to ground on the “RSVD” pins of the W5500. The Arduino schematic does this, but the Wiznet example does not, so I guess it doesn’t matter.

Does anyone have any ideas what is going on?

Here’s my schematic:

I can also provide the microcontroller schematic, but I doubt that will be necessary. Note that in the above schematic I already fixed the SCSn pin to pull up to the 3V3D rail. In the actual board I made, this is not pulled up.

Feedback welcome!

[quote=“seands, post:1, topic:3209”]I also used the Arduino Ethernet Shield 2.0 schematic as a reference
[/quote]
I also did this mistake in the past; Arduino shield is not the best (and sometimes not the right) design, not following several design recommendations.

Sounds line chip sees the issue on its TX/RX lines, and tries to reinitialize its PHY in order and in hope to fix the issue.

It matters, and 1% also matters. See datasheet page 12.

[quote=“seands, post:1, topic:3209”]The SCSn chip select pin on the W5500 is not pulled up to Vcc
[/quote]
Not a problem if you constantly drive this pin, and every time it has defined logical level on it.

W5500 datasheet says that pin 23 must be tied to ground, but also says that all these RSVD pins are having internal pull-downs. I would connect them to ground as Arduino does.

In general, if you want to avoid issues, just follow reference schematics.

I also did this mistake in the past; Arduino shield is not the best (and sometimes not the right) design, not following several design recommendations.[/quote]
Good to know, thanks. In my case, the difference between the reference schematic and Arduino ethernet shield was small: some resistors were slightly different values, and there weren’t damping resistors in series with the RX/TX lines, and there isn’t a separate ground plane on the Arduino for the shield. My board will not go into an EMI critical environment.

[quote=“Eugeny, post:3, topic:3209, full:true”]

Sounds line chip sees the issue on its TX/RX lines, and tries to reinitialize its PHY in order and in hope to fix the issue.[/quote]
The RJ45 on my board is on the opposite side to the PHY, so I need to use vias and I cross the RX/TX pairs on opposite sides. Could this be the issue?

[quote=“Eugeny, post:3, topic:3209, full:true”]

It matters, and 1% also matters. See datasheet page 12.[/quote]
Hmm ok. Do you think this could cause the problem I see?

[quote=“Eugeny, post:3, topic:3209, full:true”]

W5500 datasheet says that pin 23 must be tied to ground, but also says that all these RSVD pins are having internal pull-downs. I would connect them to ground as Arduino does.[/quote]
I tied them to ground directly. I guess since there are internal pull-downs, I don’t need to connect them the way the Arduino does, but keep it the way I did it already?

Your help is much appreciated!

I do not think so. They may increase probability of transmission errors, but not this behavior you observe. Put appropriate resistor (or resistors in series) to EXRES1 pin, and then we will see.

Alright, I’ll swap the resistor and hopefully it’ll work. Cheers.

I swapped the resistor and it didn’t make a difference. I fired up my Atmel ICE JTAG debugger and set some breakpoints in the code. It turns out the program infinitely loops between the following lines of code, in socket.cpp within the sendUDP() function within the Arduino Ethernet2 library:

/* +2008.01 bj */
while ( (w5500.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) 
{
  if (w5500.readSnIR(s) & SnIR::TIMEOUT)
  {
    /* +2008.01 [bj]: clear interrupt */
    w5500.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT));
    return 0;
  }
}

Basically, the program jumps between evaluating the while() part and the if() part. I guess that the expression (w5500.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK is never true. If I go to W5500.h and find the SnIR definition, it turns out it’s the socket’s interrupt signal.

I’m not sure exactly what this means, but I guess some sort of hardware problem is causing the software problem.

Thanks for telling us about your progress.

This code should be run after SEND command is written to socket’s command register, and does the simple things -

while ( (w5500.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK )

checks if interrupt register is having SEND_OK bit set. If it is set, then W5500 completed sending data without errors.

    if (w5500.readSnIR(s) & SnIR::TIMEOUT)

performs check if TIMEOUT bit in interrupt register is set in case SEND_OK bit is not set (there is no indication from W5500 that it has successfully sent data).

        w5500.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT));
        return 0;

this code resets SEND_OK and TIMEOUT bits in interrupt register, and is executed when W5500 did not send data (bit SEND_OK was not eventually set), and W5500 set timeout bit = error.

Again, this code must be executing after issuing SEND command to the socket; if executed in another circumstances it will hang. After SEND command, W5500 must return either timeout, or send_ok status.

You also must check that you supply correct socket # as input parameter, and in general check if this code is being executed in correct moment as I described above.

Thanks for the breakdown. I assume that the commands are being issued, because I’m using the Arduino library without modification, and running the “print DHCP” example that comes with it (basically it just gets an IP address via DHCP, then prints it to the serial port - I’ve disabled the serial port printing since I don’t have one). If you look at the code I linked to, there is a call to

w5500.execCmdSn(s, Sock_SEND);

immediately before the while loop that hangs. That function looks like this:

void W5500Class::execCmdSn(SOCKET s, SockCMD _cmd)
{
  // Send command to socket
  writeSnCR(s, _cmd);
  // Wait for command to complete
  while (readSnCR(s))
    ;
}

The writeSnCR function just writes the command into the W5500’s command register, I think.

So, it looks like the software is doing what it is supposed to do, and sending the UDP DHCP message to the W5500, but the W5500 is not acknowledging it…?

Seems so, the only question is why it happens. Let’s return back to your original issue, you said that link LED is flashing 0.5 Hz, in other words with period of every 2 seconds.

May I make a guess that W5100 is resetting every 2 seconds; after reset, of course, no interrupt will happen, and code will hang.

Do the following - put register dump into this loop; it should then be easy to see if chip has reset because all its mandatory for configuration registers in common register set will reset.

[quote=“Eugeny, post:10, topic:3209, full:true”]Do the following - put register dump into this loop; it should then be easy to see if chip has reset because all its mandatory for configuration registers in common register set will reset.
[/quote]
Ok, I changed the code to the following:

int sendUDP(SOCKET s)
{
  w5500.execCmdSn(s, Sock_SEND);

  uint8_t x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16 = 0;
	
  /* +2008.01 bj */
  while ( (w5500.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) 
  {
	  x1 = w5500.readSnMR(s);
	  x2 = w5500.readSnCR(s);
	  x3 = w5500.readSnIR(s);
	  x4 = w5500.readSnSR(s);
	  x5 = w5500.readSnPORT(s);
	  x6 = w5500.readSnDPORT(s);
	  x7 = w5500.readSnMSSR(s);
	  x8 = w5500.readSnPROTO(s);
	  x9 = w5500.readSnTOS(s);
	  x10 = w5500.readSnTTL(s);
	  x11 = w5500.readSnTX_FSR(s);
	  x12 = w5500.readSnTX_RD(s);
	  x13 = w5500.readSnTX_WR(s);
	  x14 = w5500.readSnRX_RSR(s);
	  x15 = w5500.readSnRX_RD(s);
	  x16 = w5500.readSnRX_WR(s);
  
    if (w5500.readSnIR(s) & SnIR::TIMEOUT)
    {
      /* +2008.01 [bj]: clear interrupt */
      w5500.writeSnIR(s, (SnIR::SEND_OK|SnIR::TIMEOUT));
      return 0;
    }
  }

  /* +2008.01 bj */	
  w5500.writeSnIR(s, SnIR::SEND_OK);

  /* Sent ok */
  return 1;
}

Before the first evaluation of the while() expression (i.e. immediately after x1…x16 are initialised), all variable values are listed as 0 except x5 = 105, x6 = 10, x7 = 9, x9 = 10, x10 = 84, x12 = 255, x13 = 25, x14 = 54. I’m not sure why that is, because this is just after I initialise all of those variables to 0. Perhaps the variables instead see some junk from previously used memory. I don’t know C well enough to understand what’s going on there.

Anyway, when the program gets to the line if (w5500.readSnIR(s) & SnIR::TIMEOUT), all of the variables are reset to 0. If I continue to run through the loop, the variables stay at 0.

I guess this supports your hypothesis about resetting…?

Oops, I realised that x5…x16 are uint16_t and not uint8_t. If I set these correctly, then all are 0 except x1 = 10, x5 = 17674, x7 = 5152, x10 = 26880, x11 = 2314, x12 = 2560, x13 = 84, x14 = 6655, x15 = 54, x16 = 26880. I’m not sure if these values mean anything. They still all get set to 0 before the if(...) statement.

Your readings look strange. For DHCP (at least) destination port should be 53 (0x35), in your can it differs.

How do you initialize W5500? Which registers do you write to before sending data?
Can you make dump of the common register set? Do not use these special register read commands, there should be generic register read command allowing reading bytes from specific area of the register block.

The readings all get set to 0 after the registers are read with the readSn___ commands though, so wouldn’t this indicate a reset like you thought? It looks like the values in the registers before these read commands are just junk left over in memory.

Initialisation of the W5500 is done by the Arduino library. I guess it sets the MAC address, because that’s the only information I give it. After that, it enters into the DHCP routine to obtain an IP address, gateway, DNS, etc.

I’m not sure how to dump the register set - is that something I can do with some C?

Do the following: before performing SEND command in UDP routine, perform dump of common registers. There should be specific pattern how registers must look like; I have a feeling that there’s something wrong with access to W5500 as processor seems to read garbage out of there.

And we need byte-by-byte dump of registers - in other words contents of common reg set +0, of +1, of +2 etc. All these values are byte values.

I added a bunch of direct read commands before the SEND:

uint8_t y1 = w5500.read(0x0000, 0x00); // mode
uint8_t y2 = w5500.read(0x0001, 0x00); // gateway IP 1/4
uint8_t y3 = w5500.read(0x0002, 0x00); // gateway IP 2/4
uint8_t y4 = w5500.read(0x0003, 0x00); // gateway IP 3/4
uint8_t y5 = w5500.read(0x0004, 0x00); // gateway IP 4/4
uint8_t y6 = w5500.read(0x0005, 0x00); // subnet mask 1/4
uint8_t y7 = w5500.read(0x0006, 0x00); // subnet mask 2/4
uint8_t y8 = w5500.read(0x0007, 0x00); // subnet mask 3/4
uint8_t y9 = w5500.read(0x0008, 0x00); // subnet mask 4/4

The read function does this:

uint8_t W5500Class::read(uint16_t _addr, uint8_t _cb)
{
  SPI.beginTransaction(wiznet_SPI_settings);
  setSS();
  SPI.transfer(_addr >> 8);
  SPI.transfer(_addr & 0xFF);
  SPI.transfer(_cb);
  uint8_t _data = SPI.transfer(0);
  resetSS();
  SPI.endTransaction();

  return _data;
}

All of the y values are zero after I read them.

I also verified the slave select goes low at the appropriate time between setSS() and resetSS(). However, I noticed that before the program starts the voltage on the SS pin is 2.7V, but after it goes low and goes high again, it is 3.3V (which is my microcontroller/W5500 supply voltage). I wonder if this is caused by my lack of pull-up?

I am more interested to see if you can read MAC address back.

If microcontroller’s pin is always in output state it should not happen - as most probably MCU is LVC/CMOS, and SCS pin does not sink much current, thus level should be very close to Vcc. Please check your application that, just after power up, the pins connected to SCS, SCLK and MOSI are configured as outputs, and are not being toggled (except SCLK). Use scope if you have to see what is going on there.

[quote=“Eugeny, post:17, topic:3209, full:true”]
I am more interested to see if you can read MAC address back.[/quote]
Good idea. I’ll check that tonight.

It’s weird, because as I say I’m using a barely changed Arduino Ethernet2 library. It should set everything up properly, which is why I suspect it’s my hardware. In terms of software, all I really changed was the slave select pin, but that’s mapped to a #define so it’s easily overridden. In terms of hardware, of course, I’ve left out some pull down and pull up resistors here and there, and some capacitors are slightly different.

Thanks a lot for your great help so far.

I added

uint16_t y10 = w5500.read(0x0009, 0x00); // Source MAC address 1/6
uint16_t y11 = w5500.read(0x000A, 0x00); // Source MAC address 2/6
uint16_t y12 = w5500.read(0x000B, 0x00); // Source MAC address 3/6
uint16_t y13 = w5500.read(0x000C, 0x00); // Source MAC address 4/6
uint16_t y14 = w5500.read(0x000D, 0x00); // Source MAC address 5/6
uint16_t y15 = w5500.read(0x000E, 0x00); // Source MAC address 6/6

They are all zero both before the while loop and during it.

Use scope to see what is going on SPI interface. Check circuit to see is you confused lines (e.g. MOSI/MISO). You must be able to read MAC address back after you wrote it to the W5500.