allow preserving pin state during deep sleep

This commit is contained in:
Dan Halbert 2022-09-12 09:48:41 -04:00
parent 5a053f9ae5
commit 60f43b1703
18 changed files with 186 additions and 65 deletions

View File

@ -66,13 +66,16 @@ msgid ""
"%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" "%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d"
msgstr "" msgstr ""
#: ports/atmel-samd/common-hal/alarm/__init__.c
#: ports/cxd56/common-hal/analogio/AnalogOut.c ports/cxd56/common-hal/rtc/RTC.c #: ports/cxd56/common-hal/analogio/AnalogOut.c ports/cxd56/common-hal/rtc/RTC.c
#: ports/espressif/common-hal/rtc/RTC.c #: ports/espressif/common-hal/rtc/RTC.c
#: ports/mimxrt10xx/common-hal/analogio/AnalogOut.c #: ports/mimxrt10xx/common-hal/analogio/AnalogOut.c
#: ports/mimxrt10xx/common-hal/rtc/RTC.c #: ports/mimxrt10xx/common-hal/rtc/RTC.c ports/nrf/common-hal/alarm/__init__.c
#: ports/nrf/common-hal/analogio/AnalogOut.c ports/nrf/common-hal/rtc/RTC.c #: ports/nrf/common-hal/analogio/AnalogOut.c ports/nrf/common-hal/rtc/RTC.c
#: ports/raspberrypi/common-hal/alarm/__init__.c
#: ports/raspberrypi/common-hal/analogio/AnalogOut.c #: ports/raspberrypi/common-hal/analogio/AnalogOut.c
#: ports/raspberrypi/common-hal/rtc/RTC.c ports/stm/common-hal/rtc/RTC.c #: ports/raspberrypi/common-hal/rtc/RTC.c ports/stm/common-hal/alarm/__init__.c
#: ports/stm/common-hal/rtc/RTC.c
msgid "%q" msgid "%q"
msgstr "" msgstr ""
@ -902,13 +905,14 @@ msgstr ""
#: ports/raspberrypi/bindings/rp2pio/StateMachine.c py/enum.c #: ports/raspberrypi/bindings/rp2pio/StateMachine.c py/enum.c
#: shared-bindings/_bleio/__init__.c shared-bindings/aesio/aes.c #: shared-bindings/_bleio/__init__.c shared-bindings/aesio/aes.c
#: shared-bindings/busio/SPI.c shared-bindings/microcontroller/Pin.c #: shared-bindings/alarm/__init__.c shared-bindings/busio/SPI.c
#: shared-bindings/microcontroller/Pin.c
#: shared-bindings/neopixel_write/__init__.c #: shared-bindings/neopixel_write/__init__.c
msgid "Expected a %q" msgid "Expected a %q"
msgstr "" msgstr ""
#: shared-bindings/alarm/__init__.c #: shared-bindings/alarm/__init__.c
msgid "Expected an alarm" msgid "Expected an %q"
msgstr "" msgstr ""
#: ports/espressif/common-hal/_bleio/Adapter.c #: ports/espressif/common-hal/_bleio/Adapter.c
@ -3798,6 +3802,7 @@ msgstr ""
#: ports/espressif/boards/adafruit_qtpy_esp32c3/mpconfigboard.h #: ports/espressif/boards/adafruit_qtpy_esp32c3/mpconfigboard.h
#: ports/espressif/boards/beetle-esp32-c3/mpconfigboard.h #: ports/espressif/boards/beetle-esp32-c3/mpconfigboard.h
#: ports/espressif/boards/lolin_c3_mini/mpconfigboard.h #: ports/espressif/boards/lolin_c3_mini/mpconfigboard.h
#: ports/espressif/boards/microdev_micro_c3/mpconfigboard.h
#: supervisor/shared/safe_mode.c #: supervisor/shared/safe_mode.c
msgid "pressing boot button at start up.\n" msgid "pressing boot button at start up.\n"
msgstr "" msgstr ""

View File

