summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2022-06-29 00:22:49 +1000
committerDamien George <damien@micropython.org>2022-06-30 11:40:17 +1000
commitb004e7e397577d95404fd31aec68a5c54904a48c (patch)
tree266fa9f6938df7d28cbfb7a7a191f45c104d0eb8
parent932556d5fc09a196139170fe52436ea8a02020c1 (diff)
rp2/modmachine: Implement lightsleep() with optional sleep period.
This gets basic machine.lightsleep([n]) behaviour working on the rp2 port. It supports: - Calling lightsleep without a specified period, in which case it uses xosc dormant mode. There's currently no way to wake it up from this state, unless you write to raw registers to enable a GPIO wake up source. - Calling lightsleep with a period n in milliseconds. This period must be less than about 72 minutes and uses timer alarm3 to wake it up. The RTC continues to run during lightsleep, but other peripherals have their clock turned off during the sleep. It doesn't yet support longer periods than 72 minutes, or waking up from GPIO IRQ. Measured current consumption from the USB port on a PICO board is about 1.5mA when doing machine.lightsleep(5000), and about 0.9mA when doing machine.lightsleep(). Addresses issue #8770. Signed-off-by: Damien George <damien@micropython.org>
-rw-r--r--ports/rp2/CMakeLists.txt2
-rw-r--r--ports/rp2/modmachine.c78
2 files changed, 76 insertions, 4 deletions
diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt
index 0088ba2c6..9f71f6584 100644
--- a/ports/rp2/CMakeLists.txt
+++ b/ports/rp2/CMakeLists.txt
@@ -150,6 +150,7 @@ set(PICO_SDK_COMPONENTS
hardware_i2c
hardware_irq
hardware_pio
+ hardware_pll
hardware_pwm
hardware_regs
hardware_rtc
@@ -159,6 +160,7 @@ set(PICO_SDK_COMPONENTS
hardware_timer
hardware_uart
hardware_watchdog
+ hardware_xosc
pico_base_headers
pico_binary_info
pico_bootrom
diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c
index b988afdf3..3c8922c41 100644
--- a/ports/rp2/modmachine.c
+++ b/ports/rp2/modmachine.c
@@ -38,7 +38,12 @@
#include "modmachine.h"
#include "uart.h"
#include "hardware/clocks.h"
+#include "hardware/pll.h"
+#include "hardware/structs/rosc.h"
+#include "hardware/structs/scb.h"
+#include "hardware/structs/syscfg.h"
#include "hardware/watchdog.h"
+#include "hardware/xosc.h"
#include "pico/bootrom.h"
#include "pico/stdlib.h"
#include "pico/unique_id.h"
@@ -83,6 +88,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause);
NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args) {
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
+ rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB;
reset_usb_boot(0, 0);
for (;;) {
}
@@ -113,13 +119,77 @@ STATIC mp_obj_t machine_idle(void) {
STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle);
STATIC mp_obj_t machine_lightsleep(size_t n_args, const mp_obj_t *args) {
- if (n_args == 0) {
- for (;;) {
- MICROPY_EVENT_POLL_HOOK
+ mp_int_t delay_ms = 0;
+ bool use_timer_alarm = false;
+
+ if (n_args == 1) {
+ delay_ms = mp_obj_get_int(args[0]);
+ if (delay_ms <= 1) {
+ // Sleep is too small, just use standard delay.
+ mp_hal_delay_ms(delay_ms);
+ return mp_const_none;
+ }
+ use_timer_alarm = delay_ms < (1ULL << 32) / 1000;
+ if (use_timer_alarm) {
+ // Use timer alarm to wake.
+ } else {
+ // TODO: Use RTC alarm to wake.
+ mp_raise_ValueError(MP_ERROR_TEXT("sleep too long"));
}
+ }
+
+ const uint32_t xosc_hz = XOSC_MHZ * 1000000;
+
+ // Disable USB and ADC clocks.
+ clock_stop(clk_usb);
+ clock_stop(clk_adc);
+
+ // CLK_REF = XOSC
+ clock_configure(clk_ref, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, 0, xosc_hz, xosc_hz);
+
+ // CLK_SYS = CLK_REF
+ clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF, 0, xosc_hz, xosc_hz);
+
+ // CLK_RTC = XOSC / 256
+ clock_configure(clk_rtc, 0, CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC, xosc_hz, xosc_hz / 256);
+
+ // CLK_PERI = CLK_SYS
+ clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, xosc_hz, xosc_hz);
+
+ // Disable PLLs.
+ pll_deinit(pll_sys);
+ pll_deinit(pll_usb);
+
+ // Disable ROSC.
+ rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB;
+
+ if (n_args == 0) {
+ xosc_dormant();
} else {
- mp_hal_delay_ms(mp_obj_get_int(args[0]));
+ uint32_t sleep_en0 = clocks_hw->sleep_en0;
+ uint32_t sleep_en1 = clocks_hw->sleep_en1;
+ clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS;
+ if (use_timer_alarm) {
+ // Use timer alarm to wake.
+ clocks_hw->sleep_en1 = CLOCKS_SLEEP_EN1_CLK_SYS_TIMER_BITS;
+ timer_hw->alarm[3] = timer_hw->timerawl + delay_ms * 1000;
+ } else {
+ // TODO: Use RTC alarm to wake.
+ clocks_hw->sleep_en1 = 0;
+ }
+ scb_hw->scr |= M0PLUS_SCR_SLEEPDEEP_BITS;
+ __wfi();
+ scb_hw->scr &= ~M0PLUS_SCR_SLEEPDEEP_BITS;
+ clocks_hw->sleep_en0 = sleep_en0;
+ clocks_hw->sleep_en1 = sleep_en1;
}
+
+ // Enable ROSC.
+ rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB;
+
+ // Bring back all clocks.
+ clocks_init();
+
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lightsleep_obj, 0, 1, machine_lightsleep);