diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index c04405b99f..ea1c8c7a09 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -846,6 +846,10 @@ msgstr "" msgid "Data too large for advertisement packet" msgstr "" +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +msgid "Deep sleep pins must use a rising edge with pulldown" +msgstr "" + #: shared-bindings/audiobusio/PDMIn.c msgid "Destination capacity is smaller than destination_length." msgstr "" @@ -1670,6 +1674,10 @@ msgid "" "Only Windows format, uncompressed BMP supported: given header size is %d" msgstr "" +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +msgid "Only edge detection is available on this hardware" +msgstr "" + #: shared-module/displayio/OnDiskBitmap.c #, c-format msgid "" @@ -1683,6 +1691,7 @@ msgstr "" #: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c #: ports/nrf/common-hal/alarm/time/TimeAlarm.c +#: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." msgstr "" @@ -1758,6 +1767,10 @@ msgstr "" msgid "Permission denied" msgstr "" +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +msgid "Pin cannot wake from Deep Sleep" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Pin count must be at least 1" msgstr "" @@ -1776,6 +1789,11 @@ msgstr "" msgid "Pin does not have ADC capabilities" msgstr "" +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +#: ports/stm/common-hal/pulseio/PulseIn.c +msgid "Pin interrupt already in use" +msgstr "" + #: shared-bindings/adafruit_bus_device/SPIDevice.c #: shared-bindings/digitalio/DigitalInOut.c msgid "Pin is input only" @@ -1789,10 +1807,6 @@ msgstr "" msgid "Pin must support hardware interrupts" msgstr "" -#: ports/stm/common-hal/pulseio/PulseIn.c -msgid "Pin number already reserved by EXTI" -msgstr "" - #: shared-bindings/rgbmatrix/RGBMatrix.c #, c-format msgid "" @@ -2013,6 +2027,10 @@ msgstr "" msgid "Size not supported" msgstr "" +#: ports/stm/common-hal/alarm/SleepMemory.c +msgid "Sleep Memory not available" +msgstr "" + #: shared-bindings/alarm/SleepMemory.c shared-bindings/nvm/ByteArray.c msgid "Slice and value different lengths." msgstr "" @@ -2153,6 +2171,10 @@ msgstr "" msgid "Total data to write is larger than %q" msgstr "" +#: ports/stm/common-hal/alarm/touch/TouchAlarm.c +msgid "Touch alarms not available" +msgstr "" + #: py/obj.c msgid "Traceback (most recent call last):\n" msgstr "" diff --git a/main.c b/main.c index 48373b6834..3730124863 100755 --- a/main.c +++ b/main.c @@ -297,13 +297,17 @@ STATIC bool run_code_py(safe_mode_t safe_mode) { stack_resize(); filesystem_flush(); supervisor_allocation* heap = allocate_remaining_memory(); + + // Prepare the VM state. Includes an alarm check/reset for sleep. start_mp(heap); #if CIRCUITPY_USB usb_setup_with_vm(); #endif + // This is where the user's python code is actually executed: found_main = maybe_run_list(supported_filenames, &result); + // If that didn't work, double check the extensions #if CIRCUITPY_FULL_BUILD if (!found_main){ found_main = maybe_run_list(double_extension_filenames, &result); @@ -313,6 +317,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode) { } #endif + // Finished executing python code. Cleanup includes a board reset. cleanup_after_vm(heap); if (result.return_code & PYEXEC_FORCED_EXIT) { diff --git a/ports/esp32s2/common-hal/alarm/__init__.c b/ports/esp32s2/common-hal/alarm/__init__.c index 63a19ab948..696e3ec150 100644 --- a/ports/esp32s2/common-hal/alarm/__init__.c +++ b/ports/esp32s2/common-hal/alarm/__init__.c @@ -79,6 +79,8 @@ bool common_hal_alarm_woken_from_sleep(void) { return _get_wakeup_cause() != ESP_SLEEP_WAKEUP_UNDEFINED; } +// When called to populate the global dict, the module functions create new alarm objects. +// Otherwise, they scan the existing alarms for matches. STATIC mp_obj_t _get_wake_alarm(size_t n_alarms, const mp_obj_t *alarms) { esp_sleep_wakeup_cause_t cause = _get_wakeup_cause(); switch (cause) { @@ -104,6 +106,8 @@ STATIC mp_obj_t _get_wake_alarm(size_t n_alarms, const mp_obj_t *alarms) { return mp_const_none; } +// This function is used to create a new alarm object for the global dict after deep sleep, +// rather than finding an existing one during runtime. mp_obj_t common_hal_alarm_get_wake_alarm(void) { return _get_wake_alarm(0, NULL); } @@ -121,6 +125,7 @@ STATIC void _idle_until_alarm(void) { RUN_BACKGROUND_TASKS; // Allow ctrl-C interrupt. if (common_hal_alarm_woken_from_sleep()) { + // This saves the return of common_hal_alarm_get_wake_alarm through Shared Bindings shared_alarm_save_wake_alarm(); return; } diff --git a/ports/esp32s2/common-hal/alarm/pin/PinAlarm.c b/ports/esp32s2/common-hal/alarm/pin/PinAlarm.c index 872c0f7124..34c8a5690e 100644 --- a/ports/esp32s2/common-hal/alarm/pin/PinAlarm.c +++ b/ports/esp32s2/common-hal/alarm/pin/PinAlarm.c @@ -38,7 +38,7 @@ #include "components/soc/src/esp32s2/include/hal/gpio_ll.h" #include "components/xtensa/include/esp_debug_helpers.h" -void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, mcu_pin_obj_t *pin, bool value, bool edge, bool pull) { +void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, const mcu_pin_obj_t *pin, bool value, bool edge, bool pull) { if (edge) { mp_raise_ValueError(translate("Cannot wake on pin edge. Only level.")); } @@ -51,7 +51,7 @@ void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, mcu self->pull = pull; } -mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self) { +const mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self) { return self->pin; } @@ -101,7 +101,7 @@ bool alarm_pin_pinalarm_woke_us_up(void) { } 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 light sleep, we check if we match any existing alarms uint64_t pin_status = ((uint64_t)pin_63_32_status) << 32 | pin_31_0_status; for (size_t i = 0; i < n_alarms; i++) { if (!mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) { @@ -112,6 +112,7 @@ mp_obj_t alarm_pin_pinalarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *al return alarms[i]; } } + // For deep sleep, a new alarm must be created esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); size_t pin_number = 64; if (cause == ESP_SLEEP_WAKEUP_EXT0) { diff --git a/ports/esp32s2/common-hal/alarm/pin/PinAlarm.h b/ports/esp32s2/common-hal/alarm/pin/PinAlarm.h index 93672c1f2d..4d86af4c3d 100644 --- a/ports/esp32s2/common-hal/alarm/pin/PinAlarm.h +++ b/ports/esp32s2/common-hal/alarm/pin/PinAlarm.h @@ -29,7 +29,7 @@ typedef struct { mp_obj_base_t base; - mcu_pin_obj_t *pin; + const mcu_pin_obj_t *pin; bool value; bool pull; } alarm_pin_pinalarm_obj_t; diff --git a/ports/nrf/common-hal/alarm/pin/PinAlarm.c b/ports/nrf/common-hal/alarm/pin/PinAlarm.c index cf602e8202..be4df66172 100644 --- a/ports/nrf/common-hal/alarm/pin/PinAlarm.c +++ b/ports/nrf/common-hal/alarm/pin/PinAlarm.c @@ -50,7 +50,7 @@ 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) { +void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, const mcu_pin_obj_t *pin, bool value, bool edge, bool pull) { if (edge) { mp_raise_ValueError(translate("Cannot wake on pin edge. Only level.")); } @@ -62,7 +62,7 @@ void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, mcu self->pull = pull; } -mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self) { +const mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self) { return self->pin; } diff --git a/ports/nrf/common-hal/alarm/pin/PinAlarm.h b/ports/nrf/common-hal/alarm/pin/PinAlarm.h index 69684386fa..42f9f115c8 100644 --- a/ports/nrf/common-hal/alarm/pin/PinAlarm.h +++ b/ports/nrf/common-hal/alarm/pin/PinAlarm.h @@ -29,7 +29,7 @@ typedef struct { mp_obj_base_t base; - mcu_pin_obj_t *pin; + const mcu_pin_obj_t *pin; bool value; bool pull; } alarm_pin_pinalarm_obj_t; diff --git a/ports/stm/Makefile b/ports/stm/Makefile index f3993c3f26..41f2e50e3d 100755 --- a/ports/stm/Makefile +++ b/ports/stm/Makefile @@ -220,6 +220,8 @@ SRC_C += \ boards/$(BOARD)/board.c \ boards/$(BOARD)/pins.c \ peripherals/timers.c \ + peripherals/exti.c \ + peripherals/rtc.c \ peripherals/stm32$(MCU_SERIES_LOWER)/clocks.c \ peripherals/stm32$(MCU_SERIES_LOWER)/$(MCU_VARIANT_LOWER)/pins.c \ peripherals/stm32$(MCU_SERIES_LOWER)/$(MCU_VARIANT_LOWER)/gpio.c \ diff --git a/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk b/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk index 2dff6e8955..37dccadb99 100644 --- a/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk +++ b/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk @@ -17,5 +17,9 @@ MCU_PACKAGE = UFQFPN48 LD_COMMON = boards/common_default.ld LD_FILE = boards/STM32F411_nvm_nofs.ld +# Too big for the flash +CIRCUITPY_AUDIOCORE = 0 +CIRCUITPY_AUDIOPWMIO = 0 +CIRCUITPY_SYNTHIO = 0 CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_VECTORIO = 0 diff --git a/ports/stm/common-hal/alarm/SleepMemory.c b/ports/stm/common-hal/alarm/SleepMemory.c new file mode 100644 index 0000000000..3b89efbca3 --- /dev/null +++ b/ports/stm/common-hal/alarm/SleepMemory.c @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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/SleepMemory.h" + +void alarm_sleep_memory_reset(void) { + +} + +uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self) { + mp_raise_NotImplementedError(translate("Sleep Memory not available")); + return 0; +} + +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) { + mp_raise_NotImplementedError(translate("Sleep Memory not available")); + return false; +} + +void common_hal_alarm_sleep_memory_get_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, uint8_t *values, uint32_t len) { + mp_raise_NotImplementedError(translate("Sleep Memory not available")); + return; +} diff --git a/ports/stm/common-hal/alarm/SleepMemory.h b/ports/stm/common-hal/alarm/SleepMemory.h new file mode 100644 index 0000000000..0e9fc54904 --- /dev/null +++ b/ports/stm/common-hal/alarm/SleepMemory.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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_STM32_COMMON_HAL_ALARM_SLEEPMEMORY_H +#define MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM_SLEEPMEMORY_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +} alarm_sleep_memory_obj_t; + +extern void alarm_sleep_memory_reset(void); + +#endif // MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM_SLEEPMEMORY_H diff --git a/ports/stm/common-hal/alarm/__init__.c b/ports/stm/common-hal/alarm/__init__.c new file mode 100644 index 0000000000..e0b900bbfc --- /dev/null +++ b/ports/stm/common-hal/alarm/__init__.c @@ -0,0 +1,190 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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 "lib/utils/interrupt_char.h" + +#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/microcontroller/__init__.h" + +#include "supervisor/port.h" +#include "supervisor/workflow.h" + +// Singleton instance of SleepMemory. +const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = { + .base = { + .type = &alarm_sleep_memory_type, + }, +}; + +STATIC stm_sleep_source_t true_deep_wake_reason; +STATIC mp_obj_t most_recent_alarm; + +void alarm_reset(void) { + most_recent_alarm = NULL; + // Reset the alarm flag + STM_ALARM_FLAG = 0x00; + alarm_pin_pinalarm_reset(); + alarm_time_timealarm_reset(); +} + +// Kind of a hack, required as RTC is reset in port.c +// TODO: in the future, don't reset it at all, just override critical flags +void alarm_set_wakeup_reason(stm_sleep_source_t reason) { + true_deep_wake_reason = reason; +} + +STATIC stm_sleep_source_t _get_wakeup_cause(void) { + // If in light/fake sleep, check modules + if (alarm_pin_pinalarm_woke_us_up()) { + return STM_WAKEUP_GPIO; + } + if (alarm_time_timealarm_woke_us_up()) { + return STM_WAKEUP_RTC; + } + // Check to see if we woke from deep sleep (reason set in port_init) + if (true_deep_wake_reason) { + return true_deep_wake_reason; + } + return STM_WAKEUP_UNDEF; +} + +bool common_hal_alarm_woken_from_sleep(void) { + return _get_wakeup_cause() != STM_WAKEUP_UNDEF; +} + +STATIC mp_obj_t _get_wake_alarm(size_t n_alarms, const mp_obj_t *alarms) { + stm_sleep_source_t cause = _get_wakeup_cause(); + switch (cause) { + case STM_WAKEUP_RTC: { + return alarm_time_timealarm_get_wakeup_alarm(n_alarms, alarms); + } + case STM_WAKEUP_GPIO: { + return alarm_pin_pinalarm_get_wakeup_alarm(n_alarms, alarms); + } + case STM_WAKEUP_UNDEF: + default: + // Not a deep sleep reset. + break; + } + return mp_const_none; +} + +mp_obj_t common_hal_alarm_get_wake_alarm(void) { + // If we woke from light sleep, override with that alarm + if (most_recent_alarm != NULL) { + return most_recent_alarm; + } + return _get_wake_alarm(0, NULL); +} + +// 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); +} + +STATIC void _idle_until_alarm(void) { + // Poll for alarms. + while (!mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + // Detect if interrupt was alarm or ctrl-C interrupt. + if (common_hal_alarm_woken_from_sleep()) { + return; + } + port_idle_until_interrupt(); + } +} + +mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms) { + // If USB is active, only pretend to sleep. Otherwise, light sleep + if (supervisor_workflow_active()) { + _setup_sleep_alarms(false, n_alarms, alarms); + _idle_until_alarm(); + } else { + _setup_sleep_alarms(false, n_alarms, alarms); + port_disable_tick(); + HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); + port_enable_tick(); + } + + mp_obj_t wake_alarm = _get_wake_alarm(n_alarms, alarms); + + // TODO: make assignment to global array less roundabout + most_recent_alarm = wake_alarm; + shared_alarm_save_wake_alarm(); + + // Can't use alarm_reset since it resets most_recent_alarm + alarm_pin_pinalarm_reset(); + alarm_time_timealarm_reset(); + return wake_alarm; +} + +void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) { + most_recent_alarm = NULL; + _setup_sleep_alarms(true, n_alarms, alarms); +} + +void NORETURN common_hal_alarm_enter_deep_sleep(void) { + alarm_pin_pinalarm_prepare_for_deep_sleep(); + alarm_time_timealarm_prepare_for_deep_sleep(); + port_disable_tick(); + __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); + + // Set a flag in the backup registers to indicate sleep wakeup + STM_ALARM_FLAG = 0x01; + + HAL_PWR_EnterSTANDBYMode(); + + // The above shuts down RAM, so we should never hit this + while (1) { + ; + } +} + +void common_hal_alarm_pretending_deep_sleep(void) { + // Re-enable the WKUP pin (PA00) since VM cleanup resets it + // If there are no PinAlarms, EXTI won't be turned on, and this won't do anything + // TODO: replace with `prepare_for_fake_deep_sleep` if other WKUP are added. + GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = pin_mask(0); + GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + port_idle_until_interrupt(); +} + +void common_hal_alarm_gc_collect(void) { + gc_collect_ptr(shared_alarm_get_wake_alarm()); +} diff --git a/ports/stm/common-hal/alarm/__init__.h b/ports/stm/common-hal/alarm/__init__.h new file mode 100644 index 0000000000..4ec7222d2e --- /dev/null +++ b/ports/stm/common-hal/alarm/__init__.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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_STM32_COMMON_HAL_ALARM__INIT__H +#define MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM__INIT__H + +#include "common-hal/alarm/SleepMemory.h" + +extern const alarm_sleep_memory_obj_t alarm_sleep_memory_obj; + +typedef enum { + STM_WAKEUP_UNDEF, + STM_WAKEUP_GPIO, + STM_WAKEUP_RTC +} stm_sleep_source_t; + +#define STM_ALARM_FLAG (RTC->BKP0R) + +extern void alarm_set_wakeup_reason(stm_sleep_source_t reason); +extern void alarm_reset(void); + +#endif // MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM__INIT__H diff --git a/ports/stm/common-hal/alarm/pin/PinAlarm.c b/ports/stm/common-hal/alarm/pin/PinAlarm.c new file mode 100644 index 0000000000..381a13b220 --- /dev/null +++ b/ports/stm/common-hal/alarm/pin/PinAlarm.c @@ -0,0 +1,163 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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/pin/PinAlarm.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "peripherals/exti.h" + +STATIC bool woke_up; + +STATIC uint16_t alarm_pin_triggered; +STATIC bool deep_wkup_enabled; +STATIC bool reserved_alarms[STM32_GPIO_PORT_SIZE]; + +STATIC void pin_alarm_callback(uint8_t num) { + alarm_pin_triggered |= (1 << num); + woke_up = true; + HAL_GPIO_EXTI_IRQHandler(pin_mask(num)); +} + +void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, const mcu_pin_obj_t *pin, bool value, bool edge, bool pull) { + if (!edge) { + mp_raise_NotImplementedError(translate("Only edge detection is available on this hardware")); + } + if (!stm_peripherals_exti_is_free(pin->number)) { + mp_raise_RuntimeError(translate("Pin interrupt already in use")); + } + GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = pin_mask(pin->number); + GPIO_InitStruct.Mode = value ? GPIO_MODE_IT_RISING : GPIO_MODE_IT_FALLING; + // Pull is automatically set to oppose value. + // TODO: match digitalIO API instead? + if (value) { + GPIO_InitStruct.Pull = pull ? GPIO_PULLDOWN : GPIO_NOPULL; + } else { + GPIO_InitStruct.Pull = pull ? GPIO_PULLUP : GPIO_NOPULL; + } + HAL_GPIO_Init(pin_port(pin->port), &GPIO_InitStruct); + + // EXTI is set up and enabled in set_alarm + self->pin = pin; + self->value = value; + self->pull = pull; +} + +const 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 true; +} + +bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self) { + return self->pull; +} + +bool alarm_pin_pinalarm_woke_us_up(void) { + return woke_up; +} + +mp_obj_t alarm_pin_pinalarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *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_triggered & (1 << alarm->pin->number)) { + return alarms[i]; + } + } + + // If the above isn't true, we woke from deep sleep, so create a new alarm + alarm_pin_pinalarm_obj_t *alarm = m_new_obj(alarm_pin_pinalarm_obj_t); + alarm->base.type = &alarm_pin_pinalarm_type; + // TODO: replace this if/when other WKUP pins are supported + alarm->pin = &pin_PA00; + return alarm; +} + +void alarm_pin_pinalarm_reset(void) { + HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1); + alarm_pin_triggered = 0; + woke_up = false; + deep_wkup_enabled = false; + for (uint8_t i = 0; i < STM32_GPIO_PORT_SIZE; i++) { + if (reserved_alarms[i]) { + stm_peripherals_exti_reset_exti(i); + reserved_alarms[i] = false; + } + } +} + +// Deep sleep alarms don't actually make use of EXTI, but we pretend they're the same. +void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { + for (size_t i = 0; i < n_alarms; i++) { + if (mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) { + alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]); + if (deep_sleep) { + // Deep sleep only wakes on a rising edge from one pin, WKUP (PA00) + // All pin settings are handled automatically. + if (alarm->pin != &pin_PA00) { + mp_raise_ValueError(translate("Pin cannot wake from Deep Sleep")); + } + if (alarm->value == false || alarm->pull == false) { + // Enabling WakeUp automatically sets this, but warn anyway to set expectations + mp_raise_ValueError(translate("Deep sleep pins must use a rising edge with pulldown")); + } + // We can't actually turn WakeUp on here, since enabling it disables EXTI, + // so we put it off until right before sleeping. + deep_wkup_enabled = true; + // EXTI needs to persist past the VM cleanup for fake deep sleep + stm_peripherals_exti_never_reset(alarm->pin->number); + } + if (!stm_peripherals_exti_reserve(alarm->pin->number)) { + mp_raise_RuntimeError(translate("Pin interrupt already in use")); + } + stm_peripherals_exti_set_callback(pin_alarm_callback,alarm->pin->number); + stm_peripherals_exti_enable(alarm->pin->number); + reserved_alarms[alarm->pin->number] = true; + } + } +} + +// If we don't have WKUP enabled, ensure it's disabled +// TODO; is this really required? +void alarm_pin_pinalarm_prepare_for_deep_sleep(void) { + if (deep_wkup_enabled) { + HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1); + __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); + HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); + } +} diff --git a/ports/stm/common-hal/alarm/pin/PinAlarm.h b/ports/stm/common-hal/alarm/pin/PinAlarm.h new file mode 100644 index 0000000000..bd18c840ff --- /dev/null +++ b/ports/stm/common-hal/alarm/pin/PinAlarm.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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_STM32_COMMON_HAL_ALARM_PINALARM_H +#define MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM_PINALARM_H + +#include "py/obj.h" +#include "py/objtuple.h" + +typedef struct { + mp_obj_base_t base; + const 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); + +#endif // MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM_PINALARM_H diff --git a/ports/stm/common-hal/alarm/time/TimeAlarm.c b/ports/stm/common-hal/alarm/time/TimeAlarm.c new file mode 100644 index 0000000000..2c38066d3a --- /dev/null +++ b/ports/stm/common-hal/alarm/time/TimeAlarm.c @@ -0,0 +1,117 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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/time/TimeAlarm.h" +#include "shared-bindings/time/__init__.h" +#include "supervisor/port.h" +#include "peripherals/rtc.h" + +#include STM32_HAL_H + +STATIC volatile bool woke_up; +STATIC uint32_t deep_sleep_ticks; + +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; +} + +// This is run in the timer task. We use it to wake the main CircuitPython task. +STATIC void timer_callback(void) { + woke_up = true; +} + +bool alarm_time_timealarm_woke_us_up(void) { + return woke_up; +} + +void alarm_time_timealarm_reset(void) { + woke_up = false; +} + +void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) { + // Search through alarms for TimeAlarm instances, and check that there's only one + bool timealarm_set = false; + alarm_time_timealarm_obj_t *timealarm = MP_OBJ_NULL; + 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; + } + + // Compute how long to actually sleep, considering the time now. + mp_float_t now_secs = uint64_to_float(common_hal_time_monotonic_ms()) / 1000.0f; + uint32_t wakeup_in_secs = MAX(0.0f, timealarm->monotonic_time - now_secs); + uint32_t wakeup_in_ticks = wakeup_in_secs * 1024; + + // In the deep sleep case, we can't start the timer until the USB delay has finished + if (deep_sleep) { + deep_sleep_ticks = wakeup_in_ticks; + } else { + deep_sleep_ticks = 0; + } + // Use alarm B, since port reserves A + // If true deep sleep is called, it will either ignore or overwrite this depending on + // whether it is shorter or longer than the USB delay + stm32_peripherals_rtc_assign_alarm_callback(PERIPHERALS_ALARM_B,timer_callback); + stm32_peripherals_rtc_set_alarm(PERIPHERALS_ALARM_B,wakeup_in_ticks); +} + +void alarm_time_timealarm_prepare_for_deep_sleep(void) { + if (deep_sleep_ticks) { + // This is used for both fake and real deep sleep, so it still needs the callback + stm32_peripherals_rtc_assign_alarm_callback(PERIPHERALS_ALARM_B,timer_callback); + stm32_peripherals_rtc_set_alarm(PERIPHERALS_ALARM_B,deep_sleep_ticks); + deep_sleep_ticks = 0; + } +} diff --git a/ports/stm/common-hal/alarm/time/TimeAlarm.h b/ports/stm/common-hal/alarm/time/TimeAlarm.h new file mode 100644 index 0000000000..0d0c25deae --- /dev/null +++ b/ports/stm/common-hal/alarm/time/TimeAlarm.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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_STM32_COMMON_HAL_ALARM_TIMEALARM_H +#define MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM_TIMEALARM_H + +#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; + +// 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); + +void alarm_time_timealarm_prepare_for_deep_sleep(void); + +#endif // MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM_TIMEALARM_H diff --git a/ports/stm/common-hal/alarm/touch/TouchAlarm.c b/ports/stm/common-hal/alarm/touch/TouchAlarm.c new file mode 100644 index 0000000000..88c73726ee --- /dev/null +++ b/ports/stm/common-hal/alarm/touch/TouchAlarm.c @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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 "shared-bindings/alarm/touch/TouchAlarm.h" +#include "shared-bindings/microcontroller/__init__.h" + +void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) { + mp_raise_NotImplementedError(translate("Touch alarms not available")); +} diff --git a/ports/stm/common-hal/alarm/touch/TouchAlarm.h b/ports/stm/common-hal/alarm/touch/TouchAlarm.h new file mode 100644 index 0000000000..6c89353f93 --- /dev/null +++ b/ports/stm/common-hal/alarm/touch/TouchAlarm.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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_STM32_COMMON_HAL_ALARM_TOUCHALARM_H +#define MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM_TOUCHALARM_H + +typedef struct { + mp_obj_base_t base; + const mcu_pin_obj_t *pin; +} alarm_touch_touchalarm_obj_t; + +#endif // MICROPY_INCLUDED_STM32_COMMON_HAL_ALARM_TOUCHALARM_H diff --git a/ports/stm/common-hal/pulseio/PulseIn.c b/ports/stm/common-hal/pulseio/PulseIn.c index ece02f66a8..27ef978dac 100644 --- a/ports/stm/common-hal/pulseio/PulseIn.c +++ b/ports/stm/common-hal/pulseio/PulseIn.c @@ -35,17 +35,17 @@ #include "shared-bindings/pulseio/PulseIn.h" #include "timers.h" +#include "peripherals/exti.h" + #include STM32_HAL_H #define STM32_GPIO_PORT_SIZE 16 -static pulseio_pulsein_obj_t *_objs[STM32_GPIO_PORT_SIZE]; +STATIC pulseio_pulsein_obj_t *callback_obj_ref[STM32_GPIO_PORT_SIZE]; STATIC TIM_HandleTypeDef tim_handle; -static uint32_t overflow_count = 0; +STATIC uint32_t overflow_count = 0; STATIC uint8_t refcount = 0; -static void assign_EXTI_Interrupt(pulseio_pulsein_obj_t *self, uint8_t num); - void pulsein_timer_event_handler(void) { // Detect TIM Update event if (__HAL_TIM_GET_FLAG(&tim_handle, TIM_FLAG_UPDATE) != RESET) { @@ -56,7 +56,7 @@ void pulsein_timer_event_handler(void) { } } -static void pulsein_exti_event_handler(uint8_t num) { +STATIC void pulsein_exti_event_handler(uint8_t num) { // Grab the current time first. uint32_t current_overflow = overflow_count; uint32_t current_count = tim_handle.Instance->CNT; @@ -64,7 +64,7 @@ static void pulsein_exti_event_handler(uint8_t num) { // Interrupt register must be cleared manually EXTI->PR = 1 << num; - pulseio_pulsein_obj_t *self = _objs[num]; + pulseio_pulsein_obj_t *self = callback_obj_ref[num]; if (!self) { return; } @@ -96,11 +96,11 @@ static void pulsein_exti_event_handler(uint8_t num) { void pulsein_reset(void) { // Disable all active interrupts and clear array for (uint i = 0; i < STM32_GPIO_PORT_SIZE; i++) { - if (_objs[i] != NULL) { - HAL_NVIC_DisableIRQ(_objs[i]->irq); + if (callback_obj_ref[i] != NULL) { + stm_peripherals_exti_disable(callback_obj_ref[i]->pin->number); } } - memset(_objs, 0, sizeof(_objs)); + memset(callback_obj_ref, 0, sizeof(callback_obj_ref)); HAL_TIM_Base_DeInit(&tim_handle); tim_clock_disable(stm_peripherals_timer_get_index(tim_handle.Instance)); @@ -111,15 +111,14 @@ void pulsein_reset(void) { void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t *self, const mcu_pin_obj_t *pin, uint16_t maxlen, bool idle_state) { // STM32 has one shared EXTI for each pin number, 0-15 - uint8_t p_num = pin->number; - if (_objs[p_num]) { - mp_raise_ValueError(translate("Pin number already reserved by EXTI")); + if (!stm_peripherals_exti_is_free(pin->number)) { + mp_raise_RuntimeError(translate("Pin interrupt already in use")); } - _objs[p_num] = self; // Allocate pulse buffer self->buffer = (uint16_t *)m_malloc(maxlen * sizeof(uint16_t), false); if (self->buffer == NULL) { + // TODO: free the EXTI here? mp_raise_msg_varg(&mp_type_MemoryError, translate("Failed to allocate RX buffer of %d bytes"), maxlen * sizeof(uint16_t)); } @@ -164,16 +163,21 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t *self, const mcu // Add to active PulseIns refcount++; - // EXTI pins can also be read as an input + if (!stm_peripherals_exti_reserve(pin->number)) { + mp_raise_RuntimeError(translate("Pin interrupt already in use")); + } GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = pin_mask(pin->number); GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(pin_port(pin->port), &GPIO_InitStruct); + stm_peripherals_exti_set_callback(pulsein_exti_event_handler, pin->number); + // Store PulseIn object for use in callback + callback_obj_ref[pin->number] = self; // Interrupt starts immediately - assign_EXTI_Interrupt(self, pin->number); - HAL_NVIC_EnableIRQ(self->irq); + stm_peripherals_exti_enable(pin->number); + common_hal_mcu_pin_claim(pin); } @@ -186,7 +190,8 @@ void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t *self) { return; } // Remove pulsein slot from shared array - _objs[self->pin->number] = NULL; + callback_obj_ref[self->pin->number] = NULL; + stm_peripherals_exti_free(self->pin->number); reset_pin_number(self->pin->port, self->pin->number); self->pin = NULL; @@ -197,7 +202,7 @@ void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t *self) { } void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t *self) { - HAL_NVIC_DisableIRQ(self->irq); + stm_peripherals_exti_disable(self->pin->number); self->paused = true; } @@ -231,27 +236,27 @@ void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t *self, uint16_t tri self->last_count = 0; self->last_overflow = 0; - HAL_NVIC_EnableIRQ(self->irq); + stm_peripherals_exti_enable(self->pin->number); } void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t *self) { - HAL_NVIC_DisableIRQ(self->irq); + stm_peripherals_exti_disable(self->pin->number); self->start = 0; self->len = 0; - HAL_NVIC_EnableIRQ(self->irq); + stm_peripherals_exti_enable(self->pin->number); } uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t *self, int16_t index) { - HAL_NVIC_DisableIRQ(self->irq); + stm_peripherals_exti_disable(self->pin->number); if (index < 0) { index += self->len; } if (index < 0 || index >= self->len) { - HAL_NVIC_EnableIRQ(self->irq); + stm_peripherals_exti_enable(self->pin->number); mp_raise_IndexError_varg(translate("%q index out of range"), MP_QSTR_PulseIn); } uint16_t value = self->buffer[(self->start + index) % self->maxlen]; - HAL_NVIC_EnableIRQ(self->irq); + stm_peripherals_exti_enable(self->pin->number); return value; } @@ -259,11 +264,11 @@ uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t *self) { if (self->len == 0) { mp_raise_IndexError_varg(translate("pop from empty %q"), MP_QSTR_PulseIn); } - HAL_NVIC_DisableIRQ(self->irq); + stm_peripherals_exti_disable(self->pin->number); uint16_t value = self->buffer[self->start]; self->start = (self->start + 1) % self->maxlen; self->len--; - HAL_NVIC_EnableIRQ(self->irq); + stm_peripherals_exti_enable(self->pin->number); return value; } @@ -279,55 +284,3 @@ bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t *self) { uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t *self) { return self->len; } - -static void assign_EXTI_Interrupt(pulseio_pulsein_obj_t *self, uint8_t num) { - if (num == 0) { - self->irq = EXTI0_IRQn; - } else if (num == 1) { - self->irq = EXTI1_IRQn; - } else if (num == 2) { - self->irq = EXTI2_IRQn; - } else if (num == 3) { - self->irq = EXTI3_IRQn; - } else if (num == 4) { - self->irq = EXTI4_IRQn; - } else if (num >= 5 && num <= 9) { - self->irq = EXTI9_5_IRQn; - } else if (num >= 10 && num <= 15) { - self->irq = EXTI15_10_IRQn; - } -} - -void EXTI0_IRQHandler(void) { - pulsein_exti_event_handler(0); -} -void EXTI1_IRQHandler(void) { - pulsein_exti_event_handler(1); -} -void EXTI2_IRQHandler(void) { - pulsein_exti_event_handler(2); -} -void EXTI3_IRQHandler(void) { - pulsein_exti_event_handler(3); -} -void EXTI4_IRQHandler(void) { - pulsein_exti_event_handler(4); -} - -void EXTI9_5_IRQHandler(void) { - uint32_t pending = EXTI->PR; - for (uint i = 5; i <= 9; i++) { - if (pending & (1 << i)) { - pulsein_exti_event_handler(i); - } - } -} - -void EXTI15_10_IRQHandler(void) { - uint32_t pending = EXTI->PR; - for (uint i = 10; i <= 15; i++) { - if (pending & (1 << i)) { - pulsein_exti_event_handler(i); - } - } -} diff --git a/ports/stm/common-hal/pulseio/PulseIn.h b/ports/stm/common-hal/pulseio/PulseIn.h index 8de60b9cd5..a9d925fa5b 100644 --- a/ports/stm/common-hal/pulseio/PulseIn.h +++ b/ports/stm/common-hal/pulseio/PulseIn.h @@ -35,7 +35,6 @@ typedef struct { mp_obj_base_t base; const mcu_pin_obj_t *pin; - IRQn_Type irq; bool idle_state; bool paused; volatile bool first_edge; diff --git a/ports/stm/mpconfigport.mk b/ports/stm/mpconfigport.mk index ae021426df..2b93efa128 100644 --- a/ports/stm/mpconfigport.mk +++ b/ports/stm/mpconfigport.mk @@ -3,6 +3,7 @@ LONGINT_IMPL ?= MPZ INTERNAL_LIBM ?= 1 ifeq ($(MCU_VARIANT),$(filter $(MCU_VARIANT),STM32F405xx STM32F407xx)) + CIRCUITPY_ALARM = 1 CIRCUITPY_CANIO = 1 CIRCUITPY_FRAMEBUFFERIO ?= 1 CIRCUITPY_SDIOIO ?= 1 @@ -41,7 +42,7 @@ ifeq ($(MCU_SERIES),H7) CIRCUITPY_NEOPIXEL_WRITE ?= 0 CIRCUITPY_NVM ?= 0 CIRCUITPY_PULSEIO ?= 0 - CIRCUITPY_PWMIO ?= 0 + CIRCUITPY_PWMIO ?= 0 CIRCUITPY_ROTARYIO ?= 0 CIRCUITPY_RTC ?= 0 CIRCUITPY_USB_HID ?= 0 diff --git a/ports/stm/peripherals/exti.c b/ports/stm/peripherals/exti.c new file mode 100644 index 0000000000..3ba943a208 --- /dev/null +++ b/ports/stm/peripherals/exti.c @@ -0,0 +1,144 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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/mpconfig.h" +#include "py/gc.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "peripherals/exti.h" + +#if !(CPY_STM32H7) + +STATIC bool stm_exti_reserved[STM32_GPIO_PORT_SIZE]; +STATIC bool stm_exti_never_reset[STM32_GPIO_PORT_SIZE]; +STATIC void (*stm_exti_callback[STM32_GPIO_PORT_SIZE])(uint8_t num); + +void exti_reset(void) { + for (size_t i = 0; i < STM32_GPIO_PORT_SIZE; i++) { + if (!stm_exti_never_reset[i]) { + stm_exti_reserved[i] = false; + stm_exti_callback[i] = NULL; + stm_peripherals_exti_disable(i); + } + } +} + +void stm_peripherals_exti_never_reset(uint8_t num) { + stm_exti_never_reset[num] = true; +} + +void stm_peripherals_exti_reset_exti(uint8_t num) { + stm_peripherals_exti_disable(num); + stm_exti_never_reset[num] = false; + stm_exti_reserved[num] = false; + stm_exti_callback[num] = NULL; +} + +bool stm_peripherals_exti_is_free(uint8_t num) { + return !stm_exti_reserved[num]; +} + +bool stm_peripherals_exti_reserve(uint8_t num) { + if (!stm_peripherals_exti_is_free(num)) { + return false; + } + stm_exti_reserved[num] = true; + return true; +} + +void stm_peripherals_exti_enable(uint8_t num) { + HAL_NVIC_EnableIRQ(stm_peripherals_exti_get_irq(num)); +} + +void stm_peripherals_exti_disable(uint8_t num) { + HAL_NVIC_DisableIRQ(stm_peripherals_exti_get_irq(num)); +} + +void stm_peripherals_exti_set_callback(void (*callback)(uint8_t num), uint8_t number) { + stm_exti_callback[number] = callback; +} + +void stm_peripherals_exti_free(uint8_t num) { + stm_exti_reserved[num] = true; +} + +IRQn_Type stm_peripherals_exti_get_irq(uint8_t num) { + if (num == 0) { + return EXTI0_IRQn; + } else if (num == 1) { + return EXTI1_IRQn; + } else if (num == 2) { + return EXTI2_IRQn; + } else if (num == 3) { + return EXTI3_IRQn; + } else if (num == 4) { + return EXTI4_IRQn; + } else if (num >= 5 && num <= 9) { + return EXTI9_5_IRQn; + } else if (num >= 10 && num <= 15) { + return EXTI15_10_IRQn; + } else { + return 0; + } +} + +void EXTI0_IRQHandler(void) { + stm_exti_callback[0](0); +} +void EXTI1_IRQHandler(void) { + stm_exti_callback[1](1); +} +void EXTI2_IRQHandler(void) { + stm_exti_callback[2](2); +} +void EXTI3_IRQHandler(void) { + stm_exti_callback[3](3); +} +void EXTI4_IRQHandler(void) { + stm_exti_callback[4](4); +} + +void EXTI9_5_IRQHandler(void) { + uint32_t pending = EXTI->PR; + for (uint i = 5; i <= 9; i++) { + if (pending & (1 << i)) { + stm_exti_callback[i](i); + } + } +} + +void EXTI15_10_IRQHandler(void) { + uint32_t pending = EXTI->PR; + for (uint i = 10; i <= 15; i++) { + if (pending & (1 << i)) { + stm_exti_callback[i](i); + } + } +} + +#endif diff --git a/ports/stm/peripherals/exti.h b/ports/stm/peripherals/exti.h new file mode 100644 index 0000000000..09936fb283 --- /dev/null +++ b/ports/stm/peripherals/exti.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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_STM32_PERIPHERALS_EXTI_H__ +#define __MICROPY_INCLUDED_STM32_PERIPHERALS_EXTI_H__ + +#include STM32_HAL_H + +#define STM32_GPIO_PORT_SIZE 16 + +void exti_reset(void); +void stm_peripherals_exti_never_reset(uint8_t num); +void stm_peripherals_exti_reset_exti(uint8_t num); +bool stm_peripherals_exti_is_free(uint8_t num); +bool stm_peripherals_exti_reserve(uint8_t num); +void stm_peripherals_exti_enable(uint8_t num); +void stm_peripherals_exti_disable(uint8_t num); +void stm_peripherals_exti_set_callback(void (*callback)(uint8_t num), uint8_t number); +void stm_peripherals_exti_free(uint8_t num); +IRQn_Type stm_peripherals_exti_get_irq(uint8_t num); + +#endif // __MICROPY_INCLUDED_STM32_PERIPHERALS_EXTI_H__ diff --git a/ports/stm/peripherals/rtc.c b/ports/stm/peripherals/rtc.c new file mode 100644 index 0000000000..5f8f05b2bf --- /dev/null +++ b/ports/stm/peripherals/rtc.c @@ -0,0 +1,230 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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 "peripherals/rtc.h" +#include STM32_HAL_H + +#include "py/mpconfig.h" +#include "py/gc.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "lib/timeutils/timeutils.h" + +// Default period for ticks is 1/1024 second +#define TICK_DIVISOR 1024 + +STATIC RTC_HandleTypeDef hrtc; + +#if BOARD_HAS_LOW_SPEED_CRYSTAL +STATIC uint32_t rtc_clock_frequency = LSE_VALUE; +#else +STATIC uint32_t rtc_clock_frequency = LSI_VALUE; +#endif + +volatile uint32_t seconds_to_date = 0; +volatile uint32_t cached_date = 0; +volatile uint32_t seconds_to_minute = 0; +volatile uint32_t cached_hours_minutes = 0; + +volatile bool alarmed_already[2]; + +bool peripherals_wkup_on = false; + +static void (*wkup_callback)(void); +static void (*alarm_callbacks[2])(void); + +uint32_t stm32_peripherals_get_rtc_freq(void) { + return rtc_clock_frequency; +} + +void stm32_peripherals_rtc_init(void) { + // RTC oscillator selection is handled in peripherals///clocks.c + __HAL_RCC_RTC_ENABLE(); + hrtc.Instance = RTC; + hrtc.Init.HourFormat = RTC_HOURFORMAT_24; + // Divide async as little as possible so that we have rtc_clock_frequency count in subseconds. + // This ensures our timing > 1 second is correct. + hrtc.Init.AsynchPrediv = 0x0; + hrtc.Init.SynchPrediv = rtc_clock_frequency - 1; + hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; + + HAL_RTC_Init(&hrtc); + HAL_RTCEx_EnableBypassShadow(&hrtc); + HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); +} + +// This function is called often for timing so we cache the seconds elapsed computation based on the +// register value. The STM HAL always does shifts and conversion if we use it directly. +uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) { + // Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read + // SSR twice to make sure we didn't read across a tick. + __disable_irq(); + uint32_t first_ssr = (uint32_t)(RTC->SSR); + uint32_t time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK); + uint32_t date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK); + uint32_t ssr = (uint32_t)(RTC->SSR); + while (ssr != first_ssr) { + first_ssr = ssr; + time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK); + date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK); + ssr = (uint32_t)(RTC->SSR); + } + __enable_irq(); + + uint32_t subseconds = rtc_clock_frequency - 1 - ssr; + + if (date != cached_date) { + uint32_t year = (uint8_t)((date & (RTC_DR_YT | RTC_DR_YU)) >> 16U); + uint8_t month = (uint8_t)((date & (RTC_DR_MT | RTC_DR_MU)) >> 8U); + uint8_t day = (uint8_t)(date & (RTC_DR_DT | RTC_DR_DU)); + // Add 2000 since the year is only the last two digits. + year = 2000 + (uint32_t)RTC_Bcd2ToByte(year); + month = (uint8_t)RTC_Bcd2ToByte(month); + day = (uint8_t)RTC_Bcd2ToByte(day); + seconds_to_date = timeutils_seconds_since_2000(year, month, day, 0, 0, 0); + cached_date = date; + } + uint32_t hours_minutes = time & (RTC_TR_HT | RTC_TR_HU | RTC_TR_MNT | RTC_TR_MNU); + if (hours_minutes != cached_hours_minutes) { + uint8_t hours = (uint8_t)((time & (RTC_TR_HT | RTC_TR_HU)) >> 16U); + uint8_t minutes = (uint8_t)((time & (RTC_TR_MNT | RTC_TR_MNU)) >> 8U); + hours = (uint8_t)RTC_Bcd2ToByte(hours); + minutes = (uint8_t)RTC_Bcd2ToByte(minutes); + seconds_to_minute = 60 * (60 * hours + minutes); + cached_hours_minutes = hours_minutes; + } + uint8_t seconds = (uint8_t)(time & (RTC_TR_ST | RTC_TR_SU)); + seconds = (uint8_t)RTC_Bcd2ToByte(seconds); + if (subticks != NULL) { + *subticks = subseconds % 32; + } + + uint64_t raw_ticks = ((uint64_t)TICK_DIVISOR) * (seconds_to_date + seconds_to_minute + seconds) + subseconds / 32; + return raw_ticks; +} + +void stm32_peripherals_rtc_assign_wkup_callback(void (*callback)(void)) { + wkup_callback = callback; +} + +void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds) { + // prep stuff from CubeMX + __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); + __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF); + + HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, (rtc_clock_frequency / 16) * seconds, RTC_WAKEUPCLOCK_RTCCLK_DIV16); +} + +void stm32_peripherals_rtc_set_wakeup_mode_tick(void) { + HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, (rtc_clock_frequency / 16) / TICK_DIVISOR, RTC_WAKEUPCLOCK_RTCCLK_DIV2); +} + +void stm32_peripherals_rtc_enable_wakeup_timer(void) { + peripherals_wkup_on = true; + HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 1, 0U); + HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn); +} + +void stm32_peripherals_rtc_disable_wakeup_timer(void) { + peripherals_wkup_on = false; + HAL_NVIC_DisableIRQ(RTC_WKUP_IRQn); + HAL_RTCEx_DeactivateWakeUpTimer(&hrtc); +} + +void stm32_peripherals_rtc_reset_alarms(void) { + HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A); + HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_B); +} + +void stm32_peripherals_rtc_assign_alarm_callback(uint8_t alarm_idx, void (*callback)(void)) { + alarm_callbacks[alarm_idx] = callback; +} + +void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks) { + uint64_t raw_ticks = stm32_peripherals_rtc_raw_ticks(NULL) + ticks; + + RTC_AlarmTypeDef alarm; + if (ticks > TICK_DIVISOR) { + timeutils_struct_time_t tm; + timeutils_seconds_since_2000_to_struct_time(raw_ticks / TICK_DIVISOR, &tm); + alarm.AlarmTime.Hours = tm.tm_hour; + alarm.AlarmTime.Minutes = tm.tm_min; + alarm.AlarmTime.Seconds = tm.tm_sec; + alarm.AlarmDateWeekDay = tm.tm_mday; + // Masking here means that the value is ignored so we set none. + alarm.AlarmMask = RTC_ALARMMASK_NONE; + } else { + // Masking here means that the value is ignored so we set them all. Only the subseconds + // value matters. + alarm.AlarmMask = RTC_ALARMMASK_ALL; + } + + alarm.AlarmTime.SubSeconds = rtc_clock_frequency - 1 - + ((raw_ticks % TICK_DIVISOR) * 32); + alarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + alarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_SET; + // Masking here means that the bits are ignored so we set none of them. + alarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE; + alarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; + alarm.Alarm = (alarm_idx == PERIPHERALS_ALARM_A) ? RTC_ALARM_A : RTC_ALARM_B; + + HAL_RTC_SetAlarm_IT(&hrtc, &alarm, RTC_FORMAT_BIN); + HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); + alarmed_already[alarm_idx] = false; +} + +bool stm32_peripherals_rtc_alarm_triggered(uint8_t alarm_idx) { + return alarmed_already[alarm_idx]; +} + +void RTC_WKUP_IRQHandler(void) { + if (wkup_callback) { + wkup_callback(); + } + __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF); + __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); + hrtc.State = HAL_RTC_STATE_READY; +} + +void RTC_Alarm_IRQHandler(void) { + HAL_RTC_AlarmIRQHandler(&hrtc); +} + +void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *_hrtc) { + if (alarm_callbacks[PERIPHERALS_ALARM_A]) { + alarm_callbacks[PERIPHERALS_ALARM_A](); + } + HAL_RTC_DeactivateAlarm(_hrtc, RTC_ALARM_A); + alarmed_already[PERIPHERALS_ALARM_A] = true; +} + +void HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *_hrtc) { + if (alarm_callbacks[PERIPHERALS_ALARM_B]) { + alarm_callbacks[PERIPHERALS_ALARM_B](); + } + HAL_RTC_DeactivateAlarm(_hrtc, RTC_ALARM_B); + alarmed_already[PERIPHERALS_ALARM_B] = true; +} diff --git a/ports/stm/peripherals/rtc.h b/ports/stm/peripherals/rtc.h new file mode 100644 index 0000000000..65cae14d1c --- /dev/null +++ b/ports/stm/peripherals/rtc.h @@ -0,0 +1,51 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Lucian Copeland for Adafruit Industries + * + * 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_STM32_PERIPHERALS_RTC_H__ +#define __MICROPY_INCLUDED_STM32_PERIPHERALS_RTC_H__ + +#include +#include + +#define PERIPHERALS_ALARM_A 0 +#define PERIPHERALS_ALARM_B 1 + +uint32_t stm32_peripherals_get_rtc_freq(void); +void stm32_peripherals_rtc_init(void); +uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks); + +void stm32_peripherals_rtc_assign_wkup_callback(void (*callback)(void)); +void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds); +void stm32_peripherals_rtc_set_wakeup_mode_tick(void); +void stm32_peripherals_rtc_enable_wakeup_timer(void); +void stm32_peripherals_rtc_disable_wakeup_timer(void); + +void stm32_peripherals_rtc_reset_alarms(void); +void stm32_peripherals_rtc_assign_alarm_callback(uint8_t alarm_idx, void (*callback)(void)); +void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks); +bool stm32_peripherals_rtc_alarm_triggered(uint8_t alarm_idx); + +#endif // __MICROPY_INCLUDED_STM32_PERIPHERALS_RTC_H__ diff --git a/ports/stm/supervisor/port.c b/ports/stm/supervisor/port.c index 666e2c50db..b62fe9d24a 100644 --- a/ports/stm/supervisor/port.c +++ b/ports/stm/supervisor/port.c @@ -48,14 +48,21 @@ #include "common-hal/pwmio/PWMOut.h" #endif #if CIRCUITPY_PULSEIO || CIRCUITPY_PWMIO -#include "timers.h" +#include "peripherals/timers.h" #endif #if CIRCUITPY_SDIOIO #include "common-hal/sdioio/SDCard.h" #endif +#if CIRCUITPY_PULSEIO || CIRCUITPY_ALARM +#include "peripherals/exti.h" +#endif +#if CIRCUITPY_ALARM +#include "common-hal/alarm/__init__.h" +#endif -#include "clocks.h" -#include "gpio.h" +#include "peripherals/clocks.h" +#include "peripherals/gpio.h" +#include "peripherals/rtc.h" #include STM32_HAL_H @@ -163,38 +170,39 @@ __attribute__((used, naked)) void Reset_Handler(void) { // Low power clock variables static volatile uint32_t systick_ms; -static RTC_HandleTypeDef _hrtc; - -#if BOARD_HAS_LOW_SPEED_CRYSTAL -static uint32_t rtc_clock_frequency = LSE_VALUE; -#else -static uint32_t rtc_clock_frequency = LSI_VALUE; -#endif safe_mode_t port_init(void) { HAL_Init(); // Turns on SysTick __HAL_RCC_SYSCFG_CLK_ENABLE(); - #if (CPY_STM32F4) + #if CPY_STM32F4 __HAL_RCC_PWR_CLK_ENABLE(); + HAL_PWR_EnableBkUpAccess(); + + #if CIRCUITPY_ALARM + // TODO: don't reset RTC entirely and move this back to alarm + if (STM_ALARM_FLAG & 0x01) { + // We've woken from deep sleep. Was it the WKUP pin or the RTC? + if (RTC->ISR & RTC_FLAG_ALRBF) { + // Alarm B is the deep sleep alarm + alarm_set_wakeup_reason(STM_WAKEUP_RTC); + } else { + alarm_set_wakeup_reason(STM_WAKEUP_GPIO); + } + } + #endif + + __HAL_RCC_BACKUPRESET_FORCE(); + __HAL_RCC_BACKUPRESET_RELEASE(); + #endif stm32_peripherals_clocks_init(); stm32_peripherals_gpio_init(); + stm32_peripherals_rtc_init(); - // RTC oscillator selection is handled in peripherals///clocks.c - __HAL_RCC_RTC_ENABLE(); - _hrtc.Instance = RTC; - _hrtc.Init.HourFormat = RTC_HOURFORMAT_24; - // Divide async as little as possible so that we have rtc_clock_frequency count in subseconds. - // This ensures our timing > 1 second is correct. - _hrtc.Init.AsynchPrediv = 0x0; - _hrtc.Init.SynchPrediv = rtc_clock_frequency - 1; - _hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; - - HAL_RTC_Init(&_hrtc); - HAL_RTCEx_EnableBypassShadow(&_hrtc); - HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); + __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); + stm32_peripherals_rtc_reset_alarms(); // Turn off SysTick SysTick->CTRL = 0; @@ -223,7 +231,6 @@ uint32_t HAL_GetTick() { } } - void SysTick_Handler(void) { systick_ms += 1; // Read the CTRL register to clear the SysTick interrupt. @@ -253,6 +260,9 @@ void reset_port(void) { #if CIRCUITPY_PWMIO pwmout_reset(); #endif + #if CIRCUITPY_PULSEIO || CIRCUITPY_ALARM + exti_reset(); + #endif } void reset_to_bootloader(void) { @@ -324,118 +334,26 @@ __attribute__((used)) void HardFault_Handler(void) { } } -// This function is called often for timing so we cache the seconds elapsed computation based on the -// register value. The STM HAL always does shifts and conversion if we use it directly. -volatile uint32_t seconds_to_date = 0; -volatile uint32_t cached_date = 0; -volatile uint32_t seconds_to_minute = 0; -volatile uint32_t cached_hours_minutes = 0; uint64_t port_get_raw_ticks(uint8_t *subticks) { - // Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read - // SSR twice to make sure we didn't read across a tick. - __disable_irq(); - uint32_t first_ssr = (uint32_t)(RTC->SSR); - uint32_t time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK); - uint32_t date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK); - uint32_t ssr = (uint32_t)(RTC->SSR); - while (ssr != first_ssr) { - first_ssr = ssr; - time = (uint32_t)(RTC->TR & RTC_TR_RESERVED_MASK); - date = (uint32_t)(RTC->DR & RTC_DR_RESERVED_MASK); - ssr = (uint32_t)(RTC->SSR); - } - __enable_irq(); - - uint32_t subseconds = rtc_clock_frequency - 1 - ssr; - - if (date != cached_date) { - uint32_t year = (uint8_t)((date & (RTC_DR_YT | RTC_DR_YU)) >> 16U); - uint8_t month = (uint8_t)((date & (RTC_DR_MT | RTC_DR_MU)) >> 8U); - uint8_t day = (uint8_t)(date & (RTC_DR_DT | RTC_DR_DU)); - // Add 2000 since the year is only the last two digits. - year = 2000 + (uint32_t)RTC_Bcd2ToByte(year); - month = (uint8_t)RTC_Bcd2ToByte(month); - day = (uint8_t)RTC_Bcd2ToByte(day); - seconds_to_date = timeutils_seconds_since_2000(year, month, day, 0, 0, 0); - cached_date = date; - } - uint32_t hours_minutes = time & (RTC_TR_HT | RTC_TR_HU | RTC_TR_MNT | RTC_TR_MNU); - if (hours_minutes != cached_hours_minutes) { - uint8_t hours = (uint8_t)((time & (RTC_TR_HT | RTC_TR_HU)) >> 16U); - uint8_t minutes = (uint8_t)((time & (RTC_TR_MNT | RTC_TR_MNU)) >> 8U); - hours = (uint8_t)RTC_Bcd2ToByte(hours); - minutes = (uint8_t)RTC_Bcd2ToByte(minutes); - seconds_to_minute = 60 * (60 * hours + minutes); - cached_hours_minutes = hours_minutes; - } - uint8_t seconds = (uint8_t)(time & (RTC_TR_ST | RTC_TR_SU)); - seconds = (uint8_t)RTC_Bcd2ToByte(seconds); - if (subticks != NULL) { - *subticks = subseconds % 32; - } - - uint64_t raw_ticks = ((uint64_t)1024) * (seconds_to_date + seconds_to_minute + seconds) + subseconds / 32; - return raw_ticks; -} - -void RTC_WKUP_IRQHandler(void) { - supervisor_tick(); - __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&_hrtc, RTC_FLAG_WUTF); - __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); -} - -volatile bool alarmed_already = false; -void RTC_Alarm_IRQHandler(void) { - HAL_RTC_DeactivateAlarm(&_hrtc, RTC_ALARM_A); - __HAL_RTC_ALARM_EXTI_CLEAR_FLAG(); - __HAL_RTC_ALARM_CLEAR_FLAG(&_hrtc, RTC_FLAG_ALRAF); - alarmed_already = true; + return stm32_peripherals_rtc_raw_ticks(subticks); } // Enable 1/1024 second tick. void port_enable_tick(void) { - HAL_RTCEx_SetWakeUpTimer_IT(&_hrtc, rtc_clock_frequency / 1024 / 2, RTC_WAKEUPCLOCK_RTCCLK_DIV2); - HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 1, 0U); - HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn); + stm32_peripherals_rtc_set_wakeup_mode_tick(); + stm32_peripherals_rtc_assign_wkup_callback(supervisor_tick); + stm32_peripherals_rtc_enable_wakeup_timer(); } +// TODO: what is this? can I get rid of it? extern volatile uint32_t autoreload_delay_ms; // Disable 1/1024 second tick. void port_disable_tick(void) { - HAL_NVIC_DisableIRQ(RTC_WKUP_IRQn); - HAL_RTCEx_DeactivateWakeUpTimer(&_hrtc); + stm32_peripherals_rtc_disable_wakeup_timer(); } void port_interrupt_after_ticks(uint32_t ticks) { - uint64_t raw_ticks = port_get_raw_ticks(NULL) + ticks; - - RTC_AlarmTypeDef alarm; - if (ticks > 1024) { - timeutils_struct_time_t tm; - timeutils_seconds_since_2000_to_struct_time(raw_ticks / 1024, &tm); - alarm.AlarmTime.Hours = tm.tm_hour; - alarm.AlarmTime.Minutes = tm.tm_min; - alarm.AlarmTime.Seconds = tm.tm_sec; - alarm.AlarmDateWeekDay = tm.tm_mday; - // Masking here means that the value is ignored so we set none. - alarm.AlarmMask = RTC_ALARMMASK_NONE; - } else { - // Masking here means that the value is ignored so we set them all. Only the subseconds - // value matters. - alarm.AlarmMask = RTC_ALARMMASK_ALL; - } - - alarm.AlarmTime.SubSeconds = rtc_clock_frequency - 1 - - ((raw_ticks % 1024) * 32); - alarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; - alarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_SET; - // Masking here means that the bits are ignored so we set none of them. - alarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE; - alarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; - alarm.Alarm = RTC_ALARM_A; - - HAL_RTC_SetAlarm_IT(&_hrtc, &alarm, RTC_FORMAT_BIN); - alarmed_already = false; + stm32_peripherals_rtc_set_alarm(PERIPHERALS_ALARM_A, ticks); } void port_idle_until_interrupt(void) { @@ -444,7 +362,8 @@ void port_idle_until_interrupt(void) { __set_FPSCR(__get_FPSCR() & ~(0x9f)); (void)__get_FPSCR(); } - if (alarmed_already) { + // The alarm might have triggered before we even reach the WFI + if (stm32_peripherals_rtc_alarm_triggered(PERIPHERALS_ALARM_A)) { return; } __WFI(); @@ -455,3 +374,9 @@ void port_idle_until_interrupt(void) { void _init(void) { } + +#if CIRCUITPY_ALARM +// in case boards/xxx/board.c does not provide board_deinit() +MP_WEAK void board_deinit(void) { +} +#endif diff --git a/shared-bindings/alarm/__init__.h b/shared-bindings/alarm/__init__.h index 9c85b2e212..1c12eb1bd0 100644 --- a/shared-bindings/alarm/__init__.h +++ b/shared-bindings/alarm/__init__.h @@ -40,7 +40,7 @@ extern mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const // call alarm_woken_from_sleep to see if we've been woken by an alarm and if so, // it will exit idle as if deep sleep was exited. extern void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms); - +// Deep sleep is entered outside of the VM so we omit the `common_hal_` prefix. extern NORETURN void common_hal_alarm_enter_deep_sleep(void); extern void common_hal_alarm_pretending_deep_sleep(void); diff --git a/shared-bindings/alarm/pin/PinAlarm.c b/shared-bindings/alarm/pin/PinAlarm.c index 4267d795ac..3fe7a2bd59 100644 --- a/shared-bindings/alarm/pin/PinAlarm.c +++ b/shared-bindings/alarm/pin/PinAlarm.c @@ -73,7 +73,7 @@ STATIC mp_obj_t alarm_pin_pinalarm_make_new(const mp_obj_type_t *type, mp_uint_t mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(0, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj); + const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj); common_hal_alarm_pin_pinalarm_construct(self, pin, @@ -89,7 +89,7 @@ STATIC mp_obj_t alarm_pin_pinalarm_make_new(const mp_obj_type_t *type, mp_uint_t //| STATIC mp_obj_t alarm_pin_pinalarm_obj_get_pin(mp_obj_t self_in) { alarm_pin_pinalarm_obj_t *self = MP_OBJ_TO_PTR(self_in); - mcu_pin_obj_t *pin = common_hal_alarm_pin_pinalarm_get_pin(self); + const mcu_pin_obj_t *pin = common_hal_alarm_pin_pinalarm_get_pin(self); if (pin == NULL) { return mp_const_none; } diff --git a/shared-bindings/alarm/pin/PinAlarm.h b/shared-bindings/alarm/pin/PinAlarm.h index 48865009c3..ba74bab5f3 100644 --- a/shared-bindings/alarm/pin/PinAlarm.h +++ b/shared-bindings/alarm/pin/PinAlarm.h @@ -34,8 +34,8 @@ extern const mp_obj_type_t alarm_pin_pinalarm_type; -void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, mcu_pin_obj_t *pin, bool value, bool edge, bool pull); -extern mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self); +void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, const mcu_pin_obj_t *pin, bool value, bool edge, bool pull); +extern const mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self); extern bool common_hal_alarm_pin_pinalarm_get_value(alarm_pin_pinalarm_obj_t *self); extern bool common_hal_alarm_pin_pinalarm_get_edge(alarm_pin_pinalarm_obj_t *self); extern bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self);