mirror of
https://github.com/meshtastic/firmware.git
synced 2026-11-07 23:04:49 -05:00
Hold the deep sleep wake pad at its idle level
Before: a board whose variant does not define BUTTON_NEED_PULLUP enters deep
sleep with the wake pad set to plain INPUT and no pull on it, while ext1 is
armed to wake on a low level on that same pin. Nothing holds the line. On the
~40 ESP32 and ESP32-S3 variants whose BUTTON_PIN is GPIO0 that pin is also the
download-mode boot strap, and the reset that ends a deep sleep makes the ROM
read it again: a floating GPIO0 can bring the chip up in ROM download mode
("boot:0x0 (DOWNLOAD(USB/UART0))", "waiting for download") instead of booting
the firmware, which clears only when power is physically removed. A pad with no
pull is also a wake source that can fire on its own, so the board need not stay
asleep at all.
After: the pin ext1 wakes on keeps an internal pull at its idle level for the
whole sleep - a pull-up for a low-level wake, a pull-down where the variant
arms ESP_EXT1_WAKEUP_ANY_HIGH. A BUTTON_PIN that is not the wake pin keeps the
old pull-less INPUT, since gpio_hold_en() latches it.
The pull is applied in both register sets on purpose. The digital pull covers
the window before the sleep; the RTC pull is the one that still holds once the
RTC domain owns the pad, which is the state the ROM's strap read sees.
How: doDeepSleep() picks INPUT_PULLUP or INPUT_PULLDOWN for the wake pad rather
than INPUT, and cpuDeepSleep() asserts the matching pull unconditionally on the
pin it is about to arm. Two existing defects go with it: that pull-up was
previously gated on BUTTON_NEED_PULLUP and applied to BUTTON_PIN even when
config.device.button_gpio or DEEP_SLEEP_WAKE_PIN had moved the wake pin
elsewhere, and gpio_hold_en() skipped BUTTON_PIN on a DEEP_SLEEP_WAKE_PIN board
although it is not the pin being woken on. The wake-pin choice moves to the top
of cpuDeepSleep(), out of the SOC_RTCIO_HOLD_SUPPORTED block that the ext1 mask
below it was already reading it from.
An internal pull is a few hundred nA, less than the leakage the pull-less INPUT
was there to avoid.
Co-Authored-By: Jonathan Bennett <jbennett@incomsystems.biz>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTiifXrVc4YdonmLRCeWhw
This commit is contained in:
1 parent
e75d85f8be
commit
5e2acd13a6
2 files changed
+67
-18
No files matched your search
@@ -359,6 +359,26 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
Note: we don't isolate pins that are used for the LORA, LED, i2c, or ST7735 Display for the Chatter2, spi or the wake
|
||||
button(s), maybe we should not include any other GPIOs...
|
||||
*/
|
||||
// The pad deep sleep wakes on. A variant whose button is not an RTC IO can nominate a different one
|
||||
// with DEEP_SLEEP_WAKE_PIN; that replaces the button rather than joining it, because
|
||||
// esp_sleep_enable_ext1_wakeup() validates the whole mask and arms nothing if any pin in it lacks
|
||||
// RTC function, so one bad pin would veto the good one.
|
||||
#if defined(DEEP_SLEEP_WAKE_PIN)
|
||||
const int wakeButton = DEEP_SLEEP_WAKE_PIN;
|
||||
#elif defined(BUTTON_PIN)
|
||||
const int wakeButton = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN;
|
||||
#else
|
||||
const int wakeButton = -1;
|
||||
#endif
|
||||
#if defined(BUTTON_PIN) || defined(DEEP_SLEEP_WAKE_PIN)
|
||||
// Which level ext1 is armed for below, so the pull we hold the pad at is the idle one.
|
||||
#if defined(ESP32S3_WAKE_TYPE)
|
||||
const bool wakeOnHigh = (ESP32S3_WAKE_TYPE == ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
#else
|
||||
const bool wakeOnHigh = false;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if SOC_RTCIO_HOLD_SUPPORTED
|
||||
static const uint8_t rtcGpios[] = {
|
||||
#ifndef HELTEC_VISION_MASTER_E213
|
||||
@@ -371,16 +391,6 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
#endif
|
||||
34, 35, 37};
|
||||
|
||||
// A variant whose button is not an RTC IO can nominate a different pad here. This replaces the
|
||||
// button rather than joining it: esp_sleep_enable_ext1_wakeup() validates the whole mask and
|
||||
// arms nothing if any pin in it lacks RTC function, so one bad pin would veto the good one.
|
||||
#if defined(DEEP_SLEEP_WAKE_PIN)
|
||||
const int wakeButton = DEEP_SLEEP_WAKE_PIN;
|
||||
#elif defined(BUTTON_PIN)
|
||||
const int wakeButton = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN;
|
||||
#else
|
||||
const int wakeButton = -1;
|
||||
#endif
|
||||
// Isolating a pad holds it, and ext1 cannot re-arm a held pad - skip the pin we wake on.
|
||||
for (int i = 0; i < sizeof(rtcGpios); i++) {
|
||||
if (rtcGpios[i] != wakeButton)
|
||||
@@ -404,13 +414,37 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
#ifdef ALT_BUTTON_WAKE
|
||||
gpioMask |= (1ULL << BUTTON_PIN_ALT);
|
||||
#endif
|
||||
#ifdef BUTTON_NEED_PULLUP
|
||||
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
// Hold the wake pad at its idle level for the whole sleep. Two reasons, and the second is the one
|
||||
// that bites: ext1 samples the line, and on a strapping pin the ROM reads it again at the reset that
|
||||
// ends the sleep - GPIO0 is the ESP32 download-mode strap, so a pad left floating can bring the chip
|
||||
// up in ROM download mode instead of booting the firmware, which needs power physically removed to
|
||||
// clear. ~40 variants use GPIO0 as BUTTON_PIN. This was previously gated on BUTTON_NEED_PULLUP and
|
||||
// applied to BUTTON_PIN rather than to the pin actually armed; an internal pull is a few hundred nA,
|
||||
// less than the leakage the gate was there to avoid.
|
||||
//
|
||||
// Both register sets are written on purpose: the digital pull covers the window before the sleep,
|
||||
// the RTC pull is the one that holds once the RTC domain owns the pad.
|
||||
if (wakeButton >= 0) {
|
||||
const gpio_num_t wakePad = (gpio_num_t)wakeButton;
|
||||
if (wakeOnHigh) {
|
||||
gpio_pullup_dis(wakePad);
|
||||
gpio_pulldown_en(wakePad);
|
||||
} else {
|
||||
gpio_pulldown_dis(wakePad);
|
||||
gpio_pullup_en(wakePad);
|
||||
}
|
||||
#if SOC_RTCIO_HOLD_SUPPORTED
|
||||
if (rtc_gpio_is_valid_gpio(wakePad)) {
|
||||
if (wakeOnHigh) {
|
||||
rtc_gpio_pullup_dis(wakePad);
|
||||
rtc_gpio_pulldown_en(wakePad);
|
||||
} else {
|
||||
rtc_gpio_pulldown_dis(wakePad);
|
||||
rtc_gpio_pullup_en(wakePad);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Not needed because both of the current boards have external pullups
|
||||
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead
|
||||
// of just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
}
|
||||
|
||||
#if SOC_PM_SUPPORT_EXT1_WAKEUP
|
||||
gpioMask = filterExt1WakeMask(gpioMask);
|
||||
|
||||
+17
-2
@@ -311,13 +311,28 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN
|
||||
#ifdef BUTTON_PIN
|
||||
// Avoid leakage through button pin
|
||||
if (GPIO_IS_VALID_OUTPUT_GPIO(BUTTON_PIN)) {
|
||||
#if defined(DEEP_SLEEP_WAKE_PIN)
|
||||
const int deepSleepWakePin = DEEP_SLEEP_WAKE_PIN;
|
||||
#else
|
||||
const int deepSleepWakePin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN;
|
||||
#endif
|
||||
#ifdef BUTTON_NEED_PULLUP
|
||||
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
||||
#else
|
||||
pinMode(BUTTON_PIN, INPUT);
|
||||
#if defined(ESP32S3_WAKE_TYPE)
|
||||
const bool wakeOnHigh = (ESP32S3_WAKE_TYPE == ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
#else
|
||||
const bool wakeOnHigh = false;
|
||||
#endif
|
||||
// Leaving the pad with no pull is only safe for a pin we are not waking on, which gpio_hold_en()
|
||||
// latches below. The pin ext1 does wake on has to hold its idle level instead: the ROM reads a
|
||||
// strapping pin again at the reset that ends the sleep, and GPIO0 - BUTTON_PIN on ~40 variants -
|
||||
// is the download-mode strap, so a floating pad can come back up in ROM download mode.
|
||||
// cpuDeepSleep() asserts the same pull in the RTC registers, which are what hold through the sleep.
|
||||
pinMode(BUTTON_PIN, BUTTON_PIN != deepSleepWakePin ? INPUT : (wakeOnHigh ? INPUT_PULLDOWN : INPUT_PULLUP));
|
||||
#endif
|
||||
// A held pad ignores ext1_wakeup_prepare()'s re-route to RTC, so never hold the pin we wake on.
|
||||
if (config.device.button_gpio && config.device.button_gpio != BUTTON_PIN)
|
||||
if (BUTTON_PIN != deepSleepWakePin)
|
||||
gpio_hold_en((gpio_num_t)BUTTON_PIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user