This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Unknown interrupt wakes chip immediately after calling sd_app_evt_wait()

Hi Nordic team,

I'm struggling with an issue where my chip wakes immediately after calling sd_app_evt_wait() - specifically if I call app_timer_start() before the wait for event command.

I'd had this issue for a while which I managed by calling  sd_app_evt_wait() twice in succession - but getting toward the end of my development I'm seeing in some edge use cases this 2x call causes issues.

For reference I'm using an nrf52832 on the nrf52DK - with Softdevice 7.2.0 & SDK 17.0.2.

A simplified version of my code to highlight the issue looks like this:

//main Loop function
while (true){
    printf("Sleep\n");
    
    app_timer_stop(rtc_wake);                                //Stop any running wake timers
    app_timer_start(rtc_wake, APP_TIMER_TICKS(10000), NULL); //Start timer for 10s
    sd_app_evt_wait();                                       //Go to sleep
}      

Ideally the system should wake via app_timer interrupt every 10s and print "Sleep". However I'm seeing it wake immediately and repeatedly print over and over. I've tried checking for and clearing all IRQs for pending interrupts via a for loop before sleep & added code to clear the FPU errata interrupt before sleep, but none of these work.

I've possibly narrowed down the cause to the app_timer_start() - which when successfully started triggers an interrupt which immediately wakes - the loop runs again with app_timer being stopped and successfully started which repeats the process.

This is highlighted even more If I remove the app_timer_stop() - the system wakes once after the 1st app_timer_start() then the next loop's app_timer_start() call is ignored as it's already running & the system sleeps for 10s. However in my full code I'll need to stop any timers before calling so this isn't a solution. 

Could you please advise how to correct to this behaviour.

Many Thanks,

