cancel
Showing results for 
Search instead for 
Did you mean: 

STM32/LwIP/FreeRTOS on Nucleo : how to handle ethernet link status change ?

Aurelien Robert
Associate III

Hello,

I'm developping a demonstrator with a shield I made to fit a Nucleo 144 - F207ZG board. My application uses ethernet link (TCP, UDP client and server) and FreeRTOS. Base project is generated from CubeMX and I work under Atollic True Studio. This is my first projet with STM32, FreeRTOS and LwIP.

My application works fine, but the ethernet low level management is pretty basic (generated by CubeMX) : wait for 5 seconds to get a link up, then run app. If link is down at startup and becomes up later, the driver does not get the updated information and the system remains down. If link is up at start and application runs correctly, I'm not informed by low level driver of link loss.

I looked at code examples for EVAL boards and in this case this is handled by an additionnal task because the PHY has an INT signal connected to the STM32, that triggers the link check routines. But on the NUCLEO144 boards, using a low cost PHY (LAN8742) the INT signal is not connected and used a clock signal to synchronise PHY and MAC. So I'm not able to use that kind of warning to handle link status changes.

The only solution I see would be to check periodically the PHY internal STATUS register to check link status, but I'm not confident enough to modify the driver and perform checks without inferering with existing running driver task. However, my idea is to create a task with priority higher than ethernet task, executed periodically :

 - if link is down, check the PHY status register, and if link is now up, restart the init sequence and create all the threads required by my application using ethernet

 - if link is up, check if the heth->State is equal to HAL_ETH_STATE_READY, and if ready, check the PHY register to see if link is still up. If link down, call the function that disables the MAC, and stop the high level ethernet tasks. But I'm afraid of interfering with ETH interrupt. What happens if I'm accessing the PHY with my test task, and an IT triggers with received data to handle ?

Would that be reliable ? Does someone has some tips to give to me, or have better implementations of low level ethernet management with freertos/lwip ?

Would it be more reliable if I get the functions related to link state change management from an EVAL demo, and I just modify the trigger of the call to be not an interrupt, but a periodic delay (ie every second) ?

Moreover, I'm surprised that the init function is so long for initializing. In stm32f2xx_hal_eth.c, each step is limited by a timeout, for example 5 seconds to get the link (ETH_TIMEOUT_LINKED_STATE), and this is defined inside the file itself, not a configurable feature without modifying the driver. This is a blocking execution ; when running at startup it's not really a problem, but if running from a thread (when app needs to reinit MAC after link switching from down to up), that does not give the hand to lower priority task until operations are finished. That means that this task should be executed with the lowest priority, am I right ?

Thanks

Aurelien

2 REPLIES 2
Tomasz Zeman
Associate II

If you look into the HAL_ETH_IRQHandler (I used as example F4 series) you will see that there is not use of MII bus. The IRQ handler in stm32fxxx_hal_eth.c only checks DMA flags, so if there is no code using the MII bus - if the IRQ user callbacks do not use it. You can use another thread probing the Basic Status Register in PHY to see the link status and trigger correct action.

I used low priority ETH controlling thread with state machine switching processing between NO_ETHERNET / STATIC IP / DHCP + PHY link status check - configurable e.g from BLE etc.

On the ETH IRQ, when data present you can signal semaphore to wake-up another task, ETH incoming data thread, that will empty quickly the ETH DMA descriptors.

Because the the ETH controlling thread mostly sleeps, it does not disturb the ETH incoming data thread.

In even simpler configuration you can use single thread blocking for some time on a semaphore and ETH IRQ to wake it up for data processing.

I am not sure about other STMCube examples, but in the STM32H7 ST engineers finally came up with elegant Ethernet PHY handling and LwIP synchronisation.

They defined the result from GetLinkState using the following defines:

// LAN_CHIP Status

#define LAN_CHIP_STATUS_READ_ERROR      ((int32_t)-5)

#define LAN_CHIP_STATUS_WRITE_ERROR      ((int32_t)-4)

#define LAN_CHIP_STATUS_ADDRESS_ERROR     ((int32_t)-3)

#define LAN_CHIP_STATUS_RESET_TIMEOUT     ((int32_t)-2)

