Merge pull request #4236 from jun2sak/nrf52-sleep

sleep and wakeup for nrf52
This commit is contained in:
Dan Halbert 2021-04-28 12:11:31 -04:00 committed by GitHub
commit d4d96bb25f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1553 additions and 7 deletions

View File

@ -681,6 +681,7 @@ msgid "Cannot vary frequency on a timer that is already in use"
msgstr ""
#: ports/esp32s2/common-hal/alarm/pin/PinAlarm.c
#: ports/nrf/common-hal/alarm/pin/PinAlarm.c
msgid "Cannot wake on pin edge. Only level."
msgstr ""
@ -1285,7 +1286,8 @@ msgstr ""
#: ports/atmel-samd/common-hal/audioio/AudioOut.c
#: ports/atmel-samd/common-hal/touchio/TouchIn.c
#: ports/esp32s2/common-hal/alarm/touch/TouchAlarm.c
#: ports/esp32s2/common-hal/touchio/TouchIn.c shared-bindings/pwmio/PWMOut.c
#: ports/esp32s2/common-hal/touchio/TouchIn.c
#: ports/nrf/common-hal/alarm/pin/PinAlarm.c shared-bindings/pwmio/PWMOut.c
#: shared-module/rgbmatrix/RGBMatrix.c
msgid "Invalid pin"
msgstr ""
@ -1663,6 +1665,7 @@ msgid "Only one TouchAlarm can be set in deep sleep."
msgstr ""
#: ports/esp32s2/common-hal/alarm/time/TimeAlarm.c
#: ports/nrf/common-hal/alarm/time/TimeAlarm.c
msgid "Only one alarm.time alarm can be set."
msgstr ""

1
main.c
View File

@ -427,6 +427,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
// it may also return due to another interrupt, that's why we check
// for deep sleep alarms above. If it wasn't a deep sleep alarm,
// then we'll idle here again.
#if CIRCUITPY_ALARM
common_hal_alarm_pretending_deep_sleep();
#else

View File

@ -94,6 +94,11 @@ else
CFLAGS += -flto -flto-partition=none
endif
ifeq ($(NRF_DEBUG_PRINT), 1)
CFLAGS += -DNRF_DEBUG_PRINT=1
SRC_SUPERVISOR += supervisor/debug_uart.c
endif
# option to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk
CFLAGS += $(OPTIMIZATION_FLAGS)

View File

@ -7,6 +7,7 @@ MCU_CHIP = nrf52833
INTERNAL_FLASH_FILESYSTEM = 1
CIRCUITPY_ALARM = 0
CIRCUITPY_AUDIOMP3 = 0
CIRCUITPY_BITBANGIO = 0
CIRCUITPY_BITMAPTOOLS = 0

View File

@ -10,6 +10,7 @@ MCU_CHIP = nrf52833
INTERNAL_FLASH_FILESYSTEM = 1
CIRCUITPY_ALARM = 0
CIRCUITPY_AESIO = 1
CIRCUITPY_AUDIOMP3 = 0
CIRCUITPY_BITMAPTOOLS = 0
@ -24,6 +25,8 @@ CIRCUITPY_MSGPACK = 0
CIRCUITPY_NEOPIXEL_WRITE = 0
CIRCUITPY_NVM = 0
CIRCUITPY_PIXELBUF = 0
CIRCUITPY_PULSEIO = 0
CIRCUITPY_PWMIO = 1
CIRCUITPY_RGBMATRIX = 0
CIRCUITPY_ROTARYIO = 0
CIRCUITPY_RTC = 1

View File

@ -0,0 +1,118 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Junji Sakai
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "py/runtime.h"
#include "common-hal/alarm/__init__.h"
#include "common-hal/alarm/SleepMemory.h"
#include "nrf_power.h"
#ifdef NRF_DEBUG_PRINT
extern void dbg_dump_RAMreg(void);
#include "supervisor/serial.h" // dbg_printf()
#endif
__attribute__((section(".uninitialized"))) static uint8_t _sleepmem[SLEEP_MEMORY_LENGTH];
__attribute__((section(".uninitialized"))) uint8_t sleepmem_wakeup_event;
__attribute__((section(".uninitialized"))) uint8_t sleepmem_wakeup_pin;
__attribute__((section(".uninitialized"))) static uint32_t _sleepmem_magicnum;
#define SLEEP_MEMORY_DATA_GUARD 0xad0000af
#define SLEEP_MEMORY_DATA_GUARD_MASK 0xff0000ff
static int is_sleep_memory_valid(void) {
if ((_sleepmem_magicnum & SLEEP_MEMORY_DATA_GUARD_MASK)
== SLEEP_MEMORY_DATA_GUARD) {
return 1;
}
return 0;
}
void set_memory_retention(void) {
// set RAM[n].POWER register for RAM retention
// nRF52840 has RAM[0..7].Section[0..1] and RAM[8].Section[0..5]
// nRF52833 has RAM[0..7].Section[0..1] and RAM[8].Section[0,1]
for(int block = 0; block <= 7; ++block) {
nrf_power_rampower_mask_on(NRF_POWER, block,
NRF_POWER_RAMPOWER_S0RETENTION_MASK |
NRF_POWER_RAMPOWER_S1RETENTION_MASK);
};
#ifdef NRF52840
nrf_power_rampower_mask_on(NRF_POWER, 8,
NRF_POWER_RAMPOWER_S0RETENTION_MASK |
NRF_POWER_RAMPOWER_S1RETENTION_MASK |
NRF_POWER_RAMPOWER_S2RETENTION_MASK |
NRF_POWER_RAMPOWER_S3RETENTION_MASK |
NRF_POWER_RAMPOWER_S4RETENTION_MASK |
NRF_POWER_RAMPOWER_S5RETENTION_MASK);
#endif
#ifdef NRF52833
nrf_power_rampower_mask_on(NRF_POWER, 8,
NRF_POWER_RAMPOWER_S0RETENTION_MASK |
NRF_POWER_RAMPOWER_S1RETENTION_MASK);
#endif
}
static void initialize_sleep_memory(void) {
memset((uint8_t *)_sleepmem, 0, SLEEP_MEMORY_LENGTH);
sleepmem_wakeup_event = 0;
sleepmem_wakeup_pin = 0;
set_memory_retention();
#ifdef NRF_DEBUG_PRINT
//dbg_dump_RAMreg();
#endif
_sleepmem_magicnum = SLEEP_MEMORY_DATA_GUARD;
}
void alarm_sleep_memory_reset(void) {
if (!is_sleep_memory_valid()) {
initialize_sleep_memory();
#ifdef NRF_DEBUG_PRINT
dbg_printf("sleep memory initialized\r\n");
#endif
}
}
uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self) {
return sizeof(_sleepmem);
}
bool common_hal_alarm_sleep_memory_set_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, const uint8_t* values, uint32_t len) {
if (start_index + len > sizeof(_sleepmem)) {
return false;
}
memcpy((uint8_t *) (_sleepmem + start_index), values, len);
return true;
}
void common_hal_alarm_sleep_memory_get_bytes(alarm_sleep_memory_obj_t *self, uint32_t start_index, uint8_t* values, uint32_t len) {
if (start_index + len > sizeof(_sleepmem)) {
return;
}
memcpy(values, (uint8_t *) (_sleepmem + start_index), len);
}

