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
  • I hit break inside sd_app_event_wait(), it's only about 10 lines of assembler and it's not too hard to see what it does. If the two bytes are equal, it loops back to the __wfe(), if they aren't, it exits the wait (having set them equal again).

    I don't think your suggestion will help. The softdevice is constantly generating interrupts and events so that code will pretty much instantly exit in all cases. Those internal flags seem to act as a filter to keep it looping on internal events and interrupts and only exit sd_app_event_wait() when the softdevice has generated an event the user might want to wake up for. What I don't know is what increments the flag which says 'this is a real user event' and causes the loop to exit.

Reply
  • I hit break inside sd_app_event_wait(), it's only about 10 lines of assembler and it's not too hard to see what it does. If the two bytes are equal, it loops back to the __wfe(), if they aren't, it exits the wait (having set them equal again).

    I don't think your suggestion will help. The softdevice is constantly generating interrupts and events so that code will pretty much instantly exit in all cases. Those internal flags seem to act as a filter to keep it looping on internal events and interrupts and only exit sd_app_event_wait() when the softdevice has generated an event the user might want to wake up for. What I don't know is what increments the flag which says 'this is a real user event' and causes the loop to exit.

Children
No Data
Related