From 637934878fd0da203b85bf8097bc5b58fe6867c4 Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Fri, 31 Jul 2026 14:23:10 +0900 Subject: [PATCH 1/4] Implement getAPSVoltage and return NaN from the unavailable AXP2101 getters (#86 #215) Seven getters of AXP2101_Class returned 0 without reading anything, which is indistinguishable from a valid measurement of zero. That is what both issues report. The ADC of the AXP2101 covers VBAT ( 0x34 ), TS ( 0x36 ), VBUS ( 0x38 ), VSYS ( 0x3A ) and TDIE ( 0x3C ). - getAPSVoltage() now reads VSYS, which is the equivalent measurement point on the AXP2101 of the APS rail of the AXP192. Measured 3.776 V on a CoreS3 while the AXP2101 was supplying the system. - the six getters that fall outside the set above return NaN, so that a caller can tell an unavailable reading from a real zero with std::isnan(). Nothing inside the library calls them, so no other behaviour changes. - document which readings each getter provides, and note in Power_Class::getBatteryCurrent() that the value comes from an AXP192 or from a dedicated current sense IC ( INA3221 on Core2 v1.1, INA226 on M5Tab5 ), and that boards without either of them return 0. --- src/utility/Power_Class.hpp | 3 +++ src/utility/power/AXP2101_Class.cpp | 42 +++++++++++++++++++---------- src/utility/power/AXP2101_Class.hpp | 15 +++++++++++ 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/utility/Power_Class.hpp b/src/utility/Power_Class.hpp index 70ec618..291b8f7 100644 --- a/src/utility/Power_Class.hpp +++ b/src/utility/Power_Class.hpp @@ -184,6 +184,9 @@ namespace m5 /// get battery current /// @return battery current [mA] ( +=charge / -=discharge ) + /// @attention This reading comes from the hardware of the board: an AXP192, or a + /// dedicated current sense IC ( ex. Core2 v1.1 , M5Tab5 , M5PowerHub ). + /// Boards without either of them return 0. int32_t getBatteryCurrent(void); /// Get Ext Port voltage diff --git a/src/utility/power/AXP2101_Class.cpp b/src/utility/power/AXP2101_Class.cpp index 0710d00..d65891b 100644 --- a/src/utility/power/AXP2101_Class.cpp +++ b/src/utility/power/AXP2101_Class.cpp @@ -8,6 +8,7 @@ #endif #include +#include #define IS_BIT_SET(val,mask) (((val)&(mask)) == (mask)) @@ -213,14 +214,21 @@ return false; return val >> 2; } - float AXP2101_Class::getACINVoltage(void) + /// Used where the AXP2101 provides no reading for the requested value. + /// Returning NaN instead of 0 lets the caller tell it from a real zero. + static constexpr float not_available(void) { -return 0; + return std::numeric_limits::quiet_NaN(); + } + + float AXP2101_Class::getACINVoltage(void) + { // The AXP2101 takes its input from VBUS. ( see getVBUSVoltage ) + return not_available(); } float AXP2101_Class::getACINCurrent(void) - { -return 0; + { // The AXP2101 takes its input from VBUS. + return not_available(); } float AXP2101_Class::getVBUSVoltage(void) @@ -234,8 +242,8 @@ return 0; } float AXP2101_Class::getVBUSCurrent(void) - { -return 0; + { // Not provided by the AXP2101. ( see getBatteryChargeCurrent ) + return not_available(); } float AXP2101_Class::getTSVoltage(void) @@ -252,8 +260,8 @@ return 0; } float AXP2101_Class::getBatteryPower(void) - { -return 0; + { // Derived from the battery current, which the AXP2101 does not provide. + return not_available(); } float AXP2101_Class::getBatteryVoltage(void) @@ -262,18 +270,24 @@ return 0; } float AXP2101_Class::getBatteryChargeCurrent(void) - { -return 0; + { // The ADC of the AXP2101 covers VBAT / TS / VBUS / VSYS / TDIE. Current is measured + // by a dedicated sense IC where the board provides one ( ex. INA3221 on Core2 v1.1 , + // INA226 on Tab5 ) , and Power_Class::getBatteryCurrent() reads that IC. + return not_available(); } float AXP2101_Class::getBatteryDischargeCurrent(void) - { -return 0; + { // Not provided by the AXP2101. ( see getBatteryChargeCurrent ) + return not_available(); } float AXP2101_Class::getAPSVoltage(void) - { -return 0; + { // VSYS is the equivalent measurement point on the AXP2101 of the APS rail + // of the AXP192. + float volt = readRegister14(0x3A); + if (volt >= 16375) { return 0.0f; } + + return volt / 1000.0f; } bool AXP2101_Class::enableIRQ(std::uint64_t registerEn) diff --git a/src/utility/power/AXP2101_Class.hpp b/src/utility/power/AXP2101_Class.hpp index be09f14..faf3736 100644 --- a/src/utility/power/AXP2101_Class.hpp +++ b/src/utility/power/AXP2101_Class.hpp @@ -122,15 +122,30 @@ namespace m5 bool isVBUS(void); bool getBatState(void); + /// The ADC of the AXP2101 covers these measurement points: + /// VBAT , TS , VBUS , VSYS , TDIE + /// The getters below that fall outside this set return NaN, so that a caller can + /// tell an unavailable reading from a real zero with std::isnan(). + /// Current is measured by a dedicated sense IC where the board provides one + /// ( ex. INA3221 on Core2 v1.1 , INA226 on M5Tab5 ) , and Power_Class reads that IC. + float getBatteryVoltage(void); + /// @return NaN. This reading is not provided by the AXP2101. float getBatteryDischargeCurrent(void); + /// @return NaN. This reading is not provided by the AXP2101. float getBatteryChargeCurrent(void); + /// @return NaN. Derived from the battery current, which the AXP2101 does not provide. float getBatteryPower(void); + /// @return NaN. The AXP2101 takes its input from VBUS. ( see getVBUSVoltage ) float getACINVoltage(void); + /// @return NaN. The AXP2101 takes its input from VBUS. float getACINCurrent(void); float getVBUSVoltage(void); + /// @return NaN. This reading is not provided by the AXP2101. float getVBUSCurrent(void); float getTSVoltage(void); + /// The VSYS voltage is returned, which is the equivalent measurement point on the + /// AXP2101 of the APS rail of the AXP192. float getAPSVoltage(void); float getInternalTemperature(void); From 63f09663f5015daecdfb29a84348d9d0247bb8a6 Mon Sep 17 00:00:00 2001 From: lovyan03 <42724151+lovyan03@users.noreply.github.com> Date: Fri, 31 Jul 2026 14:47:53 +0900 Subject: [PATCH 2/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/utility/power/AXP2101_Class.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utility/power/AXP2101_Class.cpp b/src/utility/power/AXP2101_Class.cpp index d65891b..bbffea5 100644 --- a/src/utility/power/AXP2101_Class.cpp +++ b/src/utility/power/AXP2101_Class.cpp @@ -272,7 +272,7 @@ return false; float AXP2101_Class::getBatteryChargeCurrent(void) { // The ADC of the AXP2101 covers VBAT / TS / VBUS / VSYS / TDIE. Current is measured // by a dedicated sense IC where the board provides one ( ex. INA3221 on Core2 v1.1 , - // INA226 on Tab5 ) , and Power_Class::getBatteryCurrent() reads that IC. + // INA226 on M5Tab5 ) , and Power_Class::getBatteryCurrent() reads that IC. return not_available(); } From 4ddb28c7255de1c468ba5b7e49022accd1912e3e Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Fri, 31 Jul 2026 14:59:46 +0900 Subject: [PATCH 3/4] Name the ADC threshold that marks a channel as having nothing to read The literal 16375 appeared at every place that checks whether a 14 bit ADC channel returned a reading, and getAPSVoltage() added a third one. Give it a name so that the intent is visible and the value lives in one place. No behaviour change. --- src/utility/power/AXP2101_Class.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/utility/power/AXP2101_Class.cpp b/src/utility/power/AXP2101_Class.cpp index bbffea5..a6afb93 100644 --- a/src/utility/power/AXP2101_Class.cpp +++ b/src/utility/power/AXP2101_Class.cpp @@ -221,6 +221,10 @@ return false; return std::numeric_limits::quiet_NaN(); } + /// A 14 bit ADC channel reads close to its full scale ( 0x3FFF ) when there is + /// nothing to measure on it. Values from here up are not treated as a reading. + static constexpr std::size_t adc_invalid_min = 16375; + float AXP2101_Class::getACINVoltage(void) { // The AXP2101 takes its input from VBUS. ( see getVBUSVoltage ) return not_available(); @@ -236,7 +240,7 @@ return false; if (isVBUS() == false) { return 0.0f; } float vBus = readRegister14(0x38); - if (vBus >= 16375) { return 0.0f; } + if (vBus >= adc_invalid_min) { return 0.0f; } return vBus / 1000.0f; } @@ -249,7 +253,7 @@ return false; float AXP2101_Class::getTSVoltage(void) { float volt = readRegister14(0x36); - if (volt >= 16375) { return 0.0f; } + if (volt >= adc_invalid_min) { return 0.0f; } return volt / 2000.0f; } @@ -285,7 +289,7 @@ return false; { // VSYS is the equivalent measurement point on the AXP2101 of the APS rail // of the AXP192. float volt = readRegister14(0x3A); - if (volt >= 16375) { return 0.0f; } + if (volt >= adc_invalid_min) { return 0.0f; } return volt / 1000.0f; } From 80ec90ad85859b837a70d1fae30e133c0e78ceac Mon Sep 17 00:00:00 2001 From: ainyan03 Date: Fri, 31 Jul 2026 15:55:26 +0900 Subject: [PATCH 4/4] Widen the margin that marks an ADC channel as having nothing to read The threshold was full scale minus 8, which is narrower than the value a channel actually settles at. Measured on a CoreS3, VBUS with nothing connected reads 16372, or full scale minus 11, so it slipped through the check and would have been reported as a 16.372 V reading. Keep the way the threshold is defined and widen the margin to 32. That is three times the distance measured above, while a channel that does have an input stays far away from it: the closest on the same board was TS at 5563, and every other channel read below 5100. This does not change what any of the getters return today. getVBUSVoltage() is already guarded by isVBUS(), and TS and VSYS always have an input on the boards in question, so no path reached the threshold with a railed value. --- src/utility/power/AXP2101_Class.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/utility/power/AXP2101_Class.cpp b/src/utility/power/AXP2101_Class.cpp index a6afb93..30a072e 100644 --- a/src/utility/power/AXP2101_Class.cpp +++ b/src/utility/power/AXP2101_Class.cpp @@ -221,9 +221,12 @@ return false; return std::numeric_limits::quiet_NaN(); } - /// A 14 bit ADC channel reads close to its full scale ( 0x3FFF ) when there is - /// nothing to measure on it. Values from here up are not treated as a reading. - static constexpr std::size_t adc_invalid_min = 16375; + /// A 14 bit ADC channel sits at the top of its range when there is nothing to measure + /// on it, so a reading within this margin of full scale is not treated as a value. + /// A channel that does have an input reads far below the margin; measured on a CoreS3, + /// the closest was TS at 5563, while VBUS with nothing connected read 16372. + static constexpr std::size_t adc_full_scale = 0x3FFF; + static constexpr std::size_t adc_invalid_min = adc_full_scale - 32; float AXP2101_Class::getACINVoltage(void) { // The AXP2101 takes its input from VBUS. ( see getVBUSVoltage )