@ -142,7 +142,10 @@ mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj
return wake_alarm; return wake_alarm;
} }
void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) { void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms, size_t n_dios, digitalio_digitalinout_obj_t **preserve_dios) {
if (n_dios > 0) {
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_preserve_dios);
}
_setup_sleep_alarms(true, n_alarms, alarms); _setup_sleep_alarms(true, n_alarms, alarms);
} }

View File

@ -31,17 +31,14 @@
#include "components/hal/include/hal/gpio_hal.h" #include "components/hal/include/hal/gpio_hal.h"
#include "common-hal/microcontroller/Pin.h" #include "common-hal/microcontroller/Pin.h"
void board_init(void) {
reset_board();
}
void reset_board(void) {
// Turn on NeoPixel and I2C power by default.
gpio_set_direction(2, GPIO_MODE_DEF_OUTPUT);
gpio_set_level(2, true);
}
bool espressif_board_reset_pin_number(gpio_num_t pin_number) { bool espressif_board_reset_pin_number(gpio_num_t pin_number) {
if (pin_number == 2) {
// Turn on NeoPixel and I2C power by default.
gpio_set_direction(pin_number, GPIO_MODE_DEF_OUTPUT);
gpio_set_level(pin_number, true);
return true;
}
return false; return false;
} }

View File

@ -31,7 +31,6 @@
#include "common-hal/alarm/SleepMemory.h" #include "common-hal/alarm/SleepMemory.h"
#include "shared-bindings/alarm/SleepMemory.h" #include "shared-bindings/alarm/SleepMemory.h"
#include "esp_log.h"
#include "esp_sleep.h" #include "esp_sleep.h"
// Data storage for singleton instance of SleepMemory. // Data storage for singleton instance of SleepMemory.
@ -39,7 +38,7 @@
static RTC_DATA_ATTR uint8_t _sleep_mem[SLEEP_MEMORY_LENGTH]; static RTC_DATA_ATTR uint8_t _sleep_mem[SLEEP_MEMORY_LENGTH];
void alarm_sleep_memory_reset(void) { void alarm_sleep_memory_reset(void) {
// ESP-IDF build system takes care of doing esp_sleep_pd_config() or the equivalentwith // ESP-IDF build system takes care of doing esp_sleep_pd_config() or the equivalent with
// the correct settings, depending on which RTC mem we are using. // the correct settings, depending on which RTC mem we are using.
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/sleep_modes.html#power-down-of-rtc-peripherals-and-memories // https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/sleep_modes.html#power-down-of-rtc-peripherals-and-memories
} }

View File

