From 98a513a403f228a4a1c246e6c936c4735309544a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hermann=20Bj=C3=B6rgvin=20Haraldsson?= Date: Sun, 24 May 2026 19:10:20 +0000 Subject: [PATCH] HAL: add PWR long-press + release edges to power_hal Adds power_hal_pwr_long_pressed() and power_hal_pwr_released() to the HAL contract so shared code can implement a hold-to-pair gesture board-agnostically. - 216: enable the AXP2101 PKEY LONG + POSITIVE IRQs; bump press-off to 8s so a ~3-6s pair hold doesn't trip the hardware shutdown. - 1.8": synthesize the same long/release edges in software from the polled XCA9554 EXIO4 level (short-press now fires on release-if-short). - template: stub the two new functions (no gesture) for new ports. Co-Authored-By: Claude Opus 4.7 --- firmware/src/boards/template/power.cpp | 4 ++ .../src/boards/waveshare_amoled_18/power.cpp | 56 ++++++++++++++----- .../src/boards/waveshare_amoled_216/power.cpp | 51 +++++++++++------ .../boards/waveshare_amoled_216_c6/power.cpp | 46 ++++++++++----- firmware/src/hal/power_hal.h | 8 +++ 5 files changed, 120 insertions(+), 45 deletions(-) diff --git a/firmware/src/boards/template/power.cpp b/firmware/src/boards/template/power.cpp index c5d36eb..362c9c5 100644 --- a/firmware/src/boards/template/power.cpp +++ b/firmware/src/boards/template/power.cpp @@ -20,3 +20,7 @@ int power_hal_battery_pct(void) { return -1; } bool power_hal_is_charging(void) { return false; } bool power_hal_is_vbus_in(void) { return false; } bool power_hal_pwr_pressed(void) { return false; } +// Hold-to-pair gesture signals. Mirror the 216 (PMU PKEY long/positive IRQs) +// or the 1.8" (software hold-timing off a polled GPIO) port. Stub = no gesture. +bool power_hal_pwr_long_pressed(void) { return false; } +bool power_hal_pwr_released(void) { return false; } diff --git a/firmware/src/boards/waveshare_amoled_18/power.cpp b/firmware/src/boards/waveshare_amoled_18/power.cpp index 5e6a9ed..727d538 100644 --- a/firmware/src/boards/waveshare_amoled_18/power.cpp +++ b/firmware/src/boards/waveshare_amoled_18/power.cpp @@ -7,21 +7,33 @@ // PWR button comes from XCA9554 EXIO4 (active HIGH). The PMU still // provides battery monitoring; we just don't subscribe to its PKEY IRQ. +// +// The AXP2101 PKEY IRQs (short/long/release) aren't available here, so we +// synthesize the same three edges in software from the polled EXIO4 level: +// short — fired on release if the hold was shorter than PWR_LONG_MS +// long — fired once when a hold crosses PWR_LONG_MS +// release — fired on every falling edge +// This keeps the hold-to-pair gesture logic in main.cpp board-agnostic. #define BATTERY_POLL_MS 2000 #define CHARGING_POLL_MS 500 #define PWR_POLL_MS 50 +#define PWR_LONG_MS 1500 // hold threshold, mirrors the AXP LONG IRQ static XPowersPMU pmu; -static int cached_pct = -1; -static bool cached_charging = false; -static bool cached_vbus = false; -static bool pwr_pressed_flag = false; -static bool last_pwr_state = false; // edge detector for EXIO4 -static uint32_t last_battery_ms = 0; -static uint32_t last_charging_ms = 0; -static uint32_t last_pwr_ms = 0; +static int cached_pct = -1; +static bool cached_charging = false; +static bool cached_vbus = false; +static bool pwr_pressed_flag = false; +static bool pwr_long_flag = false; +static bool pwr_released_flag = false; +static bool last_pwr_state = false; // edge detector for EXIO4 +static uint32_t pwr_press_started_ms = 0; +static bool pwr_long_fired = false; // long already fired for this hold +static uint32_t last_battery_ms = 0; +static uint32_t last_charging_ms = 0; +static uint32_t last_pwr_ms = 0; void power_hal_init(void) { if (!pmu.begin(Wire, AXP2101_ADDR, IIC_SDA, IIC_SCL)) { @@ -54,8 +66,17 @@ void power_hal_tick(void) { if (now - last_pwr_ms >= PWR_POLL_MS) { last_pwr_ms = now; bool pwr_now = io_expander_get(IOX_PIN_PWR_BTN); - if (pwr_now && !last_pwr_state) { - pwr_pressed_flag = true; + if (pwr_now && !last_pwr_state) { // rising edge — hold begins + pwr_press_started_ms = now; + pwr_long_fired = false; + } else if (pwr_now && last_pwr_state) { // held + if (!pwr_long_fired && (now - pwr_press_started_ms >= PWR_LONG_MS)) { + pwr_long_flag = true; + pwr_long_fired = true; + } + } else if (!pwr_now && last_pwr_state) { // falling edge — release + pwr_released_flag = true; + if (!pwr_long_fired) pwr_pressed_flag = true; // short press } last_pwr_state = pwr_now; } @@ -66,9 +87,16 @@ bool power_hal_is_charging(void) { return cached_charging; } bool power_hal_is_vbus_in(void) { return cached_vbus; } bool power_hal_pwr_pressed(void) { - if (pwr_pressed_flag) { - pwr_pressed_flag = false; - return true; - } + if (pwr_pressed_flag) { pwr_pressed_flag = false; return true; } + return false; +} + +bool power_hal_pwr_long_pressed(void) { + if (pwr_long_flag) { pwr_long_flag = false; return true; } + return false; +} + +bool power_hal_pwr_released(void) { + if (pwr_released_flag) { pwr_released_flag = false; return true; } return false; } diff --git a/firmware/src/boards/waveshare_amoled_216/power.cpp b/firmware/src/boards/waveshare_amoled_216/power.cpp index 5533263..1c04fb6 100644 --- a/firmware/src/boards/waveshare_amoled_216/power.cpp +++ b/firmware/src/boards/waveshare_amoled_216/power.cpp @@ -4,7 +4,10 @@ #include #include -// PWR button comes from AXP2101 PKEY short-press IRQ. +// PWR button comes from AXP2101 PKEY IRQs: +// SHORT — quick tap (cycle splash animations) +// LONG — ~1.5s mark, starts the hold-to-pair countdown +// POSITIVE — release edge, completes/cancels the gesture #define BATTERY_POLL_MS 2000 #define CHARGING_POLL_MS 500 @@ -12,13 +15,15 @@ static XPowersPMU pmu; -static int cached_pct = -1; -static bool cached_charging = false; -static bool cached_vbus = false; -static bool pwr_pressed_flag = false; -static uint32_t last_battery_ms = 0; -static uint32_t last_charging_ms = 0; -static uint32_t last_pwr_ms = 0; +static int cached_pct = -1; +static bool cached_charging = false; +static bool cached_vbus = false; +static bool pwr_pressed_flag = false; +static bool pwr_long_flag = false; +static bool pwr_released_flag = false; +static uint32_t last_battery_ms = 0; +static uint32_t last_charging_ms = 0; +static uint32_t last_pwr_ms = 0; void power_hal_init(void) { if (!pmu.begin(Wire, AXP2101_ADDR, IIC_SDA, IIC_SCL)) { @@ -32,7 +37,14 @@ void power_hal_init(void) { pmu.disableIRQ(XPOWERS_AXP2101_ALL_IRQ); pmu.clearIrqStatus(); - pmu.enableIRQ(XPOWERS_AXP2101_PKEY_SHORT_IRQ); + pmu.enableIRQ(XPOWERS_AXP2101_PKEY_SHORT_IRQ + | XPOWERS_AXP2101_PKEY_LONG_IRQ + | XPOWERS_AXP2101_PKEY_POSITIVE_IRQ); + + // Default press-off (force-shutdown) is 6s, only 2s after the pair gesture + // arms at ~3s and disarms at ~6s. Bump to 8s so a slightly-too-long hold + // doesn't shut the device down mid-gesture. + pmu.setPowerKeyPressOffTime(XPOWERS_POWEROFF_8S); cached_charging = pmu.isCharging(); cached_vbus = pmu.isVbusIn(); @@ -54,9 +66,9 @@ void power_hal_tick(void) { if (now - last_pwr_ms >= PWR_POLL_MS) { last_pwr_ms = now; pmu.getIrqStatus(); - if (pmu.isPekeyShortPressIrq()) { - pwr_pressed_flag = true; - } + if (pmu.isPekeyShortPressIrq()) pwr_pressed_flag = true; + if (pmu.isPekeyLongPressIrq()) pwr_long_flag = true; + if (pmu.isPekeyPositiveIrq()) pwr_released_flag = true; pmu.clearIrqStatus(); } } @@ -66,9 +78,16 @@ bool power_hal_is_charging(void) { return cached_charging; } bool power_hal_is_vbus_in(void) { return cached_vbus; } bool power_hal_pwr_pressed(void) { - if (pwr_pressed_flag) { - pwr_pressed_flag = false; - return true; - } + if (pwr_pressed_flag) { pwr_pressed_flag = false; return true; } + return false; +} + +bool power_hal_pwr_long_pressed(void) { + if (pwr_long_flag) { pwr_long_flag = false; return true; } + return false; +} + +bool power_hal_pwr_released(void) { + if (pwr_released_flag) { pwr_released_flag = false; return true; } return false; } diff --git a/firmware/src/boards/waveshare_amoled_216_c6/power.cpp b/firmware/src/boards/waveshare_amoled_216_c6/power.cpp index 395ef05..659162c 100644 --- a/firmware/src/boards/waveshare_amoled_216_c6/power.cpp +++ b/firmware/src/boards/waveshare_amoled_216_c6/power.cpp @@ -16,13 +16,15 @@ extern XPowersPMU board_pmu; #define pmu board_pmu -static int cached_pct = -1; -static bool cached_charging = false; -static bool cached_vbus = false; -static bool pwr_pressed_flag = false; -static uint32_t last_battery_ms = 0; -static uint32_t last_charging_ms = 0; -static uint32_t last_pwr_ms = 0; +static int cached_pct = -1; +static bool cached_charging = false; +static bool cached_vbus = false; +static bool pwr_pressed_flag = false; +static bool pwr_long_flag = false; +static bool pwr_released_flag = false; +static uint32_t last_battery_ms = 0; +static uint32_t last_charging_ms = 0; +static uint32_t last_pwr_ms = 0; void power_hal_init(void) { // pmu.begin() already ran in board_init(); just configure battery + @@ -36,7 +38,14 @@ void power_hal_init(void) { pmu.disableIRQ(XPOWERS_AXP2101_ALL_IRQ); pmu.clearIrqStatus(); - pmu.enableIRQ(XPOWERS_AXP2101_PKEY_SHORT_IRQ); + pmu.enableIRQ(XPOWERS_AXP2101_PKEY_SHORT_IRQ + | XPOWERS_AXP2101_PKEY_LONG_IRQ + | XPOWERS_AXP2101_PKEY_POSITIVE_IRQ); + + // Default press-off is 6s, only 2s after the pair gesture arms at ~3s and + // disarms at ~6s. Bump to 8s so a slightly-too-long hold doesn't shut the + // device down mid-gesture. + pmu.setPowerKeyPressOffTime(XPOWERS_POWEROFF_8S); cached_charging = pmu.isCharging(); cached_vbus = pmu.isVbusIn(); @@ -58,9 +67,9 @@ void power_hal_tick(void) { if (now - last_pwr_ms >= PWR_POLL_MS) { last_pwr_ms = now; pmu.getIrqStatus(); - if (pmu.isPekeyShortPressIrq()) { - pwr_pressed_flag = true; - } + if (pmu.isPekeyShortPressIrq()) pwr_pressed_flag = true; + if (pmu.isPekeyLongPressIrq()) pwr_long_flag = true; + if (pmu.isPekeyPositiveIrq()) pwr_released_flag = true; pmu.clearIrqStatus(); } } @@ -70,9 +79,16 @@ bool power_hal_is_charging(void) { return cached_charging; } bool power_hal_is_vbus_in(void) { return cached_vbus; } bool power_hal_pwr_pressed(void) { - if (pwr_pressed_flag) { - pwr_pressed_flag = false; - return true; - } + if (pwr_pressed_flag) { pwr_pressed_flag = false; return true; } + return false; +} + +bool power_hal_pwr_long_pressed(void) { + if (pwr_long_flag) { pwr_long_flag = false; return true; } + return false; +} + +bool power_hal_pwr_released(void) { + if (pwr_released_flag) { pwr_released_flag = false; return true; } return false; } diff --git a/firmware/src/hal/power_hal.h b/firmware/src/hal/power_hal.h index 05b08d8..08bbd10 100644 --- a/firmware/src/hal/power_hal.h +++ b/firmware/src/hal/power_hal.h @@ -17,3 +17,11 @@ bool power_hal_is_vbus_in(void); // USB cable present (true even without a bat // Edge-triggered: returns true once per PWR short-press, then clears. bool power_hal_pwr_pressed(void); + +// Edge-triggered: true once when a PWR hold crosses the long-press threshold +// (~1.5s), then clears. Starts the hold-to-pair gesture. +bool power_hal_pwr_long_pressed(void); + +// Edge-triggered: true once on the PWR release edge, then clears. Completes +// or cancels the hold-to-pair gesture. +bool power_hal_pwr_released(void);