Debugging STM32 Ethernet, LwIP, and PHY Initialization Failures
When Ethernet fails on an STM32 microcontroller, the issue typically resides in clock configuration, PHY address mismatches, DMA memory caching, or link status handling in LwIP. Follow this technical troubleshooting guide to isolate and resolve the root cause.
1. PHY Address and Hardware Clock Verification
Before debugging LwIP, verify that the MAC can communicate with the PHY transceiver over the SMI (MDIO/MDC) bus.
- RMII Reference Clock: Ensure the 50 MHz clock on
RMII_REF_CLK(usuallyPA1) is stable. If using an external oscillator, verify its output with an oscilloscope. If driven by the MCU's MCO1/MCO2 pin, verify PLL settings. - PHY Address: Match the PHY hardware address in software to the physical pin straps (e.g., RXER/PHYAD0). LAN8720 defaults to address
0or1; DP83848 defaults to1. - Hardware Reset: Always toggle the PHY reset pin manually in code before calling
HAL_ETH_Init().
// Toggle PHY Hardware Reset Pin
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(50);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_2, GPIO_PIN_SET);
HAL_Delay(50);
// Read PHY Identifier Register to verify SMI communication
uint32_t phy_id1 = 0;
HAL_ETH_ReadPHYRegister(&heth, PHY_ADDRESS, PHY_SR_REG, &phy_id1);
2. Memory Protection Unit (MPU) and DMA Buffers (F7 / H7 Series)
On Cortex-M7 core devices (STM32F7/H7), Ethernet DMA descriptors and packet buffers placed in cacheable SRAM will cause RX/TX corruption due to cache incoherency.
- Place Ethernet DMA descriptors and RX/TX buffers in a dedicated non-cacheable RAM section using the MPU.
- Align Ethernet buffers to 32-byte boundaries (cache line size).
// Example MPU configuration for Ethernet buffers (Non-cacheable)
MPU_Region_InitTypeDef MPU_InitStruct = {0};
HAL_MPU_Disable();
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x30040000; // SRAM3 address for H7
MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
3. LwIP Link Status Synchronization
LwIP will not transmit or process packets if the network interface flags are down. You must poll the PHY Basic Status Register (BSR) to update LwIP's state dynamically.
// Call this periodically inside a task or main loop
void check_ethernet_link(struct netif *netif) {
uint32_t phy_reg = 0;
HAL_ETH_ReadPHYRegister(&heth, PHY_ADDRESS, PHY_BSR, &phy_reg);
if (phy_reg & PHY_LINKED_STATUS) {
if (!netif_is_link_up(netif)) {
netif_set_link_up(netif);
}
} else {
if (netif_is_link_up(netif)) {
netif_set_link_down(netif);
}
}
}
4. Step-by-Step Isolation Checklist
- Check Interrupt Service Routine: Verify that
ETH_IRQHandler()is executing on frame reception. EnsureHAL_ETH_Start_IT()was called instead ofHAL_ETH_Start()if using interrupts. - Inspect Wireshark: If transmitting, check if gratuitous ARP or DHCP requests leave the MCU. If visible in Wireshark but no response is accepted, check the RX interrupt and descriptor memory settings.
- Increase LwIP Allocation: Out-of-memory errors (
ERR_MEM) freeze packet flow. IncreasePBUF_POOL_SIZE(minimum 8 to 16) andMEM_SIZE(minimum 10KB to 16KB) inlwipopts.h.
Need this done fast? order embedded help on Kwork.
I take on freelance fixes and builds in this area.