@ -39,12 +39,15 @@
#include "shared-bindings/wifi/__init__.h" #include "shared-bindings/wifi/__init__.h"
#include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/__init__.h"
#include "common-hal/digitalio/DigitalInOut.h"
#include "supervisor/port.h" #include "supervisor/port.h"
#include "supervisor/shared/workflow.h" #include "supervisor/shared/workflow.h"
#include "esp_sleep.h" #include "esp_sleep.h"
#include "soc/rtc_cntl_reg.h" #include "soc/rtc_cntl_reg.h"
#include "components/driver/include/driver/gpio.h"
#include "components/driver/include/driver/uart.h" #include "components/driver/include/driver/uart.h"
// Singleton instance of SleepMemory. // Singleton instance of SleepMemory.
@ -158,7 +161,8 @@ mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj
return wake_alarm; return wake_alarm;
} }
void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) { void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms, size_t n_dios, digitalio_digitalinout_obj_t *preserve_dios[]) {
digitalio_digitalinout_preserve_for_deep_sleep(n_dios, preserve_dios);
_setup_sleep_alarms(true, n_alarms, alarms); _setup_sleep_alarms(true, n_alarms, alarms);
} }
@ -166,6 +170,12 @@ void NORETURN common_hal_alarm_enter_deep_sleep(void) {
alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_pin_pinalarm_prepare_for_deep_sleep();
alarm_touch_touchalarm_prepare_for_deep_sleep(); alarm_touch_touchalarm_prepare_for_deep_sleep();
// We no longer need to remember the pin preservations, since any pin resets are all done.
clear_pin_preservations();
// Allow pin holds to work during deep sleep.
gpio_deep_sleep_hold_en();
// The ESP-IDF caches the deep sleep settings and applies them before sleep. // The ESP-IDF caches the deep sleep settings and applies them before sleep.
// We don't need to worry about resetting them in the interim. // We don't need to worry about resetting them in the interim.
esp_deep_sleep_start(); esp_deep_sleep_start();

View File

@ -32,6 +32,20 @@
#include "components/hal/include/hal/gpio_hal.h" #include "components/hal/include/hal/gpio_hal.h"
STATIC bool _pin_is_input(uint8_t pin_number) {
const uint32_t iomux = READ_PERI_REG(GPIO_PIN_MUX_REG[pin_number]);
return (iomux & FUN_IE) != 0;
}
void digitalio_digitalinout_preserve_for_deep_sleep(size_t n_dios, digitalio_digitalinout_obj_t *preserve_dios[]) {
// Mark the pin states of the given DigitalInOuts for preservation during deep sleep
for (size_t i = 0; i < n_dios; i++) {
if (!common_hal_digitalio_digitalinout_deinited(preserve_dios[i])) {
preserve_pin_number(preserve_dios[i]->pin->number);
}
}
}
void common_hal_digitalio_digitalinout_never_reset( void common_hal_digitalio_digitalinout_never_reset(
digitalio_digitalinout_obj_t *self) { digitalio_digitalinout_obj_t *self) {
never_reset_pin_number(self->pin->number); never_reset_pin_number(self->pin->number);
@ -83,8 +97,7 @@ digitalinout_result_t common_hal_digitalio_digitalinout_switch_to_output(
digitalio_direction_t common_hal_digitalio_digitalinout_get_direction( digitalio_direction_t common_hal_digitalio_digitalinout_get_direction(
digitalio_digitalinout_obj_t *self) { digitalio_digitalinout_obj_t *self) {
uint32_t iomux = READ_PERI_REG(GPIO_PIN_MUX_REG[self->pin->number]); if (_pin_is_input(self->pin->number)) {
if ((iomux & FUN_IE) != 0) {
return DIRECTION_INPUT; return DIRECTION_INPUT;
} }
return DIRECTION_OUTPUT; return DIRECTION_OUTPUT;

View File

@ -36,4 +36,6 @@ typedef struct {
bool output_value; bool output_value;
} digitalio_digitalinout_obj_t; } digitalio_digitalinout_obj_t;
extern void digitalio_digitalinout_preserve_for_deep_sleep(size_t n_dios, digitalio_digitalinout_obj_t *preserve_dios[]);
#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_DIGITALIO_DIGITALINOUT_H #endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_DIGITALIO_DIGITALINOUT_H

View File

@ -191,8 +191,6 @@ framesize_t common_hal_esp32_camera_camera_get_frame_size(esp32_camera_camera_ob
return self->camera_config.frame_size; return self->camera_config.frame_size;
} }
#include "esp_log.h"
void common_hal_esp32_camera_camera_reconfigure(esp32_camera_camera_obj_t *self, framesize_t frame_size, pixformat_t pixel_format, camera_grab_mode_t grab_mode, mp_int_t framebuffer_count) { void common_hal_esp32_camera_camera_reconfigure(esp32_camera_camera_obj_t *self, framesize_t frame_size, pixformat_t pixel_format, camera_grab_mode_t grab_mode, mp_int_t framebuffer_count) {
sensor_t *sensor = esp_camera_sensor_get(); sensor_t *sensor = esp_camera_sensor_get();
camera_sensor_info_t *sensor_info = esp_camera_sensor_get_info(&sensor->id); camera_sensor_info_t *sensor_info = esp_camera_sensor_get_info(&sensor->id);

View File

@ -33,14 +33,13 @@
#include "components/driver/include/driver/gpio.h" #include "components/driver/include/driver/gpio.h"
#include "components/hal/include/hal/gpio_hal.h" #include "components/hal/include/hal/gpio_hal.h"
STATIC uint64_t never_reset_pins; STATIC uint64_t _never_reset_pin_mask;
STATIC uint64_t in_use; STATIC uint64_t _preserved_pin_mask;
STATIC uint64_t _in_use_pin_mask;
// 64-bit pin mask for a single bit // Bit mask of all pins that should never EVER be reset.
#define PIN_BIT(pin_number) (((uint64_t)1) << pin_number)
// Bit mask of all pins that should never ever be reset.
// Typically these are SPI flash and PSRAM control pins, and communication pins. // Typically these are SPI flash and PSRAM control pins, and communication pins.
// "Reset forbidden" is stronger than "never reset" below, which may only be temporary.
static const uint64_t pin_mask_reset_forbidden = static const uint64_t pin_mask_reset_forbidden =
#if defined(CONFIG_IDF_TARGET_ESP32) #if defined(CONFIG_IDF_TARGET_ESP32)
// Never ever reset serial pins for bootloader and possibly USB-serial converter. // Never ever reset serial pins for bootloader and possibly USB-serial converter.
@ -105,7 +104,7 @@ void never_reset_pin_number(gpio_num_t pin_number) {
if (pin_number == NO_PIN || pin_number == (uint8_t)NO_PIN) { if (pin_number == NO_PIN || pin_number == (uint8_t)NO_PIN) {
return; return;
} }
never_reset_pins |= PIN_BIT(pin_number); _never_reset_pin_mask |= PIN_BIT(pin_number);
} }
void common_hal_never_reset_pin(const mcu_pin_obj_t *pin) { void common_hal_never_reset_pin(const mcu_pin_obj_t *pin) {
@ -119,12 +118,29 @@ MP_WEAK bool espressif_board_reset_pin_number(gpio_num_t pin_number) {
return false; return false;
} }
STATIC bool _reset_forbidden(gpio_num_t pin_number) {
return pin_mask_reset_forbidden & PIN_BIT(pin_number);
}
STATIC bool _never_reset(gpio_num_t pin_number) {
return _never_reset_pin_mask & PIN_BIT(pin_number);
}
STATIC bool _preserved_pin(gpio_num_t pin_number) {
return _preserved_pin_mask & PIN_BIT(pin_number);
}
STATIC void _reset_pin(gpio_num_t pin_number) { STATIC void _reset_pin(gpio_num_t pin_number) {
// Never ever reset pins used for flash, RAM, and basic communication. // Never ever reset pins used for flash, RAM, and basic communication.
if (pin_mask_reset_forbidden & PIN_BIT(pin_number)) { if (_reset_forbidden(pin_number)) {
return; return;
} }
// Disable any existing hold on this pin,
if (GPIO_IS_VALID_OUTPUT_GPIO(pin_number)) {
gpio_hold_dis(pin_number);
}
// Give the board a chance to reset the pin in a particular way. // Give the board a chance to reset the pin in a particular way.
if (espressif_board_reset_pin_number(pin_number)) { if (espressif_board_reset_pin_number(pin_number)) {
return; return;
@ -152,6 +168,18 @@ STATIC void _reset_pin(gpio_num_t pin_number) {
} }
} }
void preserve_pin_number(gpio_num_t pin_number) {
if (GPIO_IS_VALID_OUTPUT_GPIO(pin_number)) {
gpio_hold_en(pin_number);
_preserved_pin_mask |= PIN_BIT(pin_number);
}
}
void clear_pin_preservations(void) {
_preserved_pin_mask = 0;
}
// Mark pin as free and return it to a quiescent state. // Mark pin as free and return it to a quiescent state.
void reset_pin_number(gpio_num_t pin_number) { void reset_pin_number(gpio_num_t pin_number) {
// Some CircuitPython APIs deal in uint8_t pin numbers, but NO_PIN is -1. // Some CircuitPython APIs deal in uint8_t pin numbers, but NO_PIN is -1.
@ -159,8 +187,8 @@ void reset_pin_number(gpio_num_t pin_number) {
if (pin_number == NO_PIN || pin_number == (uint8_t)NO_PIN) { if (pin_number == NO_PIN || pin_number == (uint8_t)NO_PIN) {
return; return;
} }
never_reset_pins &= ~PIN_BIT(pin_number); _never_reset_pin_mask &= ~PIN_BIT(pin_number);
in_use &= ~PIN_BIT(pin_number); _in_use_pin_mask &= ~PIN_BIT(pin_number);
_reset_pin(pin_number); _reset_pin(pin_number);
} }
@ -177,15 +205,20 @@ void common_hal_reset_pin(const mcu_pin_obj_t *pin) {
} }
void reset_all_pins(void) { void reset_all_pins(void) {
// Undo deep sleep holds in case we woke up from deep sleep.
// We still need to unhold individual pins, which is done by _reset_pin.
gpio_deep_sleep_hold_dis();
for (uint8_t i = 0; i < GPIO_PIN_COUNT; i++) { for (uint8_t i = 0; i < GPIO_PIN_COUNT; i++) {
uint32_t iomux_address = GPIO_PIN_MUX_REG[i]; uint32_t iomux_address = GPIO_PIN_MUX_REG[i];
if (iomux_address == 0 || if (iomux_address == 0 ||
(never_reset_pins & PIN_BIT(i))) { _never_reset(i) ||
_preserved_pin(i)) {
continue; continue;
} }
_reset_pin(i); _reset_pin(i);
} }
in_use = never_reset_pins; _in_use_pin_mask = _never_reset_pin_mask;
} }
void claim_pin_number(gpio_num_t pin_number) { void claim_pin_number(gpio_num_t pin_number) {
@ -194,7 +227,7 @@ void claim_pin_number(gpio_num_t pin_number) {
if (pin_number == NO_PIN || pin_number == (uint8_t)NO_PIN) { if (pin_number == NO_PIN || pin_number == (uint8_t)NO_PIN) {
return; return;
} }
in_use |= PIN_BIT(pin_number); _in_use_pin_mask |= PIN_BIT(pin_number);
} }
void claim_pin(const mcu_pin_obj_t *pin) { void claim_pin(const mcu_pin_obj_t *pin) {
@ -206,7 +239,7 @@ void common_hal_mcu_pin_claim(const mcu_pin_obj_t *pin) {
} }
bool pin_number_is_free(gpio_num_t pin_number) { bool pin_number_is_free(gpio_num_t pin_number) {
return !(in_use & PIN_BIT(pin_number)); return !(_in_use_pin_mask & PIN_BIT(pin_number));
} }
bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t *pin) { bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t *pin) {

View File

@ -31,21 +31,28 @@
#include "peripherals/pins.h" #include "peripherals/pins.h"
void reset_all_pins(void); // 64-bit pin mask for a single bit
#define PIN_BIT(pin_number) (((uint64_t)1) << pin_number)
extern void common_hal_reset_pin(const mcu_pin_obj_t *pin);
extern void common_hal_never_reset_pin(const mcu_pin_obj_t *pin);
extern void reset_all_pins(void);
// reset_pin_number takes the pin number instead of the pointer so that objects don't // reset_pin_number takes the pin number instead of the pointer so that objects don't
// need to store a full pointer. // need to store a full pointer.
void reset_pin_number(gpio_num_t pin_number); extern void reset_pin_number(gpio_num_t pin_number);
void common_hal_reset_pin(const mcu_pin_obj_t *pin); extern void claim_pin(const mcu_pin_obj_t *pin);
void common_hal_never_reset_pin(const mcu_pin_obj_t *pin); extern void claim_pin_number(gpio_num_t pin_number);
void claim_pin(const mcu_pin_obj_t *pin); extern bool pin_number_is_free(gpio_num_t pin_number);
void claim_pin_number(gpio_num_t pin_number); extern void never_reset_pin_number(gpio_num_t pin_number);
bool pin_number_is_free(gpio_num_t pin_number);
void never_reset_pin_number(gpio_num_t pin_number); extern void preserve_pin_number(gpio_num_t pin_number);
extern void clear_pin_preservations(void);
// Allow the board to reset a pin in a board-specific way. This can be used // Allow the board to reset a pin in a board-specific way. This can be used
// for LEDs or enable pins to put them in a state beside the default pull-up. // for LEDs or enable pins to put them in a state beside the default pull-up.
// Return true to indicate that the pin was reset. Returning false will lead to // Return true to indicate that the pin was reset. Returning false will lead to
// the port-default reset behavior. // the port-default reset behavior.
bool espressif_board_reset_pin_number(gpio_num_t pin_number); extern bool espressif_board_reset_pin_number(gpio_num_t pin_number);
#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_MICROCONTROLLER_PIN_H #endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_MICROCONTROLLER_PIN_H

View File

@ -247,7 +247,10 @@ mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj
return wake_alarm; return wake_alarm;
} }
void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) { void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms, size_t n_dios, digitalio_digitalinout_obj_t **preserve_dios) {
if (n_dios > 0) {
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_preserve_dios);
}
_setup_sleep_alarms(true, n_alarms, alarms); _setup_sleep_alarms(true, n_alarms, alarms);
} }

View File

@ -194,7 +194,10 @@ mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj
return wake_alarm; return wake_alarm;
} }
void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) { void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms, size_t n_dios, digitalio_digitalinout_obj_t **preserve_dios) {
if (n_dios > 0) {
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_preserve_dios);
}
_setup_sleep_alarms(true, n_alarms, alarms); _setup_sleep_alarms(true, n_alarms, alarms);
} }

