diff --git a/.gitmodules b/.gitmodules index 75bffdadddeb8..95675132938ef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -59,3 +59,6 @@ [submodule "lib/protobuf-c"] path = lib/protobuf-c url = https://github.com/protobuf-c/protobuf-c.git +[submodule "lib/pico-extras"] + path = lib/pico-extras + url = https://github.com/raspberrypi/pico-extras.git diff --git a/README.md b/README.md index f5bc6d78f0a40..ca2450ea292b6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,38 @@ [![Unix CI badge](https://github.com/micropython/micropython/actions/workflows/ports_unix.yml/badge.svg)](https://github.com/micropython/micropython/actions?query=branch%3Amaster+event%3Apush) [![STM32 CI badge](https://github.com/micropython/micropython/actions/workflows/ports_stm32.yml/badge.svg)](https://github.com/micropython/micropython/actions?query=branch%3Amaster+event%3Apush) [![Docs CI badge](https://github.com/micropython/micropython/actions/workflows/docs.yml/badge.svg)](https://docs.micropython.org/) [![codecov](https://codecov.io/gh/micropython/micropython/branch/master/graph/badge.svg?token=I92PfD05sD)](https://codecov.io/gh/micropython/micropython) +This is a fork of the MicroPython project to allow deep sleeping and waking +from the internal real time clock of the Raspberry Pico. + +This can be built with the following commands: + +```bash +git clone https://github.com/ghubcoder/micropython-pico-deepsleep.git +cd micropython-pico-deepsleep/ +make -C mpy-cross/ +git submodule update --init -- lib/pico-sdk +git submodule update --init -- lib/pico-extras +git submodule update --init -- lib/tinyusb +cd ports/rp2sleep +make -j4 +``` + +This will create a `firmware.uf2` file which can then be loaded onto your +Pico in the usual way using the BOOTSEL button. + +A precompiled binary can be found [here](https://github.com/ghubcoder/micropython-pico-deepsleep/releases/tag/v1.1-pico-deepsleep). + +Example usage: + +```python +import picosleep +picosleep.seconds(60) +``` + +Please see [this](https://ghubcoder.github.io/posts/deep-sleeping-the-pico-micropython/) +post for more information. + +Original readme follows: + The MicroPython project =======================

