Power off drops the peripheral rail (PWR_ON low), sleeps the panel and puts the chip into deep sleep with ext1 wake armed on the encoder pins, choosing any-low or any-high from the knob's resting state so a single detent wakes it. The button (GPIO0) is not a wake source on purpose.
61 lines
2.1 KiB
C
61 lines
2.1 KiB
C
#include "power.h"
|
|
#include "board.h"
|
|
#include "display.h"
|
|
#include "leds.h"
|
|
#include "ui.h"
|
|
#include "driver/gpio.h"
|
|
#include "driver/rtc_io.h"
|
|
#include "esp_log.h"
|
|
#include "esp_sleep.h"
|
|
#include "esp_wifi.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
|
|
static const char *TAG = "power";
|
|
|
|
// Arm ext1 wake on the encoder pins. The S3 has one level mode for all selected
|
|
// pins, so pick it from the knob's resting state: pins that are high wake on
|
|
// low; if both rest low, wake on high. A single detent toggles both pins.
|
|
static void arm_knob_wakeup(void)
|
|
{
|
|
int pins[2] = {board->enc_a, board->enc_b};
|
|
uint64_t high = 0, low = 0;
|
|
for (int i = 0; i < 2; i++) {
|
|
rtc_gpio_init(pins[i]);
|
|
rtc_gpio_set_direction(pins[i], RTC_GPIO_MODE_INPUT_ONLY);
|
|
rtc_gpio_pulldown_dis(pins[i]);
|
|
rtc_gpio_pullup_en(pins[i]);
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(5));
|
|
for (int i = 0; i < 2; i++) {
|
|
if (rtc_gpio_get_level(pins[i])) high |= 1ULL << pins[i];
|
|
else low |= 1ULL << pins[i];
|
|
}
|
|
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // keeps the RTC pull-ups alive
|
|
if (high) esp_sleep_enable_ext1_wakeup_io(high, ESP_EXT1_WAKEUP_ANY_LOW);
|
|
else esp_sleep_enable_ext1_wakeup_io(low, ESP_EXT1_WAKEUP_ANY_HIGH);
|
|
ESP_LOGI(TAG, "wake on knob: A%d=%d B%d=%d", board->enc_a, !!(high & (1ULL << board->enc_a)),
|
|
board->enc_b, !!(high & (1ULL << board->enc_b)));
|
|
}
|
|
|
|
void power_off(void)
|
|
{
|
|
ESP_LOGI(TAG, "powering off");
|
|
ui_status("Off", "turn the knob to wake");
|
|
vTaskDelay(pdMS_TO_TICKS(700));
|
|
leds_set_mode(LED_OFF, 0);
|
|
vTaskDelay(pdMS_TO_TICKS(80)); // let the ring task push the dark frame
|
|
display_off();
|
|
esp_wifi_stop();
|
|
gpio_set_level(board->pwr_en, 0); // peripheral rail off (screen, ring, codec)
|
|
arm_knob_wakeup();
|
|
esp_deep_sleep_start();
|
|
}
|
|
|
|
void power_report_wakeup(void)
|
|
{
|
|
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
|
if (cause == ESP_SLEEP_WAKEUP_EXT1) ESP_LOGI(TAG, "woken by the knob");
|
|
else if (cause != ESP_SLEEP_WAKEUP_UNDEFINED) ESP_LOGI(TAG, "wakeup cause %d", cause);
|
|
}
|