View File

@ -0,0 +1,41 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Junji Sakai
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM_SLEEPMEMORY_H
#define MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM_SLEEPMEMORY_H
#include "py/obj.h"
#define SLEEP_MEMORY_LENGTH (256)
typedef struct {
mp_obj_base_t base;
} alarm_sleep_memory_obj_t;
extern void set_memory_retention(void);
extern void alarm_sleep_memory_reset(void);
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM_SLEEPMEMORY_H

View File

@ -0,0 +1,349 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
* Copyright (c) 2020 Dan Halbert for Adafruit Industries
* Copyright (c) 2021 Junji Sakai
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/gc.h"
#include "py/obj.h"
#include "py/objtuple.h"
#include "py/runtime.h"
#include <stdio.h>
#include <string.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/alarm/touch/TouchAlarm.h"
#include "shared-bindings/time/__init__.h"
#include "supervisor/port.h"
#include "supervisor/serial.h" // serial_connected()
#ifdef NRF_DEBUG_PRINT
#include "supervisor/serial.h" // dbg_printf()
extern int dbg_check_RTCprescaler(void);
#endif
#include "supervisor/qspi_flash.h"
#include "nrf.h"
#include "nrf_power.h"
#include "nrfx.h"
#include "nrfx_gpiote.h"
// Singleton instance of SleepMemory.
const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = {
.base = {
.type = &alarm_sleep_memory_type,
},
};
void alarm_reset(void) {
alarm_sleep_memory_reset();
alarm_pin_pinalarm_reset();
alarm_time_timealarm_reset();
alarm_touch_touchalarm_reset();
}
extern uint32_t reset_reason_saved;
STATIC nrf_sleep_source_t _get_wakeup_cause(void) {
if (alarm_pin_pinalarm_woke_us_up()) {
return NRF_SLEEP_WAKEUP_GPIO;
}
if (alarm_time_timealarm_woke_us_up()) {
return NRF_SLEEP_WAKEUP_TIMER;
}
if (alarm_touch_touchalarm_woke_us_up()) {
return NRF_SLEEP_WAKEUP_TOUCHPAD;
}
if (reset_reason_saved & NRF_POWER_RESETREAS_RESETPIN_MASK) {
return NRF_SLEEP_WAKEUP_RESETPIN;
}
else if (reset_reason_saved & NRF_POWER_RESETREAS_OFF_MASK) {
return NRF_SLEEP_WAKEUP_GPIO;
}
else if (reset_reason_saved & NRF_POWER_RESETREAS_VBUS_MASK) {
return NRF_SLEEP_WAKEUP_VBUS;
}
return NRF_SLEEP_WAKEUP_UNDEFINED;
}
#ifdef NRF_DEBUG_PRINT
static const char* cause_str[] = {
"UNDEFINED",
"GPIO",
"TIMER",
"TOUCHPAD",
"VBUS",
"RESETPIN",
};
void print_wakeup_cause(nrf_sleep_source_t cause) {
if (cause >= 0 && cause < NRF_SLEEP_WAKEUP_ZZZ) {
dbg_printf("wakeup cause = NRF_SLEEP_WAKEUP_%s\r\n",
cause_str[(int)cause]);
}
}
#endif
bool common_hal_alarm_woken_from_sleep(void) {
nrf_sleep_source_t cause = _get_wakeup_cause();
#ifdef NRF_DEBUG_PRINT
if (cause != NRF_SLEEP_WAKEUP_UNDEFINED) {
//print_wakeup_cause(cause);
}
#endif
return (cause == NRF_SLEEP_WAKEUP_GPIO || cause == NRF_SLEEP_WAKEUP_TIMER
|| cause == NRF_SLEEP_WAKEUP_TOUCHPAD);
}
STATIC mp_obj_t _get_wake_alarm(size_t n_alarms, const mp_obj_t *alarms) {
nrf_sleep_source_t cause = _get_wakeup_cause();
switch (cause) {
case NRF_SLEEP_WAKEUP_TIMER: {
return alarm_time_timealarm_get_wakeup_alarm(n_alarms, alarms);
}
case NRF_SLEEP_WAKEUP_TOUCHPAD: {
return alarm_touch_touchalarm_get_wakeup_alarm(n_alarms, alarms);
}
case NRF_SLEEP_WAKEUP_GPIO: {
return alarm_pin_pinalarm_get_wakeup_alarm(n_alarms, alarms);
}
default:
break;
}
return mp_const_none;
}
mp_obj_t common_hal_alarm_get_wake_alarm(void) {
mp_obj_t obj = _get_wake_alarm(0, NULL);
return obj;
}
// Set up light sleep or deep sleep alarms.
STATIC void _setup_sleep_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
alarm_pin_pinalarm_set_alarms(deep_sleep, n_alarms, alarms);
alarm_time_timealarm_set_alarms(deep_sleep, n_alarms, alarms);
alarm_touch_touchalarm_set_alarm(deep_sleep, n_alarms, alarms);
}
// TODO: this handles all possible types of wakeup, which is redundant with main.
// revise to extract all parts essential to enabling sleep wakeup, but leave the
// alarm/non-alarm sorting to the existing main loop.
void system_on_idle_until_alarm(int64_t timediff_ms, uint32_t prescaler) {
bool have_timeout = false;
uint64_t start_tick = 0, end_tick = 0;
int64_t tickdiff;
#if defined(MICROPY_QSPI_CS)
qspi_flash_enter_sleep();
#endif
if (timediff_ms != -1) {
have_timeout = true;
#if 0
int64_t now = common_hal_time_monotonic_ms();
dbg_printf("now_ms=%ld timediff_ms=%ld\r\n", (long)now, (long)timediff_ms);
#endif
if (timediff_ms < 0) timediff_ms = 0;
if (prescaler == 0) {
// 1 tick = 1/1024 sec = 1000/1024 ms
// -> 1 ms = 1024/1000 ticks
tickdiff = (mp_uint_t)(timediff_ms * 1024 / 1000); // ms -> ticks
}
else {
// 1 tick = prescaler/1024 sec = prescaler*1000/1024 ms
// -> 1ms = 1024/(1000*prescaler) ticks
tickdiff = (mp_uint_t)(timediff_ms * 1024 / (1000 * prescaler));
}
start_tick = port_get_raw_ticks(NULL);
end_tick = start_tick + tickdiff;
}
#if 0
dbg_printf("start_tick=%ld end_tick=%ld have_timeout=%c\r\n", (long)start_tick, (long)end_tick, have_timeout ? 'T' : 'F');
#endif
int64_t remaining;
sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_NONE;
sleepmem_wakeup_pin = WAKEUP_PIN_UNDEF;
#ifdef NRF_DEBUG_PRINT
int ct = 40;
char reason = '?';
#define WAKEUP_REASON(x) reason = (x)
#else
#define WAKEUP_REASON(x)
#endif
while(1) {
if (mp_hal_is_interrupted()) {
WAKEUP_REASON('I');
break;
}
if (serial_connected() && serial_bytes_available()) {
WAKEUP_REASON('S');
break;
}
RUN_BACKGROUND_TASKS;
if (common_hal_alarm_woken_from_sleep()) {
WAKEUP_REASON('W');
break;
}
if (have_timeout) {
remaining = end_tick - port_get_raw_ticks(NULL);
// We break a bit early so we don't risk setting the alarm before the time when we call
// sleep.
if (remaining < 1) {
WAKEUP_REASON('t');
break;
}
port_interrupt_after_ticks(remaining);
}
// Idle until an interrupt happens.
port_idle_until_interrupt();
#ifdef NRF_DEBUG_PRINT
if (ct > 0) {
dbg_printf("_");
--ct;
}
#endif
if (have_timeout) {
remaining = end_tick - port_get_raw_ticks(NULL);
if (remaining <= 0) {
sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_TIMER;
WAKEUP_REASON('T');
break;
}
}
}
#ifdef NRF_DEBUG_PRINT
dbg_printf("%c\r\n", reason);
#endif
#if defined(MICROPY_QSPI_CS)
qspi_flash_exit_sleep();
#endif
#ifdef NRF_DEBUG_PRINT
tickdiff = port_get_raw_ticks(NULL) - start_tick;
double sec;
if (prescaler == 0) {
sec = (double)tickdiff / 1024;
}
else {
sec = (double)(tickdiff * prescaler) / 1024;
}
dbg_printf("lapse %6.1f sec\r\n", sec);
#endif
}
mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj_t *alarms) {
mp_obj_t wake_alarm;
alarm_time_timealarm_clear_wakeup_time();
_setup_sleep_alarms(false, n_alarms, alarms);
#ifdef NRF_DEBUG_PRINT
dbg_printf("\r\nlight sleep...");
#endif
int64_t timediff_ms = alarm_time_timealarm_get_wakeup_timediff_ms();
system_on_idle_until_alarm(timediff_ms, 0);
if (mp_hal_is_interrupted()) {
wake_alarm = mp_const_none;
}
else {
wake_alarm = _get_wake_alarm(n_alarms, alarms);
}
alarm_reset();
return wake_alarm;
}
void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms) {
alarm_time_timealarm_clear_wakeup_time();
_setup_sleep_alarms(true, n_alarms, alarms);
}
#define PRESCALER_VALUE_IN_DEEP_SLEEP (1024)
void NORETURN common_hal_alarm_enter_deep_sleep(void) {
alarm_pin_pinalarm_prepare_for_deep_sleep();
alarm_time_timealarm_prepare_for_deep_sleep();
#ifdef NRF_DEBUG_PRINT
dbg_printf("\r\ndeep sleep...");
#endif
int64_t timediff_ms = alarm_time_timealarm_get_wakeup_timediff_ms();
tick_set_prescaler(PRESCALER_VALUE_IN_DEEP_SLEEP -1);
#ifdef NRF_DEBUG_PRINT
dbg_check_RTCprescaler(); //XXX
#endif
system_on_idle_until_alarm(timediff_ms, PRESCALER_VALUE_IN_DEEP_SLEEP);
#ifdef NRF_DEBUG_PRINT
dbg_printf("RESET...\r\n\r\n");
#endif
reset_cpu();
// should not reach here..
while(1) ;
}
// old version deep sleep code that was used in common_hal_alarm_enter_deep_sleep()
// for anyone who might want true System OFF sleep ..
#if 0
void OLD_go_system_off(void) {
sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_NONE;
sleepmem_wakeup_pin = WAKEUP_PIN_UNDEF;
uint8_t sd_enabled;
sd_softdevice_is_enabled(&sd_enabled);
set_memory_retention();
dbg_printf("OLD go system off.. %d\r\n", sd_enabled);
if (sd_enabled) {
sd_power_system_off();
}
else {
NRF_POWER->SYSTEMOFF = 1;
}
}
#endif
void common_hal_alarm_pretending_deep_sleep(void) {
alarm_pin_pinalarm_prepare_for_deep_sleep();
alarm_time_timealarm_prepare_for_deep_sleep();
#ifdef NRF_DEBUG_PRINT
dbg_printf("\r\npretending to deep sleep...");
#endif
int64_t timediff_ms = alarm_time_timealarm_get_wakeup_timediff_ms();
system_on_idle_until_alarm(timediff_ms, 0);
alarm_reset();
}
void common_hal_alarm_gc_collect(void) {
gc_collect_ptr(shared_alarm_get_wake_alarm());
}

