Skip to main content
Visitor II
November 4, 2016
Question

Initialize LWIP with Ethernet disconnected

  • November 4, 2016
  • 11 replies
  • 11709 views
Posted on November 04, 2016 at 14:45

Hello !

The board I am using for this topic is based on the ST

http://www.st.com/en/evaluation-tools/stm3220g-eval.html

hardware for the Ethernet part. I have generated a basic code with cubeMx (nice tool!) and Ethernet communication is working well on this board. The problem is that Ethernet is working only when Ethernet cable is connected at startup (especially when initialization function is run). If I start my board without cable, Ethernet is not working (ping test failed) and will never work even if I connect the cable. To solve this, I added the following function after LWIP initialization:

netif_set_link_callback(&gnetif, ethernetif_update_config);

The ''ethernetif_update_config'' function is generated by cubeMx (in ethernetif.c file) to restart auto-negotiation:

/**
* @brief Link callback function, this function is called on change of link status
* to update low level driver configuration.
* @param netif: The network interface
* @retval None
*/
void
ethernetif_update_config(
struct
netif *netif)
{
__IO uint32_t tickstart = 0;
uint32_t regvalue = 0;
if
(netif_is_link_up(netif))
{ 
/* Restart the auto-negotiation */
if
(heth.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE)
{
/* Enable Auto-Negotiation */
HAL_ETH_WritePHYRegister(&heth, PHY_BCR, PHY_AUTONEGOTIATION);
/* Get tick */
tickstart = HAL_GetTick();
/* Wait until the auto-negotiation will be completed */
do
{
HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, ®value);
/* Check for the Timeout ( 1s ) */
if
((HAL_GetTick() - tickstart ) > 1000)
{ 
/* In case of timeout */
goto
error;
} 
} 
while
(((regvalue & PHY_AUTONEGO_COMPLETE) != PHY_AUTONEGO_COMPLETE));
/* Read the result of the auto-negotiation */
HAL_ETH_ReadPHYRegister(&heth, PHY_SR, ®value);
/* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */
if
((regvalue & PHY_DUPLEX_STATUS) != (uint32_t)RESET)
{
/* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */
heth.Init.DuplexMode = ETH_MODE_FULLDUPLEX; 
}
else
{
/* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */
heth.Init.DuplexMode = ETH_MODE_HALFDUPLEX; 
}
/* Configure the MAC with the speed fixed by the auto-negotiation process */
if
(regvalue & PHY_SPEED_STATUS)
{ 
/* Set Ethernet speed to 10M following the auto-negotiation */
heth.Init.Speed = ETH_SPEED_10M; 
}
else
{ 
/* Set Ethernet speed to 100M following the auto-negotiation */
heth.Init.Speed = ETH_SPEED_100M;
}
}
else
/* AutoNegotiation Disable */
{
error :
/* Check parameters */
assert_param(IS_ETH_SPEED(heth.Init.Speed));
assert_param(IS_ETH_DUPLEX_MODE(heth.Init.DuplexMode));
/* Set MAC Speed and Duplex Mode to PHY */
HAL_ETH_WritePHYRegister(&heth, PHY_BCR, ((uint16_t)(heth.Init.DuplexMode >> 3) |
(uint16_t)(heth.Init.Speed >> 1))); 
}
/* ETHERNET MAC Re-Configuration */
HAL_ETH_ConfigMAC(&heth, (ETH_MACInitTypeDef *) NULL);
/* Restart MAC interface */
HAL_ETH_Start(&heth); 
}
else
{
/* Stop MAC interface */
HAL_ETH_Stop(&heth);
}
ethernetif_notify_conn_changed(netif);
}

