Skip to content

Sleep and wake up #696

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
bobricius opened this issue Mar 23, 2018 · 29 comments
Closed

Sleep and wake up #696

bobricius opened this issue Mar 23, 2018 · 29 comments

Comments

@bobricius
Copy link

bobricius commented Mar 23, 2018

Hi, I am sorry for opening similar issue, now without RTC feature.

I missing only one feature, It can very help for battery operated devices.
Sleep and wait for button for wakeup, I thing that it can be great advantage to expand cpython to wearables. This guys make something for sleeping but wake not working https://github.com/edgecollective/circuitpython. Can anybody make this feature?

@ladyada
Copy link
Member

ladyada commented Mar 23, 2018

hiya - this would be same as #119 - we'd need #544 first, because you cannot sleep with USB connected :)

@tannewt
Copy link
Member

tannewt commented Mar 23, 2018

This is definitely on my radar. I think one of our major releases should focus on battery-operated datalogging. Saving battery life isn't simple because of peripheral and clock management too. I'd also like to tackle flash wear leveling to coincide with that.

I'm happy to advise anyone trying to do this earlier but its not a focus for us yet.

@bobricius
Copy link
Author

my God, and I thought that it would be only one instruction. It is really complicated thing.

@crowecawcaw
Copy link

I am interested in implementing some version of low power sleep for the nrf52840. From looking through the code it bit, it seems like there are timers and loops that need to be run continuously for CircuitPython to function as expected which complicate the issue.

My first goal would be to implement a new module to sleep on command. A secondary goal would be to merge the low power code in with the time.sleep function.

Could someone give me some pointers on where to start, what things to keep in mind, and what traps you'd foresee?

@dhalbert
Copy link
Collaborator

We currently use the SysTick timer in all our ports for general timekeeping (msec ticks and smaller): see ports/nrf/tick.c. SysTick is available on all M0/M4 processors: it is not manufacturer-specific. SysTick is based on the processor clock; when the processor sleeps, its clock is turned off, so SysTick will stop working.

So tick.c et al would need to be changed to use the RTC instead. The RTC can be set up to run all the time, even during sleep. There are similar RTC peripherals on the other chips we support.

This is a substantial change in the timekeeping logic. it's possible, and makes sense, but it requires some thought.

@tannewt
Copy link
Member

tannewt commented Jul 29, 2019

@crowecawcaw I'd recommend starting without usb and just loading code over a debugger. USB is timing critical and will be unhappy if the background task isn't called. Once you get it going off of usb then I'd start by just disabling sleep when on usb. Later, if needed, we can make it work with usb.

@crowecawcaw
Copy link

This may be a silly question but is there a good way to develop and debug the CircuitPython source? Does anyone have suggestions for a free IDE that could make things easier? Right now, I'm editing code, compiling, and refreshing every change but it's difficult to tell what's going on without breakpoints or the like.

@sommersoft
Copy link

@crowecawcaw, when I was dev'ing primarily on Win10, I used Atmel Studio 7. Breakpoints and I/O registers were a bit of breeze. The [major] downside though was being forced to compile in a VM. It may be possible to overcome that, but I never figured out a way.

I did try to get Segger Embedded Studio working as an IDE on Linux and Win10, but never got anywhere with it.

On Linux, using GDB became just as fast as AS7 in most cases. Especially after I started using a breakpoints file. That reduced some iteration time by only requiring source <filename> whenever I had to set common breakpoints.

@crowecawcaw
Copy link

After spending some time looking through the code base, my plan is to make a module that has a function low_power_sleep() that will put the processor in a low power state until the time elapses. From what I can tell, there are 3 types of timers in play:

  • RTC: This is constantly running and is used for time.time(). It runs even if the processor is in low power modes.
  • SysTick: Based on the processor clock cycles (very precise) and is used for general timing including time.sleep() and time.monotonic() and for controlling timeouts in various functions (e.g. waiting for characters from the UART)
  • Timers: The general timers are configurable for different frequencies. They are used for PulseOut, PWMOut, and internally for Bluetooth. The nrf52840 has several of these. The processor can go into some low power modes but not others while the timers are running.