View File

@ -0,0 +1,56 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Dan Halbert for Adafruit Industries.
* Copyright (c) 2021 Junji Sakai
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM__INIT__H
#define MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM__INIT__H
#include "common-hal/alarm/SleepMemory.h"
typedef enum {
NRF_SLEEP_WAKEUP_UNDEFINED,
NRF_SLEEP_WAKEUP_GPIO,
NRF_SLEEP_WAKEUP_TIMER,
NRF_SLEEP_WAKEUP_TOUCHPAD,
NRF_SLEEP_WAKEUP_VBUS,
NRF_SLEEP_WAKEUP_RESETPIN,
NRF_SLEEP_WAKEUP_ZZZ
} nrf_sleep_source_t;
extern const alarm_sleep_memory_obj_t alarm_sleep_memory_obj;
enum {
SLEEPMEM_WAKEUP_BY_NONE = 0,
SLEEPMEM_WAKEUP_BY_PIN = 1,
SLEEPMEM_WAKEUP_BY_TIMER = 2,
};
#define WAKEUP_PIN_UNDEF 0xFF
extern uint8_t sleepmem_wakeup_event;
extern uint8_t sleepmem_wakeup_pin;
extern void alarm_reset(void);
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_ALARM__INIT__H

View File

@ -0,0 +1,242 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Dan Halbert for Adafruit Industries
* Copyright (c) 2020 Scott Shawcroft for Adafruit Industries
* Copyright (c) 2021 Junji Sakai
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include <stdio.h>
#include <assert.h>
#include "shared-bindings/alarm/pin/PinAlarm.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "common-hal/alarm/__init__.h"
#include "nrfx.h"
#include "nrf_gpio.h"
#include "nrfx_gpiote.h"
#include "nrf_soc.h"
#include <string.h>
#include "supervisor/serial.h" // dbg_print
#define WPIN_UNUSED 0xFF
volatile char _pinhandler_gpiote_count;
static bool pins_configured = false;
extern uint32_t reset_reason_saved;
extern void dbg_dump_GPIOregs(void);
void common_hal_alarm_pin_pinalarm_construct(alarm_pin_pinalarm_obj_t *self, mcu_pin_obj_t *pin, bool value, bool edge, bool pull) {
if (edge) {
mp_raise_ValueError(translate("Cannot wake on pin edge. Only level."));
}
if (pin->number >= NUMBER_OF_PINS) {
mp_raise_ValueError(translate("Invalid pin"));
}
self->pin = pin;
self->value = value;
self->pull = pull;
}
mcu_pin_obj_t *common_hal_alarm_pin_pinalarm_get_pin(alarm_pin_pinalarm_obj_t *self) {
return self->pin;
}
bool common_hal_alarm_pin_pinalarm_get_value(alarm_pin_pinalarm_obj_t *self) {
return self->value;
}
bool common_hal_alarm_pin_pinalarm_get_edge(alarm_pin_pinalarm_obj_t *self) {
return false;
}
bool common_hal_alarm_pin_pinalarm_get_pull(alarm_pin_pinalarm_obj_t *self) {
return self->pull;
}
static void pinalarm_gpiote_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
++_pinhandler_gpiote_count;
sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_PIN;
sleepmem_wakeup_pin = pin & 0xFF;
}
bool alarm_pin_pinalarm_woke_us_up(void) {
return (sleepmem_wakeup_event == SLEEPMEM_WAKEUP_BY_PIN &&
sleepmem_wakeup_pin != WAKEUP_PIN_UNDEF);
}
mp_obj_t alarm_pin_pinalarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms) {
// First, check to see if we match any given alarms.
for (size_t i = 0; i < n_alarms; i++) {
if (!MP_OBJ_IS_TYPE(alarms[i], &alarm_pin_pinalarm_type)) {
continue;
}
alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
if (alarm->pin->number == sleepmem_wakeup_pin) {
return alarms[i];
}
}
alarm_pin_pinalarm_obj_t *alarm = m_new_obj(alarm_pin_pinalarm_obj_t);
alarm->base.type = &alarm_pin_pinalarm_type;
alarm->pin = NULL;
// Map the pin number back to a pin object.
for (size_t i = 0; i < mcu_pin_globals.map.used; i++) {
const mcu_pin_obj_t* pin_obj = MP_OBJ_TO_PTR(mcu_pin_globals.map.table[i].value);
if ((size_t) pin_obj->number == sleepmem_wakeup_pin) {
alarm->pin = mcu_pin_globals.map.table[i].value;
break;
}
}
return alarm;
}
// These must be static because we need to configure pulls later, right before
// deep sleep.
static uint64_t high_alarms = 0;
static uint64_t low_alarms = 0;
static uint64_t pull_pins = 0;
void alarm_pin_pinalarm_reset(void) {
for (size_t i = 0; i < 64; i++) {
uint64_t mask = 1ull << i;
bool high = (high_alarms & mask) != 0;
bool low = (low_alarms & mask) != 0;
if (!(high || low)) {
continue;
}
reset_pin_number(i);
nrfx_gpiote_in_event_disable((nrfx_gpiote_pin_t)i);
nrfx_gpiote_in_uninit((nrfx_gpiote_pin_t)i);
}
high_alarms = 0;
low_alarms = 0;
pull_pins = 0;
}
static void configure_pins_for_sleep(void) {
nrfx_err_t err;
if ( nrfx_gpiote_is_init() ) {
nrfx_gpiote_uninit();
}
err = nrfx_gpiote_init(NRFX_GPIOTE_CONFIG_IRQ_PRIORITY);
assert(err == NRFX_SUCCESS);
(void)err; // to suppress unused warning
_pinhandler_gpiote_count = 0;
nrfx_gpiote_in_config_t cfg = {
.sense = NRF_GPIOTE_POLARITY_TOGGLE,
.pull = NRF_GPIO_PIN_PULLUP,
.is_watcher = false,
.hi_accuracy = true,
.skip_gpio_setup = false
};
for(size_t i = 0; i < 64; ++i) {
uint64_t mask = 1ull << i;
if (((high_alarms & mask) == 0) && ((low_alarms & mask) == 0)) {
continue;
}
if (((high_alarms & mask) != 0) && ((low_alarms & mask) == 0)) {
cfg.sense = NRF_GPIOTE_POLARITY_LOTOHI;
cfg.pull = ((pull_pins & mask) != 0) ?
NRF_GPIO_PIN_PULLDOWN : NRF_GPIO_PIN_NOPULL;
}
else
if (((high_alarms & mask) == 0) && ((low_alarms & mask) != 0)) {
cfg.sense = NRF_GPIOTE_POLARITY_HITOLO;
cfg.pull = ((pull_pins & mask) != 0) ?
NRF_GPIO_PIN_PULLUP : NRF_GPIO_PIN_NOPULL;
}
else {
cfg.sense = NRF_GPIOTE_POLARITY_TOGGLE;
cfg.pull = NRF_GPIO_PIN_NOPULL;
}
err = nrfx_gpiote_in_init((nrfx_gpiote_pin_t)i, &cfg,
pinalarm_gpiote_handler);
assert(err == NRFX_SUCCESS);
nrfx_gpiote_in_event_enable((nrfx_gpiote_pin_t)i, true);
if (((high_alarms & mask) != 0) && ((low_alarms & mask) == 0)) {
nrf_gpio_cfg_sense_set((uint32_t)i, NRF_GPIO_PIN_SENSE_HIGH);
}
if (((high_alarms & mask) == 0) && ((low_alarms & mask) != 0)) {
nrf_gpio_cfg_sense_set((uint32_t)i, NRF_GPIO_PIN_SENSE_LOW);
}
}
}
void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
// Bitmask of wake up settings.
size_t high_count = 0;
size_t low_count = 0;
int pin_number = -1;
for (size_t i = 0; i < n_alarms; i++) {
if (!MP_OBJ_IS_TYPE(alarms[i], &alarm_pin_pinalarm_type)) {
continue;
}
alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
pin_number = alarm->pin->number;
//dbg_printf("alarm_pin_pinalarm_set_alarms(pin#=%d, val=%d, pull=%d)\r\n", pin_number, alarm->value, alarm->pull);
if (alarm->value) {
high_alarms |= 1ull << pin_number;
high_count++;
} else {
low_alarms |= 1ull << pin_number;
low_count++;
}
if (alarm->pull) {
pull_pins |= 1ull << pin_number;
}
}
if (pin_number != -1) {
if (!deep_sleep) {
configure_pins_for_sleep();
}
else {
// we don't setup gpio HW here but do them in
// alarm_pin_pinalarm_prepare_for_deep_sleep() below
reset_reason_saved = 0;
pins_configured = false;
}
}
else {
//dbg_printf("alarm_pin_pinalarm_set_alarms() no valid pins\r\n");
}
}
void alarm_pin_pinalarm_prepare_for_deep_sleep(void) {
if (!pins_configured) {
configure_pins_for_sleep();
pins_configured = true;
#ifdef NRF_DEBUG_PRINT
//dbg_dump_GPIOregs();
#endif
}
}