View File

@ -144,7 +144,10 @@ mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj
return wake_alarm; return wake_alarm;
} }
void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) { void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms, size_t n_dios, digitalio_digitalinout_obj_t **preserve_dios) {
if (n_dios > 0) {
mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_preserve_dios);
}
_setup_sleep_alarms(true, n_alarms, alarms); _setup_sleep_alarms(true, n_alarms, alarms);
} }

View File

@ -244,6 +244,7 @@ void SysTick_Handler(void) {
void reset_port(void) { void reset_port(void) {
reset_all_pins(); reset_all_pins();
#if CIRCUITPY_RTC #if CIRCUITPY_RTC
rtc_reset(); rtc_reset();
#endif #endif

View File

@ -32,6 +32,7 @@
#include "shared-bindings/alarm/pin/PinAlarm.h" #include "shared-bindings/alarm/pin/PinAlarm.h"
#include "shared-bindings/alarm/time/TimeAlarm.h" #include "shared-bindings/alarm/time/TimeAlarm.h"
#include "shared-bindings/alarm/touch/TouchAlarm.h" #include "shared-bindings/alarm/touch/TouchAlarm.h"
#include "shared-bindings/digitalio/DigitalInOut.h"
#include "shared-bindings/supervisor/Runtime.h" #include "shared-bindings/supervisor/Runtime.h"
#include "shared-bindings/time/__init__.h" #include "shared-bindings/time/__init__.h"
#include "supervisor/shared/workflow.h" #include "supervisor/shared/workflow.h"
@ -80,7 +81,7 @@ STATIC void validate_objs_are_alarms(size_t n_args, const mp_obj_t *objs) {
mp_obj_is_type(objs[i], &alarm_touch_touchalarm_type)) { mp_obj_is_type(objs[i], &alarm_touch_touchalarm_type)) {
continue; continue;
} }
mp_raise_TypeError_varg(translate("Expected an alarm")); mp_raise_TypeError_varg(translate("Expected an %q"), MP_QSTR_Alarm);
} }
} }
@ -112,7 +113,7 @@ STATIC mp_obj_t alarm_light_sleep_until_alarms(size_t n_args, const mp_obj_t *ar
} }
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_light_sleep_until_alarms_obj, 1, MP_OBJ_FUN_ARGS_MAX, alarm_light_sleep_until_alarms); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_light_sleep_until_alarms_obj, 1, MP_OBJ_FUN_ARGS_MAX, alarm_light_sleep_until_alarms);
//| def exit_and_deep_sleep_until_alarms(*alarms: circuitpython_typing.Alarm) -> None: //| def exit_and_deep_sleep_until_alarms(*alarms: circuitpython_typing.Alarm, preserve_dios: Sequence[digitalio.DigitalInOut] = ()) -> None:
//| """Exit the program and go into a deep sleep, until awakened by one of the alarms. //| """Exit the program and go into a deep sleep, until awakened by one of the alarms.
//| This function does not return. //| This function does not return.
//| //|
@ -126,6 +127,22 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_light_sleep_until_alarms_obj, 1, MP_OB
//| //|
//| If no alarms are specified, the microcontroller will deep sleep until reset. //| If no alarms are specified, the microcontroller will deep sleep until reset.
//| //|
//| :param circuitpython_typing.Alarm alarms: the alarms that can wake the microcontroller.
//| :param Sequence[digitalio.DigitalInOut] preserve_dios: A sequence of `DigitalInOut` objects
//| whose state should be preserved during deep sleep.
//| If a `DigitalInOut` in the sequence is set to be an output,
//| its current `DigitalInOut.value` (``True`` or ``False``)
//| will be preserved during the deep sleep.
//| If a `DigitalInOut` in the sequence is set to be an input,
//| its current `DigitalInOut.pull` value (``DOWN``, ``UP``, or ``None``)
//| will be preserved during deep sleep.
//|
//| Preserving `DigitalInOut` states during deep sleep can be used to ensure that
//| external or on-board devices are powered or unpowered during sleep, among other purposes.
//|
//| On some microcontrollers, some pins cannot remain in their original state for hardware reasons.
//|
//|
//| **If CircuitPython is connected to a host computer via USB or BLE //| **If CircuitPython is connected to a host computer via USB or BLE
//| the first time a deep sleep is requested, //| the first time a deep sleep is requested,
//| the connection will be maintained and the system will not go into deep sleep.** //| the connection will be maintained and the system will not go into deep sleep.**
@ -136,28 +153,50 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_light_sleep_until_alarms_obj, 1, MP_OB
//| the next deep sleep will still be a true deep sleep. You must do a hard reset //| the next deep sleep will still be a true deep sleep. You must do a hard reset
//| or power-cycle to exit a true deep sleep loop. //| or power-cycle to exit a true deep sleep loop.
//| //|
//| Here is skeletal example that deep-sleeps and restarts every 60 seconds: //| Here is a skeletal example:
//| //|
//| .. code-block:: python //| .. code-block:: python
//| //|
//| import alarm //| import alarm
//| import time //| import time
//| import board
//| //|
//| print("Waking up") //| print("Waking up")
//| //|
//| # Set an alarm for 60 seconds from now. //| # Create an alarm for 60 seconds from now, and also a pin alarm.
//| time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 60) //| time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 60)
//| pin_alarm = alarm.pin.PinAlarm(board.D7, False)
//| //|
//| # Deep sleep until the alarm goes off. Then restart the program. //| # Deep sleep until one of the alarm goes off. Then restart the program.
//| alarm.exit_and_deep_sleep_until_alarms(time_alarm) //| alarm.exit_and_deep_sleep_until_alarms(time_alarm, pin_alarm)
//| """ //| """
//| ... //| ...
//| //|
STATIC mp_obj_t alarm_exit_and_deep_sleep_until_alarms(size_t n_args, const mp_obj_t *args) { STATIC mp_obj_t alarm_exit_and_deep_sleep_until_alarms(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
validate_objs_are_alarms(n_args, args); enum { ARG_preserve_dios };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_preserve_dios, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_tuple} },
};
// Validate the alarms and set them. // args will contain only the value for preserve_dios. The *alarms args are in pos_args.
common_hal_alarm_set_deep_sleep_alarms(n_args, args); 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);
validate_objs_are_alarms(n_args, pos_args);
mp_obj_t preserve_dios = args[ARG_preserve_dios].u_obj;
const size_t num_dios = (size_t)MP_OBJ_SMALL_INT_VALUE(mp_obj_len(preserve_dios));
digitalio_digitalinout_obj_t *dios_array[num_dios];
for (mp_uint_t i = 0; i < num_dios; i++) {
mp_obj_t dio = mp_obj_subscr(preserve_dios, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL);
if (!mp_obj_is_type(dio, &digitalio_digitalinout_type)) {
mp_raise_TypeError_varg(translate("Expected a %q"), MP_QSTR_DigitalInOut);
}
dios_array[i] = MP_OBJ_TO_PTR(dio);
}
common_hal_alarm_set_deep_sleep_alarms(n_args, pos_args, num_dios, dios_array);
// Raise an exception, which will be processed in main.c. // Raise an exception, which will be processed in main.c.
mp_raise_type_arg(&mp_type_DeepSleepRequest, NULL); mp_raise_type_arg(&mp_type_DeepSleepRequest, NULL);
@ -165,7 +204,7 @@ STATIC mp_obj_t alarm_exit_and_deep_sleep_until_alarms(size_t n_args, const mp_o
// Doesn't get here. // Doesn't get here.
return mp_const_none; return mp_const_none;
} }
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(alarm_exit_and_deep_sleep_until_alarms_obj, 1, MP_OBJ_FUN_ARGS_MAX, alarm_exit_and_deep_sleep_until_alarms); MP_DEFINE_CONST_FUN_OBJ_KW(alarm_exit_and_deep_sleep_until_alarms_obj, 0, alarm_exit_and_deep_sleep_until_alarms);
STATIC const mp_map_elem_t alarm_pin_globals_table[] = { STATIC const mp_map_elem_t alarm_pin_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pin) }, { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pin) },

