October 9, 2026

First experiences with STM32 CubeMX - CubeMX and network/ethernet, part 1

The H743 has an ethernet peripheral.

One of my reasons for fooling around with CubeMX was to find out if I could get CubeMX to setup code including an ethernet driver.

Doing so looks promising. I see that lwip is offered as "middleware". Also it seems possible to get an http server going "out of the box".

The tutorials works with 3 boards, F207-Nucleo, F7508-Disco, and H745-Disco.

Note that the first two use RMII, while the third uses MII.
On my boards:

So the H745 Disco (which I don't have) is the odd one, using MII. It also uses the LAN8740A Phy chip rather than the 8742A chip.

I see a warning for t he H747 Disco that the ETH_MDC pin is shared with the on-board digital microphone. This must be resolved by physical jumper (solder bridge) setup on the board itself.

Note that the usual thing is that a 25 Mhz crystal clocks the PHY, which in turn generates the 50 Mhz clock (RMII_REF_CLK) and passes it to the MAC on the STM32.

Setup CubeMX for the H743 Nucleo

I start a new project, calling it "Net1". I now follow a checklist generated by a Google AI search: Ethernet warns me about a conflict with PB0. I check the schematic on this, but PB0 doesn't seem to be involved at all. I think this would only happen on a discovery board, yet I get the warning.
There is an entirely separate clock setup tab. I know the HSE clock is 8 Mhz from the ST-Link, but I don't see how to set that up. I can enter 480 for the cpu core clock and it accepts that and sets it up.

I don't resolve the HSE issue for now, (See below).

I decide to set a static IP 192.168.0.74 for lwip. This is easy to do.

I go to Project Manager and tell it to generate a Makefile, calling the project Net1. It warns me that some LwIP parameters may not be set up right. I go back under "platform parameters" and find that I need to select the 8742 PHY chip and the driver for it, which is easy.

I generate code!

I go to ~/Cube/Net1 and type "make". It builds clean and fairly quickly. Mostly I want to see two things. One is I want to see what it digs up as source for the ethernet drive. The other is I want to see how it handles it when I go back to change configuration and want to regenerate code.

I go back and type:

cd ~/Cube/Net1
make clean
make >make.log
This gives me a file I can search using vim to see what components were used in the build and where they came from.

What about that HSE clock?

The trick is that this is not handled entirely within the clock tree diagram. You go back to the "pinout" tab, go to "system core" and "rcc" and then you can enable the HSE, selecting "bypass clock source" since the clock comes from ST-Link, not a crystal.

Now you can use the clock configuration tab. I have seen misinformation that this is 25/3 (8.33), but I don't believe it and enter 8.00. Then enter 480 for the desired CPU frequency, and it works out all the PLL settings and divisors.

The PLL takes the 8 Mhz unchanged (first divisor is 1) then multiplies by 120 and has a final divide by 2 to give 480. It divides by 2 (HPRE) to get 240, then again by 2 to give 120 for PCLK1,2,...

Give it a try

cd ~/Cube/Net1
make clean
make -j20
cd build
flash Net1.elf
It is still running the LED demo that changes when I press the user button. On my linux machine, I add an entry to my hosts file:
# 10-11-2026 Nucleo board
192.168.0.74    st743
I find and connect a network cable. The LED on the RJ45 jack both begin to blink at about 1 Hz.
A peek at main.c in Core/src shows only this network related call:
MX_LWIP_Init();
I also see a call to printf() -- and I see the output on the USB console.

Get ping to work

At this point it does not work. I start reading the tutorial I have links to above. They indicate that I need to add this to main.c:
while (1) {
    MX_LWIP_Process();
}
No ping yet running the code with this addition. So we go back and start double-checking everything.

Note: Looking through the various LwiP settings in CubeMX, I see that it offers HTTPD (but it is currently disabled).

I don't see any place to configure the PHY address. Curiously, I configure the PHY device in the LwiP settings.

As recommended, I check the pin configuration. I start from ETH under Connectivity, which as a GPIO section. This shows several pins misconfigured, but you don't change them here. You go back to Core->GPIO and use the package diagram.

I see PB11 assigned to TX_EN, but it should be PG11
I see PB12 assigned to TXDO, but it should be PG13

By finding PG11 and PG13 on the edges of the package diagram, I can get a menu and select the desired function. When I do this, PB11 and PB12 get automatically deassigned, which is nice.

I use "generate code" then rebuild. I fully expected the small handful of changes I made to main.c to be overwritten, but it seems to respect the markings that say "USER CODE BEGIN" and "END".
No ping yet.

I go to LwiP and select the heap size under "Key Options", changing it from 1600 to 5000. I also change the LwiP debugging level to All.

Now I go to "System Core", "Cortex M7" and set up an MPU region. I see only MPU region 0 starting at 0 and 4G in size. I leave this be. I select MPU region 1 and enable it. Then I set up the following:

Base - 0x30000000
Size - 32KB (the default)
TEX field level - level 1
Access permission - ALL
Instruction access - disable
Shareability - enable
Cacheability - disable
Bufferable - disable
Next I am advised to add these lines to the linker script:
.lwip_sec (NOLOAD) : {
    . = ABSOLUTE(0x30000000);
    *(.RxDescripSection)

    . = ABSOLUTE(0x30000080);
    *(.TxDescripSection)

    . = ABSOLUTE(0x30000100);
    *(.Rx_PoolSection)
  } >RAM_D2
I add this to STM32H743xx_FLASH.ld, generate code, and rebuild.
No ping yet.
No serial port messages either, which is surprising given that I set the LwiP debug level to All.

Dig into the source

So far we are working blind in a trial and error mode. We get no console log output, and all we can do is to try a ping to see if it fails or works.

We have two starting points to dig into the source code, maybe three:

ETH_IRQHandler ()
MX_LWIP_Init ()
MX_LWIP_Process ()
We find ETH_IRQHandler() in Src/stm32h7xx_it.c It simply bounces the call:
extern ETH_HandleTypeDef heth;
HAL_ETH_IRQHandler(&heth);
This is in Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_eth.c I add a line here to call printf("ETH") and see nothing on the console.

We find MX_LWIP_Init() in LWIP/App/lwip.c
We find MX_LWIP_Process() in LWIP/App/lwip.c

The process routine looks like:

void MX_LWIP_Process(void)
{
  ethernetif_input(&gnetif);
  sys_check_timeouts();
  Ethernet_Link_Periodic_Handle(&gnetif);
}
So, it reads a packet if there is one, passing it to Ethernet_Link_Periodic_Handle() -- apparently the status of whether or not there actually is a packet is in the structure. This function is in the same file and is pretty simple.
static void Ethernet_Link_Periodic_Handle(struct netif *netif)
{
  /* Ethernet Link every 100ms */
  if (HAL_GetTick() - EthernetLinkTimer >= 100) {
    EthernetLinkTimer = HAL_GetTick();
    ethernet_link_check_state(netif);
  }
}
In fact lwip.c is only 250 lines and is apparently generated by CubeMX as a glue layer on top of LwiP itself..

Status

I am dropping this here and trying some other things before I come back to it. I may try setting up a timer interrupt and/or USB.


Have any comments? Questions? Drop me a line!

Tom's software pages / tom@mmto.org