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

All interrupt seems to be triggered twice ?

Hi,

I noticed at least using pwr_mgmt we are calling two times nrf_pwr_mgmt_run for every interrupts !
All interrupt seems to be triggered twice ! Or wakes from idle state 2 times !
Why ?

In my case, it is the case for every app_timer interrupt, IO port interrupt, and every SPI interrupt. With not any concurrent interrupt.

I have done a test starting with pwr_mgmt example of SDK 17.0.2.
Main file is modified to :

/**
 * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <stdbool.h>
#include <stdint.h>
#include "boards.h"
#include "bsp.h"
#include "bsp_nfc.h"
#include "app_timer.h"
#include "nordic_common.h"
#include "app_error.h"
#include "nrf_drv_clock.h"
#include "nrf_pwr_mgmt.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
#include "app_scheduler.h"
#define APP_SCHED_MAX_EVENT_SIZE    0   /**< Maximum size of scheduler events. */
#define APP_SCHED_QUEUE_SIZE        4   /**< Maximum number of events in the scheduler queue. */
#endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER

#define BTN_ID_READY                0   /**< ID of the button used to change the readiness to sleep. */
#define BTN_ID_SLEEP                1   /**< ID of the button used to put the application into sleep/system OFF mode. */
#define BTN_ID_WAKEUP               1   /**< ID of the button used to wake up the application. */
#define BTN_ID_RESET                2   /**< ID of the button used to reset the application. */

static volatile bool m_stay_in_sysoff;  /**< True if the application should stay in system OFF mode. */
static volatile bool m_is_ready;        /**< True if the application is ready to enter sleep/system OFF mode. */
static volatile bool m_sysoff_started;  /**< True if the application started sleep preparation. */

/**@brief Handler for shutdown preparation.
 */
bool shutdown_handler(nrf_pwr_mgmt_evt_t event)
{
    uint32_t err_code;

    if (m_is_ready == false)
    {
        m_sysoff_started = true;
        return false;
    }

    switch (event)
    {
        case NRF_PWR_MGMT_EVT_PREPARE_SYSOFF:
            NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_SYSOFF");
            err_code = bsp_buttons_disable();
            APP_ERROR_CHECK(err_code);
            break;

        case NRF_PWR_MGMT_EVT_PREPARE_WAKEUP:
            NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_WAKEUP");
            err_code = bsp_buttons_disable();
            // Suppress NRF_ERROR_NOT_SUPPORTED return code.
            UNUSED_VARIABLE(err_code);

            err_code = bsp_wakeup_button_enable(BTN_ID_WAKEUP);
            // Suppress NRF_ERROR_NOT_SUPPORTED return code.
            UNUSED_VARIABLE(err_code);

            err_code = bsp_nfc_sleep_mode_prepare();
            // Suppress NRF_ERROR_NOT_SUPPORTED return code.
            UNUSED_VARIABLE(err_code);
            break;

        case NRF_PWR_MGMT_EVT_PREPARE_DFU:
            NRF_LOG_ERROR("Entering DFU is not supported by this example.");
            APP_ERROR_HANDLER(NRF_ERROR_API_NOT_IMPLEMENTED);
            break;

        case NRF_PWR_MGMT_EVT_PREPARE_RESET:
            NRF_LOG_INFO("NRF_PWR_MGMT_EVT_PREPARE_RESET");
            break;
    }

    err_code = app_timer_stop_all();
    APP_ERROR_CHECK(err_code);

    return true;
}

/**@brief Register application shutdown handler with priority 0. */
NRF_PWR_MGMT_HANDLER_REGISTER(shutdown_handler, 0);

/**@brief Function for initializing low-frequency clock.
 */
static void lfclk_config(void)
{
    ret_code_t err_code = nrf_drv_clock_init();
    APP_ERROR_CHECK(err_code);

    nrf_drv_clock_lfclk_request(NULL);
}


static uint32_t sleep_count=0;

void timer_handler(void * p_context)
{	
	static uint32_t timer_handler_count=0;
	
	timer_handler_count++;
	if (timer_handler_count%100==0) {
		NRF_LOG_INFO("sleep_count / timer_handler_count = %u%%", (100 * sleep_count / timer_handler_count) );
		timer_handler_count=0;
		sleep_count=0;
	}	
}

APP_TIMER_DEF(m_app_timer_id);                   /**< measurement timer. */
/**
 * @brief Function for application main entry.
 */