View File

@ -0,0 +1,41 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Junji Sakai
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/obj.h"
#include "py/objtuple.h"
typedef struct {
mp_obj_base_t base;
mcu_pin_obj_t *pin;
bool value;
bool pull;
} alarm_pin_pinalarm_obj_t;
void alarm_pin_pinalarm_reset(void);
void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
void alarm_pin_pinalarm_prepare_for_deep_sleep(void);
mp_obj_t alarm_pin_pinalarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms);
bool alarm_pin_pinalarm_woke_us_up(void);

View File

@ -0,0 +1,102 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Dan Halbert for Adafruit Industries
* Copyright (c) 2021 Junji Sakai
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include <stdio.h>
#include "common-hal/alarm/__init__.h"
#include "shared-bindings/alarm/time/TimeAlarm.h"
#include "shared-bindings/time/__init__.h"
void common_hal_alarm_time_timealarm_construct(alarm_time_timealarm_obj_t *self, mp_float_t monotonic_time) {
self->monotonic_time = monotonic_time;
}
mp_float_t common_hal_alarm_time_timealarm_get_monotonic_time(alarm_time_timealarm_obj_t *self) {
return self->monotonic_time;
}
mp_obj_t alarm_time_timealarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms) {
// First, check to see if we match
for (size_t i = 0; i < n_alarms; i++) {
if (MP_OBJ_IS_TYPE(alarms[i], &alarm_time_timealarm_type)) {
return alarms[i];
}
}
alarm_time_timealarm_obj_t *timer = m_new_obj(alarm_time_timealarm_obj_t);
timer->base.type = &alarm_time_timealarm_type;
// TODO: Set monotonic_time based on the RTC state.
timer->monotonic_time = 0.0f;
return timer;
}
bool alarm_time_timealarm_woke_us_up(void) {
return sleepmem_wakeup_event == SLEEPMEM_WAKEUP_BY_TIMER;
}
int64_t wakeup_time_saved =0;
int64_t alarm_time_timealarm_get_wakeup_timediff_ms(void) {
if (wakeup_time_saved == 0) {
return -1;
}
return wakeup_time_saved - common_hal_time_monotonic_ms();
}
void alarm_time_timealarm_clear_wakeup_time(void) {
wakeup_time_saved = 0;
}
void alarm_time_timealarm_reset(void) {
port_disable_interrupt_after_ticks_ch(1);
wakeup_time_saved = 0;
}
void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
bool timealarm_set = false;
alarm_time_timealarm_obj_t *timealarm = MP_OBJ_NULL;
wakeup_time_saved = 0;
for (size_t i = 0; i < n_alarms; i++) {
if (!MP_OBJ_IS_TYPE(alarms[i], &alarm_time_timealarm_type)) {
continue;
}
if (timealarm_set) {
mp_raise_ValueError(translate("Only one alarm.time alarm can be set."));
}
timealarm = MP_OBJ_TO_PTR(alarms[i]);
timealarm_set = true;
}
if (!timealarm_set) {
return;
}
wakeup_time_saved = (int64_t)(timealarm->monotonic_time * 1000.0f);
}
void alarm_time_timealarm_prepare_for_deep_sleep(void) {
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Junji Sakai
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
mp_float_t monotonic_time; // values compatible with time.monotonic_time()
} alarm_time_timealarm_obj_t;
extern volatile int rtc_woke_up_counter;
extern void port_disable_interrupt_after_ticks_ch(uint32_t channel);
extern void port_interrupt_after_ticks_ch(uint32_t channel, uint32_t ticks);
// Find the alarm object that caused us to wake up or create an equivalent one.
mp_obj_t alarm_time_timealarm_get_wakeup_alarm(size_t n_alarms, const mp_obj_t *alarms);
// Check for the wake up alarm from pretend deep sleep.
bool alarm_time_timealarm_woke_us_up(void);
void alarm_time_timealarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
void alarm_time_timealarm_reset(void);
extern void alarm_time_timealarm_prepare_for_deep_sleep(void);
extern int64_t alarm_time_timealarm_get_wakeup_timediff_ms(void);
extern void alarm_time_timealarm_clear_wakeup_time(void);
extern void dbg_dump_RTCreg(void);
extern void tick_set_prescaler(uint32_t prescaler_val);

