Skip to content
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

Going into dormant state #659

Closed
0xa10 opened this issue Jul 29, 2023 · 7 comments
Closed

Going into dormant state #659

0xa10 opened this issue Jul 29, 2023 · 7 comments

Comments

@0xa10
Copy link

0xa10 commented Jul 29, 2023

Hi, in my application I'd like the RP2040 to completely power off in certain cases, so it consumes minimal current.
According to several sources its possible to go into a dormant state in which the power consumption is <1mA.

Ive looked around and it seems this involves disabling interrupts and then disabling several of the system clocks.
I also saw this PR #613 which seems to have a halt function, but that doesnt put the main core to sleep as far as I could tell. Using WFE/WFI even with interrupts disabled still puts my Pico at ~20mA of draw. A timer sleep puts it at around 9mA.

Is there any way using the existing HAL code to go into deep sleep?

@ithinuel
Copy link
Member

Hello,
Out of curiosity, have you tried the c-sdk's hello_dormant to verify what could be achieved with your setup?

20mA seem to be quite a lot as the bootsel mode typically runs around 14mA.
This makes me think that your MCU may not be sleeping at all.

From reading the rp2040 datasheet, it seems to me that core1 enters deepsleep at boot (WFE + SCR.SLEEPDEEP set).
The first core on the other hand needs to be configured (setting SCR.SLEEPDEEP) to behave like so.

Based on what I can see from the listed source, the dormant mode is not linked to deepsleep/WFI/WFE.
It's literally stopping the clock.
https://github.com/raspberrypi/pico-extras/blob/master/src/rp2_common/pico_sleep/sleep.c
https://github.com/raspberrypi/pico-sdk/blob/6a7db34ff63345a7badec79ebea3aaef1712f374/src/rp2_common/hardware_xosc/xosc.c#L52

Anyways, I should have started with this:

The rp2040-hal implements CrystalOscillator::dormant() which should (partially) achieve what you are looking for.
One could argue this is incomplete as there does not seem to be a way to recover a CrystalOscillator<Stable> from a CrystalOscillator<Dormant> to which I'd reply that all contributions are more than welcome 😃.

@0xa10
Copy link
Author

0xa10 commented Jul 30, 2023

Thank you. I will look into that and make sure to submit a PR if I end up writing recovery logic 🙂

@0xa10
Copy link
Author

0xa10 commented Aug 19, 2023

So I've gotten around to play with this now and at the moment I haven't even been able to put the Pico into a deep sleep where it consumes less than 8mA, which is still quite a lot.

The examples in pico-extras and in the sdk are kind of confusing.
As far as I could tell from the documentation - the only thing I need to do to achieve the low dynamic power sleep is stop the PLLs and the external oscillator.

However some of the examples disable the xosc outright, and some put it into dormant mode.
In the RTC wake example the sleep_en0/1 masks are modified, in the rest they aren't (?).
In that same example the SCB register is modified to enable deep sleep - but this isn't present in the other examples.

So I'm not quite sure what I'm doing wrong - my setup and codes as follows:
I'm initializing the bare minimum I need to get the onboard LED flashing for 5 seconds, before attempting to do into deep sleep.
The bare minimum being:

  1. Setting up the external oscillator and binding it as the system clock
  2. Minimal GPIO setup so I can access the LED (bsp::Pins)

I'm not initializing the PLLs nor am I configured any of the other clocks, so as far as I understand I shouldn't need to stop them explicitly.

So far I've tried -

  1. Disabling the xosc outright
  2. Setting the xosc to dormant
  3. Setting sleep_en0 and sleep_en1 to 0
  4. Setting SCB_SCR_SLEEPDEEP in the SCB

Of course non of these are meant to be recoverable at the moment - just trying to go into deep sleep.

This is my code:

#![no_std]
#![no_main]

use bsp::entry;
use defmt_rtt as _;
use embedded_hal::digital::v2::OutputPin;
use panic_halt as _;

use rp_pico as bsp;

use bsp::hal::{
    clocks::{Clock, ClocksManager, ClockSource, StoppableClock},
    pac,
    sio::Sio,
    watchdog::Watchdog,
    pll::{
        common_configs::{PLL_SYS_125MHZ, PLL_USB_48MHZ},
        setup_pll_blocking, Error as PllError, Locked, PhaseLockedLoop,
    },
};

use fugit::{HertzU32, RateExtU32};

#[entry]
fn main() -> ! {
    let mut pac = pac::Peripherals::take().unwrap();
    let core = pac::CorePeripherals::take().unwrap();
    let sio = Sio::new(pac.SIO);

    let external_xtal_freq_hz = 12_000_000u32;
    let xosc =  bsp::hal::xosc::setup_xosc_blocking(pac.XOSC, external_xtal_freq_hz.Hz()).unwrap();

    let mut clocks = ClocksManager::new(pac.CLOCKS);
    clocks.system_clock.configure_clock(&xosc, xosc.get_freq()).unwrap();


    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());

    let pins = bsp::Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );

    let mut led_pin = pins.led.into_push_pull_output();

    for i in 0..5 {
        led_pin.set_high().unwrap();
        delay.delay_ms(500);
        led_pin.set_low().unwrap();
        delay.delay_ms(500);
    }
    // Halt forever
    unsafe {
        let SCB_SCR_SLEEPDEEP: u32 = 0x1 << 2;
        (*pac::SCB::PTR).scr.modify(|scr| scr | SCB_SCR_SLEEPDEEP);
        xosc.disable();
        //xosc.dormant();
    }
    loop {
        cortex_m::asm::wfi();
    }
}

@ithinuel
Copy link
Member

Hello,

I got it to go to deepsleep and wake from it. I don't have any power measurement tool accurate enough to see any consumption at all (it must always be <1mA at 5V, or my tool is really crappy).

I'll continue trying to identify the issues under this in the coming days.

@ithinuel
Copy link
Member

Actually, I wasn't far from making it work: https://github.com/ithinuel/hello-dormant-rp2040

As mentionned before, I don't have the tools to measure power consumption.
Also, my code may not be the shiniest.
Let me know if this helps identify the issue you're having.

@ithinuel
Copy link
Member

ithinuel commented Aug 27, 2023

I got hold of a power measurement tool and I can confirm that I get power usage consistent with the rp-pico's datasheet: https://datasheets.raspberrypi.com/pico/pico-datasheet.pdf

According to the datasheet, the rtc is required to take its input clock from an external pin to allow for waking from Dormant mode.

Another limitation I found is that the rp2040-hal lacks the ability to recover from that dormant mode and the clock object gets consumed in the process, so one cannot re-enter dormant after waking up.
It also lacks the ability to enable gpio interrupts to wake from dormant. This has been fixed in the GPIO overhaul.

This PR should cover the needs for deep-sleep:

Last edit: 28/08/23 23:53 BST.

@0xa10
Copy link
Author

0xa10 commented Aug 29, 2023

Amazing, thank you.

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

No branches or pull requests

2 participants