on
2020-03-11
07:50 AM
- edited on
2024-06-17
06:55 AM
by
Laurids_PETERSE
This readme is intended for STM32CubeIDE version 1.9.0 and STM32CubeH7 version 1.10.0. For older tool versions please see older version of this readme in the repository.
If you have any questions regarding Ethernet topics in general, please visit our MCU forum and create a thread. Doing so will provide you with a shorter response time.
Example project code and older version of this article is provided on Github: https://github.com/stm32-hotspot/STM32H7-LwIP-Examples. Detailed how to step-by-step is provided below.
ETH_CODE
keywordVersion | STM32CubeIDE version | STM32CubeH7 version | Description / Update | Date |
---|---|---|---|---|
1.2 | 1.9.0 | 1.10.0 | Ported to new IDE/library version. Ethernet driver reworked in new library release. Added iperf measurement and TCP/IP settings tuned. Published on Github | August 9th 2022 |
1.1 | 1.6.1 | 1.9.0 | Added Cortex-M4 base examples | July 19th 2021 |
1.0 | 1.6.1 | 1.9.0 | Initial release on ST community (with minor changes on Github) | June 21st 2021 |
Using GIT tags it should be easy to find examples for particular version of STM32CubeIDE and HAL library
Below configuration is necessary to achieve good TCP/IP performance
Parameter | Value | Formula | Needs to be changed in MX |
---|---|---|---|
TCP_MSS | 1460 |
|
yes |
TCP_SND_BUF | 5840 |
|
yes |
TCP_WND | 5840 |
|
no |
TCP_SND_QUEUELEN | 16 |
|
yes |
On STM32H74x/H75x devices, all data related to Ethernet and LwIP are placed in D2 SRAM memory (288kB). First 128kB of this memory are reserved for Cortex-M4 on dual-core devices. On single core devices this part can be used for other purposes.
Variable | STM32H74x/H75x address | Cortex-M4 alias | Size | Source file |
---|---|---|---|---|
DMARxDscrTab | 0x30040000 | 0x10040000 | 96 (256 max.) | ethernetif.c |
DMATxDscrTab | 0x30040100 | 0x10040100 | 96 (256 max.) | ethernetif.c |
memp_memory_RX_POOL_base | 0x30040200 | 0x10040200 | 12*(1536 + 24) | ethernetif.c |
LwIP heap | 0x30020000 | 0x10020000 | 131048 (128kB - 24) | lwipopts.h |
For STM32H72x/H73x devices, the D2 SRAM is more limited (only 32kB). The RX buffers need to be placed in AXI SRAM, since they won't fit to D2 RAM, together with LwIP heap. The LwIP heap is reduced to fit the rest of D2 RAM together with DMA descriptors.
Variable | STM32H72x/H73x address | Size | Source file |
---|---|---|---|
DMARxDscrTab | 0x30000000 | 96 (256 max.) | ethernetif.c |
DMATxDscrTab | 0x30000100 | 96 (256 max.) | ethernetif.c |
memp_memory_RX_POOL_base | AXI SRAM (32-byte aligned) | 12*(1536 + 24) | ethernetif.c |
LwIP heap | 0x30000200 | 32232 (32kB - 512 - 24) | lwipopts.h |
Value provided here are an example implementation, other configurations are also possible in order to optimize memory usage. When changing memory layout, the MPU configuration needs to be updated accordingly.
Libraries and middleware is taken from STM32CubeH7 package. So the same licenses apply to the these examples. There is minimum code added on top of STM32CubeMX and HAL libraries, this code is provided AS-IS.
Goal of this example is to:
Although the example is using STM32H750-Discovery, it might be easy to use the same steps for other STM32H7 based boards. The main differences are usually pinout and clock configuration. You might also need to check board solder bridges to make sure the Ethernet is connected to MCU.
Configure clock tree:
The ETH_MDC speed couldn't be changed for some reason, but it doesn't affect the application and it was already fixed in new versions.
This step can be skipped when using Cortex-M4 core.
Above example is for STM32H743 device. For other devices or Cortex-M4 core on dual-core device, different addresses and size might be necessary. Please refer to section Memory layout
When using dual-core device and running Ethernet on Cortex-M7 core, it must be ensured that memory used by Ethernet is not used by Cortex-M4. Also note the Cortex-M4 can use different address alias for D2 RAM
In "Key options" tab:
Save project to some folder of your selection. Now you can generate the project for IDE. We will use STM32CubeIDE in this example, but it should work with other IDEs.
There are several places where some additional code should be placed, also depending on selected device. In the examples all these places are marked with comment containing
ETH_CODE
and some basic explanation. Searching for
ETH_CODE
can show all these places.
Only main interesting points are mentioned below:
/* USER CODE BEGIN 2 */ #if defined ( __ICCARM__ ) /*!< IAR Compiler */ #pragma location = 0x30040200 extern u8_t memp_memory_RX_POOL_base[]; #elif defined ( __CC_ARM ) /* MDK ARM Compiler */ __attribute__((at(0x30040200)) extern u8_t memp_memory_RX_POOL_base[]; #elif defined ( __GNUC__ ) /* GNU Compiler */ __attribute__((section(".Rx_PoolSection"))) extern u8_t memp_memory_RX_POOL_base[]; #endif /* USER CODE END 2 */
/* USER CODE BEGIN 1 / #undef LWIP_PROVIDE_ERRNO #define LWIP_ERRNO_STDINCLUDE / USER CODE END 1 */
This step should be skipped for Keil and IAR, since they support placing variables at specific address in C code. Modify the linkerscript (*.ld) that the ETH descriptors and buffers are located in D2 SRAM. Also it is recommended to place all RAM to RAM_D1. In STM32CubeMX generated project, the "_FLASH" suffix linkerscript should be modified, which is used by default (e.g.: STM32H750XBHx_FLASH.ld). The "_RAM" suffix linkerscript is template for executing code from internal RAM memory.
} >RAM_D1 /* Modification start */ .lwip_sec (NOLOAD) : { . = ABSOLUTE(0x30040000); *(.RxDecripSection) . = ABSOLUTE(0x30040060); *(.TxDecripSection) . = ABSOLUTE(0x30040200); *(.Rx_PoolSection) } >RAM_D2 /* Modification end */ /* Remove information from the compiler libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) }
The memory definitions at the beginning of the linkerscript should look like:
MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K }
For dual core devices it is better to restrict RAM_D2 section to avoid collision with Cortex-M4. Please see the linkerscripts in examples.
#include “lwip/udp.h” #include <string.h>
/* USER CODE BEGIN 5 */ const char* message = "Hello UDP message!\n\r"; osDelay(1000); ip_addr_t PC_IPADDR; IP_ADDR4(&PC_IPADDR, 192, 168, 1, 1); struct udp_pcb* my_udp = udp_new(); udp_connect(my_udp, &PC_IPADDR, 55151); struct pbuf* udp_buffer = NULL; /* Infinite loop */ for (;;) { osDelay(1000); /* !! PBUF_RAM is critical for correct operation !! */ udp_buffer = pbuf_alloc(PBUF_TRANSPORT, strlen(message), PBUF_RAM); if (udp_buffer != NULL) { memcpy(udp_buffer->payload, message, strlen(message)); udp_send(my_udp, udp_buffer); pbuf_free(udp_buffer); } } /* USER CODE END 5 */
Now you should be able to ping the device and receive UDP messages, assuming that you configure IP address 192.168.1.1 for the receiving device (the 192.168.1.0/24 network is used by attached examples). On Linux machine you can observe the messages with the following command:
netcat –ul 55151
When facing issues with your own project:
If you see any issue with these examples please fill an issue inside this repository. If you face some difficulties, but not sure if this is an issue you can start discussion inside this repository, or you can start thread on ST community
Exact size required for different stacks might depend on used compiler and optimization flags. Same goes for FreeRTOS heap size, since thread stacks are allocated from this heap.↩
Some addresses and sizes depend on the device or core used. Please refer to section Memory layout.↩↩↩↩
If you have any questions regarding Ethernet topics in general, please visit our MCU forum and create a thread. Doing so will provide you with a shorter response time.
Hi,
I'll try to get this working on a STM32H757i-Eval board without success. Is it possible to have a project or direction of what need to be modified to get this board running on LWIP with FreeRTOS on the M7 core ?
@Adam BERLINGER - hey thanks for the article.
is it possible to provide a simple LWIP project without FREERTOS with the latest CubeMX (6.9.2)?
I can't find an LWIP project only for STM32H743 with the latest CubeMX... tried to generate one myself with google example but the LWIP UDP not working.
see Wireshark:
pls help, I really want to use the most updated CubeMX.
thanks
Hi there!
Following this awesome gude i configured the Ethernet port and the LWIP stack for a nucleo-H755 board project.
I added the MX_LWIP_Process() function into the M7 core main inifinite loop and I'm able to ping my board.
But if I add even a simple instruction into the infinite loop (like a couter) it results in an hard fault with an LWIP assertion message:
"Assertion "pc->custom_free_function != NULL" failed at line 767 in C:/Users/PF5766/Documents/REPO/NucleoH755ZI/H755lwipTest/Middlewares/Third_Party/LwIP/src/core/pbuf.c"
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
MX_LWIP_Process();
if((HAL_GetTick() - uiCnt) >= 200U)
{
uiCnt = HAL_GetTick();
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
uiTimeOutCounter++; // Causes Hard Fault!
}
/* USER CODE END 3 */
}
Here the code causing the Hard fault: commenting the uiTimeOutCounter increase fix the problem... but why?
It seems a MPU configuration issue. It could be?
BTW, enabling the MPU in the .ioc file, seems generate 2 functions: MPU_Initialize(void) and MPU_Config(void).
But the first one (MPU_Initialize) is only declared and never defined (giving also a warning at compile time).
Any Idea?
iTTy
Hello everyone.
The comment section on this article will be locked.
As previously mentioned, if you have any questions please go ahead and create your own thread on our STM32 MCU forum (click here).
You can refer to this knowledge base article in your post.
Best regards,
Laurids