2024-10-17 09:12 PM - last edited on 2024-10-18 01:59 AM by SofLit
Hi
Using the following tools:
TouchGFX Designer 4.24.0
CubeIDE 1.16.1 and
CubeMx 6.11.0
I have attempted to build a system with ethernet capability.
I added the ethernet peripheral, LwIP compiled it without any errors, but the ethernet does not work. The router it is connected to does not "see" the MAC address and fails to assign an IP address. I have DHCP enabled.
I bolted Adam Berlinger's code into one of my projects
and that worked fine, with DHCP but the I cannot get the latest CubeMX build to work.
I have used the same board and cable that I used with Adams ethernet code for the new build so it's not the hardware.
Is the latest CubeMX build supposed to at least connect to a router and allow a ping test or do I have to do something else. The touchGFX code is working fine.
To be quite frank I find the ST Ethernet firmware and builds to be a nightmare. You basically have to be an experienced network engineer first to understand the jargon. SMTP, DHCP etc which is never spelled out in full in the cube help panel and secondly to work your way through the code which is like spaghetti to find out what is going wrong. LwIP is very clever, but its documentation is a joke considering how widely it is used. Compared to a system like FreeRTOS which has excellent documentation, a vibrant forum and heap and heaps (pardon the pun) of examples, LwIP is more like a hackers bad dream. Its been around for over twenty years yet the documentation is sparse to say the least.
The Wiki content for the socket API is just a regurgitation of the header file for the macro or function. Here is the entry for setsocketopt:
Now I'm guessing that setsockopt sets some options for the socket the application programmer is trying to instantiate. But it doesn't explain that, or what the various macro parameters do and what valid inputs they can be.
I am guessing that s is the socket handle, but what the rest of them are is beyond me. An application Programming Interface needs a lot more than code it needs an explanation of what the function does, when you should use it and what its parameters are.
Surely someone in ST could come up with some code that would provide simple things like ubiquitous for ethernet such as DHCP, mDNS, SNTP and a simple socket interface without the applications programmer needing to do a PhD in advanced network engineering. The hardware works fine, but the software...
From my little rant the perceptive among you probably sense I am just a little frustrated. I reckon I have spent more time trying to get ethernet to work than I have spent on all the other peripherals combined.
Am I being silly, am I missing something? What do others think?
Kind regards
Rob
Solved! Go to Solution.
2024-10-23 04:27 AM - edited 2024-10-23 08:22 AM
Hello @Garnett.Robert ,
Did you try to implement the second suggestion on the FreeRTOS forum other than the first workaround.
Can you share with us the project version with FreeRTOS only reproducing the issue as on a fresh project with the same setting for FreeRTOS as yours I'm unable to reproduce this problem the rand function is working as expected.
Regards
2024-10-18 09:50 AM
Hello @Garnett.Robert ,
Thank you for sharing your experience. I get that it can be quite challenging with Ethernet and related Middleware such as LWIP for the first time without initialization to basic networking concepts .i also agree with you on the basis that LWIP documentation lacks some details in the functions description but we try to explain its basics and ways of using it with our own documentation such as this AN3966 UM1713 AN3102 with LWIP concepts applicable on all STM32 series and a lot of examples using LWIP in community knowledge base such as the article you tried written by Adam and other example present in CubeFirmwareH7.and we are continently working on making the Ethernet Programming easier thro examples and documentation so any suggestions would be appreciated.
From your problem description I don't get exactly where you get stuck at and what is not working exactly.
If you could share with us more details about your software environment your IOC file and the relevant code changes apported to the generated it will help us investigate this issue.
Regards
2024-10-19 10:35 PM
Hi, ST
Thankyou for your reply.
I will arc up the example I created and trace the program flow with the debugger to attempt to find the issue. The problem is that this is a very time consuming process. I will also have a look at the documents you listed although I am pretty sure I have read at least one of them.
I have got the ethernet to work with an F7 board with a very old version of Cube, probably ten years or more ago but as I remember that was quite a struggle to. One of the problems I encountered since was that the ethernet driver has changed so much. Some functions that were provided in the older versions are no incorporated in new releases. it is such a moving target.
I think ST should work with AWS's Richard Barry and port in FreeRTOS+TCP. Some people have got this working on STF4 and H7 platforms. FreeRTOS+TCP may not have everything that LwIP does, but it is simpler and the documentation is much better. Also the support by Richard and his colleagues is excellent. In my view developers are never going to use an ST micro as the basis for some complex communications application, all you need is basic functions, DHCP, mDNS, sockets, SNTP, SMTP and IGCP and these should be set up to work out of the box.
You should be able to ping the thing, get date and time, bring up a web page and move data around at modest rates.
The reason I want to use ethernet is to for remote time stamped logging data and to set the RTC so time-stamping can use network time to set the clock so that clock setting functions don't have to be provided to the user. No one has to set the time on their mobile phone anymore or worry about IP addresses.
I did think of trying ThreadX as a number of people claim that LwIP works better with this, however my understanding is that TouchGFX only works with FreeRTOs so ThreadX is only an option for headless systems.
On another topic. A lot of people complain about the HAL_Drivers, I like them. With the exception of the ethernet driver they are generally easy to follow and if they don't provide the exact functionality you need you can clone and modify them. You can speed them up by stripping out unused functionality and doing small optimisations. I also like the CubeMX suite and TouchGFX. CuibeIDE has some great features, rtos aware, profiling, the Build analyser etc which are easy to use and very powerful. I started embedded programming using Keil uVision which was robust, but the analytical functions were lacking. I had to write a .map analyser for uVision to do the same thing that the Build Analyser does. Nobody wants to wade through fifty or more pages of a map file to get the info they want.
Kind regards
Rob
2024-10-19 11:54 PM
Hi , i agree, that MX not prepare working peripheral code , but this is normal , it create only first hw level init.
Too @STea appnotes have zero lines about MX setup to do it simpler.
You need forget , that this exist here. But when you preffer realy worked envi choice for example GitHub - stm32duino/STM32Ethernet: Arduino library to support Ethernet for STM32 based board
2024-10-20 06:13 PM
Hi Again ST
I ran traced the program flow of MX_LWIP_Init() to determine where things were going wrong. It didn't take long to find as the error occurs in the tcpip_init( NULL, NULL ); function in the tcpip.c file function lwip_init(void) and more precisely in the udp_init() function. This may be found in file udp,c,
/**
* Initialize this module.
*/
#pragma GCC push_options
#pragma GCC optimize ("O0")
void udp_init(void)
{
#ifdef LWIP_RAND
uint32_t randNo = LWIP_RAND(); /* <== Fails in the rand function rjg debug */
udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
#endif /* LWIP_RAND */
}
#pragma GCC pop_options
The call to the stdlib, rand() function fails at the assert function:
rand:
90033524: ldr r3, [pc, #88] @ (0x90033580 <rand+92>)
90033526: push {r4, lr}
90033528: ldr r4, [r3, #0]
9003352a: ldr r3, [r4, #48] @ 0x30
9003352c: cbnz r3, 0x9003355c <rand+56>
9003352e: movs r0, #24
90033530: bl 0x900333a8 <malloc>
90033534: mov r2, r0
90033536: str r0, [r4, #48] @ 0x30
90033538: cbnz r0, 0x90033544 <rand+32>
9003353a: ldr r3, [pc, #72] @ (0x90033584 <rand+96>)
9003353c: ldr r0, [pc, #72] @ (0x90033588 <rand+100>)
9003353e: movs r1, #82 @ 0x52
90033540: bl 0x90033360 <__assert_func> ;<== Fails here program hangs no hard fault
90033544: ldr r1, [pc, #68] @ (0x9003358c <rand+104>)
90033546: ldr r3, [pc, #72] @ (0x90033590 <rand+108>)
90033548: strd r1, r3, [r0]
9003354c: ldr r3, [pc, #68] @ (0x90033594 <rand+112>)
9003354e: str r3, [r0, #8]
90033550: movs r3, #11
90033552: strh r3, [r0, #12]
The program ends in the while(1) loop in syscalls
void _exit (int status)
{
_kill(status, -1);
while (1) {} /* Make sure we hang here */
}
This is weird so I tried the rand function in main.c. and it worked.
I call the MX_LWIP_Init from the default task that i renamed monitorTask so I checked the stack assignment, it was only 128 words so I increased it to 4096 thinking it might be a stack problem. This didn't fix the problem. I looked at the heap allocation as rand calls malloc. My heap in FreeRTOS is set to 100000 and the system heap set in the linker script is 0x1000 (4k) which seems OK.
The rand function is not guaranteed thread safe so i disabled interrupts before ran is called, but it still failed at the
_exit function.
I removed the call to rand and made the number a constant 0xCOAD1234. Viz.
/**
* Initialize this module.
*/
#pragma GCC push_options
#pragma GCC optimize ("O0")
void udp_init(void)
{
#ifdef LWIP_RAND
taskDISABLE_INTERRUPTS();
// uint32_t randNo = LWIP_RAND(); /* rjg debug */
// udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(59876);
udp_port = 59876;
taskENABLE_INTERRUPTS();
#endif /* LWIP_RAND */
}
#pragma GCC pop_options
This fixed the call to udp_init, but then the program crashed on the following call to tcp_init on the same macro:
void
tcp_init(void)
{
#ifdef LWIP_RAND
tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
#endif /* LWIP_RAND */
}
I replaced the call to rand with a fixed value:
void
tcp_init(void)
{
#ifdef LWIP_RAND
// tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
tcp_port = 59123;
#endif /* LWIP_RAND */
}
This allowed the tcp_init function to complete without crashing, but sadly something else went wrong in the DHCP module in the dhcp_discover function. And yes, you guessed it, the lead in the saddlebags was our old friend rand(), line 1913 of the dhcp,c module! viz:
/* DHCP_REQUEST should reuse 'xid' from DHCPOFFER */
if ((message_type != DHCP_REQUEST) || (dhcp->state == DHCP_STATE_REBOOTING)) {
/* reuse transaction identifier in retransmissions */
if (dhcp->tries == 0) {
#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
xid = LWIP_RAND(); /* <== ****************** This one **********/
#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
xid++;
#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
}
dhcp->xid = xid;
}
LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
("transaction id xid(%"X32_F")\n", xid));
I did a search for LWIP_RAND and found it is sprinkelrd liberally around the LWIP code:
That's it for now it is lunch time.
I will make my own rand code and tackle the problem on a full stomach. Fixing this is not for the mal-nourished!
2024-10-21 06:52 AM - edited 2024-10-21 06:56 AM
Hello @Garnett.Robert ,
can you share your IOC file generating this code after some examination it seems like you are running from address space 90000000 meaning you are executing from external memory.
are you using an external clock source other then systick as you are using FreeRTOS ?
are you configuring the Ethernet in MII mode with the correct pins assigned to the board.
a don't see why you should be stuck in a call from the standard library this is most probably a miss configuration, or we are missing something to get the full picture.
if you are using external memory, it is to note the following mentioned in UM2488
Regards
2024-10-21 08:24 PM
Hi ST,
I have configured for 100Mb full duplex and have checked this. I also checked the solder bridges and resistors and they are in the default configuration. I am using the same config as Adams i.e. MII not RMII.
I will check the pinout and config again just to be certain and will get back to you. Like you I don't understand why rand() works in main, but not in a task.
Kind Regards
Rob
2024-10-22 07:29 PM
Hello again,
I replaced all of the calls to LWIP_RAND aka rand() with some "magic numbers" which allowed the Ethernet initialisation to complete and viola it works.
I went on to the FreeRTOS forum and asked why rand() is failing from within tasks. Like me they thought it was a malloc - heap problem as rand calls malloc but returns with invalid addresses in the registers which causes the error at the assembler
<__assert_func>
They made a few suggestions.
The reason I'm using external flash is that this processor has only limited on-board flash and as the project was started from TouchGFX the program has a bootloader that uses the 2 MByte of external flash.
I have attached my IOC and the linker script to assist you.
I will let you know if the FreeRTOS boys come up with anything else.
Kind regards
Rob
I have attached the IOC file.
2024-10-23 04:27 AM - edited 2024-10-23 08:22 AM
Hello @Garnett.Robert ,
Did you try to implement the second suggestion on the FreeRTOS forum other than the first workaround.
Can you share with us the project version with FreeRTOS only reproducing the issue as on a fresh project with the same setting for FreeRTOS as yours I'm unable to reproduce this problem the rand function is working as expected.
Regards
2024-10-24 07:17 PM
Hi Stea,
I failed to inform you that I produced the project from TouchGFX. (4.24.0) To assist you I go through the steps I used below ( Iwrote these down as I progressed)
1. Initial build from TouchGFX for an STM32H750B
2. Use the cube IOC file embedded in the project to add ethernet and LwIP. On opening the CubeMX window the IDE asks if I wish to migrate the project to the newer CubeMX version (6.12.1). I pushed yes migrate.
3. I Select Eth in the CubeMX peripheral tree and set it to MII Full Duplex Only and I activate the Rx and Tx error signals
4. I then set all of the 16 Ethernet I/O Pins to Speed Very High
5. I set the Ethernet global interrupt to enabled. I leave the wake-up interrupt disabled.
6. I then enable LwIP in the cube Middleware and Software pack tree.
7. I set the Ethernet parameters thus:
- Platform LAN8742
- Set LWIP_DNS module = Enabled
- Set LWIP_MULTICAST_TX_OPTIONS = enabled (for IGMP)
- Check that memP_NUM_IGMP_GROUP > 1 (it is 8)
- Set LWIP_SNTP to enabled
- Set SNTP_MAX_SERVERS = 2
- Set SNTP_GET_SERVERS_FROM_DHCP = 1
- Set SNTP_COMP_ROUNDTRIP = enabled
8. Saved the IOC file from within CubeIDE then answered "Yes" to the do you wish to generate code dialog.
9. I then do a full clean and build of the project
10. Under the debugger I run the project on my board and the LWIP_RAND function rand() from stdlib errors into syscalls _exit(int status) with status equal to 1.
11. On the advice of the FreewRTOS team I changed the caddr_t _sbrk(int incr) function to
#include <errno.h>
#include <stdio.h>
#include <sys/types.h> // Added as a workaround for Bugzilla 158966
/* Variables */
extern int errno;
/* Functions */
/**
_sbrk
Increase program data space. Malloc and related functions depend on this
**/
caddr_t _sbrk(int incr)
{
extern char end asm("end");
static char *heap_end;
char *prev_heap_end;
char * stack_ptr;
if (heap_end == 0)
heap_end = &end;
__asm volatile ("MRS %0, msp" : "=r" (stack_ptr) );
prev_heap_end = heap_end;
if (heap_end + incr > stack_ptr)
{
errno = ENOMEM;
return (caddr_t) -1;
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
The code then runs without the rand() error, the TouchGFX responds to screen touches, but no ethernet port appears on my network monitor (Linksys Modem or Advanced IP Scanner.)
12. GPIO examination. I checked the GPIO's assigned for the ether net peripheral against Adams working version and I found a difference. viz.
/**Adams ETH GPIO Configuration
PG11 ------> ETH_TX_EN
PE2 ------> ETH_TXD3
PG12 ------> ETH_TXD1
PG13 ------> ETH_TXD0
PA9 ------> ETH_TX_ER
PI10 ------> ETH_RX_ER
PC1 ------> ETH_MDC
PC2 ------> ETH_TXD2
PC3 ------> ETH_TX_CLK
PA2 ------> ETH_MDIO
PA1 ------> ETH_RX_CLK
PA0 ------> ETH_CRS
PA7 ------> ETH_RX_DV
PC4 ------> ETH_RXD0
PB1 ------> ETH_RXD3
PA3 ------> ETH_COL
PC5 ------> ETH_RXD1
PB0 ------> ETH_RXD2
/**Cube MX ETH GPIO Configuration
PG11 ------> ETH_TX_EN
PE2 ------> ETH_TXD3
PG12 ------> ETH_TXD1
PG13 ------> ETH_TXD0
PA9 ------> ETH_TX_ER
PI10 ------> ETH_RX_ER
PC1 ------> ETH_MDC
PC2 ------> ETH_TXD2
PC3 ------> ETH_TX_CLK
PA2 ------> ETH_MDIO
PA1 ------> ETH_RX_CLK
PA7 ------> ETH_RX_DV
PC4 ------> ETH_RXD0
PB1 ------> ETH_RXD3
PC5 ------> ETH_RXD1
PB0 ------> ETH_RXD2
*/
13. to make it easier to identify the board in my network maps I changed the MAC ADDRESS TO
uint8_t MACAddr[6] ;
heth.Instance = ETH;
MACAddr[0] = 0xE0;
MACAddr[1] = 0xE1;
MACAddr[2] = 0xE2;
MACAddr[3] = 0xE3;
MACAddr[4] = 0xE4;
MACAddr[5] = 0xE5;
i.e "E0 E1 E2 E3 E4 E5"
I then compiled and ran the code, but still no IP address appeared for that MAC Address.
14. I then edited the GPIO initialisation to match Adam's. I did a full clean and build and ran the program under the debugger again. I waiter ten minutes for the router to identify and connect to myboard, but again no internet connection.
15. I added a breakpoint after the RxPktSemaphore in the ethernetif_input function to observe whether valis packets were being recieved but the program never hit that interrupt. I checked the ETH_DMACIER register and the following interrupts were enabled: TIE, RIE, RBUE, FBEE, AIE and NIE. I compared this with Adams working version and found after the HAL_ETH_Start_IT(&heth); has executed the same interrupts with the exception of the RBUE interrupt were set.
16. I then added some linker script code consistant with Adam's example viz:
/*********************************************************************/
/* Ethernet Buffers */
.lwip_sec (NOLOAD) : {
. = ABSOLUTE(0x30040000);
*(.RxDecripSection)
. = ABSOLUTE(0x30040060);
*(.TxDecripSection)
. = ABSOLUTE(0x30040200);
*(.RxArraySection)
} >RAM_D2
This did not fix the problem.
17. Added in additional options in lwipopts.h, but still did not work.
*************************************************
I think that if you are building without TouchGFX and just using CubeMX perhaps it does work. The rand() problem may be a conseqeunce of the sysmem.c code produced under TouchGFX being different from that produced with CubeMX alone. I will try this.
I would would like to try the system with no TouchGFX, just using FreeRTOS and LWIP. The problem I have is that I need the external flash and bootloader provided by the TouchGFX build to fit LwIP in and I haven't a clue how to do that and I don't have the time to learn as I need to get on with my current project. I have spent about 24 hours work time on this.
I will try the rand() function on a project generated by CubeMX and see if it is different from that produced by TouchGFX.
Kind regards
Rob