Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Does FreeRTOS functionality, such as Semaphores work without tasks?

Hello, I'm getting started with FreeRTOS in Nordic SDK 15.3

I have created a small test code, using binary Semaphore from FreeRTOS, that waits signal from SAADC callback. I also use SDK timer module to setup continuous ADC sampling via PPI

However, I found that the code stucks in PendSV handler, sometimes from Semaphore wait function sometimes earlier.(see pictures). Used same config as in blinky freertos example.

My question is should I use it inside tasks only, or it actually should work.

int main(void)
{
    (void)NRF_LOG_INIT(NULL);
    NRF_LOG_DEFAULT_BACKENDS_INIT();
    // spim_init();
    //
    // ext_flash_init();
    drv_saadc_init();
    comp_battery_init();

    drv_saadc_sampling_start();

    start_up_reason();

    NRF_LOG_INFO("\r\nBAt example started.\r\n");

    while (true)
    {
        nrf_delay_ms(1000);
        (void)comp_battery_get_soc();
    }
}

void comp_battery_init(void)
{
    // Configure BATTERY_ENABLE_PIN and CHARGE_CONTROL_PIN
    nrf_gpio_cfg_output(BATTERY_ENABLE_PIN);
    nrf_gpio_cfg_output(BAT_ADC_EN);

    /* Create a binary semaphore without using any dynamic memory
       allocation.  The semaphore's data structures will be saved into
       the xSemaphoreBuffer variable. */
    bat_semaphore = xSemaphoreCreateBinaryStatic(&bat_SemaphoreBuffer);

    /* The pxSemaphoreBuffer was not NULL, so it is expected that the
    handle will not be NULL. */
    configASSERT(bat_semaphore);

    drv_saadc_register_user_cb(ADC_4V2, battery_adc_cb);

    battery_connection(CONNECTED);
}


uint8_t comp_battery_get_soc(void)
{
    uint16_t soc_mapped = 0;
    static uint32_t soc_sum = 0;
    static int32_t bat_vol_prev = 0;

    if (xSemaphoreTake(bat_semaphore, (TickType_t) 100) == pdTRUE)
    {
        /* SOC code here
    }
    else
    {
        NRF_LOG_DEBUG("FAILED TO GET SEMAPHORE");
    }
    
    return bat_soc;
}


/*************************************************************************************
  * @brief  Briefly explain what function does
  * @param  Describe input params and their limitations
  * @retval Describe return param
**************************************************************************************/
static void battery_adc_cb(int32_t adc_converted_results)
{
    static BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    xHigherPriorityTaskWoken = pdFALSE;

    /* Unblock the task by releasing the semaphore. */
    xSemaphoreGiveFromISR(bat_semaphore, &xHigherPriorityTaskWoken );

    current_battery_voltage = (uint16_t )adc_converted_results;
}

  • Semaphore wait blocks the tasks they are called in if the semaphore is not available. So I presume, the design is to call them from inside the tasks. There is no point in calling it from Main Thread and the documentation  says that you should not use this API from ISR (xSemaphoreTakeFromISR should be ok from ISR)

  • Hi Susheel,

    It seems that Semaphore object is separated from task, so theoretical it seems it should be possible to use separately. The idea was just to test it's functionality. When xSemaphoreTake is called, it should be waiting from signal from ADC callback that come from ISR. xSemaphoreTake is called from regular function(not ISR), in this example.

  • OK, I have not tested this outside the task, but when it comes to a waiting object, I strongly assumed that the kernel will move the currently running task out of the runnable queue and hence that wait is not relevant to any other context apart from RTOS task. I started my holiday and would want to have verified this myself. But I believe you have tested this in Thread mode and the wait is still happening? I am not certain what kind of queue handling the kernel does if it sees that the current context is neither a task nor an ISR.

    However, I found that the code stucks in PendSV handler, sometimes from Semaphore wait function sometimes earlier.(see pictures).

     This sounds like something happened at the time scheduler is triggered. Why not just move this functionality into another task? I normally do not want to do anything in the main thread after the scheduler is started. Not saying that using the main thread is a bad design, but saying that just keep it simple to tasks and ISR.

Related