#define LAN_CHIP_STATUS_ERROR         ((int32_t)-1)

#define LAN_CHIP_STATUS_OK          ((int32_t) 0)

#define LAN_CHIP_STATUS_LINK_DOWN       ((int32_t) 1)

#define LAN_CHIP_STATUS_100MBITS_FULLDUPLEX  ((int32_t) 2)

#define LAN_CHIP_STATUS_100MBITS_HALFDUPLEX  ((int32_t) 3)

#define LAN_CHIP_STATUS_10MBITS_FULLDUPLEX  ((int32_t) 4)

#define LAN_CHIP_STATUS_10MBITS_HALFDUPLEX  ((int32_t) 5)

#define LAN_CHIP_STATUS_AUTONEGO_NOTDONE   ((int32_t) 6)

Then you check periodically:

PHYLinkState is result from PHY query (look into code for complete listing, one of the above enums)

if (netif_is_link_up(netif) && (PHYLinkState <= LAN_CHIP_STATUS_LINK_DOWN))

{

HAL_ETH_Stop(&EthHandle);

netif_set_down(&gnetif);

netif_set_link_down(&gnetif);

}

else if (!netif_is_link_up(netif) && (PHYLinkState > LAN_CHIP_STATUS_LINK_DOWN))

{

// Here you have

// LAN_CHIP_STATUS_100MBITS_FULLDUPLEX

// LAN_CHIP_STATUS_100MBITS_HALFDUPLEX

// LAN_CHIP_STATUS_10MBITS_FULLDUPLEX

// LAN_CHIP_STATUS_10MBITS_HALFDUPLEX

// Update here Ethernet MAC with Duplex and Speed flags

// to recalculate MAC clocks etc. and synchronise with PHY.

netif_set_up(netif);

netif_set_link_up(netif);

}

As far as I can see the network interface will always be correctly updated from the point of view of link status and auto negotiation in PHY, if enabled.

Aurelien Robert
Associate III

Hello,

Thank you very much for your answer, sorry for late reply, I had to work on another project and I'm just getting back to the initial project today 🙂

I read your answer and I continued analysing the code from the F207_EVAL board that uses another PHY and perform automatic link change detection through an external pin change ISR.

All I need to do is :

 - enabling LWIP_NETIF_LINK_CALLBACK label to 1 in lwiopts.h, to get access to function ethernetif_update_config() called by netif_set_link_up() and netif_set_link_down() after callback linking performed with following line :

 - in Netif_config(), adding the line netif_set_link_callback(&gnetif, ethernetif_update_config)

 - and in my general management task (that perform the init and creates all the others tasks, and then is only woke up every 250ms to make a led blink), I checks periodically the PHY status register :

 while (1)

 {

  // Toggle running state LED

  osDelay(250);

  HAL_GPIO_TogglePin(GPIOB, LD2_Pin);

  // Notify user about the network interface config

  User_notification(&gnetif);

  // Read PHY link status

  if (HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, &regvalue) == HAL_OK)

  {

   if((regvalue & PHY_LINKED_STATUS)== (uint16_t)RESET)

   {

    // Link status = disconnected

    if (netif_is_link_up(&gnetif))

    {

     netif_set_down(&gnetif);

     netif_set_link_down(&gnetif);

    }

   }

   else

   {

    // Link status = connected

    if (!netif_is_link_up(&gnetif))

    {

     netif_set_up(&gnetif);

     netif_set_link_up(&gnetif);

    }

   }

  }

 }

And that worked at the first try 🙂 I suppressed your HAL_ETH_Stop() call because it is already included in ethernetif_update_config(), called by netif_set_down().

I'm just wondering if that way of management is safe.

Does the priority level of that global management task (in which I'm accessing the PHY register) is important ? I don't care if the 250ms timing is respected or not, this is not critic. But since I'm not very confident with the full low level stack, I want to be sure that my periodic readings to PHY register are safe and will not interfere with other operations that could be performed by others tasks, or automatic transfers that may occurs between PHY and MAC...

When I look at the code provided for F207_EVAL board, I don't see anything more than I have already done above, but if somebody could ensure me of that...

Thank you