Unfortunately, when I manually launch this callback by calling the ''netif_set_link_up(&gnetif)'' function after connecting the cable, Ethernet is still not working. Is anybody having an idea on how to make Ethernet working in this configuration? Thank you for your help #lwip-ethernet-disconnected
    This topic has been closed for replies.

    11 replies

    Visitor II
    November 9, 2016
    Posted on November 09, 2016 at 12:03

    Hello,

    You can start with a LwIP example under STM32CubeF2 with a similar type of your project to deduce what is going wrong in your case.

    The working example available at this path can help you as an implementation example: 

    STM32Cube_FW_F2_V1.4.0\STM32Cube_FW_F2_V1.4.0\Projects\STM322xG_EVAL\Applications\LwIP

    Regards

    Visitor II
    December 16, 2016
    Posted on December 16, 2016 at 22:48

    Hi Gates,

    Have you solved this issue?  I am running into the same problem.  I am using 429 Eval and the code is working on the eval board.  My hardware system is using a different 429 and is working well except for the ethernet cable disconnect issue.

    Even though I set  CUBEMX to define LWIP_NETIF_LINK_CALLBACK        1, it did not do so.   I went in and defined LWIP_NETIF_LINK_CALLBACK        1

    Like you, I added 

    netif_set_link_callback(&gnetif, ethernetif_update_config); where it appears in the LWIP initialization function like in the example projects.  

    I set a breakpoint in 

    ethernetif_update_config to see if the code reaches and it does not.  In the LWIP HTTP RAW example, the breakpoint activates.  I am not sure what I am missing.

    Thanks!

    Visitor II
    December 19, 2016
    Posted on December 19, 2016 at 15:25

    Hi All!

    I have  same issue on stm32f746g-disco board. But for my case cubeMx generates code shich is not compiling in case 

    I set  CUBEMX to define LWIP_NETIF_LINK_CALLBACK        1. It tries to use some interrupt masks which not defined in stm32f7xx_hal_conf.h. I investigated that PHY_MISR define and PHY_MICR not situated for my PHY, i have only PHY_ISFR_INT4, and have no ideas for now how to properly hadle that.

    But if board got eth cable from the start it configuring properly and working.

    If u'll got some tips to fix it please let me know.

    Visitor II
    December 19, 2016
    Posted on December 19, 2016 at 23:49

    Hi VK,

    I have made some progress on my issue using the F429 MCU.  Maybe this will help you investigate.  

    In the F429 eval board schematics, the PHY is configured to have a hardware interrupt and also PHY_MISR register bit that sets when a the link status changes. Link status in this case is ethernet cable status change. 

    In the 429eval system, the INT pin of the PHY goes to the i2c module which has multiple inputs.  The i2c module sets an external interrupt on the MCU.   If you look at the HTTP_RAW example for the 429 eval board you will see this eternal interrupt in main.c HAL_GPIO_EXTI_Callback.

    Since I do not have a physical connection between PHY INT pin and MCU, I have set a timer interrupt to run every 1 second. In that interrupt I am reading PHY_SR->PHY_LINK_STATUS bit to see if the link is active or not. I am also checking 

    PHY_MISR->PHY_LINK_INTERRUPT bit.  If the PHY_LINK_INTERRUPT bit is set, I am calling ethernetif_set_link(&gnetif); 

    In this PHY chip though, I have to modify the default ethernetif_set_link function created by CUBE because I can not read the

    PHY_MISR->

    PHY_LINK_INTERRUPT bit twice.  Once I read, the bit clears the interrupt.  The ethernetif_set_link handles the change in the ethernet cable's status.

    Visitor II
    January 8, 2018
    Posted on January 08, 2018 at 17:27

    Don't know if it is relevant any more, but when you generate your CubeMx project with the ETH device you will see it generates the thread: 'ethernetif_set_link'. Make sure this is part of a thread of your OS. It observes the link and recovers it as soon you your ETH cable is pluged in again.

    Visitor II
    February 28, 2018
    Posted on February 28, 2018 at 11:14

    Hi,

    I had a similar problem with Ethernet not working properly if the ethernet cable was unplugged on boot.

    The solution was to make sure to call netif_set_up(). This should not be confused with netif_set_link_up(). The problem is that the CubeMX auto generated code only calls 

    netif_set_up() (from MX_LWIP_Init() in lwip.c) if the network link is up...

    In addition to this, you also have to provide a semaphore to the (auto generated) tcp link thread to be able to handle link up/down events in that thread from time to time.

    PS: Also look out for the fact that DHCP is not started properly if dhcp_start() is called when the network interface is not 'up'.

    Explorer
    April 3, 2018
    Posted on April 03, 2018 at 19:08

    That semaphore is probably best posted by an interrupt service routine, triggered by the real PHY MII interrupt signal. Depending on the PHY it might need extra configuration to actually toggle some pin and certainly you need GPIO configuration and extra code for that. The alternative is, as you write, to simply post the semaphore regularly.

    Visitor II
    February 28, 2018
    Posted on February 28, 2018 at 14:50

    Terje has nailed it. This is a persistent bug that should be fixed by ST. That said the other way to fix it locally is with but there are not many places to insert the solution without it getting overwritten by CUBEMX after every change.

    But there is more going on here - Gates is correct about the 

    LWIP_NETIF_LINK_CALLBACK. It is getting defined to 0 in opt.h and is not being overridden by the value lwiopts.h (set by CUBEMX).

    For Cube projects without FreeRTOS, insert the following code into MX_LWIP_Process:

    ethernetif_set_link(&gnetif);

    /* Fix for un-plugged cable on start-up */

    /* If the link is down at start-up, netif_set_up() never gets */

    /* called by MX_LWIP_Init() */

    if (netif_is_link_up(&gnetif) && !netif_is_up(&gnetif)) {

    printf('Setting NETIF_FLAG_UP and restarting DHCP\r\n');

    netif_set_up(&gnetif);

    dhcp_start(&gnetif);

    }

    Still working on cleanest approach for FreeRTOS. 

    Visitor II
    March 1, 2018
    Posted on March 01, 2018 at 14:30

    I started with a new Cube project and the issue with opt.h vs lwipopts.h is gone - Enable 

    LWIP_NETIF_LINK_CALLBACK is enough.

    Add the following the 

    netif_set_up() to ethernetif_notify_conn_changed().

    Terje - can you elaborate on the tcp semaphore you mentioned?

    Visitor II
    March 6, 2018
    Posted on March 06, 2018 at 14:33

    When combining FreeRTOS and LWIP, enabling 

    LWIP_NETIF_LINK_CALLBACK is important, but does not work 'out of the box'.

    The auto generated code adds a 'LinkThr' FreeRTOS Task, with main function 'ethernetif_set_link()' created in ethernetif.c. The eternal loop here only executes the code that actually checks if the network link is up/down and calls  netif_set_link_up()/netif_set_link_down(), if it receives a signal from a semaphore:

    ' if (osSemaphoreWait( link_arg->semaphore, 100)== osOK)'

    And that semaphore was not released anywhere in the code, so I had to make sure that  osSemaphoreRelease() is regularly called with the correct semaphore as an argument, or else the network link status change is not handled ever.

    Visitor II
    October 8, 2018

    Just add ethernetif_set_link(netif_default); to main loop because:

    /**

     * @brief This function sets the netif link status.

     * @note  This function should be included in the main loop to poll 

     *     for the link status update  

     * @param netif: the network interface

     * @retval None

     */

    and handle event in user code:

    void ethernetif_notify_conn_changed(struct netif *netif) {

    if (netif_is_link_up(netif)) {

    netif_set_up(netif);

    } else {

    netif_set_down(netif);

    }

    }

    Visitor II
    September 9, 2019

    Moreover.

    If dhcp is enabled, raw 727 in dhcp.c (LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);) should be comment out, otherwise dhcp will never start.

    Explorer II
    November 12, 2020

    I was looking for this solution also, thank you for the tips. However, there is one thing missing still.

    I am using CubeMX 6.0.1 and firmware STM32Cube FW_F4 V1.25.1.

    Freertos is enabled with V2 api.

    Ethernet settings in Cubemx are:

    auto negotiation enabled, rx mode = interrupt, checksum by hardware. Make sure you PHY register setting correspond to your PHY chip. I have KSZ8081 which required some register setting to be altered from what cubemx offers by default.

    In lwip settings in cubemx, inside key options, enable LWIP_NETIF_LINK_CALLBACK if not yet enabled.

    The cube generated code initially only works when link is up at boot. To fix this, insert the code mentioned earlier in this thread by @ph7​ to cubemx generated weak function ethernetif_notify_conn_changed() inside ethernetif.c. However, this alone wont suffice since this function will not be called at boot. So, add a call to ethernetif_notify_conn_changed() in MX_LWIP_Init() as the last call of the function. Cubemx nicely includes /*user code begin 3*/ for this so it wont be deleted by further cubemx code generations.

    These two additions to the cubemx generated code will remove the bug that Ethernet will not work if the link is not up at reset.

    Furthermore, there is a long delay waiting for the link inside HAL_ETH_Init() due to ETH_TIMEOUT_LINKED_STATE increasing boot time when there is no link. I dont know of any cubemx friendly way to fix this though. I just documented this in my readme file and change it by hand every time cubemx code is generated.

    Visitor II
    November 15, 2020

    Hi,

    I did it but not work,

    My board is Nucleo F4439ZI

    Explorer II
    November 12, 2020

    I also had to fix this bug for a non-os program. Again CubeMX 6.0.1 and firmware STM32Cube FW_F4 V1.25.1.

    No freertos.

    Ethernet settings in Cubemx are:

    auto negotiation enabled, rx mode = polling, checksum by hardware.

    In lwip settings in cubemx, inside key options, enable LWIP_NETIF_LINK_CALLBACK.

    In a polling application you will need to call MX_LWIP_Process() in the mainloop. Inside MX_LWIP_Process() add a call to function ethernetif_set_link(&gnetif), this function should be generated for you by cubemx. I put the call inside /* USER CODE BEGIN 4_3 */ so it's cubemx regeneration safe.

    Now link should work properly even if not available at boot.

    Visitor II
    November 15, 2020
    Mute
    Nimal Lobo