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

Going low power in freertos app

Hi, 

I have an app based on freertos_hrs example (nRF52832, SDK14.2)

I'm trying to get it into low power(sleep) mode, however the idle task keep on running and the system consumes ~4.5mA constantly (ble is off most of the time)

I tried the following:

  • Setting #define configUSE_TICKLESS_IDLE   1 in freeRTOSConfig.h but is does not seem to help.
  • Disabled NRF_Log options in sdk_config.h as several posts suggested
  • Verified that none of my app's tasks is running (All are blocked), neither is the timer task. The only task that seem to run is the idle task.
  • Verified that the idle task handler portTASK_FUNCTION() (part of freertos port) do get to the line portSUPPRESS_TICKS_AND_SLEEP() that eventually calls sd_app_evt_wait()

  • I tried to forcefully call sd_app_evt_wait() from within  the applications idle hook (vApplicationIdleHook(), #define configUSE_IDLE_HOOK  1)
    It does lower the consumed power to few uA, but I keep getting hard faults when ble activity is encountered (not sure exactly what is the cause) - e.g. advertising goes well but when connection is established ble_evt_handler() crashes in case BLE_GAP_EVT_CONNECTED block

Reading the doc it seems that implementing the 1st bullet above should take care of lowering the power but apparently something else is not set correctly.

Any help will be highly appreciated

Thanks in advance

Parents
  • which device are you running this on? 

    instead of calling sd_app_evt_wait in vApplicationIdleHook, can you try to call below

                    do{
                        __WFE();
                    } while (0 == (NVIC->ISPR[0] | NVIC->ISPR[1]));
                    

    instead of 

                if (nrf_sdh_is_enabled())
                {
                    uint32_t err_code = sd_app_evt_wait();
                    APP_ERROR_CHECK(err_code);
                }

  • Hi Susheel, 

    Thanks for your response. 

    your 2nd code snippet is in the 'heart' of vPortSuppressTicksAndSleep() in port_cmsys_systick.c which is part of the freertos package (sort of 'system file' in higher OSes)

    Do you mean to replace this in place (vPortSuppressTicksAndSleep()) or add the 1st snippet to vApplicationIdleHook() which is in the application file (main.c)

  • Hi, 

    I followed your advice and placed a breakpoint right after sd_app_evt_wait()

    • __NRF_NVIC_SD_IRQS_0 (nrf_nvic.h) calculated value is : 0x4200F903
    • NVIC->ISPR[0] (@0xE0000100) = 0x02002801 / 0x02000801 

    It seems that all NVIC's bits are SD related (RNG(occasional), RTC0, RADIO & NVMC_IRQ).

    In this case shouldn't the app remain a sleep ?

  • For me it looks like

    0x02002801 are

    0- POWER/CLOCK, 

    11 - RTC0

    13 RNG

    25 SWI5

    all of which should owned by softdevice. 

    I am sorry, there is a flaw in my suggestion, if your application has any higher priority interrupt that is waking your system then our breakpoint here will not work as that interrupt will already have been serviced by the time your breakpoint is hit.

    Can you start by looking at NVIC->ISER[0] bit to see which application interrupts are enabled and start by  disabling those interrupts from your application at the peripheral level. I mean you need to disable that application interrupt inside the peripheral module that is generating that interrupt (NRF_XXX->INTENCLR) and leave it as it is in NVIC. This way we disable the interrupt at the source one by one to see which interrupt is causing your system to wake that frequently.

  • Hi Susheel

    I've done some 'binary search', deactivarting increasing pieces of my code, eventually I've found that the problems lies with the 'ble related' functionality - specifically softdevice_task.

    When the full app is running, the idle task hook is called every ~23us (~43KHz) (its only toggling a pin for debug) even if there's no ble activity (advertising/connection)

    void vApplicationIdleHook( void )
    {
    	static uint32_t cnt = 0;
    	static TickType_t ticks = 0;
    	
    //#if NRF_LOG_ENABLED		
    //        NRF_LOG_FLUSH();
    //#endif	
    //	(void) sd_app_evt_wait();
    //	do{
    //		__WFE();
    //	} while (0 == (NVIC->ISPR[0] | NVIC->ISPR[1]));	
    
    	nrf_gpio_pin_set(BSP_TP1);
    	nrf_gpio_pin_clear(BSP_TP1);
    	cnt++;
    	ticks = xTaskGetTickCount();
    
    }
    

    When 'ble stuff' (below) is left out, the idle task hook does not run at all (app's 4 tasks are blocked and so is the idle task and timer task)

    #define BLE_DISABLE
    #ifndef BLE_DISABLE	
        ble_stack_init();
    #endif
    
        // Initialize modules.
    	buttons_leds_init();
    	
    #ifndef BLE_DISABLE	
    	timers_init();    
        gap_params_init();
        gatt_init();
        services_init();	// services_init() preceeds advertising_init() as it defines vendor speciic uuid that is used in 
        advertising_init();
        conn_params_init();
    
        // Create a FreeRTOS task for the BLE stack.
        // The task will run advertising_start() before entering its loop.
        //nrf_sdh_freertos_init(advertising_start, &erase_bonds);	// don't erase bonds
    	nrf_sdh_freertos_init(NULL, NULL);	// don't start with advertising
    #endif	
    	
        // Start FreeRTOS scheduler.	
        vTaskStartScheduler();
    
        while (true)
        {
            APP_ERROR_HANDLER(NRF_ERROR_FORBIDDEN);
        }
    

    I'm using sdk 14.2 on nRF52832

    What can be the reason for that ?

  • I am guessing that since nothing is expected to happen, the wakeup time is programmed to be large and there is absolutely no activity on your chip when you define BLE_DISABLE and the chip does not wake up from __WFE

  • you are correct.

    When I define BLE_DISABLE idle task hook is not being called (as expected).

    The problem occurs when the BLE stack is initialized - ble_stack_init() and softdevice task is created - nrf_sdh_freertos_init(), then the idle task hook is constantly been called every ~23us....

Reply Children
  • something is weird with application idle hook here, but can you not use application idle hook and toggle the pin after the system wakes up in vPortSuppressTicksAndSleep? 

    make sure you disable application idle hook in FreeRTOSConfig.h file

  • same behavior when I toggle the bit in vPortSuppressTicksAndSleep()...

  • and its getting weirder …
    On my post yesterday I've mentioned a 'binary search'. What I did was to run my app with the ble stuff disabled - idle task hook was not triggered. Than I've re-enabled the ble stuff and the comb in the photo above appeared.

    Today I did the opposite - started with the ble stuff enabled and disabled all the application code. Everything looks fine.

    Than I've started to add my code - first all initialization code (still not creating tasks). Everything still looks OK.
    Now I'm adding the first createTask() statement and the pulse train comes out again.
    I carefully comment out larger and larger sections of the 'offending task' code till I get to a single line in a function that is called as part of the task's initialization (before the while(1) loop)
    This is the "suspicious line":
    thisObj->_.numSamplesInBlock = (uint32_t)(TSAMPLE2SEC(thisObj->cfg->tInterval) * ACC_SAMPLE_RATE_Hz);

    this long expression is merely a floating point calculation casted to uint32.

    I've replaced this line with
    thisObj->_.numSamplesInBlock = 7800;

    and surprisingly (for me :) the system behave nice. No pulse train from the idle task hook !
    Now I start to play around and 'strip down' the complex term above. nothing seems to help.
    Just out of curiosity I added a 'dummy' float calculation -
     float val = (float)300000; 
     val *= (float)1e-3; 
     thisObj->_.numSamplesInBlock = 7800;

    and the 'beast' rises again
    So here I'm standing - a naïve floating point calculation in the initialization code in one of my tasks (I have ton of places with floating calculation) seem to drive the system crazy.
    Furthermore, I've deleted the 2 offending lines (float val ..& val *= ...) and copied them to main.c , just before nrf_sdh_freertos_init() and the system behaves nicely (no pulses)
    Now I'm not sure if this plethora of information is helpful or not, but I'm scratching my head (or better said banging).
    It has been a long post. phew...
    btw, same behavior when pulses are driven from vPortSuppressTicksAndSleep()
  • This seems to be due to the fact that we did not handle any floating point exceptions.

    We can do one experiment

    Can you implement the FPU_IRQHandler and see if the power consumption goes low. Add below to your code and make sure that your vector table has FPU_IRQHandler in its FPU IRQ address

    void FPU_IRQHandler(void)
    {
        uint32_t *fpscr = (uint32_t *)(FPU->FPCAR+0x40);
        (void)__get_FPSCR();
    
        *fpscr = *fpscr & ~(FPU_EXCEPTION_MASK);
    }

  • Hi Susheel, 

    Do you feel that this may be the problem ?

    I can see that PWR_MGMT_FPU_SLEEP_PREPARE macro (that seems to workaround PAN87) is called in nrf_pwr_mgmt_run() just before sd_app_evt_wait() in 'standard' app but NOT in freertos based app....

    Will update as soon as I get back to the office...

    Thanks

    btw, 

    I feel silly to ask, but how do I register an ISR? (so it appears in the vector table)

    I only used event handlers in Nordic SDK so far. 

Related