K

  • Hi,

    Ideally the system should wake via app_timer interrupt every 10s and print "Sleep".

    The code you attached is now printing "Sleep" each time an interrupt occurs. You should instead print that in your "rtc_wake" app_timer callback/timeout function, specified when you created the timer with app_timer_create().

    ret_code_t app_timer_create(app_timer_id_t const *      p_timer_id,
                                app_timer_mode_t            mode,
                                app_timer_timeout_handler_t timeout_handler);

    in the end of your timeout function, you start the timer again with app_timer_start().

    Printing as shown in the main-loop snippet you attached, could if you are using UART for the printing, trigger e.g. the UART ENDTX event/interrupt, and cause an "infinite printing-loop".

    I recommend using the power management module for sleeping.

    Snippets:

        // Enter main loop.
        for (;;)
        {
           idle_state_handle();
        }

    /**@brief Function for handling the idle state (main loop).
     *
     * @details If there is no pending log operation, then sleep until next the next event occurs.
     */
    static void idle_state_handle(void)
    {
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();
        }
    }

    void nrf_pwr_mgmt_run(void)
    {
        PWR_MGMT_FPU_SLEEP_PREPARE();
        PWR_MGMT_SLEEP_LOCK_ACQUIRE();
        PWR_MGMT_CPU_USAGE_MONITOR_SECTION_ENTER();
        PWR_MGMT_DEBUG_PIN_SET();
    
        // Wait for an event.
    #ifdef SOFTDEVICE_PRESENT
        if (nrf_sdh_is_enabled())
        {
            ret_code_t ret_code = sd_app_evt_wait();
            ASSERT((ret_code == NRF_SUCCESS) || (ret_code == NRF_ERROR_SOFTDEVICE_NOT_ENABLED));
            UNUSED_VARIABLE(ret_code);
        }
        else
    #endif // SOFTDEVICE_PRESENT
        {
            // Wait for an event.
            __WFE();
            // Clear the internal event register.
            __SEV();
            __WFE();
        }
    
        PWR_MGMT_DEBUG_PIN_CLEAR();
        PWR_MGMT_CPU_USAGE_MONITOR_SECTION_EXIT();
        PWR_MGMT_SLEEP_LOCK_RELEASE();
    }

    With the power management you can e.g. set NRF_PWR_MGMT_CONFIG_DEBUG_PIN_ENABLED / NRF_PWR_MGMT_SLEEP_DEBUG_PIN (in sdk_config.h), to monitor how long your application/CPU is sleeping.

  • Hi Sigurd, Thanks for the quick response.

    The code you attached is now printing "Sleep" each time an interrupt occurs. You should instead print that in your "rtc_wake" app_timer callback/timeout function

    The print function I use is exactly that - an indicator to see when any interrupt occurs & the system wakes.

    in the end of your timeout function, you start the timer again with app_timer_start().

    Unfortunately in my application the timer must be started within the loop as the sleep duration can vary and in the full code, is calculated at the start of the loop. This calculation can't be done in a timeout handler as the variables it uses wont have been updated yet. E.g. consider this example:

    1.Chip wakes from timeout_handler -> 2.Loop: reads sensor val -> 3.calculates when to wake next based on sensor value -> 4.starts timer -> 5.sleeps
     
    The code won't know when to wake next during the timeout handler i.e. step 1. Apologies, I may have simplified the code in my first post too much and this wasn't clear.
    Printing as shown in the main-loop snippet you attached, could if you are using UART for the printing, trigger e.g. the UART ENDTX event/interrupt, and cause an "infinite printing-loop".

    The printing via UART was just an easy indicator I used - & doesn't seem to be the issue. For example, using an LED toggle instead still shows the issue persists. This is also seen when keeping the print command but removing the app_timer_stop command I aforementioned in my 1st post - the system sleeps in the 2nd loop despite the print command.

    I recommend using the power management module for sleeping.

    Thanks for the snippets - I tried sleeping via the power management module as suggested but the issue still persists. 

    I have a feeling the problem may be with app_timer_start(). Are you able to reproduce the issue?

    Thanks in advance.

  • Hi,

    K.Eranda said:
    Are you able to reproduce the issue?

    Yes. I took a closer look. Looks like when you call app_timer_stop()/app_timer_start(), a RTC interrupt is triggered, the request(start/stop) is handled in the RTC interrupt handler. timer_request_proc_trigger(start/stop) --- > interrupt triggers -> rtc_irq -> timer_req_process -> start/stop timer.


    K.Eranda said:

    Unfortunately in my application the timer must be started within the loop as the sleep duration can vary and in the full code, is calculated at the start of the loop. This calculation can't be done in a timeout handler as the variables it uses wont have been updated yet. E.g. consider this example:

    1.Chip wakes from timeout_handler -> 2.Loop: reads sensor val -> 3.calculates when to wake next based on sensor value -> 4.starts timer -> 5.sleeps
     
    The code won't know when to wake next during the timeout handler i.e. step 1

    I assume this sensor does not have any GPIO interrupt line then.

    How about something like this:

    main
    {
    .
    .
    init code
    .
    .
    
    
        // first time pooling rate:
        app_timer_start(rtc_wake, APP_TIMER_TICKS(10), NULL); //wake up in 10ms to check if first sensor reading and variables are ready.
    
        for (;;)
        {
            idle_state_handle();
        }
    }
    
    -------
    
    /**@brief Timeout handler for the rtc_wake timer
     */
    static void rtc_wake_timer_handler(void * p_context)
    {
         1. read sensor ?
         if sensor is not ready,  --> app_timer_start(m_single_shot_timer_id, APP_TIMER_TICKS(10), NULL); //wake up in 10ms again to check if sensor is ready
         if sensor is ready and variables it uses are updated --> app_timer_start(m_single_shot_timer_id, APP_TIMER_TICKS(sensor_reading), NULL) // wake up in x ms based on the variables / sensor readings?
    }

  • Looks like when you call app_timer_stop()/app_timer_start(), a RTC interrupt is triggered

    That's what I expected but couldn't figure out what the interrupt was  - I assumed it was SWI0 based on how the app_timer is implemented: "When calling app_timer_start() or app_timer_stop(), the timer operation is just queued, and the software interrupt is triggered. The actual timer start/stop operation is executed by the SWI0 interrupt handler". However this wasn't showing a pending interrupt when I checked.  

    The 2 questions I have then are:

    1. Is there a way to prevent this behavior without restructuring the rest of my code?
    2. Perhaps waiting for the RTC timer of the app_timer_start to fire before calling the sleep/idle command - is there a flag that is accessible from main.c that shows the "timer_req_process" -> "TIMER_REQ_START" has been fired/successfully created? (Else I can try to make an extern var in app_timer2.c where this interrupt handler is)

    Many thanks

  • @ just following up on the above. Wondering if you or the team have had a chance to take a look. 

Related