My thought is to use a general purpose timer for tracking time while sleeping because there are unused timers that can be configured and run without interacting with any other code. The side effects will be that the usb background task will not run and that time.monotonic() and SysTick will not increment while sleeping.

Does all of this seem correct? Does my approach seem reasonable?

@deshipu
Copy link

deshipu commented Jul 31, 2019

I wonder if we should have separate issues for tracking this on different ports?

Personally, I would be more interested in being able to wake up on a gpio change than on a timer, so that we can get rid of those power switches from our hardware designs.

@dhalbert
Copy link
Collaborator

The nRF and SAMD RTCs both tick with 30.5us precision, so it could be used for time.sleep() and time.monotonic() pretty successfully, I think.

@crowecawcaw
Copy link

I thought that if an interrupt was firing quickly, it would prevent the processor for staying in the low power state for very long causing the power consumption to remain high. In this instance, the RTC would be triggering interrupts every 30.5us to increment the ticks. By configuring a timer to interrupt less frequently (100ms or so), the processor would be mostly sleeping and its power consumption would remain very low. The power consumption then would not depend much on the type of timer or its clock speed but on how often it interrupts. Am I thinking correctly?

@tannewt
Copy link
Member

tannewt commented Jul 31, 2019

@crowecawcaw My debug process is documented here: https://learn.adafruit.com/debugging-the-samd21-with-gdb I use it on SAMD51 and nRF too.

@dhalbert
Copy link
Collaborator

There's a prescaler for the RTC's, so you can divide down the 30.5us ticks to a slower interval.

@crowecawcaw
Copy link

Thank you @tannewt and @dhalbert both for the tips!

I have a new lowpower module running with a sleep() function that runs correctly off a timer. The function waits for an interrupt repeatedly until the timer interrupt fires. Current the current draw is sitting at 2.5mA. I've compiled the firmware without the softdevice option which I'm assuming prevents the bluetooth hardware from being initialized and run. Are there other interrupts which could be preventing the processor from sleeping?

My code is below:

#include <string.h>

#include "py/obj.h"
#include "py/objnamedtuple.h"
#include "py/runtime.h"
#include "supervisor/shared/translate.h"
#include "nrf/timers.h"

static nrfx_timer_t *timer = NULL;

volatile uint8_t timeout = 0;
static void lowpower_timer_interrupt(nrf_timer_event_t event_type, void *p_context) {
  if (event_type == NRF_TIMER_EVENT_COMPARE0) {
    timeout = 1;
  }
}

STATIC mp_obj_t lowpower_sleep(mp_obj_t seconds_o) {
    #if MICROPY_PY_BUILTINS_FLOAT
    float seconds = mp_obj_get_float(seconds_o);
    #else
    int seconds = mp_obj_get_int(seconds_o);
    #endif

    uint32_t ticks = seconds * 31250;

    if (seconds < 0) {
        mp_raise_ValueError(translate("sleep length must be non-negative"));
    }



    timer = nrf_peripherals_allocate_timer();
    if (timer == NULL) {
      mp_raise_RuntimeError(translate("All timers in use"));
    }

    nrfx_timer_config_t timer_config = {
        // PulseOut durations are in microseconds, so this is convenient.
        .frequency = NRF_TIMER_FREQ_31250Hz,
        .mode = NRF_TIMER_MODE_TIMER,
        .bit_width = NRF_TIMER_BIT_WIDTH_32,
        .interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY
    };
    nrfx_timer_init(timer, &timer_config, &lowpower_timer_interrupt);
    nrfx_timer_enable(timer);
    nrfx_timer_pause(timer);
    nrfx_timer_clear(timer);
    timeout = 0;
    nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL0, ticks, true);
    nrfx_timer_resume(timer);

    while(timeout == 0) {
      // __SEV();
      // __WFE();
      // __WFE();
      __WFI();
    }

    nrfx_timer_disable(timer);
    nrf_peripherals_free_timer(timer);
    
    return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(lowpower_sleep_obj, lowpower_sleep);

