diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 975197391c..e115bab770 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -681,6 +681,7 @@ msgid "Cannot vary frequency on a timer that is already in use" msgstr "" #: ports/esp32s2/common-hal/alarm/pin/PinAlarm.c +#: ports/nrf/common-hal/alarm/pin/PinAlarm.c msgid "Cannot wake on pin edge. Only level." msgstr "" @@ -1285,7 +1286,8 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/touchio/TouchIn.c #: ports/esp32s2/common-hal/alarm/touch/TouchAlarm.c -#: ports/esp32s2/common-hal/touchio/TouchIn.c shared-bindings/pwmio/PWMOut.c +#: ports/esp32s2/common-hal/touchio/TouchIn.c +#: ports/nrf/common-hal/alarm/pin/PinAlarm.c shared-bindings/pwmio/PWMOut.c #: shared-module/rgbmatrix/RGBMatrix.c msgid "Invalid pin" msgstr "" @@ -1663,6 +1665,7 @@ msgid "Only one TouchAlarm can be set in deep sleep." msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c +#: ports/nrf/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" diff --git a/main.c b/main.c index c30c2f960d..cd80ad0fa6 100755 --- a/main.c +++ b/main.c @@ -427,6 +427,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode) { // it may also return due to another interrupt, that's why we check // for deep sleep alarms above. If it wasn't a deep sleep alarm, // then we'll idle here again. + #if CIRCUITPY_ALARM common_hal_alarm_pretending_deep_sleep(); #else diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index dea373cd12..091bd7599b 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -94,6 +94,11 @@ else CFLAGS += -flto -flto-partition=none endif +ifeq ($(NRF_DEBUG_PRINT), 1) + CFLAGS += -DNRF_DEBUG_PRINT=1 + SRC_SUPERVISOR += supervisor/debug_uart.c +endif + # option to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk CFLAGS += $(OPTIMIZATION_FLAGS) diff --git a/ports/nrf/boards/pca10100/mpconfigboard.mk b/ports/nrf/boards/pca10100/mpconfigboard.mk index 74080a1ed9..dd59eb96c9 100644 --- a/ports/nrf/boards/pca10100/mpconfigboard.mk +++ b/ports/nrf/boards/pca10100/mpconfigboard.mk @@ -7,6 +7,7 @@ MCU_CHIP = nrf52833 INTERNAL_FLASH_FILESYSTEM = 1 +CIRCUITPY_ALARM = 0 CIRCUITPY_AUDIOMP3 = 0 CIRCUITPY_BITBANGIO = 0 CIRCUITPY_BITMAPTOOLS = 0 diff --git a/ports/nrf/boards/simmel/mpconfigboard.mk b/ports/nrf/boards/simmel/mpconfigboard.mk index 71d64bd670..e235fd1051 100644 --- a/ports/nrf/boards/simmel/mpconfigboard.mk +++ b/ports/nrf/boards/simmel/mpconfigboard.mk @@ -10,6 +10,7 @@ MCU_CHIP = nrf52833 INTERNAL_FLASH_FILESYSTEM = 1 +CIRCUITPY_ALARM = 0 CIRCUITPY_AESIO = 1 CIRCUITPY_AUDIOMP3 = 0 CIRCUITPY_BITMAPTOOLS = 0 @@ -24,6 +25,8 @@ CIRCUITPY_MSGPACK = 0 CIRCUITPY_NEOPIXEL_WRITE = 0 CIRCUITPY_NVM = 0 CIRCUITPY_PIXELBUF = 0 +CIRCUITPY_PULSEIO = 0 +CIRCUITPY_PWMIO = 1 CIRCUITPY_RGBMATRIX = 0 CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 1 diff --git a/ports/nrf/common-hal/alarm/SleepMemory.c b/ports/nrf/common-hal/alarm/SleepMemory.c new file mode 100644 index 0000000000..e20f893567 --- /dev/null +++ b/ports/nrf/common-hal/alarm/SleepMemory.c @@ -0,0 +1,118 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Junji Sakai + * + * 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 +#include "py/runtime.h" +#include "common-hal/alarm/__init__.h" +#include "common-hal/alarm/SleepMemory.h" +#include "nrf_power.h" + +#ifdef NRF_DEBUG_PRINT +extern void dbg_dump_RAMreg(void); +#include "supervisor/serial.h" // dbg_printf() +#endif + +__attribute__((section(".uninitialized"))) static uint8_t _sleepmem[SLEEP_MEMORY_LENGTH]; +__attribute__((section(".uninitialized"))) uint8_t sleepmem_wakeup_event; +__attribute__((section(".uninitialized"))) uint8_t sleepmem_wakeup_pin; +__attribute__((section(".uninitialized"))) static uint32_t _sleepmem_magicnum; +#define SLEEP_MEMORY_DATA_GUARD 0xad0000af +#define SLEEP_MEMORY_DATA_GUARD_MASK 0xff0000ff + +static int is_sleep_memory_valid(void) { + if ((_sleepmem_magicnum & SLEEP_MEMORY_DATA_GUARD_MASK) + == SLEEP_MEMORY_DATA_GUARD) { + return 1; + } + return 0; +} + +void set_memory_retention(void) { + // set RAM[n].POWER register for RAM retention + // nRF52840 has RAM[0..7].Section[0..1] and RAM[8].Section[0..5] + // nRF52833 has RAM[0..7].Section[0..1] and RAM[8].Section[0,1] + for(int block = 0; block <= 7; ++block) { + nrf_power_rampower_mask_on(NRF_POWER, block, + NRF_POWER_RAMPOWER_S0RETENTION_MASK | + NRF_POWER_RAMPOWER_S1RETENTION_MASK); + }; +#ifdef NRF52840 + nrf_power_rampower_mask_on(NRF_POWER, 8, + NRF_POWER_RAMPOWER_S0RETENTION_MASK | + NRF_POWER_RAMPOWER_S1RETENTION_MASK | + NRF_POWER_RAMPOWER_S2RETENTION_MASK | + NRF_POWER_RAMPOWER_S3RETENTION_MASK | + NRF_POWER_RAMPOWER_S4RETENTION_MASK | + NRF_POWER_RAMPOWER_S5RETENTION_MASK); +#endif +#ifdef NRF52833 + nrf_power_rampower_mask_on(NRF_POWER, 8, + NRF_POWER_RAMPOWER_S0RETENTION_MASK | + NRF_POWER_RAMPOWER_S1RETENTION_MASK); +#endif +} + +static void initialize_sleep_memory(void) { + memset((uint8_t *)_sleepmem, 0, SLEEP_MEMORY_LENGTH); + sleepmem_wakeup_event = 0; + sleepmem_wakeup_pin = 0; + + set_memory_retention(); +#ifdef NRF_DEBUG_PRINT + //dbg_dump_RAMreg(); +#endif + + _sleepmem_magicnum = SLEEP_MEMORY_DATA_GUARD; +} + +void alarm_sleep_memory_reset(void) { + if (!is_sleep_memory_valid()) { + initialize_sleep_memory(); +#ifdef NRF_DEBUG_PRINT + dbg_printf("sleep memory initialized\r\n"); +#endif + } +} + +uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self) { + return sizeof(_sleepmem); +} + +bool common_hal_alarm_sleep_memory_set_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, const uint8_t* values, uint32_t len) { + if (start_index + len > sizeof(_sleepmem)) { + return false; + } + + memcpy((uint8_t *) (_sleepmem + start_index), values, len); + return true; +} + +void common_hal_alarm_sleep_memory_get_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, uint8_t* values, uint32_t len) { + if (start_index + len > sizeof(_sleepmem)) { + return; + } + memcpy(values, (uint8_t *) (_sleepmem + start_index), len); +} diff --git a/ports/nrf/common-hal/alarm/SleepMemory.h b/ports/nrf/common-hal/alarm/SleepMemory.h new file mode 100644 index 0000000000..8fd702ea83 --- /dev/null +++ b/ports/nrf/common-hal/alarm/SleepMemory.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Junji Sakai + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM_SLEEPMEMORY_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM_SLEEPMEMORY_H + +#include "py/obj.h" + +#define SLEEP_MEMORY_LENGTH (256) + +typedef struct { + mp_obj_base_t base; +} alarm_sleep_memory_obj_t; + +extern void set_memory_retention(void); +extern void alarm_sleep_memory_reset(void); + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM_SLEEPMEMORY_H diff --git a/ports/nrf/common-hal/alarm/__init__.c b/ports/nrf/common-hal/alarm/__init__.c new file mode 100644 index 0000000000..3ce963f0d6 --- /dev/null +++ b/ports/nrf/common-hal/alarm/__init__.c @@ -0,0 +1,349 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * Copyright (c) 2021 Junji Sakai + * + * 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/gc.h" +#include "py/obj.h" +#include "py/objtuple.h" +#include "py/runtime.h" +#include +#include + +#include "shared-bindings/alarm/__init__.h" +#include "shared-bindings/alarm/SleepMemory.h" +#include "shared-bindings/alarm/pin/PinAlarm.h" +#include "shared-bindings/alarm/time/TimeAlarm.h" +#include "shared-bindings/alarm/touch/TouchAlarm.h" +#include "shared-bindings/time/__init__.h" + +#include "supervisor/port.h" +#include "supervisor/serial.h" // serial_connected() +#ifdef NRF_DEBUG_PRINT +#include "supervisor/serial.h" // dbg_printf() +extern int dbg_check_RTCprescaler(void); +#endif +#include "supervisor/qspi_flash.h" + +#include "nrf.h" +#include "nrf_power.h" +#include "nrfx.h" +#include "nrfx_gpiote.h" + +// Singleton instance of SleepMemory. +const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = { + .base = { + .type = &alarm_sleep_memory_type, + }, +}; + +void alarm_reset(void) { + alarm_sleep_memory_reset(); + alarm_pin_pinalarm_reset(); + alarm_time_timealarm_reset(); + alarm_touch_touchalarm_reset(); +} + +extern uint32_t reset_reason_saved; +STATIC nrf_sleep_source_t _get_wakeup_cause(void) { + if (alarm_pin_pinalarm_woke_us_up()) { + return NRF_SLEEP_WAKEUP_GPIO; + } + if (alarm_time_timealarm_woke_us_up()) { + return NRF_SLEEP_WAKEUP_TIMER; + } + if (alarm_touch_touchalarm_woke_us_up()) { + return NRF_SLEEP_WAKEUP_TOUCHPAD; + } + if (reset_reason_saved & NRF_POWER_RESETREAS_RESETPIN_MASK) { + return NRF_SLEEP_WAKEUP_RESETPIN; + } + else if (reset_reason_saved & NRF_POWER_RESETREAS_OFF_MASK) { + return NRF_SLEEP_WAKEUP_GPIO; + } + else if (reset_reason_saved & NRF_POWER_RESETREAS_VBUS_MASK) { + return NRF_SLEEP_WAKEUP_VBUS; + } + return NRF_SLEEP_WAKEUP_UNDEFINED; +} + +#ifdef NRF_DEBUG_PRINT +static const char* cause_str[] = { + "UNDEFINED", + "GPIO", + "TIMER", + "TOUCHPAD", + "VBUS", + "RESETPIN", +}; +void print_wakeup_cause(nrf_sleep_source_t cause) { + if (cause >= 0 && cause < NRF_SLEEP_WAKEUP_ZZZ) { + dbg_printf("wakeup cause = NRF_SLEEP_WAKEUP_%s\r\n", + cause_str[(int)cause]); + } +} +#endif + +bool common_hal_alarm_woken_from_sleep(void) { + nrf_sleep_source_t cause = _get_wakeup_cause(); +#ifdef NRF_DEBUG_PRINT + if (cause != NRF_SLEEP_WAKEUP_UNDEFINED) { + //print_wakeup_cause(cause); + } +#endif + return (cause == NRF_SLEEP_WAKEUP_GPIO || cause == NRF_SLEEP_WAKEUP_TIMER + || cause == NRF_SLEEP_WAKEUP_TOUCHPAD); +} + +STATIC mp_obj_t _get_wake_alarm(size_t n_alarms, const mp_obj_t *alarms) { + nrf_sleep_source_t cause = _get_wakeup_cause(); + switch (cause) { + case NRF_SLEEP_WAKEUP_TIMER: { + return alarm_time_timealarm_get_wakeup_alarm(n_alarms, alarms); + } + case NRF_SLEEP_WAKEUP_TOUCHPAD: { + return alarm_touch_touchalarm_get_wakeup_alarm(n_alarms, alarms); + } + case NRF_SLEEP_WAKEUP_GPIO: { + return alarm_pin_pinalarm_get_wakeup_alarm(n_alarms, alarms); + } + default: + break; + } + return mp_const_none; +} + +mp_obj_t common_hal_alarm_get_wake_alarm(void) { + mp_obj_t obj = _get_wake_alarm(0, NULL); + return obj; +} + +// Set up light sleep or deep sleep alarms. +STATIC void _setup_sleep_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { + alarm_pin_pinalarm_set_alarms(deep_sleep, n_alarms, alarms); + alarm_time_timealarm_set_alarms(deep_sleep, n_alarms, alarms); + alarm_touch_touchalarm_set_alarm(deep_sleep, n_alarms, alarms); +} + +// TODO: this handles all possible types of wakeup, which is redundant with main. +// revise to extract all parts essential to enabling sleep wakeup, but leave the +// alarm/non-alarm sorting to the existing main loop. +void system_on_idle_until_alarm(int64_t timediff_ms, uint32_t prescaler) { + bool have_timeout = false; + uint64_t start_tick = 0, end_tick = 0; + int64_t tickdiff; + +#if defined(MICROPY_QSPI_CS) + qspi_flash_enter_sleep(); +#endif + + if (timediff_ms != -1) { + have_timeout = true; +#if 0 + int64_t now = common_hal_time_monotonic_ms(); + dbg_printf("now_ms=%ld timediff_ms=%ld\r\n", (long)now, (long)timediff_ms); +#endif + if (timediff_ms < 0) timediff_ms = 0; + if (prescaler == 0) { + // 1 tick = 1/1024 sec = 1000/1024 ms + // -> 1 ms = 1024/1000 ticks + tickdiff = (mp_uint_t)(timediff_ms * 1024 / 1000); // ms -> ticks + } + else { + // 1 tick = prescaler/1024 sec = prescaler*1000/1024 ms + // -> 1ms = 1024/(1000*prescaler) ticks + tickdiff = (mp_uint_t)(timediff_ms * 1024 / (1000 * prescaler)); + } + start_tick = port_get_raw_ticks(NULL); + end_tick = start_tick + tickdiff; + } +#if 0 + dbg_printf("start_tick=%ld end_tick=%ld have_timeout=%c\r\n", (long)start_tick, (long)end_tick, have_timeout ? 'T' : 'F'); +#endif + + int64_t remaining; + sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_NONE; + sleepmem_wakeup_pin = WAKEUP_PIN_UNDEF; + +#ifdef NRF_DEBUG_PRINT + int ct = 40; + char reason = '?'; +#define WAKEUP_REASON(x) reason = (x) +#else +#define WAKEUP_REASON(x) +#endif + + while(1) { + if (mp_hal_is_interrupted()) { + WAKEUP_REASON('I'); + break; + } + if (serial_connected() && serial_bytes_available()) { + WAKEUP_REASON('S'); + break; + } + RUN_BACKGROUND_TASKS; + if (common_hal_alarm_woken_from_sleep()) { + WAKEUP_REASON('W'); + break; + } + if (have_timeout) { + remaining = end_tick - port_get_raw_ticks(NULL); + // We break a bit early so we don't risk setting the alarm before the time when we call + // sleep. + if (remaining < 1) { + WAKEUP_REASON('t'); + break; + } + port_interrupt_after_ticks(remaining); + } + // Idle until an interrupt happens. + port_idle_until_interrupt(); +#ifdef NRF_DEBUG_PRINT + if (ct > 0) { + dbg_printf("_"); + --ct; + } +#endif + if (have_timeout) { + remaining = end_tick - port_get_raw_ticks(NULL); + if (remaining <= 0) { + sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_TIMER; + WAKEUP_REASON('T'); + break; + } + } + } +#ifdef NRF_DEBUG_PRINT + dbg_printf("%c\r\n", reason); +#endif + +#if defined(MICROPY_QSPI_CS) + qspi_flash_exit_sleep(); +#endif + +#ifdef NRF_DEBUG_PRINT + tickdiff = port_get_raw_ticks(NULL) - start_tick; + double sec; + if (prescaler == 0) { + sec = (double)tickdiff / 1024; + } + else { + sec = (double)(tickdiff * prescaler) / 1024; + } + dbg_printf("lapse %6.1f sec\r\n", sec); +#endif +} + +mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms) { + mp_obj_t wake_alarm; + alarm_time_timealarm_clear_wakeup_time(); + _setup_sleep_alarms(false, n_alarms, alarms); + +#ifdef NRF_DEBUG_PRINT + dbg_printf("\r\nlight sleep..."); +#endif + + int64_t timediff_ms = alarm_time_timealarm_get_wakeup_timediff_ms(); + system_on_idle_until_alarm(timediff_ms, 0); + + if (mp_hal_is_interrupted()) { + wake_alarm = mp_const_none; + } + else { + wake_alarm = _get_wake_alarm(n_alarms, alarms); + } + alarm_reset(); + return wake_alarm; +} + +void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) { + alarm_time_timealarm_clear_wakeup_time(); + _setup_sleep_alarms(true, n_alarms, alarms); +} + +#define PRESCALER_VALUE_IN_DEEP_SLEEP (1024) + +void NORETURN common_hal_alarm_enter_deep_sleep(void) { + alarm_pin_pinalarm_prepare_for_deep_sleep(); + alarm_time_timealarm_prepare_for_deep_sleep(); + +#ifdef NRF_DEBUG_PRINT + dbg_printf("\r\ndeep sleep..."); +#endif + int64_t timediff_ms = alarm_time_timealarm_get_wakeup_timediff_ms(); + tick_set_prescaler(PRESCALER_VALUE_IN_DEEP_SLEEP -1); +#ifdef NRF_DEBUG_PRINT + dbg_check_RTCprescaler(); //XXX +#endif + system_on_idle_until_alarm(timediff_ms, PRESCALER_VALUE_IN_DEEP_SLEEP); + +#ifdef NRF_DEBUG_PRINT + dbg_printf("RESET...\r\n\r\n"); +#endif + + reset_cpu(); + + // should not reach here.. + while(1) ; +} + +// old version deep sleep code that was used in common_hal_alarm_enter_deep_sleep() +// for anyone who might want true System OFF sleep .. +#if 0 +void OLD_go_system_off(void) { + sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_NONE; + sleepmem_wakeup_pin = WAKEUP_PIN_UNDEF; + uint8_t sd_enabled; + sd_softdevice_is_enabled(&sd_enabled); + set_memory_retention(); + dbg_printf("OLD go system off.. %d\r\n", sd_enabled); + if (sd_enabled) { + sd_power_system_off(); + } + else { + NRF_POWER->SYSTEMOFF = 1; + } +} +#endif + +void common_hal_alarm_pretending_deep_sleep(void) { + alarm_pin_pinalarm_prepare_for_deep_sleep(); + alarm_time_timealarm_prepare_for_deep_sleep(); + +#ifdef NRF_DEBUG_PRINT + dbg_printf("\r\npretending to deep sleep..."); +#endif + + int64_t timediff_ms = alarm_time_timealarm_get_wakeup_timediff_ms(); + system_on_idle_until_alarm(timediff_ms, 0); + + alarm_reset(); +} + +void common_hal_alarm_gc_collect(void) { + gc_collect_ptr(shared_alarm_get_wake_alarm()); +} diff --git a/ports/nrf/common-hal/alarm/__init__.h b/ports/nrf/common-hal/alarm/__init__.h new file mode 100644 index 0000000000..47051dbd72 --- /dev/null +++ b/ports/nrf/common-hal/alarm/__init__.h @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert for Adafruit Industries. + * Copyright (c) 2021 Junji Sakai + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM__INIT__H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM__INIT__H + +#include "common-hal/alarm/SleepMemory.h" + +typedef enum { + NRF_SLEEP_WAKEUP_UNDEFINED, + NRF_SLEEP_WAKEUP_GPIO, + NRF_SLEEP_WAKEUP_TIMER, + NRF_SLEEP_WAKEUP_TOUCHPAD, + NRF_SLEEP_WAKEUP_VBUS, + NRF_SLEEP_WAKEUP_RESETPIN, + NRF_SLEEP_WAKEUP_ZZZ +} nrf_sleep_source_t; + +extern const alarm_sleep_memory_obj_t alarm_sleep_memory_obj; + +enum { + SLEEPMEM_WAKEUP_BY_NONE = 0, + SLEEPMEM_WAKEUP_BY_PIN = 1, + SLEEPMEM_WAKEUP_BY_TIMER = 2, +}; +#define WAKEUP_PIN_UNDEF 0xFF +extern uint8_t sleepmem_wakeup_event; +extern uint8_t sleepmem_wakeup_pin; + +extern void alarm_reset(void); + +#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM__INIT__H diff --git a/ports/nrf/common-hal/alarm/pin/PinAlarm.c b/ports/nrf/common-hal/alarm/pin/PinAlarm.c new file mode 100644 index 0000000000..59bb4a84a1 --- /dev/null +++ b/ports/nrf/common-hal/alarm/pin/PinAlarm.c @@ -0,0 +1,242 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2021 Junji Sakai + * + * 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 +#include + +#include "shared-bindings/alarm/pin/PinAlarm.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "common-hal/alarm/__init__.h" + +#include "nrfx.h" +#include "nrf_gpio.h" +#include "nrfx_gpiote.h" +#include "nrf_soc.h" +#include + +#include "supervisor/serial.h" // dbg_print + +#define WPIN_UNUSED 0xFF +volatile char _pinhandler_gpiote_count; +static bool pins_configured = false; + +extern uint32_t reset_reason_saved; +extern void dbg_dump_GPIOregs(void); + +void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, mcu_pin_obj_t *pin, bool value, bool edge, bool pull) { + if (edge) { + mp_raise_ValueError(translate("Cannot wake on pin edge. Only level.")); + } + if (pin->number >= NUMBER_OF_PINS) { + mp_raise_ValueError(translate("Invalid pin")); + } + self->pin = pin; + self->value = value; + self->pull = pull; +} + +mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self) { + return self->pin; +} + +bool common_hal_alarm_pin_pinalarm_get_value(alarm_pin_pinalarm_obj_t *self) { + return self->value; +} + +bool common_hal_alarm_pin_pinalarm_get_edge(alarm_pin_pinalarm_obj_t *self) { + return false; +} + +bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self) { + return self->pull; +} + + +static void pinalarm_gpiote_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { + ++_pinhandler_gpiote_count; + sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_PIN; + sleepmem_wakeup_pin = pin & 0xFF; +} + +bool alarm_pin_pinalarm_woke_us_up(void) { + return (sleepmem_wakeup_event == SLEEPMEM_WAKEUP_BY_PIN && + sleepmem_wakeup_pin != WAKEUP_PIN_UNDEF); +} + +mp_obj_t alarm_pin_pinalarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms) { + // First, check to see if we match any given alarms. + for (size_t i = 0; i < n_alarms; i++) { + if (!MP_OBJ_IS_TYPE(alarms[i], &alarm_pin_pinalarm_type)) { + continue; + } + alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); + if (alarm->pin->number == sleepmem_wakeup_pin) { + return alarms[i]; + } + } + alarm_pin_pinalarm_obj_t *alarm = m_new_obj(alarm_pin_pinalarm_obj_t); + alarm->base.type = &alarm_pin_pinalarm_type; + alarm->pin = NULL; + // Map the pin number back to a pin object. + for (size_t i = 0; i < mcu_pin_globals.map.used; i++) { + const mcu_pin_obj_t* pin_obj = MP_OBJ_TO_PTR(mcu_pin_globals.map.table[i].value); + if ((size_t) pin_obj->number == sleepmem_wakeup_pin) { + alarm->pin = mcu_pin_globals.map.table[i].value; + break; + } + } + return alarm; +} + +// These must be static because we need to configure pulls later, right before +// deep sleep. +static uint64_t high_alarms = 0; +static uint64_t low_alarms = 0; +static uint64_t pull_pins = 0; + +void alarm_pin_pinalarm_reset(void) { + for (size_t i = 0; i < 64; i++) { + uint64_t mask = 1ull << i; + bool high = (high_alarms & mask) != 0; + bool low = (low_alarms & mask) != 0; + if (!(high || low)) { + continue; + } + reset_pin_number(i); + nrfx_gpiote_in_event_disable((nrfx_gpiote_pin_t)i); + nrfx_gpiote_in_uninit((nrfx_gpiote_pin_t)i); + } + + high_alarms = 0; + low_alarms = 0; + pull_pins = 0; +} + +static void configure_pins_for_sleep(void) { + nrfx_err_t err; + if ( nrfx_gpiote_is_init() ) { + nrfx_gpiote_uninit(); + } + err = nrfx_gpiote_init(NRFX_GPIOTE_CONFIG_IRQ_PRIORITY); + assert(err == NRFX_SUCCESS); + (void)err; // to suppress unused warning + + _pinhandler_gpiote_count = 0; + + nrfx_gpiote_in_config_t cfg = { + .sense = NRF_GPIOTE_POLARITY_TOGGLE, + .pull = NRF_GPIO_PIN_PULLUP, + .is_watcher = false, + .hi_accuracy = true, + .skip_gpio_setup = false + }; + for(size_t i = 0; i < 64; ++i) { + uint64_t mask = 1ull << i; + if (((high_alarms & mask) == 0) && ((low_alarms & mask) == 0)) { + continue; + } + if (((high_alarms & mask) != 0) && ((low_alarms & mask) == 0)) { + cfg.sense = NRF_GPIOTE_POLARITY_LOTOHI; + cfg.pull = ((pull_pins & mask) != 0) ? + NRF_GPIO_PIN_PULLDOWN : NRF_GPIO_PIN_NOPULL; + } + else + if (((high_alarms & mask) == 0) && ((low_alarms & mask) != 0)) { + cfg.sense = NRF_GPIOTE_POLARITY_HITOLO; + cfg.pull = ((pull_pins & mask) != 0) ? + NRF_GPIO_PIN_PULLUP : NRF_GPIO_PIN_NOPULL; + } + else { + cfg.sense = NRF_GPIOTE_POLARITY_TOGGLE; + cfg.pull = NRF_GPIO_PIN_NOPULL; + } + err = nrfx_gpiote_in_init((nrfx_gpiote_pin_t)i, &cfg, + pinalarm_gpiote_handler); + assert(err == NRFX_SUCCESS); + nrfx_gpiote_in_event_enable((nrfx_gpiote_pin_t)i, true); + if (((high_alarms & mask) != 0) && ((low_alarms & mask) == 0)) { + nrf_gpio_cfg_sense_set((uint32_t)i, NRF_GPIO_PIN_SENSE_HIGH); + } + if (((high_alarms & mask) == 0) && ((low_alarms & mask) != 0)) { + nrf_gpio_cfg_sense_set((uint32_t)i, NRF_GPIO_PIN_SENSE_LOW); + } + } +} + +void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { + // Bitmask of wake up settings. + size_t high_count = 0; + size_t low_count = 0; + int pin_number = -1; + + for (size_t i = 0; i < n_alarms; i++) { + if (!MP_OBJ_IS_TYPE(alarms[i], &alarm_pin_pinalarm_type)) { + continue; + } + alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); + + pin_number = alarm->pin->number; + //dbg_printf("alarm_pin_pinalarm_set_alarms(pin#=%d, val=%d, pull=%d)\r\n", pin_number, alarm->value, alarm->pull); + if (alarm->value) { + high_alarms |= 1ull << pin_number; + high_count++; + } else { + low_alarms |= 1ull << pin_number; + low_count++; + } + if (alarm->pull) { + pull_pins |= 1ull << pin_number; + } + } + if (pin_number != -1) { + if (!deep_sleep) { + configure_pins_for_sleep(); + } + else { + // we don't setup gpio HW here but do them in + // alarm_pin_pinalarm_prepare_for_deep_sleep() below + reset_reason_saved = 0; + pins_configured = false; + } + } + else { + //dbg_printf("alarm_pin_pinalarm_set_alarms() no valid pins\r\n"); + } +} + +void alarm_pin_pinalarm_prepare_for_deep_sleep(void) { + if (!pins_configured) { + configure_pins_for_sleep(); + pins_configured = true; +#ifdef NRF_DEBUG_PRINT + //dbg_dump_GPIOregs(); +#endif + } +} diff --git a/ports/nrf/common-hal/alarm/pin/PinAlarm.h b/ports/nrf/common-hal/alarm/pin/PinAlarm.h new file mode 100644 index 0000000000..69684386fa --- /dev/null +++ b/ports/nrf/common-hal/alarm/pin/PinAlarm.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Junji Sakai + * + * 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/obj.h" +#include "py/objtuple.h" + +typedef struct { + mp_obj_base_t base; + mcu_pin_obj_t *pin; + bool value; + bool pull; +} alarm_pin_pinalarm_obj_t; + +void alarm_pin_pinalarm_reset(void); +void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms); +void alarm_pin_pinalarm_prepare_for_deep_sleep(void); +mp_obj_t alarm_pin_pinalarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms); +bool alarm_pin_pinalarm_woke_us_up(void); diff --git a/ports/nrf/common-hal/alarm/time/TimeAlarm.c b/ports/nrf/common-hal/alarm/time/TimeAlarm.c new file mode 100644 index 0000000000..09409e7ce4 --- /dev/null +++ b/ports/nrf/common-hal/alarm/time/TimeAlarm.c @@ -0,0 +1,102 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * Copyright (c) 2021 Junji Sakai + * + * 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 + +#include "common-hal/alarm/__init__.h" +#include "shared-bindings/alarm/time/TimeAlarm.h" +#include "shared-bindings/time/__init__.h" + +void common_hal_alarm_time_timealarm_construct(alarm_time_timealarm_obj_t *self, mp_float_t monotonic_time) { + self->monotonic_time = monotonic_time; +} + +mp_float_t common_hal_alarm_time_timealarm_get_monotonic_time(alarm_time_timealarm_obj_t *self) { + return self->monotonic_time; +} + +mp_obj_t alarm_time_timealarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms) { + // First, check to see if we match + for (size_t i = 0; i < n_alarms; i++) { + if (MP_OBJ_IS_TYPE(alarms[i], &alarm_time_timealarm_type)) { + return alarms[i]; + } + } + alarm_time_timealarm_obj_t *timer = m_new_obj(alarm_time_timealarm_obj_t); + timer->base.type = &alarm_time_timealarm_type; + // TODO: Set monotonic_time based on the RTC state. + timer->monotonic_time = 0.0f; + return timer; +} + +bool alarm_time_timealarm_woke_us_up(void) { + return sleepmem_wakeup_event == SLEEPMEM_WAKEUP_BY_TIMER; +} + +int64_t wakeup_time_saved =0; + +int64_t alarm_time_timealarm_get_wakeup_timediff_ms(void) { + if (wakeup_time_saved == 0) { + return -1; + } + return wakeup_time_saved - common_hal_time_monotonic_ms(); +} + +void alarm_time_timealarm_clear_wakeup_time(void) { + wakeup_time_saved = 0; +} + +void alarm_time_timealarm_reset(void) { + port_disable_interrupt_after_ticks_ch(1); + wakeup_time_saved = 0; +} + +void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { + bool timealarm_set = false; + alarm_time_timealarm_obj_t *timealarm = MP_OBJ_NULL; + wakeup_time_saved = 0; + + for (size_t i = 0; i < n_alarms; i++) { + if (!MP_OBJ_IS_TYPE(alarms[i], &alarm_time_timealarm_type)) { + continue; + } + if (timealarm_set) { + mp_raise_ValueError(translate("Only one alarm.time alarm can be set.")); + } + timealarm = MP_OBJ_TO_PTR(alarms[i]); + timealarm_set = true; + } + if (!timealarm_set) { + return; + } + + wakeup_time_saved = (int64_t)(timealarm->monotonic_time * 1000.0f); +} + +void alarm_time_timealarm_prepare_for_deep_sleep(void) { +} diff --git a/ports/nrf/common-hal/alarm/time/TimeAlarm.h b/ports/nrf/common-hal/alarm/time/TimeAlarm.h new file mode 100644 index 0000000000..2f65764e19 --- /dev/null +++ b/ports/nrf/common-hal/alarm/time/TimeAlarm.h @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Junji Sakai + * + * 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/obj.h" + +typedef struct { + mp_obj_base_t base; + mp_float_t monotonic_time; // values compatible with time.monotonic_time() +} alarm_time_timealarm_obj_t; + +extern volatile int rtc_woke_up_counter; +extern void port_disable_interrupt_after_ticks_ch(uint32_t channel); +extern void port_interrupt_after_ticks_ch(uint32_t channel, uint32_t ticks); + +// Find the alarm object that caused us to wake up or create an equivalent one. +mp_obj_t alarm_time_timealarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms); +// Check for the wake up alarm from pretend deep sleep. +bool alarm_time_timealarm_woke_us_up(void); +void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms); +void alarm_time_timealarm_reset(void); + +extern void alarm_time_timealarm_prepare_for_deep_sleep(void); +extern int64_t alarm_time_timealarm_get_wakeup_timediff_ms(void); +extern void alarm_time_timealarm_clear_wakeup_time(void); +extern void dbg_dump_RTCreg(void); +extern void tick_set_prescaler(uint32_t prescaler_val); diff --git a/ports/nrf/common-hal/alarm/touch/TouchAlarm.c b/ports/nrf/common-hal/alarm/touch/TouchAlarm.c new file mode 100644 index 0000000000..e85797e692 --- /dev/null +++ b/ports/nrf/common-hal/alarm/touch/TouchAlarm.c @@ -0,0 +1,53 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 microDev + * + * 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 "shared-bindings/alarm/touch/TouchAlarm.h" +#include "shared-bindings/microcontroller/__init__.h" + +//static volatile bool woke_up = false; + +void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) { + mp_raise_NotImplementedError(NULL); + (void)pin; +} + +mp_obj_t alarm_touch_touchalarm_get_wakeup_alarm(const size_t n_alarms, const mp_obj_t *alarms) { + return mp_const_none; +} + +void alarm_touch_touchalarm_set_alarm(const bool deep_sleep, const size_t n_alarms, const mp_obj_t *alarms) { +} + +void alarm_touch_touchalarm_prepare_for_deep_sleep(void) { +} + +bool alarm_touch_touchalarm_woke_us_up(void) { + return false; +} + +void alarm_touch_touchalarm_reset(void) { +} diff --git a/ports/nrf/common-hal/alarm/touch/TouchAlarm.h b/ports/nrf/common-hal/alarm/touch/TouchAlarm.h new file mode 100644 index 0000000000..755a3977ad --- /dev/null +++ b/ports/nrf/common-hal/alarm/touch/TouchAlarm.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 microDev + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H +#define MICROPY_INCLUDED_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H + +#include "py/obj.h" +#include "common-hal/microcontroller/Pin.h" + +typedef struct { + mp_obj_base_t base; + const mcu_pin_obj_t *pin; +} alarm_touch_touchalarm_obj_t; + +// Find the alarm object that caused us to wake up or create an equivalent one. +mp_obj_t alarm_touch_touchalarm_get_wakeup_alarm(const size_t n_alarms, const mp_obj_t *alarms); +// Check for the wake up alarm from pretend deep sleep. +void alarm_touch_touchalarm_set_alarm(const bool deep_sleep, const size_t n_alarms, const mp_obj_t *alarms); +void alarm_touch_touchalarm_prepare_for_deep_sleep(void); +bool alarm_touch_touchalarm_woke_us_up(void); +void alarm_touch_touchalarm_reset(void); + +#endif // MICROPY_INCLUDED_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H diff --git a/ports/nrf/common-hal/microcontroller/Processor.c b/ports/nrf/common-hal/microcontroller/Processor.c index b31c4c12d7..bcad002ffe 100644 --- a/ports/nrf/common-hal/microcontroller/Processor.c +++ b/ports/nrf/common-hal/microcontroller/Processor.c @@ -127,5 +127,24 @@ void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) { } mcu_reset_reason_t common_hal_mcu_processor_get_reset_reason(void) { - return RESET_REASON_UNKNOWN; + mcu_reset_reason_t r = RESET_REASON_UNKNOWN; + if (reset_reason_saved == 0) { + r = RESET_REASON_POWER_ON; + } + else if (reset_reason_saved & POWER_RESETREAS_RESETPIN_Msk) { + r = RESET_REASON_RESET_PIN; + } + else if (reset_reason_saved & POWER_RESETREAS_DOG_Msk) { + r = RESET_REASON_WATCHDOG; + } + else if (reset_reason_saved & POWER_RESETREAS_SREQ_Msk) { + r = RESET_REASON_SOFTWARE; + } + else if ((reset_reason_saved & POWER_RESETREAS_OFF_Msk) || + (reset_reason_saved & POWER_RESETREAS_LPCOMP_Msk) || + (reset_reason_saved & POWER_RESETREAS_NFC_Msk) || + (reset_reason_saved & POWER_RESETREAS_VBUS_Msk)) { + r = RESET_REASON_DEEP_SLEEP_ALARM; + } + return r; } diff --git a/ports/nrf/common-hal/microcontroller/Processor.h b/ports/nrf/common-hal/microcontroller/Processor.h index 4049c165fb..1abbe1e4eb 100644 --- a/ports/nrf/common-hal/microcontroller/Processor.h +++ b/ports/nrf/common-hal/microcontroller/Processor.h @@ -36,4 +36,6 @@ typedef struct { // Stores no state currently. } mcu_processor_obj_t; +extern uint32_t reset_reason_saved; + #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_MICROCONTROLLER_PROCESSOR_H diff --git a/ports/nrf/mpconfigport.mk b/ports/nrf/mpconfigport.mk index f349b3665f..bf02321410 100644 --- a/ports/nrf/mpconfigport.mk +++ b/ports/nrf/mpconfigport.mk @@ -46,6 +46,9 @@ CIRCUITPY_FRAMEBUFFERIO ?= 1 CIRCUITPY_COUNTIO ?= 1 CIRCUITPY_WATCHDOG ?= 1 +# Sleep and Wakeup +CIRCUITPY_ALARM ?= 1 + # nRF52840-specific ifeq ($(MCU_CHIP),nrf52840) diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h index 2c31263c58..912349556e 100644 --- a/ports/nrf/nrfx_config.h +++ b/ports/nrf/nrfx_config.h @@ -116,7 +116,7 @@ // GPIO interrupt #define NRFX_GPIOTE_ENABLED 1 -#define NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 1 +#define NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 2 #define NRFX_GPIOTE_CONFIG_IRQ_PRIORITY 7 // NVM controller diff --git a/ports/nrf/supervisor/debug_uart.c b/ports/nrf/supervisor/debug_uart.c new file mode 100644 index 0000000000..9cf33279f7 --- /dev/null +++ b/ports/nrf/supervisor/debug_uart.c @@ -0,0 +1,234 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Jun2Sak + * + * 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 +#include +#include +#include + +#ifdef NRF_DEBUG_PRINT + +#define DEBUG_UART_TXPIN 26 +#define DEBUG_UART_RXPIN 15 + +#include "nrfx.h" +#include "nrf_uart.h" +#include "nrf_gpio.h" +#include "nrf_rtc.h" +#include "nrfx_uarte.h" +#include "nrfx_rtc.h" +#include "supervisor/serial.h" // dbg_printf() +#include "shared-bindings/microcontroller/Processor.h" +#include "common-hal/alarm/__init__.h" + +extern const nrfx_rtc_t rtc_instance; // port.c +extern uint32_t reset_reason_saved; + +const nrfx_uarte_t _dbg_uart_inst = NRFX_UARTE_INSTANCE(1); +static int _dbg_uart_initialized = 0; +#define DBG_PBUF_LEN 80 +static char _dbg_pbuf[DBG_PBUF_LEN+1]; + +void _debug_uart_uninit(void) { + nrf_gpio_cfg(DEBUG_UART_TXPIN, + NRF_GPIO_PIN_DIR_INPUT, + NRF_GPIO_PIN_INPUT_DISCONNECT, + NRF_GPIO_PIN_NOPULL, + NRF_GPIO_PIN_S0S1, + NRF_GPIO_PIN_NOSENSE); + nrfx_uarte_uninit(&_dbg_uart_inst); +} + +void _debug_uart_init(void) { + //if (_dbg_uart_initialized) return; + nrfx_uarte_config_t config = { + .pseltxd = DEBUG_UART_TXPIN, + .pselrxd = DEBUG_UART_RXPIN, + .pselcts = NRF_UARTE_PSEL_DISCONNECTED, + .pselrts = NRF_UARTE_PSEL_DISCONNECTED, + .p_context = NULL, + .baudrate = NRF_UART_BAUDRATE_115200, + .interrupt_priority = 7, + .hal_cfg = { + .hwfc = NRF_UARTE_HWFC_DISABLED, + .parity = NRF_UARTE_PARITY_EXCLUDED + } + }; + nrfx_uarte_init(&_dbg_uart_inst, &config, NULL); + // drive config + nrf_gpio_cfg(config.pseltxd, + NRF_GPIO_PIN_DIR_OUTPUT, + NRF_GPIO_PIN_INPUT_DISCONNECT, + NRF_GPIO_PIN_PULLUP, // orig=NOPULL + NRF_GPIO_PIN_H0H1, // orig=S0S1 + NRF_GPIO_PIN_NOSENSE); + _dbg_uart_initialized = 1; + return; +} + +void _debug_print_substr(const char* text, uint32_t length) { + char* data = (char*)text; + int siz; + while(length != 0) { + if (length <= DBG_PBUF_LEN) { + siz = length; + } + else { + siz = DBG_PBUF_LEN; + } + memcpy(_dbg_pbuf, data, siz); + _dbg_pbuf[siz] = 0; + nrfx_uarte_tx(&_dbg_uart_inst, (uint8_t const*)_dbg_pbuf, siz); + data += siz; + length -= siz; + } +} + +void _debug_uart_deinit(void) { + nrfx_uarte_uninit(&_dbg_uart_inst); +} + +int dbg_printf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int ret = vprintf(fmt, ap); + va_end(ap); + return ret; +} + +void dbg_dump_RTCreg(void) { + dbg_printf("\r\nRTC2\r\n"); + NRF_RTC_Type *r = rtc_instance.p_reg; + dbg_printf("PRESCALER=%08X, ", (int)r->PRESCALER); + dbg_printf("COUNTER=%08X ", (int)r->COUNTER); + dbg_printf("INTENSET=%08X ", (int)r->INTENSET); + dbg_printf("EVTENSET=%08X\r\n", (int)r->EVTENSET); + dbg_printf("EVENTS_COMPARE[0..3]=%X,%X,%X,%X ", (int)r->EVENTS_COMPARE[0], (int)r->EVENTS_COMPARE[1], (int)r->EVENTS_COMPARE[2], (int)r->EVENTS_COMPARE[3]); + dbg_printf("CC[0..3]=%08X,%08X,%08X,%08X\r\n", (int)r->CC[0], (int)r->CC[1], (int)r->CC[2], (int)r->CC[3]); +} + +int dbg_check_RTCprescaler(void) { + NRF_RTC_Type *r = rtc_instance.p_reg; + if ((int)r->PRESCALER == 0) { + dbg_printf("****** PRESCALER == 0\r\n"); + return -1; + } + return 0; +} + +void dbg_dump_RAMreg(void) { + int i; + for(i = 0; i <= 8; ++i) { + dbg_printf(" RAM%d:%08X", i, (int)(NRF_POWER->RAM[i].POWER)); + if (i==4) dbg_printf("\r\n"); + } + dbg_printf("\r\n"); +} + +void dbg_dump_GPIOregs(void) { + int i, port, col; + + NRF_GPIO_Type *gpio[] = { NRF_P0, NRF_P1 }; + const char cnf_pull_chr[] = "-D*U"; // pull down, pull up + const char cnf_sense_chr[] = "-?HL"; // sense high, sense low + for(port=0, col=0; port<=1; ++port) { + for(i=0; i<32; ++i) { + uint32_t cnf = gpio[port]->PIN_CNF[i]; + if (cnf != 0x0002) { // changed from default value + dbg_printf("[%d_%02d]:%c%c%c%d%c ", port, i, + (cnf & 1) ? 'O' : 'I', // output, input + (cnf & 2) ? 'd' : 'c', // disconnected, connected + cnf_pull_chr[(cnf >> 2) & 3], + (int)((cnf >> 8) & 7), // drive config 0-7 + cnf_sense_chr[(cnf >> 16) & 3]); + if (++col >= 6) { + dbg_printf("\r\n"); + col = 0; + } + } + } + } + if (col > 0) dbg_printf("\r\n"); + + dbg_printf("GPIOTE\r\n"); + NRF_GPIOTE_Type const *reg = NRF_GPIOTE; + const char config_mode_chr[] = "-E-T"; // event, task + const char config_pol_chr[] = "-HLT"; // low-to-Hi, hi-to-Low, Toggle + const char config_outinit_chr[] = "01"; // initial value is 0 or 1 + for(i=0, col=0; i<8; ++i) { + uint32_t conf = reg->CONFIG[i]; + if (conf != 0) { // changed from default value + dbg_printf("CONFIG[%d]:%d_%02d,%c%c%c ", i, + (int)((conf >> 13) & 1), (int)((conf >> 8) & 0x1F), + config_mode_chr[conf & 3], + config_pol_chr[(conf >> 16) & 3], + (conf & 3) == 3 ? + config_outinit_chr[(conf >> 20) & 1] : '-'); + if (++col >= 4) { + dbg_printf("\r\n"); + col = 0; + } + } + } + if (col > 0) dbg_printf("\r\n"); + for(i=0; i<8; ++i) { + dbg_printf("EVENTS_IN[%d]:%X ", i, (int)(reg->EVENTS_IN[i])); + if ((i & 3) == 3) dbg_printf("\r\n"); + } + dbg_printf("EVENTS_PORT:%X INTENSET:%08X\r\n", + (int)(reg->EVENTS_PORT), (int)(reg->INTENSET)); +} + +void dbg_dumpQSPIreg(void) { + uint32_t r; + dbg_printf("QSPI\r\n"); + r = NRF_QSPI->IFCONFIG0; + dbg_printf("IFCONFIG0 READ=%ld write=%ld ADDR=%ld DPM=%ld PPSIZE=%ld\r\n", + r & 7, (r >> 3) & 7, (r >> 6) & 1, (r >> 7) & 1, (r >> 12) & 1); + r = NRF_QSPI->IFCONFIG1; + dbg_printf("IFCONFIG1 SCKDELAY=%ld SPIMODE=%ld SCKFREQ=%ld\r\n", + r & 0xFF, (r >> 25) & 1, (r >> 28) & 0xF); + r = NRF_QSPI->STATUS; + dbg_printf("STATUS DPM=%ld READY=%ld SREG=0x%02lX\r\n", + (r >> 2) & 1, (r >> 3) & 1, (r >> 24) & 0xFF); + r = NRF_QSPI->DPMDUR; + dbg_printf("DPMDUR ENTER=%ld EXIT=%ld\r\n", r & 0xFFFF, (r >> 16) & 0xFFFF); +} + +void dbg_dump_reset_reason(void) { + int reset_reason = (int)common_hal_mcu_processor_get_reset_reason(); + const char* rr_str[] = { + "POWER_ON", "BROWNOUT", "SOFTWARE", "DEEPSLEEPALARM", + "RESET_PIN", "WATCHDOG", "UNKNOWN" + }; + dbg_printf("reset_reason=%s\r\n", rr_str[reset_reason]); +} + +#else /*!NRF_DEBUG_PRINT*/ +int dbg_printf(const char *fmt, ...) { + return 0; +} +#endif /*!NRF_DEBUG_PRINT*/ diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 9a2acd5cab..e162c64837 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2021 Junji Sakai * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -51,6 +52,7 @@ #include "common-hal/rtc/RTC.h" #include "common-hal/neopixel_write/__init__.h" #include "common-hal/watchdog/WatchDogTimer.h" +#include "common-hal/alarm/__init__.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/rtc/__init__.h" @@ -73,9 +75,14 @@ static void power_warning_handler(void) { reset_into_safe_mode(BROWNOUT); } +#ifdef NRF_DEBUG_PRINT +extern void _debug_uart_init(void); +#endif + +uint32_t reset_reason_saved = 0; const nrfx_rtc_t rtc_instance = NRFX_RTC_INSTANCE(2); -const nrfx_rtc_config_t rtc_config = { +nrfx_rtc_config_t rtc_config = { .prescaler = RTC_FREQ_TO_PRESCALER(0x8000), .reliable = 0, .tick_latency = 0, @@ -100,6 +107,12 @@ void rtc_handler(nrfx_rtc_int_type_t int_type) { supervisor_tick(); } else if (int_type == NRFX_RTC_INT_COMPARE0) { nrfx_rtc_cc_set(&rtc_instance, 0, 0, false); + } else if (int_type == NRFX_RTC_INT_COMPARE1) { + // used in light sleep + #if CIRCUITPY_ALARM + sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_TIMER; + #endif + nrfx_rtc_cc_set(&rtc_instance, 1, 0, false); } } @@ -124,6 +137,22 @@ void tick_init(void) { } } +void tick_uninit(void) { + nrfx_rtc_counter_clear(&rtc_instance); + nrfx_rtc_disable(&rtc_instance); + nrfx_rtc_uninit(&rtc_instance); +} + +void tick_set_prescaler(uint32_t prescaler_val) { + tick_uninit(); + // update of prescaler value sometimes fails if we skip this delay.. + NRFX_DELAY_US(1000); + uint16_t prescaler_saved = rtc_config.prescaler; + rtc_config.prescaler = prescaler_val; + tick_init(); + rtc_config.prescaler = prescaler_saved; +} + safe_mode_t port_init(void) { nrf_peripherals_clocks_init(); @@ -153,11 +182,21 @@ safe_mode_t port_init(void) { analogin_init(); #endif + reset_reason_saved = NRF_POWER->RESETREAS; + // clear all RESET reason bits + NRF_POWER->RESETREAS = reset_reason_saved; + // clear wakeup event/pin when reset by reset-pin + if (reset_reason_saved & NRF_POWER_RESETREAS_RESETPIN_MASK) { + #if CIRCUITPY_ALARM + sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_NONE; + #endif + } + // If the board was reset by the WatchDogTimer, we may // need to boot into safe mode. Reset the RESETREAS bit // for the WatchDogTimer so we don't encounter this the // next time we reboot. - if (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk) { + if (reset_reason_saved & POWER_RESETREAS_DOG_Msk) { NRF_POWER->RESETREAS = POWER_RESETREAS_DOG_Msk; uint32_t usb_reg = NRF_POWER->USBREGSTATUS; @@ -219,6 +258,10 @@ void reset_port(void) { #endif reset_all_pins(); + +#ifdef NRF_DEBUG_PRINT + _debug_uart_init(); +#endif } void reset_to_bootloader(void) { @@ -295,7 +338,7 @@ void port_disable_tick(void) { nrfx_rtc_tick_disable(&rtc_instance); } -void port_interrupt_after_ticks(uint32_t ticks) { +void port_interrupt_after_ticks_ch(uint32_t channel, uint32_t ticks) { uint32_t current_ticks = nrfx_rtc_counter_get(&rtc_instance); uint32_t diff = 3; if (ticks > diff) { @@ -304,7 +347,15 @@ void port_interrupt_after_ticks(uint32_t ticks) { if (diff > 0xffffff) { diff = 0xffffff; } - nrfx_rtc_cc_set(&rtc_instance, 0, current_ticks + diff, true); + nrfx_rtc_cc_set(&rtc_instance, channel, current_ticks + diff, true); +} + +void port_disable_interrupt_after_ticks_ch(uint32_t channel) { + nrfx_rtc_cc_disable(&rtc_instance, channel); +} + +void port_interrupt_after_ticks(uint32_t ticks) { + port_interrupt_after_ticks_ch(0, ticks); } void port_idle_until_interrupt(void) { @@ -355,3 +406,9 @@ void HardFault_Handler(void) { asm ("nop;"); } } + +#if CIRCUITPY_ALARM +// in case boards/xxx/board.c does not provide board_deinit() +MP_WEAK void board_deinit(void) { +} +#endif diff --git a/ports/nrf/supervisor/qspi_flash.c b/ports/nrf/supervisor/qspi_flash.c index 1386af7c44..6d1bec4422 100644 --- a/ports/nrf/supervisor/qspi_flash.c +++ b/ports/nrf/supervisor/qspi_flash.c @@ -37,6 +37,31 @@ #include "supervisor/shared/external_flash/common_commands.h" #include "supervisor/shared/external_flash/qspi_flash.h" +#ifdef NRF_DEBUG_PRINT +#include "supervisor/serial.h" // dbg_printf() +#endif + +#ifdef QSPI_FLASH_POWERDOWN +// Parameters for external QSPI Flash power-down +// for W25Q128FV, +// tDP (nCS high to Power-down mode) = 3us +// tRES (nCS high to Standby mode) = 3us +// sck_delay = max(tDP, tRES) / 62.5ns = 48 -> 50 (w/ margin) +#define DUR_DPM_ENTER 1 // tDP in (256*62.5ns) units +#define DUR_DPM_EXIT 1 // tRES in (256*62.5ns) units +#define SCK_DELAY 50 // max(tDP, tRES) in (62.5ns) units +// wait necessary just after DPM enter/exit (cut and try) +#define WAIT_AFTER_DPM_ENTER 10 // usec +#define WAIT_AFTER_DPM_EXIT 50 // usec + +static int sck_delay_saved = 0; +#endif + +#ifdef NRF_DEBUG_PRINT +extern void dbg_dumpQSPIreg(void); +#else +#define dbg_dumpQSPIreg(...) +#endif // When USB is disconnected, disable QSPI in sleep mode to save energy void qspi_disable(void) { @@ -188,7 +213,11 @@ void spi_flash_init(void) { .readoc = NRF_QSPI_READOC_FASTREAD, .writeoc = NRF_QSPI_WRITEOC_PP, .addrmode = NRF_QSPI_ADDRMODE_24BIT, +#ifdef QSPI_FLASH_POWERDOWN + .dpmconfig = true +#else .dpmconfig = false +#endif }, .phy_if = { .sck_freq = NRF_QSPI_FREQ_32MDIV16, // Start at a slow 2MHz and speed up once we know what we're talking to. @@ -213,6 +242,13 @@ void spi_flash_init(void) { // No callback for blocking API nrfx_qspi_init(&qspi_cfg, NULL, NULL); + +#ifdef QSPI_FLASH_POWERDOWN + // If pin-reset while flash is in power-down mode, + // the flash cannot accept any commands. Send CMD_WAKE to release it. + spi_flash_write_command(CMD_WAKE, NULL, 0); + NRFX_DELAY_US(WAIT_AFTER_DPM_EXIT); +#endif } void spi_flash_init_device(const external_flash_device *device) { @@ -236,3 +272,61 @@ void spi_flash_init_device(const external_flash_device *device) { NRF_QSPI->IFCONFIG1 &= ~QSPI_IFCONFIG1_SCKFREQ_Msk; NRF_QSPI->IFCONFIG1 |= sckfreq << QSPI_IFCONFIG1_SCKFREQ_Pos; } + +void qspi_flash_enter_sleep(void) { +#ifdef QSPI_FLASH_POWERDOWN + uint32_t r; + NRF_QSPI->DPMDUR = + ((DUR_DPM_ENTER & 0xFFFF) << 16) | (DUR_DPM_EXIT & 0xFFFF); + // set sck_delay tempolarily + r = NRF_QSPI->IFCONFIG1; + sck_delay_saved = (r & QSPI_IFCONFIG1_SCKDELAY_Msk) + >> QSPI_IFCONFIG1_SCKDELAY_Pos; + NRF_QSPI->IFCONFIG1 + = (NRF_QSPI->IFCONFIG1 & ~QSPI_IFCONFIG1_SCKDELAY_Msk) + | (SCK_DELAY << QSPI_IFCONFIG1_SCKDELAY_Pos); + + // enabling IFCONFIG0.DPMENABLE here won't work. + // -> do it in spi_flash_init() + //NRF_QSPI->IFCONFIG0 |= QSPI_IFCONFIG0_DPMENABLE_Msk; + //dbg_dumpQSPIreg(); + + // enter deep power-down mode (DPM) + NRF_QSPI->IFCONFIG1 |= QSPI_IFCONFIG1_DPMEN_Msk; + NRFX_DELAY_US(WAIT_AFTER_DPM_ENTER); + if (!(NRF_QSPI->STATUS & QSPI_STATUS_DPM_Msk)) { +#ifdef NRF_DEBUG_PRINT + dbg_printf("qspi flash: DPM failed\r\n"); +#endif + } +#endif + + qspi_disable(); + //dbg_dumpQSPIreg(); +} + +void qspi_flash_exit_sleep(void) { + qspi_enable(); + +#ifdef QSPI_FLASH_POWERDOWN + if (NRF_QSPI->STATUS & QSPI_STATUS_DPM_Msk) { + // exit deep power-down mode + NRF_QSPI->IFCONFIG1 &= ~QSPI_IFCONFIG1_DPMEN_Msk; + NRFX_DELAY_US(WAIT_AFTER_DPM_EXIT); + + if (NRF_QSPI->STATUS & QSPI_STATUS_DPM_Msk) { +#ifdef NRF_DEBUG_PRINT + dbg_printf("qspi flash: exiting DPM failed\r\n"); +#endif + } + // restore sck_delay + if (sck_delay_saved == 0) { + sck_delay_saved = 10; // default + } + NRF_QSPI->IFCONFIG1 + = (NRF_QSPI->IFCONFIG1 & ~QSPI_IFCONFIG1_SCKDELAY_Msk) + | (sck_delay_saved << QSPI_IFCONFIG1_SCKDELAY_Pos); + } + //dbg_dumpQSPIreg(); +#endif +} diff --git a/ports/nrf/supervisor/qspi_flash.h b/ports/nrf/supervisor/qspi_flash.h new file mode 100644 index 0000000000..527f3cec39 --- /dev/null +++ b/ports/nrf/supervisor/qspi_flash.h @@ -0,0 +1,2 @@ +extern void qspi_flash_enter_sleep(void); +extern void qspi_flash_exit_sleep(void); diff --git a/shared-bindings/alarm/__init__.c b/shared-bindings/alarm/__init__.c index 74ec765ef4..4252175522 100644 --- a/shared-bindings/alarm/__init__.c +++ b/shared-bindings/alarm/__init__.c @@ -244,6 +244,7 @@ const mp_obj_module_t alarm_module = { }; extern void port_idle_until_interrupt(void); + MP_WEAK void common_hal_alarm_pretending_deep_sleep(void) { port_idle_until_interrupt(); } diff --git a/supervisor/serial.h b/supervisor/serial.h index e85231c6c7..4b49abb9fb 100644 --- a/supervisor/serial.h +++ b/supervisor/serial.h @@ -29,6 +29,7 @@ #include #include +#include #include "py/mpconfig.h" @@ -47,4 +48,7 @@ char serial_read(void); bool serial_bytes_available(void); bool serial_connected(void); +// XXX used in nrf52-sleep debug +int dbg_printf(const char *fmt, ...)__attribute__((format (printf, 1, 2))); + #endif // MICROPY_INCLUDED_SUPERVISOR_SERIAL_H diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index bd2bf4ba22..beb2541a15 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -37,6 +37,12 @@ #include "tusb.h" +#ifdef NRF_DEBUG_PRINT +// XXX these functions are in nrf/supervisor/debug_uart.c +extern void _debug_uart_init(void); +extern void _debug_print_substr(const char *text, uint32_t length); +#endif + /* * Note: DEBUG_UART currently only works on STM32, * enabling on another platform will cause a crash. @@ -64,10 +70,17 @@ void serial_early_init(void) { buf_array, true); common_hal_busio_uart_never_reset(&debug_uart); #endif + + #ifdef NRF_DEBUG_PRINT + _debug_uart_init(); + #endif } void serial_init(void) { usb_init(); + #ifdef NRF_DEBUG_PRINT + _debug_uart_init(); + #endif } bool serial_connected(void) { @@ -146,8 +159,14 @@ void serial_write_substring(const char *text, uint32_t length) { #if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) int uart_errcode; + common_hal_busio_uart_write(&debug_uart, (const uint8_t *)text, length, &uart_errcode); #endif + + #ifdef NRF_DEBUG_PRINT + _debug_print_substr(text, length); + #endif + } void serial_write(const char *text) {