Valentin

Some FreeRTOS helper tools

Discussion created by Valentin on Jul 12, 2017
Latest reply on Jul 13, 2017 by Valentin

Hi,

 

I would like to share some code I am now regularly using in my FreeRTOS projects. Hopefully, that may be of help for someone else. I usually have these functions sitting in my main.cpp and inISR() in main.h header declared as extern.

 

inline uint8_t inISR(void) {
     return __get_IPSR() != 0 ? 1 : 0;
}

/**
* FreeRTOS malloc, free, calloc, realloc hooks
*/

void *pvPortCalloc(size_t nelem, size_t elsize);
void *malloc(size_t size) {
     void* p = NULL;
     if (inISR())
          taskENTER_CRITICAL_FROM_ISR();
     else
          taskENTER_CRITICAL();
     p = (pvPortMalloc(size));
     if (inISR())
          taskEXIT_CRITICAL_FROM_ISR(NULL);
     else
          taskEXIT_CRITICAL();
     return p;
}

void *realloc(void *ptr, size_t size) {
     assert_param(!inISR());
     return (pvPortRealloc(ptr, size));
}

void *calloc(size_t nelem, size_t elsize) {
     assert_param(!inISR());
     return (pvPortCalloc(nelem, elsize));
}

void free(void* ptr) {
     if (ptr == NULL)
          return;
     if (inISR())
          taskENTER_CRITICAL_FROM_ISR();
     else
          taskENTER_CRITICAL();
     vPortFree(ptr);
     if (inISR())
          taskEXIT_CRITICAL_FROM_ISR(NULL);
     else
          taskEXIT_CRITICAL();
}

void *pvPortCalloc(size_t nelem, size_t elsize) {
     assert_param(!inISR());
     void *pvReturn = NULL;
     size_t len;
     len = nelem * elsize;
     pvReturn = pvPortMalloc(len);
     if (pvReturn != NULL)
          memset(pvReturn, 0x00, len);
     return (pvReturn);
}

void HAL_Delay(__IO uint32_t pms) {
     const uint32_t ms = pms;
     if (inISR()) {
          uint32_t stop = HAL_GetTick() + ms;
          while (HAL_GetTick() < stop)
               ;
     } else {
          vTaskDelay(ms);
     }
}

and in heap4.c:

 

#include <string.h>

[...]

void *pvPortRealloc(void *pv, size_t xWantedSize) {
     void *pvReturn = NULL;
     BlockLink_t *pxLink;
     unsigned char *puc = (unsigned char *) pv;
     size_t len;
     if ((pv != NULL) && (xWantedSize > 0)) {
          /* The memory will have an xBlockLink structure immediately
           before it. */

          puc -= xHeapStructSize;
          /* This casting is to keep the compiler from issuing warnings. */
          pxLink = (void *) puc;
          pvReturn = pvPortMalloc(xWantedSize);
          if (pvReturn) {
               /* check if realloc uses smaller or larger area now */
               len = pxLink->xBlockSize - xHeapStructSize;
               if (len > xWantedSize)
                    len = xWantedSize;
               memcpy(pvReturn, pv, len);
               vPortFree(pv);
          } else {
               /* keep old object unchanged */
          }
     }
     return (pvReturn);
}

 

Explanation:

inISR() determines whether the current execution happens inside an interrupt-service routine or not. This comes in very handy when using FreeRTOS methods that may only be used inside or outside of ISRs.

 

The other functions overload the memory handling to make all malloc/free/etc calls FreeRTOS compatible (using heap4.c) - and therefore also allow correct C++ new usage. I collected them from various sources and cannot guarantee that they're 100% correct but for me it's all working fine.

Suggestions for improvements are welcome!

 

-> Plug&Play, enjoy

 

Addition:

One extra tweak you can do to avoid the if(inISR()) stuff for enter/exit criticals is this:

port.c

void vPortEnterCritical( void )
{
     if(inISR()){
          taskENTER_CRITICAL_FROM_ISR();
          return;
     }
     portDISABLE_INTERRUPTS();
     uxCriticalNesting++;
     __asm volatile( "dsb" );
     __asm volatile( "isb" );

     /* This is not the interrupt safe version of the enter critical function so
     assert() if it is being called from an interrupt context.  Only API
     functions that end in "FromISR" can be used in an interrupt.  Only assert if
     the critical nesting count is 1 to protect against recursive calls if the
     assert function also uses a critical section. */

     if( uxCriticalNesting == 1 )
     {
          configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
     }
}
/*-----------------------------------------------------------*/

void vPortExitCritical( void )
{
     if(inISR()){
          taskEXIT_CRITICAL_FROM_ISR(NULL);
          return;
     }
     configASSERT( uxCriticalNesting );
     uxCriticalNesting--;
     if( uxCriticalNesting == 0 )
     {
          portENABLE_INTERRUPTS();
     }
}
/*-----------------------------------------------------------*/

 

The NULL in exitCritical simply means that there won't be any yield even if a different task has become active.

Now you can simply use EnterCritical() all the time and don't need to worry anymore.

Outcomes