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 <noreply@anthropic.com>
This commit is contained in:
Hermann Björgvin Haraldsson
2026-06-02 14:01:57 +00:00
co-authored by Claude Opus 4.7
parent 7df64eecec
commit 98a513a403
5 changed files with 120 additions and 45 deletions
+4
View File
@@ -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; }
@@ -7,10 +7,18 @@
// 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;
@@ -18,7 +26,11 @@ 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;
@@ -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;
}
@@ -4,7 +4,10 @@
#include <Wire.h>
#include <XPowersLib.h>
// 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
@@ -16,6 +19,8 @@ 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;
@@ -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;
}
@@ -20,6 +20,8 @@ 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;
@@ -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;
}
+8
View File
@@ -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);