Power: add getChargeState() with per-charger capabilities, make isCharging() a bool - #353
Open
ainyan03 wants to merge 1 commit into
Open
Power: add getChargeState() with per-charger capabilities, make isCharging() a bool#353ainyan03 wants to merge 1 commit into
ainyan03 wants to merge 1 commit into
Conversation
…rging() a bool isCharging() returned is_charging_t, which folded "no information" and "not charging" into one value and let boards report states their charger cannot actually distinguish. On IP5306 boards it read the charge enable bit and answered "discharging" while a full battery sat on USB power (m5stack#349). The setBatteryCharge / setChargeCurrent / setChargeVoltage family returned void, so callers could not tell a rejected or clamped request from an applied one. New API - charge_state_t: not_initialized / io_error / undetermined / unsupported (non-positive), charging / not_charging / full / disabled / discharging / idle (positive). Only `charging` means the battery is being charged. - charge_state_set_t with charge_states_known / any_charging / any_not_charging, getChargeStateCaps(), canReport(state): the set of states a board can report, from one table keyed by charger and board. - getChargeState(): not_initialized until M5.begin() completes, unsupported when the board can report nothing, io_error on a failed read, otherwise the per-charger procedure below. - getBatteryPresence() / battery_presence_t for boards whose charger reports battery presence (AXP2101) or where it is estimated (ToughC5, CoreMatrix). - getChargeControlCaps(): which of enable / current / voltage a board can set. Per charger - AXP192: REG00 bit2 -> charging, REG33 bit7 clear -> disabled, else not_charging. No `full` (REG01 bit6 does not report completion). - AXP2101: REG01 charger state machine (tri/pre/CC/CV -> charging, done -> full), REG18 bit1 -> disabled, REG00 bit3 -> battery presence, battery current direction -> discharging / idle. - IP5306: SYS_CTL0 bit4 clear -> disabled, REG_READ0 bit3 (effective enable) clear -> not_charging, REG_READ1 bit3 -> full, else charging. A board without I2C access to the IP5306 reports io_error. - AW32001: charge status register -> charging / full / disabled / not_charging. - M5PM1 boards (StampS3Bat, ToughC5, CoreMatrix, PaperS3, ...): CHG_STAT through the PM1 / IOE1 inputs; ToughC5 and CoreMatrix gate CHG_STAT with a battery presence estimate so a charger retrying into an empty connector is not reported as charging. Inside that estimate a failed read, a disabled charger or a setBatteryCharge() call is a gap: the evaluation ends with the cached verdict and all transient evidence (the CHG_STAT low streak, the VBAT sample baseline and counters) is dropped, so nothing observed across the gap can complete a streak or a sample count that began before it. - Tab5 / Tab5X: INA226 battery current -> charging / discharging / idle (IOE1 G6 is the IP2326 BAT_STAT stage flag and cannot distinguish charging from disabled). - PowerHub, PaperMono, PaperColor, CoreP4X: as their hardware allows. Control functions - setBatteryCharge / setChargeCurrent / setChargeVoltage return bool and take an optional `applied_*` output. A request selects the largest step not above the request; requests below the lowest step are clamped up to it; 0 is not a step. `applied_*` is untouched on failure. - Tab5 / Tab5X: setChargeCurrent selects {500, 1000} mA; 0 no longer means "stop charging" (use setBatteryCharge(false)). - StampS3Bat: CHG_PROG (PM1 G3) is driven low for 650 mA or released for 200 mA in one transaction that also sets the pin mux; every releasing step is attempted even after an earlier failure and the pin only becomes an output when all of them succeeded. begin() releases it so the default is 200 mA. - IP5306: voltage steps are reported as the effective values including the boost offset (4228 / 4314 / 4364 / 4414 mV). Robustness - Status-returning register reads in every state procedure; an I2C failure is io_error, never a positive state. - Charger identity is settled once: on the boards that carry either an AXP192 or an AXP2101 the probes retry up to three times for a positive chip ID, and once a probe has answered a later Power_Class::begin() keeps that identity. If every probe failed the board default (AXP192) stays provisional: the capability set is the default, but getChargeState() / getBatteryPresence() report io_error and the charge setters return false until a later begin() gets a positive ID, so the charge state API does not interpret, and the setters do not write, a chip that was never identified with the wrong register map. (begin() itself still applies the default's register setup, as before.) Charge state before M5.begin() completes is not_initialized. - M5PM1: getPowerSource(pwr_src_t*), checked getVbatNodePowered, and read-modify-write helpers that report I2C failures. INA226: readShuntCurrent(float*) fails unless the chip identified itself in begin(). Breaking changes - isCharging() returns bool (true only while charging). Code that compared against is_charging_t::charge_unknown must use getChargeState(). - setBatteryCharge / setChargeCurrent / setChargeVoltage return bool. - Tab5 / Tab5X: setChargeCurrent(0) applies 500 mA instead of stopping. setChargeCurrent(0) logs a warning once, pointing to setBatteryCharge(false). - StampS3Bat: charge current after begin() is 200 mA (previously left as the PM1 default).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
isCharging()returnedis_charging_t, which folded "no information" and "not charging" into one value, and several boards reported states their charger cannot actually distinguish. On IP5306 boards it read the charge-enable bit and answeredis_dischargingwhile a full battery sat on USB power (#349). ThesetBatteryCharge/setChargeCurrent/setChargeVoltagefamily returnedvoid, so a caller could not tell a rejected or clamped request from an applied one.This PR adds a charge state API that only reports what each charger can actually observe, and makes the control functions report what they applied.
New API
charge_state_t getChargeState()charging/not_charging/full/disabled/discharging/idle(positive) ornot_initialized/io_error/undetermined/unsupported(non-positive). Onlychargingmeans the battery is being charged.bool getChargeStateCaps(charge_state_set_t*)/bool canReport(charge_state_t)fullcan ever be observed.charge_states_known/charge_states_any_charging/charge_states_any_not_chargingisCharging()ischarge_states_any_charging.contains(getChargeState()).battery_presence_t getBatteryPresence()present/absenton boards whose charger reports it (AXP2101) or where it is estimated (ToughC5, CoreMatrix);unsupportedelsewhere.uint8_t getChargeControlCaps()bool setBatteryCharge(bool)/bool setChargeCurrent(mA, uint16_t* applied = nullptr)/bool setChargeVoltage(mV, uint16_t* applied = nullptr)applied_*receives the step actually selected.Step selection: the largest step not above the request; a request below the lowest step is clamped up to it; 0 is not a step.
What each charger reports
full: REG01 bit6 means "not charging or done"Every state procedure uses status-returning register reads; an I2C failure is
io_error, never a positive state. Charger identity is settled once (AXP192 / AXP2101 probes retry for a positive ID, and a laterPower.begin()keeps it). BeforeM5.begin()completes,getChargeState()isnot_initialized.Breaking changes
isCharging()returnsbool(true only while charging). Code that compared againstis_charging_t::charge_unknownshould usegetChargeState()/canReport().setBatteryCharge/setChargeCurrent/setChargeVoltagereturnboolinstead ofvoid.setChargeCurrent(0)applies 500 mA (the lowest step) instead of stopping the charge. UsesetBatteryCharge(false)to stop.setChargeCurrent(0)now logs a warning once that points tosetBatteryCharge(false).begin()releases CHG_PROG (PM1 G3), so the charge current afterbegin()is 200 mA;setChargeCurrent(650)drives it low for 650 mA. Previously the pin was left at the PM1 default.setChargeVoltageselects and reports the effective step including the boost offset (4228 / 4314 / 4364 / 4414 mV).Verification
Checked on hardware:
full(#349), disabled, charging, voltage / current stepsnot_initializedbeforebegin()setChargeCurrent0 -> 500, 700 -> 500, 1500 -> 1000Builds: ESP32 / S3 / C3 / C5 / C6 / P4 (Arduino) and native (PC build with the caps assertion), no new warnings.
Closes #349