Electronic – STM32F407 + LAN8720A + lwIP + FreeRTOS = No received Ethernet frames

freertoslan8720lwipstm32

I'm trying to bring up a PCB that uses an STM32F407 and LAN8720A Ethernet PHY, and I can't seem to receive any Ethernet frames — even though I have no problem transmitting frames.

Hardware setup

Schematic of Ethernet PHY
I have a 25 MHz crystal on the STM32F4, driving a 25 MHz clock output pin into the LAN8720A, which is in REF_CLK_OUT mode — and drives a 50 MHz clock back to the STM32F4 as part of the RMII interface.

The jack/magnetics are a generic part. Here's the datasheet:
enter image description here

Software

I'm using the latest-update STM32CubeMX to generate a System Workbench for STM32 project that contains FreeRTOS, lwIP, plus the ETH peripheral drivers. I haven't really touched any of the generated code — so the lwIP stack gets initialized inside a FreeRTOS stack.

Experiments

With my board's lwIP configured for a 10.0.0.2 static IP, and a USB-to-ethernet dongle on my computer configured for a 10.0.0.1 static IP, I connect the two devices directly with an Ethernet cable, and my board attempts to connect to a service on port 80 of the computer. I capture the interaction between my board and the computer using Wireshark (running on the computer, and bound to the USB-to-Ethernet converter).

Because of the no-received-frames problem, we never get past this ARP stuff:
Wireshark capture
As you can see, the Stmicroe (my board) can send ARP packets — heard by my computer — but it never seems to hear the response from my computer, as it keeps blasting out ARP packets.

Both devices are configured with a 255.255.255.0 mask, and both are configured with a gateway address of 10.0.0.1 (the computer). I've heard of ARP tables getting screwed up and computers ignoring ARP packets, but I can't imagine the board would ignore ARP packets specifically addressed to it by my computer — in response to the requests the board made in the first place.

So, I dive into lwIP's ethernetif.c file and notice that HAL_ETH_GetReceivedFrame_IT(&heth) is returning an error. That function returns an error because (heth->RxDesc->Status & ETH_DMARXDESC_OWN) == 0, instead of 1. I interpret that to mean that the DMA buffers are currently armed for the MAC peripheral, and haven't received anything yet.

Furthermore, I've verified that the HAL_ETH_IRQHandler never gets called.

A problem with the PHY?

At this point, I suspected my PHY itself was to blame.

To investigate further, I attached my Saleae Logic Pro 16 to all the relevant signals, and noticed there's plenty of traffic on both the TX0/TX1, as well as the RX0/RX1 lines. Here's a capture of some RX traffic with the 25 MHz input clock:

Capture of received packet

RX_ERR is low the entire time, unless I attempt to capture the 50 MHz clock output (which is obviously challenging with a device like the Saleae): in that case, RX_ERR is ocassionally blipped high for a few packets (which is actually a good sign — the pin appears to be functioning).

Next steps

I've tried manually enabling ETH interrupts by calling HAL_NVIC_EnableIRQ(ETH_IRQn); after tcpip_init() is called in the MX_LWIP_Init() task, and that doesn't seem to fix the problem. I'm not entirely sure the Ethernet interrupt routine is even supposed to be getting called — that's the challenging thing with bringing up a brand new design; I'm struggling to determine what the proper behavior of the system would be, so I can then determine how my setup differs.

While I've used the STM32/STM32CubeMX/FreeRTOS stuff before, I've never used the STM32's Ethernet peripheral, and my only experience with this stuff is on custom embedded Linux systems, which always seemed to just work out of the box. This is new territory for me!

I'm sure there's a stupid checkbox somewhere or magical Ethernet_EnableReceive() function I forget to call, but I can't really find any documentation that suggests needing to explicitly enable that stuff, and the posts I'm seeing on the internet are all due to unrelated issues.

If anyone has any ideas, I'd love some help!

Addendum: Getting rid of FreeRTOS

Just to eliminate stuff, I've removed the FreeRTOS project component, going back to a bare-metal project. In my main loop, I call MX_LWIP_Process(). This method should eliminate the need for interrupts, but it doesn't fix the problem; I'm still unable to receive frames. This makes me think there's something in the ETH HAL code generated by STM32CubeMX.

Solution

Just in case someone stumbles upon this question in the future, the problem turned out to be flipped RXD0 and RXD1 pins. This is why I was able to see traffic on my logic analyzer, but it wasn't decoded by my MCU.

As someone pointed out, the magnetics I used are asymmetric, and should not be used for auto-MDI-X. I haven't had any issues. I anticipate one of two things is happening:
– the magnetics don't actually work in the other orientation, but because everything I have uses auto-MDI-X, my board essentially stays fixed in the configuration that works, while the other device on the cable orients its signals to match.
– the magnetics provide suitable signal integrity given the short Ethernet runs, but a long-term analysis would show higher rates of packet dropping or problems over longer runs.

Honestly, it's not clear to me why it would matter on which side of the 1:1 transformer the line filters are installed, so outside of PoE applications, I'm not sure why a symmetric vs asymmetric design would matter.

Best Answer

Sorry to resurrect this topic. I couldn't pass without mentioning my experience.

I have used this HR911105A (RJ45 with magnet) with one of my projects.

HR911105A: enter image description here At a glance, one thing took my attention which was the connection between LAN8720 and RJ45 as per your schematic.

Since I see that the connection looks crossover. Although connected systems mostly use MDI-X and therefore detect Receive / Transmit pairs, It would be good to give it a less confusing connection like that:

LAN -> RJ45
=====================
TXP -> TD+ (Pin #1)
TXN -> TD- (Pin #2)
RXP -> RD+ (Pin #3)
RXN -> RD- (Pin #6)

Pin #4 and Pin #5 (so the 49.9R pull-up resistors) would be good if connected to 3V3_AN in your schematic while the other side should be coupled to the GND via a capacitor (0.1uF or 0.022uF).