STATIC const mp_rom_map_elem_t lowpower_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_lowpower) },
    { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&lowpower_sleep_obj) },
};

STATIC MP_DEFINE_CONST_DICT(lowpower_module_globals, lowpower_module_globals_table);

const mp_obj_module_t lowpower_module = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t*)&lowpower_module_globals,
};

@NAPtime2
Copy link

NAPtime2 commented Feb 3, 2020

Hi Guys, sorry to reopen this topic. Is there a circuitpython module that includes this low power feature? I will either be using the Adafruit STM32F405 EXPRESS or the Adafruit M0 Express board and would like to be able to run off the battery supply if possible. Apologies if @crowecawcaw's code is the answer, I am relatively new to circuitpython, and not experienced enough to implement it if it is.

@dhalbert
Copy link
Collaborator

dhalbert commented Feb 3, 2020

@NAPtime2 Not yet, but it's high on our agenda for doing after the 5.0.0 release, which is now in beta.

@NAPtime2
Copy link

NAPtime2 commented Feb 3, 2020

Excellent. Any idea of a rough timeframe?

@dhalbert
Copy link
Collaborator

dhalbert commented Feb 3, 2020

Not really, but sooner rather than later. We generally avoid schedules, to the disappointment of many. 🙂 5.0.0 will be done when it's done, but we're approaching done.

@tannewt tannewt self-assigned this Mar 12, 2020
@tannewt
Copy link
Member

tannewt commented Mar 12, 2020

I started working on this and have nRF working, atmel-samd in progress. I plan on doing STM and iMX RT as well. My work is here: https://github.com/tannewt/circuitpython/tree/lower_power

I don't have pin wake yet. Now it is simply that the CPU will sleep when time.sleep() is called. I do intend on adding pin wake soon after.

@volkerjaenisch
Copy link

@tannewt

I would love to test your low power version. Is it possible to test it on a Trinket M0 ?
Any precautions?
How to Build, how to deploy?

Cheers,
Volker

@tannewt
Copy link
Member

tannewt commented Apr 10, 2020

Ya! It should work ok on the Trinket M0. Each PR commit has artifacts automatically built. The latest for the lower_power branch are here: https://github.com/adafruit/circuitpython/runs/575104579 Click the artifacts link in the top right and select Trinket_m0. It'll be a zip of all uf2s for the trinket.

@volkerjaenisch
Copy link

@tannewt
Thank you so much! Will try it tomorrow!
Have you done any measurements with and without your PR?

An alternative may be to use a TI TPL5100 for deep sleep of the complete circuit.
What is your opinion?

Cheers,
Volker

@tannewt
Copy link
Member

tannewt commented Apr 13, 2020

@volkerjaenisch Ya, I've done enough to confirm that it enters sleep. If I remember right its 4x or 5x less current when idle.

An external chip will be much lower power because it can completely turn off the power. The SAMD21 does have lower power modes but they won't match an external chip.

@VR-AntHill
Copy link

I don't have pin wake yet. Now it is simply that the CPU will sleep when time.sleep() is called. I do intend on adding pin wake soon after.

any progress on the pin wake? Looking at battery powered SAMD51 ...

@tannewt
Copy link
Member

tannewt commented May 26, 2020

@VR-AntHill Nope. I've moved onto ESP32S2 support. Only the nRF52 is getting more refined lower power thanks to @xobs.

@kdb424
Copy link

kdb424 commented Oct 13, 2020

Curious if this is intending to be in Cpy6, or it it will still be a while before this gets rolling again.

@tannewt
Copy link
Member

tannewt commented Oct 14, 2020

@kdb424 6.0.0 will have light sleep during time.sleep for all ports. I've just started the light and deep sleep API for sleeping until an alarm. I'm targeting ESP32S2 to start but once the API is set, it shouldn't be too hard to bring other ports.

@dhalbert
Copy link
Collaborator

Subsumed by #2796. The original issue has a low-power implementation for nRF. We are doing ESP32-S2 first, but can open issues for other ports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests