b004e7e397
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>
268 lines
10 KiB
C
268 lines
10 KiB
C
/*
|
|
* This file is part of the MicroPython project, http://micropython.org/
|
|
*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2020-2021 Damien P. George
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "py/runtime.h"
|
|
#include "py/mphal.h"
|
|
#include "shared/runtime/pyexec.h"
|
|
#include "extmod/machine_bitstream.h"
|
|
#include "extmod/machine_i2c.h"
|
|
#include "extmod/machine_mem.h"
|
|
#include "extmod/machine_pulse.h"
|
|
#include "extmod/machine_pwm.h"
|
|
#include "extmod/machine_signal.h"
|
|
#include "extmod/machine_spi.h"
|
|
|
|
#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"
|
|
|
|
#if MICROPY_PY_MACHINE
|
|
|
|
#define RP2_RESET_PWRON (1)
|
|
#define RP2_RESET_WDT (3)
|
|
|
|
STATIC mp_obj_t machine_unique_id(void) {
|
|
pico_unique_board_id_t id;
|
|
pico_get_unique_board_id(&id);
|
|
return mp_obj_new_bytes(id.id, sizeof(id.id));
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id);
|
|
|
|
STATIC mp_obj_t machine_soft_reset(void) {
|
|
pyexec_system_exit = PYEXEC_FORCED_EXIT;
|
|
mp_raise_type(&mp_type_SystemExit);
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset);
|
|
|
|
STATIC mp_obj_t machine_reset(void) {
|
|
watchdog_reboot(0, SRAM_END, 0);
|
|
for (;;) {
|
|
__wfi();
|
|
}
|
|
return mp_const_none;
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset);
|
|
|
|
STATIC mp_obj_t machine_reset_cause(void) {
|
|
int reset_cause;
|
|
if (watchdog_caused_reboot()) {
|
|
reset_cause = RP2_RESET_WDT;
|
|
} else {
|
|
reset_cause = RP2_RESET_PWRON;
|
|
}
|
|
return MP_OBJ_NEW_SMALL_INT(reset_cause);
|
|
}
|
|
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 (;;) {
|
|
}
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_bootloader_obj, 0, 1, machine_bootloader);
|
|
|
|
STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) {
|
|
if (n_args == 0) {
|
|
return MP_OBJ_NEW_SMALL_INT(mp_hal_get_cpu_freq());
|
|
} else {
|
|
mp_int_t freq = mp_obj_get_int(args[0]);
|
|
if (!set_sys_clock_khz(freq / 1000, false)) {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("cannot change frequency"));
|
|
}
|
|
#if MICROPY_HW_ENABLE_UART_REPL
|
|
setup_default_uart();
|
|
mp_uart_init();
|
|
#endif
|
|
return mp_const_none;
|
|
}
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq);
|
|
|
|
STATIC mp_obj_t machine_idle(void) {
|
|
best_effort_wfe_or_timeout(make_timeout_time_ms(1));
|
|
return mp_const_none;
|
|
}
|
|
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) {
|
|
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 {
|
|
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);
|
|
|
|
STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *args) {
|
|
machine_lightsleep(n_args, args);
|
|
return machine_reset();
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_deepsleep_obj, 0, 1, machine_deepsleep);
|
|
|
|
STATIC mp_obj_t machine_disable_irq(void) {
|
|
uint32_t state = MICROPY_BEGIN_ATOMIC_SECTION();
|
|
return mp_obj_new_int(state);
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq);
|
|
|
|
STATIC mp_obj_t machine_enable_irq(mp_obj_t state_in) {
|
|
uint32_t state = mp_obj_get_int(state_in);
|
|
MICROPY_END_ATOMIC_SECTION(state);
|
|
return mp_const_none;
|
|
}
|
|
MP_DEFINE_CONST_FUN_OBJ_1(machine_enable_irq_obj, machine_enable_irq);
|
|
|
|
STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
|
|
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) },
|
|
{ MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&machine_soft_reset_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_bootloader), MP_ROM_PTR(&machine_bootloader_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_lightsleep), MP_ROM_PTR(&machine_lightsleep_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_deepsleep), MP_ROM_PTR(&machine_deepsleep_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) },
|
|
|
|
#if MICROPY_PY_MACHINE_BITSTREAM
|
|
{ MP_ROM_QSTR(MP_QSTR_bitstream), MP_ROM_PTR(&machine_bitstream_obj) },
|
|
#endif
|
|
{ MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hw_i2c_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_I2S), MP_ROM_PTR(&machine_i2s_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_spi_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) },
|
|
{ MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&machine_wdt_type) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_PWRON_RESET), MP_ROM_INT(RP2_RESET_PWRON) },
|
|
{ MP_ROM_QSTR(MP_QSTR_WDT_RESET), MP_ROM_INT(RP2_RESET_WDT) },
|
|
};
|
|
STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table);
|
|
|
|
const mp_obj_module_t mp_module_machine = {
|
|
.base = { &mp_type_module },
|
|
.globals = (mp_obj_dict_t *)&machine_module_globals,
|
|
};
|
|
|
|
MP_REGISTER_MODULE(MP_QSTR_umachine, mp_module_machine);
|
|
|
|
#endif // MICROPY_PY_MACHINE
|