View File

@ -0,0 +1,53 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 microDev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/runtime.h"
#include "shared-bindings/alarm/touch/TouchAlarm.h"
#include "shared-bindings/microcontroller/__init__.h"
//static volatile bool woke_up = false;
void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) {
mp_raise_NotImplementedError(NULL);
(void)pin;
}
mp_obj_t alarm_touch_touchalarm_get_wakeup_alarm(const size_t n_alarms, const mp_obj_t *alarms) {
return mp_const_none;
}
void alarm_touch_touchalarm_set_alarm(const bool deep_sleep, const size_t n_alarms, const mp_obj_t *alarms) {
}
void alarm_touch_touchalarm_prepare_for_deep_sleep(void) {
}
bool alarm_touch_touchalarm_woke_us_up(void) {
return false;
}
void alarm_touch_touchalarm_reset(void) {
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 microDev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H
#define MICROPY_INCLUDED_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H
#include "py/obj.h"
#include "common-hal/microcontroller/Pin.h"
typedef struct {
mp_obj_base_t base;
const mcu_pin_obj_t *pin;
} alarm_touch_touchalarm_obj_t;
// Find the alarm object that caused us to wake up or create an equivalent one.
mp_obj_t alarm_touch_touchalarm_get_wakeup_alarm(const size_t n_alarms, const mp_obj_t *alarms);
// Check for the wake up alarm from pretend deep sleep.
void alarm_touch_touchalarm_set_alarm(const bool deep_sleep, const size_t n_alarms, const mp_obj_t *alarms);
void alarm_touch_touchalarm_prepare_for_deep_sleep(void);
bool alarm_touch_touchalarm_woke_us_up(void);
void alarm_touch_touchalarm_reset(void);
#endif // MICROPY_INCLUDED_COMMON_HAL_ALARM_TOUCH_TOUCHALARM_H

View File

@ -127,5 +127,24 @@ void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) {
}
mcu_reset_reason_t common_hal_mcu_processor_get_reset_reason(void) {
return RESET_REASON_UNKNOWN;
mcu_reset_reason_t r = RESET_REASON_UNKNOWN;
if (reset_reason_saved == 0) {
r = RESET_REASON_POWER_ON;
}
else if (reset_reason_saved & POWER_RESETREAS_RESETPIN_Msk) {
r = RESET_REASON_RESET_PIN;
}
else if (reset_reason_saved & POWER_RESETREAS_DOG_Msk) {
r = RESET_REASON_WATCHDOG;
}
else if (reset_reason_saved & POWER_RESETREAS_SREQ_Msk) {
r = RESET_REASON_SOFTWARE;
}
else if ((reset_reason_saved & POWER_RESETREAS_OFF_Msk) ||
(reset_reason_saved & POWER_RESETREAS_LPCOMP_Msk) ||
(reset_reason_saved & POWER_RESETREAS_NFC_Msk) ||
(reset_reason_saved & POWER_RESETREAS_VBUS_Msk)) {
r = RESET_REASON_DEEP_SLEEP_ALARM;
}
return r;
}

View File

@ -36,4 +36,6 @@ typedef struct {
// Stores no state currently.
} mcu_processor_obj_t;
extern uint32_t reset_reason_saved;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_MICROCONTROLLER_PROCESSOR_H

View File

@ -46,6 +46,9 @@ CIRCUITPY_FRAMEBUFFERIO ?= 1
CIRCUITPY_COUNTIO ?= 1
CIRCUITPY_WATCHDOG ?= 1
# Sleep and Wakeup
CIRCUITPY_ALARM ?= 1
# nRF52840-specific
ifeq ($(MCU_CHIP),nrf52840)

