cancel
Showing results for 
Search instead for 
Did you mean: 

Stm32 + bare-metal + lwip + interrupt

SoFa
Associate II

Has anyone managed to use the lwIP Stack without an RTOS but still using it in an interrupt manner?

As it is not allowed to choose interrupt mode while not configuring freeRTOS in CubeMX I assume that it is not possible using it that way.

Thanks a lot for your answers.

5 REPLIES 5
Piranha
Chief II

I don't know about CubeMX code, but lwIP in itself definitely can be used without any RTOS and some time ago I've done it successfully with older 1.4.1 version. I suggest to start with reading this:

https://www.nongnu.org/lwip/2_1_x/pitfalls.html

Also there is some old and now partly incorrect, but to some extent still useful information:

http://lwip.wikia.com/wiki/Porting_For_Bare_Metal

Thank you for the informations.

For me it seems like without an RTOS you can just use the Stack by providing a polling mechanism in the mainloop.

Is is correct that the critical part is to provide a "time base" to the stack by calling sys_check_timeouts() function periodically?

I try to figure out if i can check the packages in the interrupt routine, but after some time when i try to ping the interrupt doesn't occur anymore.

Piranha
Chief II

I would advise against calling lwIP functions from interrupt routine. Much better and simpler solution is to set up some flag or notification mechanism. Inside interrupt You only set this flag. Then in main code You check the flag and, if it's set, clear it and read the frames from ETH peripheral and pass them to lwIP. It isn't polling because You do this only when the flag is set. But even better solution is to make some simple cooperative scheduler, which can put CPU into sleep, when there is nothing to process. And remember - when You receive a "new frames received" interrupt, You must read all frames not just one. This is a very common bug in many examples.

Yes, You must implement sys_now() and call sys_check_timeouts() periodically (250 ms is enough). You can also call it after each processing of frames, but that is not the best resource usage, although not very bad either.

Bob S
Principal

Look at AN3976 and STSW-SRM32070. This is bare-metal for the 32F4x7 family from 2013, so it uses a pretty old LwIP release, but it will give you an idea. I used this as the basis for a product several years ago. I think the LwIP had a major version change from the version used in this software that changed the way programs interact with the LwIP code, so it may be a simple port to a current LwIP.

https://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32070.html

As I recall, they had the interrupts handling packet rx/tx to/from the phy, and foreground polling to actually run the TCP and/or USP state machines to process the packets.

Thank you a lot for the provided solution, i will try it out. Can you explain why it is not the best resource usage to call it each time after a package receive?

Currently I'm using the Cube-generated ethernetif.c, which uses 4 forward chained DMA descriptors, each pointing to a buffer of lenght maximum package size. I'm not sure if i understand their code in low_level_init correctly, but it seems like they go through the descriptors that point to buffers with data, but not necessarily through all 4 descriptors. For me that seems correct, or is it necessary to read all 4 descriptors?

len = heth.RxFrameInfos.length;
  buffer = (uint8_t*) heth.RxFrameInfos.buffer;
 
  if (len > 0) {
    /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
  }
 
  if (p != NULL) {
    dmarxdesc = heth.RxFrameInfos.FSRxDesc;
    bufferoffset = 0;
    for (q = p; q != NULL; q = q->next) {
      byteslefttocopy = q->len;
      payloadoffset = 0;
 
      /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
      while ((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE) {
        /* Copy data to pbuf */
        memcpy((uint8_t*) ((uint8_t*) q->payload + payloadoffset), (uint8_t*) ((uint8_t*) buffer + bufferoffset),
               (ETH_RX_BUF_SIZE - bufferoffset));
 
        /* Point to next descriptor */
        dmarxdesc = (ETH_DMADescTypeDef*) (dmarxdesc->Buffer2NextDescAddr);
        buffer = (uint8_t*) (dmarxdesc->Buffer1Addr);
 
        byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
        payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
        bufferoffset = 0;
      }
      /* Copy remaining data in pbuf */
      memcpy((uint8_t*) ((uint8_t*) q->payload + payloadoffset), (uint8_t*) ((uint8_t*) buffer + bufferoffset),
             byteslefttocopy);
      bufferoffset = bufferoffset + byteslefttocopy;
    }
  }