8000 rp2/modmachine: Implement lightsleep() with optional sleep period. · alphaFred/micropython@b004e7e · GitHub
[go: up one dir, main page]

Skip to content

Commit b004e7e

Browse files
committed
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 micropython#8770. Signed-off-by: Damien George <damien@micropython.org>
1 parent 932556d commit b004e7e

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed

ports/rp2/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ set(PICO_SDK_COMPONENTS
150150
hardware_i2c
151151
hardware_irq
152152
hardware_pio
153+
hardware_pll
153154
hardware_pwm
154155
hardware_regs
155156
hardware_rtc
@@ -159,6 +160,7 @@ set(PICO_SDK_COMPONENTS
159160
hardware_timer
160161
hardware_uart
161162
hardware_watchdog
163+
hardware_xosc
162164
pico_base_headers
163165
pico_binary_info
164166
pico_bootrom

ports/rp2/modmachine.c

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@
3838
#include "modmachine.h"
3939
#include "uart.h"
4040
#include "hardware/clocks.h"
41+
#include "hardware/pll.h"
42+
#include "hardware/structs/rosc.h"
43+
#include "hardware/structs/scb.h"
44+
#include "hardware/structs/syscfg.h"
4145
#include "hardware/watchdog.h"
46+
#include "hardware/xosc.h"
4247
#include "pico/bootrom.h"
4348
#include "pico/stdlib.h"
4449
#include "pico/unique_id.h"
@@ -83,6 +88,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause);
8388

8489
NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args) {
8590
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
91+
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB;
8692
reset_usb_boot(0, 0);
8793
for (;;) {
8894
}
@@ -113,13 +119,77 @@ STATIC mp_obj_t machine_idle(void) {
113119
STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle);
114120

115121
STATIC mp_obj_t 10000 machine_lightsleep(size_t n_args, const mp_obj_t *args) {
116-
if (n_args == 0) {
117-
for (;;) {
118-
MICROPY_EVENT_POLL_HOOK
122+
mp_int_t delay_ms = 0;
123+
bool use_timer_alarm = false;
124+
125+
if (n_args == 1) {
126+
delay_ms = mp_obj_get_int(args[0]);
127+
if (delay_ms <= 1) {
128+
// Sleep is too small, just use standard delay.
129+
mp_hal_delay_ms(delay_ms);
130+
return mp_const_none;
131+
}
132+
use_timer_alarm = delay_ms < (1ULL << 32) / 1000;
133+
if (use_timer_alarm) {
134+
// Use timer alarm to wake.
135+
} else {
136+
// TODO: Use RTC alarm to wake.
137+
mp_raise_ValueError(MP_ERROR_TEXT("sleep too long"));
119138
}
139+
}
140+
141+
const uint32_t xosc_hz = XOSC_MHZ * 1000000;
142+
143+
// Disable USB and ADC clocks.
144+
clock_stop(clk_usb);
145+
clock_stop(clk_adc);
146+
147+
// CLK_REF = XOSC
148+
clock_configure(clk_ref, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, 0, xosc_hz, xosc_hz);
149+
150+
// CLK_SYS = CLK_REF
151+
clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF, 0, xosc_hz, xosc_hz);
152+
153+
// CLK_RTC = XOSC / 256
154+
clock_configure(clk_rtc, 0, CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC, xosc_hz, xosc_hz / 256);
155+
156+
// CLK_PERI = CLK_SYS
157+
clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, xosc_hz, xosc_hz);
158+
159+
// Disable PLLs.
160+
pll_deinit(pll_sys);
161+
pll_deinit(pll_usb);
162+
163+
// Disable ROSC.
164+
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB;
165+
166+
if (n_args == 0) {
167+
xosc_dormant();
120168
} else {
121-
mp_hal_delay_ms(mp_obj_get_int(args[0]));
169+
uint32_t sleep_en0 = clocks_hw->sleep_en0;
170+
uint32_t sleep_en1 = clocks_hw->sleep_en1;
171+
clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS;
172+
if (use_timer_alarm) {
173+
// Use timer alarm to wake.
174+
clocks_hw->sleep_en1 = CLOCKS_SLEEP_EN1_CLK_SYS_TIMER_BITS;
175+
timer_hw->alarm[3] = timer_hw->timerawl + delay_ms * 1000;
176+
} else {
177+
// TODO: Use RTC alarm to wake.
178+
clocks_hw->sleep_en1 = 0;
179+
}
180+
scb_hw->scr |= M0PLUS_SCR_SLEEPDEEP_BITS;
181+
__wfi();
182+
scb_hw->scr &= ~M0PLUS_SCR_SLEEPDEEP_BITS;
183+
clocks_hw->sleep_en0 = sleep_en0;
184+
clocks_hw->sleep_en1 = sleep_en1;
122185
}
186+
187+
// Enable ROSC.
188+
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB;
189+
190+
// Bring back all clocks.
191+
clocks_init();
192+
123193
return mp_const_none;
124194
}
125195
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lightsleep_obj, 0, 1, machine_lightsleep);

0 commit comments

Comments
 (0)
0