View File

@ -116,7 +116,7 @@
// GPIO interrupt
#define NRFX_GPIOTE_ENABLED 1
#define NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 1
#define NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 2
#define NRFX_GPIOTE_CONFIG_IRQ_PRIORITY 7
// NVM controller

View File

@ -0,0 +1,234 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jun2Sak
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#ifdef NRF_DEBUG_PRINT
#define DEBUG_UART_TXPIN 26
#define DEBUG_UART_RXPIN 15
#include "nrfx.h"
#include "nrf_uart.h"
#include "nrf_gpio.h"
#include "nrf_rtc.h"
#include "nrfx_uarte.h"
#include "nrfx_rtc.h"
#include "supervisor/serial.h" // dbg_printf()
#include "shared-bindings/microcontroller/Processor.h"
#include "common-hal/alarm/__init__.h"
extern const nrfx_rtc_t rtc_instance; // port.c
extern uint32_t reset_reason_saved;
const nrfx_uarte_t _dbg_uart_inst = NRFX_UARTE_INSTANCE(1);
static int _dbg_uart_initialized = 0;
#define DBG_PBUF_LEN 80
static char _dbg_pbuf[DBG_PBUF_LEN+1];
void _debug_uart_uninit(void) {
nrf_gpio_cfg(DEBUG_UART_TXPIN,
NRF_GPIO_PIN_DIR_INPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_NOPULL,
NRF_GPIO_PIN_S0S1,
NRF_GPIO_PIN_NOSENSE);
nrfx_uarte_uninit(&_dbg_uart_inst);
}
void _debug_uart_init(void) {
//if (_dbg_uart_initialized) return;
nrfx_uarte_config_t config = {
.pseltxd = DEBUG_UART_TXPIN,
.pselrxd = DEBUG_UART_RXPIN,
.pselcts = NRF_UARTE_PSEL_DISCONNECTED,
.pselrts = NRF_UARTE_PSEL_DISCONNECTED,
.p_context = NULL,
.baudrate = NRF_UART_BAUDRATE_115200,
.interrupt_priority = 7,
.hal_cfg = {
.hwfc = NRF_UARTE_HWFC_DISABLED,
.parity = NRF_UARTE_PARITY_EXCLUDED
}
};
nrfx_uarte_init(&_dbg_uart_inst, &config, NULL);
// drive config
nrf_gpio_cfg(config.pseltxd,
NRF_GPIO_PIN_DIR_OUTPUT,
NRF_GPIO_PIN_INPUT_DISCONNECT,
NRF_GPIO_PIN_PULLUP, // orig=NOPULL
NRF_GPIO_PIN_H0H1, // orig=S0S1
NRF_GPIO_PIN_NOSENSE);
_dbg_uart_initialized = 1;
return;
}
void _debug_print_substr(const char* text, uint32_t length) {
char* data = (char*)text;
int siz;
while(length != 0) {
if (length <= DBG_PBUF_LEN) {
siz = length;
}
else {
siz = DBG_PBUF_LEN;
}
memcpy(_dbg_pbuf, data, siz);
_dbg_pbuf[siz] = 0;
nrfx_uarte_tx(&_dbg_uart_inst, (uint8_t const*)_dbg_pbuf, siz);
data += siz;
length -= siz;
}
}
void _debug_uart_deinit(void) {
nrfx_uarte_uninit(&_dbg_uart_inst);
}
int dbg_printf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int ret = vprintf(fmt, ap);
va_end(ap);
return ret;
}
void dbg_dump_RTCreg(void) {
dbg_printf("\r\nRTC2\r\n");
NRF_RTC_Type *r = rtc_instance.p_reg;
dbg_printf("PRESCALER=%08X, ", (int)r->PRESCALER);
dbg_printf("COUNTER=%08X ", (int)r->COUNTER);
dbg_printf("INTENSET=%08X ", (int)r->INTENSET);
dbg_printf("EVTENSET=%08X\r\n", (int)r->EVTENSET);
dbg_printf("EVENTS_COMPARE[0..3]=%X,%X,%X,%X ", (int)r->EVENTS_COMPARE[0], (int)r->EVENTS_COMPARE[1], (int)r->EVENTS_COMPARE[2], (int)r->EVENTS_COMPARE[3]);
dbg_printf("CC[0..3]=%08X,%08X,%08X,%08X\r\n", (int)r->CC[0], (int)r->CC[1], (int)r->CC[2], (int)r->CC[3]);
}
int dbg_check_RTCprescaler(void) {
NRF_RTC_Type *r = rtc_instance.p_reg;
if ((int)r->PRESCALER == 0) {
dbg_printf("****** PRESCALER == 0\r\n");
return -1;
}
return 0;
}
void dbg_dump_RAMreg(void) {
int i;
for(i = 0; i <= 8; ++i) {
dbg_printf(" RAM%d:%08X", i, (int)(NRF_POWER->RAM[i].POWER));
if (i==4) dbg_printf("\r\n");
}
dbg_printf("\r\n");
}
void dbg_dump_GPIOregs(void) {
int i, port, col;
NRF_GPIO_Type *gpio[] = { NRF_P0, NRF_P1 };
const char cnf_pull_chr[] = "-D*U"; // pull down, pull up
const char cnf_sense_chr[] = "-?HL"; // sense high, sense low
for(port=0, col=0; port<=1; ++port) {
for(i=0; i<32; ++i) {
uint32_t cnf = gpio[port]->PIN_CNF[i];
if (cnf != 0x0002) { // changed from default value
dbg_printf("[%d_%02d]:%c%c%c%d%c ", port, i,
(cnf & 1) ? 'O' : 'I', // output, input
(cnf & 2) ? 'd' : 'c', // disconnected, connected
cnf_pull_chr[(cnf >> 2) & 3],
(int)((cnf >> 8) & 7), // drive config 0-7
cnf_sense_chr[(cnf >> 16) & 3]);
if (++col >= 6) {
dbg_printf("\r\n");
col = 0;
}
}
}
}
if (col > 0) dbg_printf("\r\n");
dbg_printf("GPIOTE\r\n");
NRF_GPIOTE_Type const *reg = NRF_GPIOTE;
const char config_mode_chr[] = "-E-T"; // event, task
const char config_pol_chr[] = "-HLT"; // low-to-Hi, hi-to-Low, Toggle
const char config_outinit_chr[] = "01"; // initial value is 0 or 1
for(i=0, col=0; i<8; ++i) {
uint32_t conf = reg->CONFIG[i];
if (conf != 0) { // changed from default value
dbg_printf("CONFIG[%d]:%d_%02d,%c%c%c ", i,
(int)((conf >> 13) & 1), (int)((conf >> 8) & 0x1F),
config_mode_chr[conf & 3],
config_pol_chr[(conf >> 16) & 3],
(conf & 3) == 3 ?
config_outinit_chr[(conf >> 20) & 1] : '-');
if (++col >= 4) {
dbg_printf("\r\n");
col = 0;
}
}
}
if (col > 0) dbg_printf("\r\n");
for(i=0; i<8; ++i) {
dbg_printf("EVENTS_IN[%d]:%X ", i, (int)(reg->EVENTS_IN[i]));
if ((i & 3) == 3) dbg_printf("\r\n");
}
dbg_printf("EVENTS_PORT:%X INTENSET:%08X\r\n",
(int)(reg->EVENTS_PORT), (int)(reg->INTENSET));
}
void dbg_dumpQSPIreg(void) {
uint32_t r;
dbg_printf("QSPI\r\n");
r = NRF_QSPI->IFCONFIG0;
dbg_printf("IFCONFIG0 READ=%ld write=%ld ADDR=%ld DPM=%ld PPSIZE=%ld\r\n",
r & 7, (r >> 3) & 7, (r >> 6) & 1, (r >> 7) & 1, (r >> 12) & 1);
r = NRF_QSPI->IFCONFIG1;
dbg_printf("IFCONFIG1 SCKDELAY=%ld SPIMODE=%ld SCKFREQ=%ld\r\n",
r & 0xFF, (r >> 25) & 1, (r >> 28) & 0xF);
r = NRF_QSPI->STATUS;
dbg_printf("STATUS DPM=%ld READY=%ld SREG=0x%02lX\r\n",
(r >> 2) & 1, (r >> 3) & 1, (r >> 24) & 0xFF);
r = NRF_QSPI->DPMDUR;
dbg_printf("DPMDUR ENTER=%ld EXIT=%ld\r\n", r & 0xFFFF, (r >> 16) & 0xFFFF);
}
void dbg_dump_reset_reason(void) {
int reset_reason = (int)common_hal_mcu_processor_get_reset_reason();
const char* rr_str[] = {
"POWER_ON", "BROWNOUT", "SOFTWARE", "DEEPSLEEPALARM",
"RESET_PIN", "WATCHDOG", "UNKNOWN"
};
dbg_printf("reset_reason=%s\r\n", rr_str[reset_reason]);
}
#else /*!NRF_DEBUG_PRINT*/
int dbg_printf(const char *fmt, ...) {
return 0;
}
#endif /*!NRF_DEBUG_PRINT*/

