diff --git a/.gitmodules b/.gitmodules index e6712c1f9f4..dbeceecb2dc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -68,3 +68,7 @@ [submodule "lib/arduino-lib"] path = lib/arduino-lib url = https://github.com/arduino/arduino-lib-mpy.git +[submodule "lib/pico-extras"] + path = lib/pico-extras + url = https://github.com/raspberrypi/pico-extras + branch = sdk-1.5.1 diff --git a/lib/pico-extras b/lib/pico-extras new file mode 160000 index 00000000000..ed98c7acb69 --- /dev/null +++ b/lib/pico-extras @@ -0,0 +1 @@ +Subproject commit ed98c7acb694757715ede81c044a7404e1762499 diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 904925ae3f5..4ed0672ad0a 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 @@ -83,6 +89,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}) @@ -139,6 +146,7 @@ set(MICROPY_SOURCE_PORT machine_spi.c machine_timer.c main.c + modpicosleep.c modrp2.c mphalport.c mpnetworkport.c @@ -169,6 +177,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 @@ -192,6 +201,8 @@ set(PICO_SDK_COMPONENTS hardware_pwm hardware_regs hardware_rtc + hardware_rosc + hardware_sleep hardware_spi hardware_structs hardware_sync @@ -204,6 +215,7 @@ set(PICO_SDK_COMPONENTS pico_bootrom pico_multicore pico_platform + pico_runtime pico_stdio pico_stdlib pico_sync diff --git a/ports/rp2/modpicosleep.c b/ports/rp2/modpicosleep.c new file mode 100644 index 00000000000..d4bd7dcf5b3 --- /dev/null +++ b/ports/rp2/modpicosleep.c @@ -0,0 +1,157 @@ +// Credit to https://github.com/ghubcoder/micropython-pico-deepsleep + +#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" + +#define STATIC static + +static void sleep_callback(void) +{ + return; +} + +void datetime_to_tm(const datetime_t *dt, struct tm *tm_time) { + tm_time->tm_year = dt->year - 1900; // struct tm year is year since 1900 + tm_time->tm_mon = dt->month - 1; // struct tm month is 0-11 + tm_time->tm_mday = dt->day; + tm_time->tm_hour = dt->hour; + tm_time->tm_min = dt->min; + tm_time->tm_sec = dt->sec; +} + +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; + } + + struct tm tm_time; + datetime_t now; + rtc_get_datetime(&now); + datetime_to_tm(&now, &tm_time); + time_t epoch = mktime(&tm_time); + epoch += seconds_to_sleep; + + // 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); + + struct tm* t = gmtime(&epoch); + + datetime_t t_alarm = { + .year = -1, //t->tm_year + 1900, + .month = t->tm_mon + 1, + .day = t->tm_mday, + .dotw = -1, //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_pin(mp_obj_t pin_obj, mp_obj_t edge_obj, mp_obj_t high_obj) { + mp_int_t pin = mp_obj_get_int(pin_obj); + bool edge = mp_obj_get_int(edge_obj) == 1; + bool high = mp_obj_get_int(high_obj) == 1; + + uint scb_orig = scb_hw->scr; + uint clock0_orig = clocks_hw->sleep_en0; + uint clock1_orig = clocks_hw->sleep_en1; + + sleep_run_from_rosc(); + + sleep_goto_dormant_until_pin(pin, edge, high); + recover_from_sleep(scb_orig, clock0_orig, clock1_orig); + + return mp_const_none; +} + +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}; + + + //sleep_run_from_xosc(); + sleep_run_from_rosc(); + // // Start the Real time clock + if (!rtc_running()) { + rtc_init(); + } + // 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); +MP_DEFINE_CONST_FUN_OBJ_3(picosleep_pin_obj, picosleep_pin); + +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)}, + {MP_ROM_QSTR(MP_QSTR_pin), MP_ROM_PTR(&picosleep_pin_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 6ef994bb832..a0f2c36d22c 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -194,6 +194,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 00000000000..ac97b74780e --- /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 ${PICO_EXTRAS_PATH}") +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) diff --git a/py/usermod.cmake b/py/usermod.cmake index 85327628374..64e132a0c6c 100644 --- a/py/usermod.cmake +++ b/py/usermod.cmake @@ -2,6 +2,15 @@ add_library(usermod INTERFACE) function(usermod_gather_sources SOURCES_VARNAME INCLUDE_DIRECTORIES_VARNAME INCLUDED_VARNAME LIB) + + message("Target ${LIB}...") + string(FIND "${LIB}" "::" pos) + if (NOT ${pos} EQUAL -1) + message(STATUS "Skipping target with '::' in its name: ${LIB}") + return() + endif() + + if (NOT ${LIB} IN_LIST ${INCLUDED_VARNAME}) list(APPEND ${INCLUDED_VARNAME} ${LIB})