int main(void)
{
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("Power Management example");

	// init timers module
    lfclk_config();

    uint32_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);
	
	// Create timers.
    err_code = app_timer_create(&m_app_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                timer_handler);
    APP_ERROR_CHECK(err_code);
	
    // Start application timers.
    err_code = app_timer_start(m_app_timer_id, APP_TIMER_TICKS(10), NULL);
    APP_ERROR_CHECK(err_code);

#if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
    APP_SCHED_INIT(APP_SCHED_MAX_EVENT_SIZE, APP_SCHED_QUEUE_SIZE);
#endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
//    bsp_configuration();

    ret_code_t ret_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(ret_code);

    while (true)
    {
#if NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
        app_sched_execute();
#endif // NRF_PWR_MGMT_CONFIG_USE_SCHEDULER
	
        if (NRF_LOG_PROCESS() == false)
        {
			// goes to sleep !
			sleep_count++;
            nrf_pwr_mgmt_run();
        }
    }
}

Output gives :

<info> app: sleep_count / timer_handler_count = 202%

<info> pwr_mgmt: CPU Usage: 0%

<info> app: sleep_count / timer_handler_count = 200%

<info> pwr_mgmt: CPU Usage: 0%

<info> app: sleep_count / timer_handler_count = 200%

<info> pwr_mgmt: CPU Usage: 0%

<info> app: sleep_count / timer_handler_count = 200%

<info> pwr_mgmt: CPU Usage: 0%

<info> app: sleep_count / timer_handler_count = 202%

<info> pwr_mgmt: CPU Usage: 0%

<info> app: sleep_count / timer_handler_count = 202%

Any explanation why ?

Matt

  • So your main loop is called twice as often as your timer handler, that is what you are saying, right?

    Without being able to pinpoint exactly what's causing this, I would say that this is not something you need to worry about. 

    You can dig a bit to see if you can figure out exactly what happens, but my guess is:

    Your timeout handler is triggered, causing your timer_handler_count to increment.

    This triggers your main loop when the timer handler is done.

    After this (but probably scheduled before) the app_timer needs to do some internal work to set up the timeout register value for your next timeout. This also triggers the main_loop.

    From time to time, you can see that there is 202. It may be the RTC (app_timer) that rolls over, and it requires a few extra operations to set up the next timeout.

    Best regards,

    Edvin

  • I have looked the source files, and for me all the app_timer functions are taking care of configurations in "rtc_irq" in "app_timer2.c" ... gaving the hand to the user handler, but all in the same irq level !
    As does nrfx_spim_XXX_irq_handler, and nrfx_gpiote_irq_handler ...

    I am also nearly sure it is not only going to the main loop, but going to sleep, and so processing 2 times all what is in the main loop for all wanted interrupts. Also waking two time from sleep, ...
    Doing it two times is not what we want, and is a waste of processing time, and so power ...

    What I think is making it sometime 202, is the processing and print of : "<info> pwr_mgmt: CPU Usage: 0%", that is also using app timer one time per seconds ! I am calling it 100 times per seconds.

    Regards,
    Matt

  • Salmat said:
    I have looked the source files, and for me all the app_timer functions are taking care of configurations in "rtc_irq" in "app_timer2.c" ... gaving the hand to the user handler, but all in the same irq level !

     Remember that the app_timer has it's own scheculer. All the tasks of the app timer is not necessarily done by the time you reach main(), and some may be handled later.

    I have not researched this exact question before, but similar. When customers are starting and stopping the app timer in the main loop they ask why the device never goes to sleep. The answer has always been that the app timer's scheduler is woken up. Then they started a new app_timer in the main context, and stopped it when some work was done. The stop call then triggered an event in the app timer scheduler that woke the device up again.

    Now, in your case, that is not an issue, because you don't start/stop any app timers from your main loop, but what I am saying is that it is the app timer that wakes up the CPU from time to time.

    Best regards,

    Edvin

  • I am not talking at all about app_timer. I have just done an example with App_timer, but it is not only en app_timer problem.
    It is the same with any interrupt !

    By the way I am not using app timer scheduler at all : #define APP_TIMER_CONFIG_USE_SCHEDULER 0

    Regard

  • Hello,

    It looks like it is actually CRITICAL_REGION_ENTER() and CRITICAL_REGION_EXIT() that triggers the main loop one extra time. These are being called inside nrf_pwr_mgmt_run();

    If you want to skip the whole nrf_pwr_mgmt_run(), you can replace it with:

        // 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();
        }

    Best regards,

    Edvin

Related