View File

@ -4,6 +4,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
* Copyright (c) 2021 Junji Sakai
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -51,6 +52,7 @@
#include "common-hal/rtc/RTC.h"
#include "common-hal/neopixel_write/__init__.h"
#include "common-hal/watchdog/WatchDogTimer.h"
#include "common-hal/alarm/__init__.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/rtc/__init__.h"
@ -73,9 +75,14 @@ static void power_warning_handler(void) {
reset_into_safe_mode(BROWNOUT);
}
#ifdef NRF_DEBUG_PRINT
extern void _debug_uart_init(void);
#endif
uint32_t reset_reason_saved = 0;
const nrfx_rtc_t rtc_instance = NRFX_RTC_INSTANCE(2);
const nrfx_rtc_config_t rtc_config = {
nrfx_rtc_config_t rtc_config = {
.prescaler = RTC_FREQ_TO_PRESCALER(0x8000),
.reliable = 0,
.tick_latency = 0,
@ -100,6 +107,12 @@ void rtc_handler(nrfx_rtc_int_type_t int_type) {
supervisor_tick();
} else if (int_type == NRFX_RTC_INT_COMPARE0) {
nrfx_rtc_cc_set(&rtc_instance, 0, 0, false);
} else if (int_type == NRFX_RTC_INT_COMPARE1) {
// used in light sleep
#if CIRCUITPY_ALARM
sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_TIMER;
#endif
nrfx_rtc_cc_set(&rtc_instance, 1, 0, false);
}
}
@ -124,6 +137,22 @@ void tick_init(void) {
}
}
void tick_uninit(void) {
nrfx_rtc_counter_clear(&rtc_instance);
nrfx_rtc_disable(&rtc_instance);
nrfx_rtc_uninit(&rtc_instance);
}
void tick_set_prescaler(uint32_t prescaler_val) {
tick_uninit();
// update of prescaler value sometimes fails if we skip this delay..
NRFX_DELAY_US(1000);
uint16_t prescaler_saved = rtc_config.prescaler;
rtc_config.prescaler = prescaler_val;
tick_init();
rtc_config.prescaler = prescaler_saved;
}
safe_mode_t port_init(void) {
nrf_peripherals_clocks_init();
@ -153,11 +182,21 @@ safe_mode_t port_init(void) {
analogin_init();
#endif
reset_reason_saved = NRF_POWER->RESETREAS;
// clear all RESET reason bits
NRF_POWER->RESETREAS = reset_reason_saved;
// clear wakeup event/pin when reset by reset-pin
if (reset_reason_saved & NRF_POWER_RESETREAS_RESETPIN_MASK) {
#if CIRCUITPY_ALARM
sleepmem_wakeup_event = SLEEPMEM_WAKEUP_BY_NONE;
#endif
}
// If the board was reset by the WatchDogTimer, we may
// need to boot into safe mode. Reset the RESETREAS bit
// for the WatchDogTimer so we don't encounter this the
// next time we reboot.
if (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk) {
if (reset_reason_saved & POWER_RESETREAS_DOG_Msk) {
NRF_POWER->RESETREAS = POWER_RESETREAS_DOG_Msk;
uint32_t usb_reg = NRF_POWER->USBREGSTATUS;
@ -219,6 +258,10 @@ void reset_port(void) {
#endif
reset_all_pins();
#ifdef NRF_DEBUG_PRINT
_debug_uart_init();
#endif
}
void reset_to_bootloader(void) {
@ -295,7 +338,7 @@ void port_disable_tick(void) {
nrfx_rtc_tick_disable(&rtc_instance);
}
void port_interrupt_after_ticks(uint32_t ticks) {
void port_interrupt_after_ticks_ch(uint32_t channel, uint32_t ticks) {
uint32_t current_ticks = nrfx_rtc_counter_get(&rtc_instance);
uint32_t diff = 3;
if (ticks > diff) {
@ -304,7 +347,15 @@ void port_interrupt_after_ticks(uint32_t ticks) {
if (diff > 0xffffff) {
diff = 0xffffff;
}
nrfx_rtc_cc_set(&rtc_instance, 0, current_ticks + diff, true);
nrfx_rtc_cc_set(&rtc_instance, channel, current_ticks + diff, true);
}
void port_disable_interrupt_after_ticks_ch(uint32_t channel) {
nrfx_rtc_cc_disable(&rtc_instance, channel);
}
void port_interrupt_after_ticks(uint32_t ticks) {
port_interrupt_after_ticks_ch(0, ticks);
}
void port_idle_until_interrupt(void) {
@ -355,3 +406,9 @@ void HardFault_Handler(void) {
asm ("nop;");
}
}
#if CIRCUITPY_ALARM
// in case boards/xxx/board.c does not provide board_deinit()
MP_WEAK void board_deinit(void) {
}
#endif

View File

@ -37,6 +37,31 @@
#include "supervisor/shared/external_flash/common_commands.h"
#include "supervisor/shared/external_flash/qspi_flash.h"
#ifdef NRF_DEBUG_PRINT
#include "supervisor/serial.h" // dbg_printf()
#endif
#ifdef QSPI_FLASH_POWERDOWN
// Parameters for external QSPI Flash power-down
// for W25Q128FV,
// tDP (nCS high to Power-down mode) = 3us
// tRES (nCS high to Standby mode) = 3us
// sck_delay = max(tDP, tRES) / 62.5ns = 48 -> 50 (w/ margin)
#define DUR_DPM_ENTER 1 // tDP in (256*62.5ns) units
#define DUR_DPM_EXIT 1 // tRES in (256*62.5ns) units
#define SCK_DELAY 50 // max(tDP, tRES) in (62.5ns) units
// wait necessary just after DPM enter/exit (cut and try)
#define WAIT_AFTER_DPM_ENTER 10 // usec
#define WAIT_AFTER_DPM_EXIT 50 // usec
static int sck_delay_saved = 0;
#endif
#ifdef NRF_DEBUG_PRINT
extern void dbg_dumpQSPIreg(void);
#else
#define dbg_dumpQSPIreg(...)
#endif
// When USB is disconnected, disable QSPI in sleep mode to save energy
void qspi_disable(void) {
@ -188,7 +213,11 @@ void spi_flash_init(void) {
.readoc = NRF_QSPI_READOC_FASTREAD,
.writeoc = NRF_QSPI_WRITEOC_PP,
.addrmode = NRF_QSPI_ADDRMODE_24BIT,
#ifdef QSPI_FLASH_POWERDOWN
.dpmconfig = true
#else
.dpmconfig = false
#endif
},
.phy_if = {
.sck_freq = NRF_QSPI_FREQ_32MDIV16, // Start at a slow 2MHz and speed up once we know what we're talking to.
@ -213,6 +242,13 @@ void spi_flash_init(void) {
// No callback for blocking API
nrfx_qspi_init(&qspi_cfg, NULL, NULL);
#ifdef QSPI_FLASH_POWERDOWN
// If pin-reset while flash is in power-down mode,
// the flash cannot accept any commands. Send CMD_WAKE to release it.
spi_flash_write_command(CMD_WAKE, NULL, 0);
NRFX_DELAY_US(WAIT_AFTER_DPM_EXIT);
#endif
}
void spi_flash_init_device(const external_flash_device *device) {
@ -236,3 +272,61 @@ void spi_flash_init_device(const external_flash_device *device) {
NRF_QSPI->IFCONFIG1 &= ~QSPI_IFCONFIG1_SCKFREQ_Msk;
NRF_QSPI->IFCONFIG1 |= sckfreq << QSPI_IFCONFIG1_SCKFREQ_Pos;
}
void qspi_flash_enter_sleep(void) {
#ifdef QSPI_FLASH_POWERDOWN
uint32_t r;
NRF_QSPI->DPMDUR =
((DUR_DPM_ENTER & 0xFFFF) << 16) | (DUR_DPM_EXIT & 0xFFFF);
// set sck_delay tempolarily
r = NRF_QSPI->IFCONFIG1;
sck_delay_saved = (r & QSPI_IFCONFIG1_SCKDELAY_Msk)
>> QSPI_IFCONFIG1_SCKDELAY_Pos;
NRF_QSPI->IFCONFIG1
= (NRF_QSPI->IFCONFIG1 & ~QSPI_IFCONFIG1_SCKDELAY_Msk)
| (SCK_DELAY << QSPI_IFCONFIG1_SCKDELAY_Pos);
// enabling IFCONFIG0.DPMENABLE here won't work.
// -> do it in spi_flash_init()
//NRF_QSPI->IFCONFIG0 |= QSPI_IFCONFIG0_DPMENABLE_Msk;
//dbg_dumpQSPIreg();
// enter deep power-down mode (DPM)
NRF_QSPI->IFCONFIG1 |= QSPI_IFCONFIG1_DPMEN_Msk;
NRFX_DELAY_US(WAIT_AFTER_DPM_ENTER);
if (!(NRF_QSPI->STATUS & QSPI_STATUS_DPM_Msk)) {
#ifdef NRF_DEBUG_PRINT
dbg_printf("qspi flash: DPM failed\r\n");
#endif
}
#endif
qspi_disable();
//dbg_dumpQSPIreg();
}
void qspi_flash_exit_sleep(void) {
qspi_enable();
#ifdef QSPI_FLASH_POWERDOWN
if (NRF_QSPI->STATUS & QSPI_STATUS_DPM_Msk) {
// exit deep power-down mode
NRF_QSPI->IFCONFIG1 &= ~QSPI_IFCONFIG1_DPMEN_Msk;
NRFX_DELAY_US(WAIT_AFTER_DPM_EXIT);
if (NRF_QSPI->STATUS & QSPI_STATUS_DPM_Msk) {
#ifdef NRF_DEBUG_PRINT
dbg_printf("qspi flash: exiting DPM failed\r\n");
#endif
}
// restore sck_delay
if (sck_delay_saved == 0) {
sck_delay_saved = 10; // default
}
NRF_QSPI->IFCONFIG1
= (NRF_QSPI->IFCONFIG1 & ~QSPI_IFCONFIG1_SCKDELAY_Msk)
| (sck_delay_saved << QSPI_IFCONFIG1_SCKDELAY_Pos);
}
//dbg_dumpQSPIreg();
#endif
}

View File

@ -0,0 +1,2 @@
extern void qspi_flash_enter_sleep(void);
extern void qspi_flash_exit_sleep(void);

View File

@ -244,6 +244,7 @@ const mp_obj_module_t alarm_module = {
};
extern void port_idle_until_interrupt(void);
MP_WEAK void common_hal_alarm_pretending_deep_sleep(void) {
port_idle_until_interrupt();
}

View File

@ -29,6 +29,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "py/mpconfig.h"
@ -47,4 +48,7 @@ char serial_read(void);
bool serial_bytes_available(void);
bool serial_connected(void);
// XXX used in nrf52-sleep debug
int dbg_printf(const char *fmt, ...)__attribute__((format (printf, 1, 2)));
#endif // MICROPY_INCLUDED_SUPERVISOR_SERIAL_H

View File

@ -37,6 +37,12 @@
#include "tusb.h"
#ifdef NRF_DEBUG_PRINT
// XXX these functions are in nrf/supervisor/debug_uart.c
extern void _debug_uart_init(void);
extern void _debug_print_substr(const char *text, uint32_t length);
#endif
/*
* Note: DEBUG_UART currently only works on STM32,
* enabling on another platform will cause a crash.
@ -64,10 +70,17 @@ void serial_early_init(void) {
buf_array, true);
common_hal_busio_uart_never_reset(&debug_uart);
#endif
#ifdef NRF_DEBUG_PRINT
_debug_uart_init();
#endif
}
void serial_init(void) {
usb_init();
#ifdef NRF_DEBUG_PRINT
_debug_uart_init();
#endif
}
bool serial_connected(void) {
@ -146,8 +159,14 @@ void serial_write_substring(const char *text, uint32_t length) {
#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
int uart_errcode;
common_hal_busio_uart_write(&debug_uart, (const uint8_t *)text, length, &uart_errcode);
#endif
#ifdef NRF_DEBUG_PRINT
_debug_print_substr(text, length);
#endif
}
void serial_write(const char *text) {