Search code examples
stm32microcontrollerfreertoscmsis

STM32 FreeRTOS give binary semaphore from ISR not working


I've generated a RTOS-cmsis_v1 project for stm32f103 using stm32cubemx and defined four tasks as below:

  osThreadDef(defaultTask, StartDefaultTask, osPriorityLow, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* definition and creation of normal */
  osThreadDef(normal, StartTask02, osPriorityNormal, 0, 128);
  normalHandle = osThreadCreate(osThread(normal), NULL);

  /* definition and creation of high */
  osThreadDef(high, StartTask03, osPriorityAboveNormal, 0, 128);
  highHandle = osThreadCreate(osThread(high), NULL);

  /* definition and creation of low */
  osThreadDef(low, StartTask04, osPriorityBelowNormal, 0, 128);
  lowHandle = osThreadCreate(osThread(low), NULL);

a binary semaphore:

  osSemaphoreDef(binsem);
  binsemHandle = osSemaphoreCreate(osSemaphore(binsem), 1);

and a timer which generates an interrupt every 1 second. Task functions and interrupt service routine:

void StartTask02(void const * argument)
{
  for (;;) {
    sprintf(buffer, "normal waiting for semaphore\r\n");
    printout();
    if (xSemaphoreTake(binsemHandle, portMAX_DELAY)) {
      sprintf(buffer, "normal begin\r\n");
      printout();

      sprintf(buffer, "normal end\r\n");
      printout();
    }

    osDelay(1000);
  }
}

void StartTask03(void const * argument)
{
  for (;;) {
    sprintf(buffer, "high begin\r\n");
    printout();

    sprintf(buffer, "high end\r\n");
    printout();
    osDelay(1000);
  }
}

void StartTask04(void const * argument)
{
  for (;;) {
    sprintf(buffer, "low begin\r\n");
    printout();

    sprintf(buffer, "low end\r\n\r\n");
    printout();

    osDelay(1000);
  }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

    if (htim->Instance == TIM1) {
    static BaseType_t xHigherPriorityTaskWoken;
    xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(binsemHandle, &xHigherPriorityTaskWoken);
    if (xHigherPriorityTaskWoken) {
      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
  }

  
  if (htim->Instance == TIM4) {
    HAL_IncTick();
  }
}

The problem is when I run the code, the interrupt executes only once and none of the task are running. If I comment out the xSemaphoreGiveFromISR(binsemHandle, NULL); all tasks (except normal, because of the semaphore check) run and also interrupt gets activated every second. TIM4 is used as time base and the default priority of TIM1 interrupt was 14. Based on this solution I checked the value of configKERNEL_INTERRUPT_PRIORITY and it's defined as 15 << 4. And also based on this solution, the value of configMAX_SYSCALL_INTERRUPT_PRIORITY is set to 5 << 4. But changing the priority level of TIM1 interrupt to 15, 240, 255, and etc didn't help either.

Giving and taking semaphores between tasks has no problem.

Also I tested using xSemaphoreGiveFromISR in TIM1_IRQ_Handler() function instead of the callback, but no luck. Using xTaskNotifyGive instead of semaphore didn't work either.

Edit

So I set my timer interrupt priority to 7 and checked it using configASSERT() and no errors. Printing the value of the register also gives the value 112 which is 7<<4.

  /* Initialize interrupts */
  //MX_NVIC_Init();
  /* USER CODE BEGIN 2 */
  NVIC_SetPriority(TIM1_IRQn, 7);
  NVIC_EnableIRQ(TIM1_IRQn);
  sprintf(buffer, "%u\r\n", NVIC->IP[TIM1_IRQn]);
  printout();
  configASSERT(NVIC->IP[TIM1_IRQn] == 112);

and here are FreeRTOSconfig.h file definitions for kernel and max priority values:

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY   15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

and according to this page, and the diagram below Example interrupt priority configuration

the timer should be able to call freertos API functions from ISR.

When commenting out the xSemaphoreGiveFromISR() in the ISR callback function, all tasks and the interrupt work. But un-commenting it results in a crash, which means the timer interrupt is not allowed to call freertos API functions fro ISR.

WHAT AM I MISSING HERE??


Solution

  • OK! So the problem was that I was enabling the timer interrupt BEFORE starting the scheduler in the main.c file:

      HAL_TIM_Base_Start_IT(&htim1);
    
      /* Call init function for freertos objects (in freertos.c) */
      MX_FREERTOS_Init();
    
      /* Start scheduler */
      osKernelStart();
    

    So after moving the HAL_TIM_Base_Start_IT(&htim1); function to freertos.c and putting it AFTER the semaphore definition, everything works fine (even printing from ISR).

      osSemaphoreDef(binsem);
      binsemHandle = osSemaphoreCreate(osSemaphore(binsem), 1);
    
      HAL_TIM_Base_Start_IT(&htim1);
    

    I came across some document mentioning this in freeRTOS documentation but I didn't pay attentions to it and now I can't find where it was. I'll add a link to it here if I found the document.