nRF5340, enter low-power/sleep while timer enabled

I'm working on optimizing power consumption in my project (using nRF5340DK; as well as Power Profiler Kit 2 which is connected to DK's VDD measurement point as an ampere meter). After trial and error, I've narrowed down the timer (set up similarly to the timer example at NCS v2.5.0, modules/hal/nordic/nrfx/samples/src/nrfx_timer/timer/) as having the biggest impact towards power. When initializing the timer, I got an average current draw of almost 1mA; when it's not initialized, I see about 0.2mA.

Several questions:

  1. Is it expected to see that amount of additional current draw due to just simply setting up the timer?
  2. What are my options for bringing the current down as low as possible, while still being able to have the timer running?
    1. It appears possible when I was using k_sleep() in my main loop, as mentioned here. Though this seems to put the thread to sleep, but not the CPU.
    2. But I just recently discovered using sys_poweroff() as mentioned in this sample, which seems to put the entire CPU to sleep if I'm not mistaken. I'm investigating at the moment on my end too but I'm not sure if the timer can be used to wake the CPU back up.
  3. Are there any other methods of setting up timers that lend itself better to low power applications?

I'd appreciate any feedback for this. Thank you!

  • So I made a simpler project doing this, but it behaved as I expected. I looked into it more on my project (which gets a little more complicated: performs SPI reads in interrupt handler, which completes in another interrupt handler, etc.) and ultimately fixed that issue. Did not find the root cause, but generally having main woken up a little earlier (i.e., before the SPI read) seemed to help.

    Thank you for offering your help though!

  • I'm happy to help. But I would suggest to keep your task outside of the main loop. I would prefer to have different threads/timer for your tasks instead of putting them into a main loop .

  • I will consider that suggestion.

    In the meanwhile, I wanted to circle back to your suggestion to "put the sleep forever into an independent thread". I tried this but it just kept the main thread running all the time. If I misinterpreted what you meant, I'd appreciate a clarification. A quick example would also be greatly appreciated if possible.

    k_tid_t main_thread_id;
    
    void wakeup_main(void)
    {
        k_wakeup(main_thread_id);
    }
    
    void interrupt_handler(void)
    {
      restart_timer();  // for example, with RTC, this is counter_set_channel_alarm()
      wakeup_main();
    }
    
    int main()
    {
      main_thread_id = k_current_get();
      init_timer();
    
      while (true) {
        execute_other_code();    // ... BUT DOESN'T EXECUTE UNTIL BEFORE NEXT INTERRUPT
      }
    }
    
    #define STACKSIZE 1024
    static void sleep_thread(void *arg1, void *arg2, void *arg3)
    {
        int rc;
        ARG_UNUSED(arg1);
        ARG_UNUSED(arg2);
        ARG_UNUSED(arg3);
        
        k_sleep(K_FOREVER);
    }
    K_THREAD_DEFINE(sleep_thread_id, STACKSIZE, sleep_thread, NULL, NULL, NULL,
            K_LOWEST_THREAD_PRIO, 0, 100);

  • Hi, 
    Sorry for confusion. No I do not mean that you should only put the  k_sleep(K_FOREVER); to an independent thread. What I meant was to put the whole 

    while (true) {
    execute_other_code(); // ... BUT DOESN'T EXECUTE UNTIL BEFORE NEXT INTERRUPT
    
    k_sleep(K_FOREVER);
    }

    Into an independent thread just in case there isn't a way to get the main thread id. But you already find the  k_current_get() so it's fine. 


Related