diff --git a/lib/fsp b/lib/fsp index e78939d32d1cc..55bffd3a71cbe 160000 --- a/lib/fsp +++ b/lib/fsp @@ -1 +1 @@ -Subproject commit e78939d32d1ccea9f0ba8bb42c51aceffd386b9b +Subproject commit 55bffd3a71cbeed2104cf30e7a39b641d8c1ff48 diff --git a/lib/pico-extras b/lib/pico-extras new file mode 160000 index 0000000000000..2309d56a3af1d --- /dev/null +++ b/lib/pico-extras @@ -0,0 +1 @@ +Subproject commit 2309d56a3af1df697b33275073c98e92ac93b196 diff --git a/lib/stm32lib b/lib/stm32lib index 928df866e4d28..eb80f0126e506 160000 --- a/lib/stm32lib +++ b/lib/stm32lib @@ -1 +1 @@ -Subproject commit 928df866e4d287ebc3c60726151513ebee609128 +Subproject commit eb80f0126e50687aac966f4c39a2b5a5deffbe78 diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 3a46fdaa7f47b..c9fa6746d4916 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -14,6 +14,12 @@ else() set(PICO_SDK_PATH ../../lib/pico-sdk) endif() +if (PICO_EXTRAS_PATH_OVERRIDE) + set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH_OVERRIDE}) +else() + set(PICO_EXTRAS_PATH ../../lib/pico-extras) +endif() + # Use the local tinyusb instead of the one in pico-sdk set(PICO_TINYUSB_PATH ${MICROPY_DIR}/lib/tinyusb) # Use the local lwip instead of the one in pico-sdk @@ -71,6 +77,7 @@ string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/tinyusb) include(${MICROPY_DIR}/py/py.cmake) include(${MICROPY_DIR}/extmod/extmod.cmake) include(${PICO_SDK_PATH}/pico_sdk_init.cmake) +include(pico_extras_import.cmake) # Define the top-level project project(${MICROPY_TARGET}) @@ -125,6 +132,7 @@ set(MICROPY_SOURCE_PORT machine_spi.c machine_timer.c main.c + modpicosleep.c modrp2.c mphalport.c mpnetworkport.c @@ -154,6 +162,7 @@ set(MICROPY_SOURCE_QSTR ${MICROPY_PORT_DIR}/machine_timer.c ${MICROPY_PORT_DIR}/machine_uart.c ${MICROPY_PORT_DIR}/machine_wdt.c + ${MICROPY_PORT_DIR}/modpicosleep.c ${MICROPY_PORT_DIR}/modrp2.c ${MICROPY_PORT_DIR}/modos.c ${MICROPY_PORT_DIR}/rp2_flash.c @@ -177,6 +186,8 @@ set(PICO_SDK_COMPONENTS hardware_pwm hardware_regs hardware_rtc + hardware_rosc + hardware_sleep hardware_spi hardware_structs hardware_sync @@ -189,6 +200,7 @@ set(PICO_SDK_COMPONENTS pico_bootrom pico_multicore pico_platform + pico_runtime pico_stdio pico_stdlib pico_sync diff --git a/ports/rp2/boards/RPI_PICO/mpconfigboard.h b/ports/rp2/boards/RPI_PICO/mpconfigboard.h index c39bc4d489bb6..aee239621ea8c 100644 --- a/ports/rp2/boards/RPI_PICO/mpconfigboard.h +++ b/ports/rp2/boards/RPI_PICO/mpconfigboard.h @@ -1,3 +1,3 @@ // Board and hardware specific configuration -#define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico" +#define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico (sleep)" #define MICROPY_HW_FLASH_STORAGE_BYTES (1408 * 1024) diff --git a/ports/rp2/boards/RPI_PICO_W/mpconfigboard.h b/ports/rp2/boards/RPI_PICO_W/mpconfigboard.h index ef812b6301356..dfbe0906d3916 100644 --- a/ports/rp2/boards/RPI_PICO_W/mpconfigboard.h +++ b/ports/rp2/boards/RPI_PICO_W/mpconfigboard.h @@ -1,5 +1,5 @@ // Board and hardware specific configuration -#define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico W" +#define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico W (sleep)" // todo: We need something to check our binary size #define MICROPY_HW_FLASH_STORAGE_BYTES (848 * 1024) diff --git a/ports/rp2/modpicosleep.c b/ports/rp2/modpicosleep.c new file mode 100644 index 0000000000000..2b4be7e8a33a9 --- /dev/null +++ b/ports/rp2/modpicosleep.c @@ -0,0 +1,113 @@ + +#include "py/runtime.h" +#include "pico/sleep.h" +#include "pico/stdlib.h" +#include +#include +#include +#include +#include +#include "hardware/clocks.h" +#include "hardware/rosc.h" +#include "hardware/structs/scb.h" + +static void sleep_callback(void) +{ + return; +} + +static void rtc_sleep_seconds(uint32_t seconds_to_sleep) +{ + + // Hangs if we attempt to sleep for 1 second.... + // Guard against this and perform a normal sleep + if (seconds_to_sleep == 1) + { + sleep_ms(1000); + return; + } + + int y = 2020, m = 6, d = 5, hour = 15, mins = 45, secs = 0; + struct tm t = {.tm_year = y - 1900, + .tm_mon = m - 1, + .tm_mday = d, + .tm_hour = hour, + .tm_min = mins, + .tm_sec = secs}; + + t.tm_sec += seconds_to_sleep; + mktime(&t); + + datetime_t t_alarm = { + .year = t.tm_year + 1900, + .month = t.tm_mon + 1, + .day = t.tm_mday, + .dotw = t.tm_wday, // 0 is Sunday, so 5 is Friday + .hour = t.tm_hour, + .min = t.tm_min, + .sec = t.tm_sec}; + + sleep_goto_sleep_until(&t_alarm, &sleep_callback); +} + +void recover_from_sleep(uint scb_orig, uint clock0_orig, uint clock1_orig) +{ + + // Re-enable ring Oscillator control + rosc_write(&rosc_hw->ctrl, ROSC_CTRL_ENABLE_BITS); + + // reset procs back to default + scb_hw->scr = scb_orig; + clocks_hw->sleep_en0 = clock0_orig; + clocks_hw->sleep_en1 = clock1_orig; + + // reset clocks + clocks_init(); + stdio_init_all(); + + return; +} + +STATIC mp_obj_t picosleep_seconds(mp_obj_t seconds_obj) +{ + mp_int_t seconds = mp_obj_get_int(seconds_obj); + stdio_init_all(); + // save values for later + uint scb_orig = scb_hw->scr; + uint clock0_orig = clocks_hw->sleep_en0; + uint clock1_orig = clocks_hw->sleep_en1; + + // crudely reset the clock each time + // to the value below + datetime_t t = { + .year = 2020, + .month = 06, + .day = 05, + .dotw = 5, // 0 is Sunday, so 5 is Friday + .hour = 15, + .min = 45, + .sec = 00}; + + // Start the Real time clock + rtc_init(); + sleep_run_from_xosc(); + rtc_set_datetime(&t); + rtc_sleep_seconds(seconds); + recover_from_sleep(scb_orig, clock0_orig, clock1_orig); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(picosleep_seconds_obj, picosleep_seconds); + +STATIC const mp_rom_map_elem_t picosleep_module_globals_table[] = { + {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picosleep)}, + {MP_ROM_QSTR(MP_QSTR_seconds), MP_ROM_PTR(&picosleep_seconds_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(picosleep_module_globals, picosleep_module_globals_table); + +const mp_obj_module_t mp_module_picosleep = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&picosleep_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_picosleep, mp_module_picosleep); \ No newline at end of file diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 8f073002e8632..2eeed7925a25e 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -185,6 +185,8 @@ #endif #endif +extern const struct _mp_obj_module_t mp_module_picosleep; + #if MICROPY_PY_NETWORK_CYW43 extern const struct _mp_obj_type_t mp_network_cyw43_type; #define MICROPY_HW_NIC_CYW43 \ diff --git a/ports/rp2/pico_extras_import.cmake b/ports/rp2/pico_extras_import.cmake new file mode 100644 index 0000000000000..c7117c4f8105f --- /dev/null +++ b/ports/rp2/pico_extras_import.cmake @@ -0,0 +1,63 @@ +# This is a copy of /external/pico_extras_import.cmake + +# This can be dropped into an external project to help locate pico-extras +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH)) + set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH}) + message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')") +endif () + +if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT)) + set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT}) + message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH)) + set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH}) + message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')") +endif () + +if (NOT PICO_EXTRAS_PATH) + if (PICO_EXTRAS_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_EXTRAS_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_extras + GIT_REPOSITORY https://github.com/raspberrypi/pico-extras + GIT_TAG master + ) + if (NOT pico_extras) + message("Downloading Raspberry Pi Pico Extras") + FetchContent_Populate(pico_extras) + set(PICO_EXTRAS_PATH ${pico_extras_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras") + set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras) + message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}") + else() + message(FATAL_ERROR + "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git." + ) + endif() + endif () +endif () + +set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS") +set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable") +set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS") + +get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") +message("PICO_EXTRAS_PATH is ${CMAKE_CURRENT_LIST_DIR}") +if (NOT EXISTS ${PICO_EXTRAS_PATH}) + message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found") +endif () + +set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE) + +add_subdirectory(${PICO_EXTRAS_PATH} pico_extras)