View File

@ -30,6 +30,7 @@
#include "py/obj.h" #include "py/obj.h"
#include "common-hal/alarm/__init__.h" #include "common-hal/alarm/__init__.h"
#include "common-hal/digitalio/DigitalInOut.h"
// Light sleep fully self-contained and does not exit user code. It will return // Light sleep fully self-contained and does not exit user code. It will return
// the same alarm object that was orignally passed in, unlike deep sleep, which // the same alarm object that was orignally passed in, unlike deep sleep, which
@ -42,7 +43,7 @@ extern mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const
// supervisor will idle using `port_wait_for_interrupt`. After each call, it will // supervisor will idle using `port_wait_for_interrupt`. After each call, it will
// call alarm_woken_from_sleep to see if we've been woken by an alarm and if so, // 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 // 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); extern void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms, size_t n_dios, digitalio_digitalinout_obj_t **preserve_dios);
extern NORETURN void common_hal_alarm_enter_deep_sleep(void); extern NORETURN void common_hal_alarm_enter_deep_sleep(void);

View File

@ -56,7 +56,7 @@
//| ... //| ...
//| //|
//| def flash(*buffer: ReadableBuffer, offset: int=0) -> None: //| def flash(buffer: ReadableBuffer, offset: int=0) -> None:
//| """Writes one of two app partitions at the given offset. //| """Writes one of two app partitions at the given offset.
//| //|
//| This can be called multiple times when flashing the firmware //| This can be called multiple times when flashing the firmware

View File

@ -168,17 +168,21 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
} }
// check for SystemExit // check for SystemExit
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type((mp_obj_t)nlr.ret_val)), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
// nlr.ret_val is an exception object.
mp_obj_t exception_obj = (mp_obj_t)nlr.ret_val;
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(exception_obj)), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
// at the moment, the value of SystemExit is unused // at the moment, the value of SystemExit is unused
ret = pyexec_system_exit; ret = pyexec_system_exit;
#if CIRCUITPY_ALARM #if CIRCUITPY_ALARM
} else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type((mp_obj_t)nlr.ret_val)), &mp_type_DeepSleepRequest)) { } else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(exception_obj)), MP_OBJ_FROM_PTR(&mp_type_DeepSleepRequest))) {
ret = PYEXEC_DEEP_SLEEP; ret = PYEXEC_DEEP_SLEEP;
#endif #endif
} else if ((mp_obj_t)nlr.ret_val == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception))) { } else if (exception_obj == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception))) {
ret = PYEXEC_RELOAD; ret = PYEXEC_RELOAD;
} else { } else {
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); mp_obj_print_exception(&mp_plat_print, exception_obj);
ret = PYEXEC_EXCEPTION; ret = PYEXEC_EXCEPTION;
} }