Use Cases

Bare-metal “allow locks”: libC functions called in Thread and Handler mode

This use case allows libc functions to be safely called from both the main context and interrupt context. It requires:

  • stm32_lock.h and the lock glue file matching the selected compiler (GCC: GCC/newlib_lock_glue.c, ARMCC: ARM/armlib_lock_glue.c, IAR: IAR/dlib_lock_glue.c).

  • STM32_THREAD_SAFE_BAREMETAL_ALLOW_LOCKS=1 defined in the build configuration.

Example ( malloc / free from ISR and main):

/* Allocation size (bytes) */
#define ALLOCATE_SIZE 100

/* HAL tick hook (called from TIM6 ISR): toggles malloc/free in ISR context */
void HAL_IncTick(void)
{
  static uint32_t *address = NULL;

  if (address == NULL)
  {
    address = (uint32_t *)malloc(ALLOCATE_SIZE);
    if (address == NULL)
    {
      Error_Handler();
    }
  }
  else
  {
    free(address);
    address = NULL;
  }
}

/* Main context: toggles malloc/free in an infinite loop */
void app_allocatefree_task_entry(void)
{
  static uint32_t *address_task = NULL;

  for (;;)
  {
    if (address_task == NULL)
    {
      address_task = (uint32_t *)malloc(ALLOCATE_SIZE);
      if (address_task == NULL)
      {
        Error_Handler();
      }
    }
    else
    {
      free(address_task);
      address_task = NULL;
    }
  }
}

FreeRTOS “allow locks”: libc functions called in tasks (Thread mode) and Handler mode

This use case protects libc calls across tasks and allows ISR usage within critical sections using taskENTER_CRITICAL / taskENTER_CRITICAL_FROM_ISR. ISR sections must remain short. It requires:

  • stm32_lock.h and the lock glue file matching the selected compiler (GCC: GCC/newlib_lock_glue.c, ARMCC: ARM/armlib_lock_glue.c, IAR: IAR/dlib_lock_glue.c).

  • STM32_THREAD_SAFE_FREERTOS_ALLOW_LOCKS=1 and configUSE_NEWLIB_REENTRANT=1 defined.

Implication: standard libc calls across tasks are protected; ISR usage is allowed within critical sections, but high-priority interrupts may not be masked.

Example (tasks allocate/free, HAL tick toggles malloc in ISR):

/* Sizes and limits */
#define ALLOCATE_SIZE          100
#define MAXENTRY_ALLOCATE      20
#define MEM_GUARD              0x55

/* Optional protection (simple mutex) for shared data access */
static SemaphoreHandle_t mutex;
#define PROTECT_INIT()  do { mutex = xSemaphoreCreateMutex(); } while (0)
#define PROTECT_ENTER() do { xSemaphoreTake(mutex, portMAX_DELAY); } while (0)
#define PROTECT_EXIT()  do { xSemaphoreGive(mutex); } while (0)

/* Shared table */
static volatile uint32_t allocate_table[MAXENTRY_ALLOCATE];

/* HAL tick ISR: toggle malloc/free with a small bound */
void HAL_IncTick(void)
{
  static uint32_t *p = NULL;

  if (p == NULL)
  {
    p = (uint32_t *)malloc(ALLOCATE_SIZE);
    if (p == NULL)
    {
      Error_Handler();
    }
  }
  else
  {
    free(p);
    p = NULL;
  }
}

/* Allocate task: fills entries */
static void app_allocate_task(void *arg)
{
  (void)arg;

  for (;;)
  {
    for (uint32_t i = 0; i < MAXENTRY_ALLOCATE; i++)
    {
      uint32_t *addr;

      PROTECT_ENTER();
      addr = (uint32_t *)allocate_table[i];
      PROTECT_EXIT();

      if (addr == NULL)
      {
        addr = (uint32_t *)malloc(ALLOCATE_SIZE);
        if (addr == NULL)
        {
          Error_Handler();
        }

        *addr = MEM_GUARD; /* Touch. */

        PROTECT_ENTER();
        allocate_table[i] = (uint32_t)addr;
        PROTECT_EXIT();
      }
    }
  }
}

/* Free task: drains entries */
static void app_free_task(void *arg)
{
  (void)arg;

  for (;;)
  {
    for (int i = (int)MAXENTRY_ALLOCATE - 1; i >= 0; i--)
    {
      uint32_t *addr;

      PROTECT_ENTER();
      addr = (uint32_t *)allocate_table[i];
      allocate_table[i] = 0;
      PROTECT_EXIT();

      free(addr);
    }

    vTaskDelay(2);
  }
}