From 0624b6fb2c32d4c02b723dd60c71aa1eb50ea76f Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Tue, 18 Aug 2026 05:53:43 +0000 Subject: [PATCH 1/2] Fix PM1 button and wake IRQ handling and correct the power API docs --- src/utility/Power_Class.hpp | 3 ++- src/utility/power/M5PM1_Class.cpp | 45 +++++++++++++------------------ src/utility/power/M5PM1_Class.hpp | 28 +++++++++++++++---- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/utility/Power_Class.hpp b/src/utility/Power_Class.hpp index 5cde802..294294c 100644 --- a/src/utility/Power_Class.hpp +++ b/src/utility/Power_Class.hpp @@ -203,7 +203,8 @@ namespace m5 /// Get Power Key Press condition. /// @return 0=none / 1=long pressed / 2=short clicked / 3=both - /// @attention Only for models with AXP192 or AXP2101 + /// @attention Only for models with AXP192, AXP2101, or M5PM1. + /// @attention M5PM1 reports only 0 or 2. /// @attention Once this function is called, the value is reset to 0, and the next time it is pressed on, the value changes. uint8_t getKeyState(void); diff --git a/src/utility/power/M5PM1_Class.cpp b/src/utility/power/M5PM1_Class.cpp index 23c3bbd..529aa8b 100644 --- a/src/utility/power/M5PM1_Class.cpp +++ b/src/utility/power/M5PM1_Class.cpp @@ -121,7 +121,8 @@ namespace m5 bool M5PM1_Class::setGPIOFunction(gpio_t pin, gpio_function_t function) { - if (!is_valid_gpio(pin)) { return false; } + if (!is_valid_gpio(pin) + || (function != gpio && function != irq && function != special)) { return false; } auto num = gpio_num(pin); auto reg = num < 4 ? M5PM1_REG_GPIO_FUNC0 : M5PM1_REG_GPIO_FUNC1; auto shift = static_cast((num < 4 ? num : num - 4) * 2); @@ -215,8 +216,7 @@ namespace m5 bool M5PM1_Class::clearWakeSource(std::uint8_t mask) { - auto src = readRegister8(M5PM1_REG_WAKE_SRC); - return writeRegister8(M5PM1_REG_WAKE_SRC, src & ~mask); + return writeRegister8(M5PM1_REG_WAKE_SRC, static_cast(~mask & 0x7F)); } bool M5PM1_Class::clearGPIOIRQStatus(void) @@ -264,7 +264,7 @@ namespace m5 bool M5PM1_Class::getBatteryCharge(bool* enabled) { - if (!_init) { return false; } + if (!_init || enabled == nullptr) { return false; } std::uint8_t cfg = 0; if (!readRegister(M5PM1_REG_PWR_CFG, &cfg, 1)) { return false; } *enabled = cfg & M5PM1_PWR_CFG_CHG_EN; @@ -274,25 +274,11 @@ namespace m5 bool M5PM1_Class::setChargeCurrent(std::uint16_t max_mA) { return false; - // if (!_init) return false; - // int value = max_mA / 8; // Convert mA to register value (8mA per step) - // if (value > 0) { value -= 1; // 0 = 8mA, 63 = 512mA - // if (value >= 64) value = 63; // max value is 512mA (8 + 63*8) - // } - // return writeRegister8(M5PM1_REG_CHR_CUR, value); } bool M5PM1_Class::setChargeVoltage(std::uint16_t max_mV) { return false; - // if (!_init) return false; - // int value = (max_mV - 3600) / 15; // Convert mV to register value (15mV per step) - // if (value > 0) { value -= 1; // 0 = 3600mV, 63 = 4545mV - // if (value >= 64) value = 63; // max value is 4545mV (3600 + 63*15) - // } - // uint8_t reg_value = readRegister8(M5PM1_REG_CHR_VOL); - // reg_value &= 0xC0; - // return writeRegister8(M5PM1_REG_CHR_VOL, reg_value | value); } std::uint16_t M5PM1_Class::getChargeCurrent(void) @@ -314,14 +300,19 @@ namespace m5 { if (!_init) return 0; uint8_t irq3 = 0; - if (readRegister(M5PM1_REG_IRQ_STATUS3, &irq3, 1)) - { - if (irq3 & ((1 << 0) | (1 << 2))) { - writeRegister8(M5PM1_REG_IRQ_STATUS3, 0); - return 2; - } - } - return 0; + if (!readRegister(M5PM1_REG_IRQ_STATUS3, &irq3, 1)) return 0; + uint8_t pending = irq3 & ((1 << 0) | (1 << 2)); + if (!pending) return 0; + if (!writeRegister8(M5PM1_REG_IRQ_STATUS3, static_cast(~pending & 0x07))) return 0; + if (pending & (1 << 2)) { _pek_double_pending = true; } + return 2; + } + + bool M5PM1_Class::wasPekDoubleClicked(void) + { + bool result = _pek_double_pending; + _pek_double_pending = false; + return result; } std::uint16_t M5PM1_Class::getVBUSVoltage(void) @@ -339,7 +330,7 @@ namespace m5 bool M5PM1_Class::getBatteryVoltage(std::uint16_t* millivolt) { - if (!_init) { return false; } + if (!_init || millivolt == nullptr) { return false; } std::uint8_t buf[2] = {}; if (!readRegister(M5PM1_REG_VBAT_L, buf, sizeof(buf))) { return false; } *millivolt = (buf[1] << 8) | buf[0]; diff --git a/src/utility/power/M5PM1_Class.hpp b/src/utility/power/M5PM1_Class.hpp index f07796d..446f5c8 100644 --- a/src/utility/power/M5PM1_Class.hpp +++ b/src/utility/power/M5PM1_Class.hpp @@ -39,7 +39,7 @@ namespace m5 enum gpio_function_t : std::uint8_t { gpio = 0b00 , irq = 0b01 - , wake = 0b10 + // 0b10 is reserved in the datasheet. , special = 0b11 }; @@ -81,7 +81,7 @@ namespace m5 /// @param enable true=enable / false=disable bool setLDOOutput(bool enable); - /// set PM1 5V DCDC output enable. + /// set PM1 3.3V DCDC rail output enable (PWR_CFG bit1 = 3.3V_DCDC_EN). /// @param enable true=enable / false=disable bool setDCDCOutput(bool enable); @@ -150,6 +150,9 @@ namespace m5 pwm_polarity_t polarity = pwm_polarity_t::normal, bool enable = true); /// clear PM1 wake source bits selected by mask. + /// Single-write selective clear assuming the write-zero-to-clear behavior + /// adopted by the official driver; the datasheet does not specify the write polarity. + /// Bits outside [6:0] are written as zero, matching the full-clear precedent. bool clearWakeSource(std::uint8_t mask = 0x7F); /// clear all PM1 GPIO IRQ status bits. @@ -183,28 +186,40 @@ namespace m5 bool getBatteryCharge(bool* enabled); /// set battery charge current - /// @param max_mA milli ampere. (8 - 512). + /// @param max_mA ignored; the PM1 has no charge current register. + /// @note The PM1 register map exposes no charge current register; this is a permanent stub returning false. bool setChargeCurrent(std::uint16_t max_mA); /// set battery charge voltage - /// @param max_mV milli volt. (3600 - 4545). + /// @param max_mV ignored; the PM1 has no charge voltage register. + /// @note The PM1 register map exposes no charge voltage register; this is a permanent stub returning false. bool setChargeVoltage(std::uint16_t max_mV); /// Get whether the battery is currently charging or not. + /// @note The PM1 register map exposes no charging status register; this is a permanent stub returning false. bool isCharging(void); // get setting value of battery charge current /// @return milli ampere. (8 - 512). 0=unknown + /// @note The PM1 register map exposes no charge current register; this is a permanent stub returning 0. std::uint16_t getChargeCurrent(void); // get setting value of battery charge voltage /// @return milli volt. (3600 - 4545). 0=unknown + /// @note The PM1 register map exposes no charge voltage register; this is a permanent stub returning 0. std::uint16_t getChargeVoltage(void); /// Get power key press condition. - /// @return 0=none / 2=short clicked + /// @return 0=none / 2=short clicked. For AXP compatibility, a double click + /// also reports 2; use wasPekDoubleClicked() to distinguish it. + /// Only the consumed click flags are cleared; the WAKEUP flag is preserved. + /// Returns 0 and leaves the event pending if the clear write fails. uint8_t getPekPress(void); + /// Returns whether the most recently reported click (getPekPress() == 2) + /// was a double click, then clears the flag. + bool wasPekDoubleClicked(void); + /// get VIN voltage. /// @return milli volt. 0=read failed std::uint16_t getVBUSVoltage(void); @@ -224,6 +239,9 @@ namespace m5 /// power off PM1. bool powerOff(void); + + private: + bool _pek_double_pending = false; }; } From 01c07525899a8f7137e001755f3920689de60e28 Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Tue, 18 Aug 2026 06:31:41 +0000 Subject: [PATCH 2/2] Reset the double-click latch on every reported click --- src/utility/power/M5PM1_Class.cpp | 2 +- src/utility/power/M5PM1_Class.hpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/utility/power/M5PM1_Class.cpp b/src/utility/power/M5PM1_Class.cpp index 529aa8b..76dc211 100644 --- a/src/utility/power/M5PM1_Class.cpp +++ b/src/utility/power/M5PM1_Class.cpp @@ -304,7 +304,7 @@ namespace m5 uint8_t pending = irq3 & ((1 << 0) | (1 << 2)); if (!pending) return 0; if (!writeRegister8(M5PM1_REG_IRQ_STATUS3, static_cast(~pending & 0x07))) return 0; - if (pending & (1 << 2)) { _pek_double_pending = true; } + _pek_double_pending = (pending & (1 << 2)) != 0; return 2; } diff --git a/src/utility/power/M5PM1_Class.hpp b/src/utility/power/M5PM1_Class.hpp index 446f5c8..88138e4 100644 --- a/src/utility/power/M5PM1_Class.hpp +++ b/src/utility/power/M5PM1_Class.hpp @@ -200,12 +200,12 @@ namespace m5 bool isCharging(void); // get setting value of battery charge current - /// @return milli ampere. (8 - 512). 0=unknown + /// @return always 0. /// @note The PM1 register map exposes no charge current register; this is a permanent stub returning 0. std::uint16_t getChargeCurrent(void); // get setting value of battery charge voltage - /// @return milli volt. (3600 - 4545). 0=unknown + /// @return always 0. /// @note The PM1 register map exposes no charge voltage register; this is a permanent stub returning 0. std::uint16_t getChargeVoltage(void); @@ -218,6 +218,8 @@ namespace m5 /// Returns whether the most recently reported click (getPekPress() == 2) /// was a double click, then clears the flag. + /// Call from the task that polls getPekPress() (typically right after + /// M5.update()); the flag is not synchronized across tasks. bool wasPekDoubleClicked(void); /// get VIN voltage.