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

Wakeup immediately from sd_app_evt_wait() with external LFCLK

Hi all

We use the nRF51822 with the S130 V1.0.0. Our application initializes the TIMER2 (f = 16MHz) with a EVENTS_COMPARE interrupt on the CC channel 1 to monitor some timings.

Afterwards the SoftDevice is enabled with the LFCLK source "NRF_CLOCK_LFCLKSRC_XTAL_20_PPM". The TIMER2 stays running.

Later on, we want to reduce the power consumption. Therefore we disable the TIMER2 with the following code:

   NRF_TIMER2->TASKS_STOP = 1;
   NRF_TIMER2->TASKS_SHUTDOWN = 1;

   NVIC_DisableIRQ(TIMER2_IRQn);
   NVIC_ClearPendingIRQ(TIMER2_IRQn);

Afterwards we call the function sd_app_evt_wait() but the nRF51822 wakes up immediately. The strange thing is, that when I change the LFCLK source at the SoftDevice initialization to "NRF_CLOCK_LFCLKSRC_RC_250_PPM_TEMP_4000MS_CALIBRATION", the nRF51822 stays in the low power mode. Therefore there should be no event/interrupt in our application that awakes the chip.

Further observations are:

  • When we disable the TIMER2 before initializing the SoftDevice (LFCLK source is the XTAL) everything works fine
  • When we initialize the TIMER2 after initializing the SoftDevice (LFCLK source is the XTAL) everything works fine too

Is there a problem when the TIMER2 is running during the SD init in combination with the LF crystal? Does anyone know this behavior?

EDIT: I've added a stripped-down version of our application to reproduce this behavior:

Function to enable the query-timer:

static void query_timer_init(void)
{
   // :NOTE:
   //    Currently the following capture/compare channels are used:
   //       - Channel 0: Used to capture the timer value
   //       - Channel 1: Used to handle the overflow of the 16bit timer

   // Stop the timer before the configuration
   NRF_TIMER2->TASKS_STOP = 1;

   // Init module variables
   gTIMER2_high_word = 0;

   // Configure TIMER2 in timer mode with 16 bits
   NRF_TIMER2->MODE = TIMER_MODE_MODE_Timer;
   NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_16Bit;

   // Use 16 MHz (disable the prescaler)
   // -> f = HFCLK / (2 ^ prescaler_value)
   NRF_TIMER2->PRESCALER = 0;

   // Clear the timer before using it
   NRF_TIMER2->TASKS_CLEAR = 1;

   // Setup capture/compare channel 1 for timer overflow
   NRF_TIMER2->CC[1] = 0xFFFF;
   NRF_TIMER2->EVENTS_COMPARE[1] = 0;
   NRF_TIMER2->INTENSET = (TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENSET_COMPARE1_Pos);

   // Enable interrupt for timer 2
   NVIC_SetPriority(TIMER2_IRQn, APP_IRQ_PRIORITY_HIGH);
   NVIC_ClearPendingIRQ(TIMER2_IRQn);
   NVIC_EnableIRQ(TIMER2_IRQn);

   // Start the timer
   NRF_TIMER2->TASKS_START = 1;
}

Function to disable the query-timer:

static void query_timer_disable(void)
{
   // Stop the timer
   NRF_TIMER2->TASKS_STOP = 1;

   // Shutdown the timer module
   NRF_TIMER2->TASKS_SHUTDOWN = 1;

   // Disable and clear the timer interrupt
   NVIC_DisableIRQ(TIMER2_IRQn);
   NVIC_ClearPendingIRQ(TIMER2_IRQn);
}

The main function:

int32_t main(void)
{
   uint32_t sd_stat = NRF_SUCCESS;
   ble_enable_params_t ble_enable_params;

   // Initialize the query timer
   query_timer_init();

   // Init the SoftDevice
   sd_stat = sd_softdevice_enable(
      //NRF_CLOCK_LFCLKSRC_RC_250_PPM_TEMP_4000MS_CALIBRATION, NULL); // -> OK
      NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, NULL);                          // -> NOK
   if (sd_stat != NRF_SUCCESS) {goto exception;}

   // Init the BLE stack
   ble_enable_params.gatts_enable_params.service_changed = 0;
   ble_enable_params.gatts_enable_params.attr_tab_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT;
   sd_stat = sd_ble_enable(&ble_enable_params);
   if (sd_stat != NRF_SUCCESS) {goto exception;}

   // Disable the query timer
   query_timer_disable();

   // Activate the system ON low power mode
   sd_stat = sd_app_evt_wait();
   if (sd_stat != NRF_SUCCESS) {goto exception;}

   // -> This point should not be reached since there is no enabled wakeup source!


   // Re-enable the query timer
   query_timer_init();

   // the main loop
   while (1) {}

   // -> Should never reach this point!


exception:
   while (sd_stat != NRF_SUCCESS) {}
   return 0;
}

Kind regards

Parents
  • That's what I expected. However I don't know what events increment the counter at 0x2000006b. You would think it would only happen when there's an event ready to be retrieved with sd_evt_get() but clearly there's at least one other happening which sets the flag. I'm afraid I can't even really hazard a guess as to what that might be. Does calling sd_evt_get(), even though it returns no pending event, reset the flags or does the event wait still wake up immediately? Did you also try getting a ble event, it could be one of those too.

    I suspect most people use sd_app_event_wait() to sleep the CPU in a processing loop and not to try and sleep the CPU exactly once and have it wake exactly once, so they don't notice the issue.

    That exhausts my knowledge on this

Reply
  • That's what I expected. However I don't know what events increment the counter at 0x2000006b. You would think it would only happen when there's an event ready to be retrieved with sd_evt_get() but clearly there's at least one other happening which sets the flag. I'm afraid I can't even really hazard a guess as to what that might be. Does calling sd_evt_get(), even though it returns no pending event, reset the flags or does the event wait still wake up immediately? Did you also try getting a ble event, it could be one of those too.

    I suspect most people use sd_app_event_wait() to sleep the CPU in a processing loop and not to try and sleep the CPU exactly once and have it wake exactly once, so they don't notice the issue.

    That exhausts my knowledge on this

Children
No Data
Related