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

nRF52 does not wake from sleep when radio timeslot is active

Hi,

We use the nRF radio timeslot API to take control over the radio and other peripherals periodically. We have noticed that when the timeslot is active (we own the peripherals) and we call nrf_pwr_mgmt_run() to sleep, the system never wakes again. To solve it we have to do the following workaround:

static void IdleStateHandler(void)
{
  // Handle pending app events before going to sleep
  app_sched_execute();
  // NOTE: Dont sleep if radio timeslot active since it does not wake again
  // Likely related to the usage of radio timeslot
  if (!RadioTimeslotIsActive() && NRF_LOG_PROCESS() == false)
  {
#if NRF_MODULE_ENABLED(NRF_PWR_MGMT)
    nrf_pwr_mgmt_run();
#endif
  }
}

I would like to know how this is meant to work!

I understand that it may be an issue if the system goes to deep sleep since the wakeup time may introduce a jitter. However, it should be possible to go to shallow sleep (WFI) to save the power consumption of the MCU core. The workaround above means that the MCU core is spinning as long as the timeslot is active, which drains the battery faster.

We use:

nRF52840 DK

nRF5 SDK ver 16.0.0

Softdevice: s113_nrf52_7.0.1_softdevice.hex

BR / Björn

Parents
  • Hello Björn,

    Are there any evens that you expect to wait up from after you enter nrf_pwr_mgmt_run()? If so, what events are you waiting for?

    If you are not sure, perhaps you can try to set up a repeated app_timer with timeout e.g. 1 second. Does that timeout event still fire after you call nrf_pwr_mgmt_run()?

    I know very little about your application. When you call the timeslots from the softdevice. Is the softdevice doing anything else? Advertising? Scanning? In a connection?

    Best regards,

    Edvin

  • Hi Edvin,

    I understand that you need more info :-)I will try to clarify by some example code, see below. First of all, I'm only waiting for a TIMER0 interrupt, and it does not come after I have received the signal NRF_RADIO_CALLBACK_SIGNAL_TYPE_START. TIMER0 is my engine, driving the timeslot state machine forward.

    Regarding the application it is based on Softdevice, but we dont use the Softdevice BLE stack at all. We just request a timeslot every 10 ms and send BLE packets directly using the RADIO. So the Softdevice does not do anything.

    /**
     * Setup the timer when a nrf timeslot starts
     */
    static void TimerSetup(uint32_t timeUs)
    {
        NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
        NRF_TIMER0->CC[0] = timeUs;
        NVIC_EnableIRQ(TIMER0_IRQn);
    }
    
    /**
     * Set time for next event
     */
    static void TimerSetNextEvent(uint32_t timeUs)
    {
        // Clear timer evt/irq
        NRF_TIMER0->EVENTS_COMPARE[0] = 0;
        // Arm next evt
        NRF_TIMER0->CC[0] = timeUs;
    }
    
    /**
     * Timeslot event handler
     */
    static nrf_radio_signal_callback_return_param_t * radio_callback(uint8_t signal_type)
    {
        switch (signal_type)
        {
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_START:
                // Start of the nrf timeslot
    
                // Setup Timer to trigger event/irq
                TimerSetup(firstJobDueTime);
    
                // Remain in timeslot, timer irq is expected
                signal_callback_return_param.params.request.p_next = NULL;
                signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
                break;
    
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO:
                // Radio IRQ
                break;
    
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
                // Timer IRQ
    
                DoSomeJob();
    
                // Constant time from IRQ to client callback is important!
                // The client shall normally send a packet in the callback
                // Radio IRQ SHOULD NOT be enabled since it will happen
                // just before next timer IRQ and MAY interfere with the timing
    
                if (moreJobToDo()) {
                    // Timer IRQ when next job
                    TimerSetNextEvent(nextJobDueTime);
    
                    // Remain in timeslot, timer IRQ is expected
                    signal_callback_return_param.params.request.p_next = NULL;
                    signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
                } else {
                    // Stop current and schedule next timeslot
                    signal_callback_return_param.params.request.p_next = &m_timeslot_request;
                    signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
                }
                break;
    
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:
                // Fall through
            case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
                // Shall never happen since extensions not requested
                ASSERT(false);
                break;
    
            default:
                //No implementation needed
                break;
        }
    
        return (&signal_callback_return_param);
    }
    
    

    Let me know if you need more info.

    BR / Björn

  • Hi Edvin,

    I have isolated the problem now and attach the whole project. Unpack it in the folder nrf5_sdk\examples\ble_peripheral, make for pca10056 and flash.

    I have copied "ble_app_beacon" into a new project "ble_app_beacon_ts". Here I have added a sample timeslot implementation that blinks the LEDs. If processing in the main context is required then the app will not work since the power manager sleeps forever. If a dummy IRQ is triggered at the end of timeslot then system will wake up.

    Play with the following defines in ts_sample.c:

    // Set to restart LED timers in main context
    // Will not work since Timeslot IRQ does not make
    // the power manager wake up
    #define START_LED_TIMERS_IN_MAIN (1)

    // Set to wake the power manager after end of timeslot
    // Workaround to make processing in main context work
    #define TRIGGER_IRQ_AT_END_OF_TIMESLOT (0)

    So the very clear conclusion is that timeslot IRQs don't wake the system, which in my mind is a bug.

    I'm looking forward to your response.

    BR / Björn

    ble_app_beacon_ts.zip

  • Have you tried to add a log message in NRF_RADIO_CALLBACK_SIGNAL_TYPE_START? I don't quite follow your LED logic, but I see that the NRF_RADIO_CALLBACK_SIGNAL_TYPE_START event occurs even if the LEDs are not toggling. Could it be that the issue is related to the LED logic or the timer?

    BR,

    Edvin

  • Hi Edvin,

    I dont understand why I should add a log message in NRF_RADIO_CALLBACK_SIGNAL_TYPE_START? I know that this signal is coming. In fact, all timeslot signals come as expected, so the whole function radio_callback() works as expected.

    The point is that nrf_pwr_mgmt_run() called from idle_state_handle() in main.c never returns, despite the fact that the timeslot signals (interrupts) are fired. Interrupts shall make the system wake up and hence make nrf_pwr_mgmt_run() return. This is the issue.

    So my sample show: Timeslot interrupts dont wake the system as expected meaning that nrf_pwr_mgmt_run() never returns. 

    I have simplified the sample to bare bone, see ts_sample_simple.c. Now it is supposed to toggle LED_1 with period 100 ms. You have the following configs in ts_sample_simple.c:

    // Set to toggle LED in main context instead of IRQ context
    // Will not work since Timeslot IRQ does not make
    // the power manager wake up
    #define TOGGLE_LED_IN_MAIN_CTX (1)
    
    // Set to wake the power manager after end of timeslot
    // Workaround to make processing in main context work
    #define TRIGGER_IRQ_AT_END_OF_TIMESLOT (0)
    

    Try the following combinations:

    1) Toggle in IRQ context => Works
    #define TOGGLE_LED_IN_MAIN_CTX (0)
    #define TRIGGER_IRQ_AT_END_OF_TIMESLOT (0)

    2) Toggle in main context => DOES NOT WORK (but should according to me)
    #define TOGGLE_LED_IN_MAIN_CTX (1)
    #define TRIGGER_IRQ_AT_END_OF_TIMESLOT (0)

    3) Toggle in main context with extra IRQ workaround => works
    #define TOGGLE_LED_IN_MAIN_CTX (1)
    #define TRIGGER_IRQ_AT_END_OF_TIMESLOT (1)

    BR / Björn

    ble_app_beacon_ts_simplified.zip

  • Oh, I thought that the issue was that your radio notifications wasn't working.

     

    Bjorn191023 said:
    2) Toggle in main context => DOES NOT WORK (but should according to me)
    #define TOGGLE_LED_IN_MAIN_CTX (1)
    #define TRIGGER_IRQ_AT_END_OF_TIMESLOT (0)

     So TsSampleMainHandler() should toggle leds if TOGGLE_LED_IN_MAIN_CTX == 1 whenever the main loop is run, but the main loop isn't run if TRIGGER_IRQ_AT_END_OF_TIMESLOT != 1. 

    So sd_app_evt_wait(), called by idle_state_handle() -> nrf_pwr_mgmt_init() -> sd_app_evt_wait(), will wake up whenever there is an application interrupt. I guess the timeslots are not counted as application interrupts, but pure softdevice interrupts. 

    sd_app_evt_wait() will not wake up the application at all softdevice events, such as an advertisement event, or connection event (events that occurs every connection interval, to maintain the connection). It will only wake up the application when there either is a peripheral event that the application has set up, or if there is a softdevice event that is sent to the application, such as if someone writes to one of the characteristics. 

    So if you enter sd_app_evt_wait(), the main context will not run again until you have an event. This is also why you see that it wakes up when you set the IRQ in your timeslot. This is something that the application should handle, and therefore it is woken up. 

    The reason it works when TOGGLE_LED_IN_MAIN_CTX(0) and TRIGGER_IRQ_AT_END_OF_TIMESLOT(0) is, as you probably know, that you toggle it in the TIMER0 callback. 

    If you want to e.g. toggle a led from your main context, you can use a timer for that purpose, pretty much like you do in the TIMER0 callback in your timeslot callback. If you want to propagate this to the application context, you can look into the EGU (event generator unit), or to have a callback function for the peripheral itself. 

    The reason this doesn't happen in the TIMER0 case is that the TIMER0 is handled by the softdevice. If you try to use for example TIMER1, and set up a callback in the application you should see it there, and it will run the main loop one more time after the callback.

    BR,

    Edvin

  • Thanks for the response Edvin. 

    So the conclusion is that nrf_pwr_mgmt_run() -> sd_app_evt_wait() only returns on app interrupts and not on softdevice interrupts even if they are routed to the timeslot signal handler.

    I think you should update the documentation of nrf_pwr_mgmt_run() and sd_app_evt_wait() and clarify how it works since it's not obvious.

    BR / Björn

Reply
  • Thanks for the response Edvin. 

    So the conclusion is that nrf_pwr_mgmt_run() -> sd_app_evt_wait() only returns on app interrupts and not on softdevice interrupts even if they are routed to the timeslot signal handler.

    I think you should update the documentation of nrf_pwr_mgmt_run() and sd_app_evt_wait() and clarify how it works since it's not obvious.

    BR / Björn

Children
No Data
Related