From 49db0e157eba7ed41801f00b90193a062680bcfc Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Thu, 23 Jan 2025 21:07:37 +0100 Subject: [PATCH] unPhone power fixes --- Boards/UnPhone/Source/PowerOn.cpp | 81 ++++++++++++++++------- Boards/UnPhone/Source/UnPhoneFeatures.cpp | 31 +++++---- Boards/UnPhone/Source/bq24295/Bq24295.cpp | 62 ++++++++++++++--- Boards/UnPhone/Source/bq24295/Bq24295.h | 18 ++++- 4 files changed, 141 insertions(+), 51 deletions(-) diff --git a/Boards/UnPhone/Source/PowerOn.cpp b/Boards/UnPhone/Source/PowerOn.cpp index 8c4a1ba8..509f5dd4 100644 --- a/Boards/UnPhone/Source/PowerOn.cpp +++ b/Boards/UnPhone/Source/PowerOn.cpp @@ -8,35 +8,72 @@ extern UnPhoneFeatures unPhoneFeatures; static std::unique_ptr powerThread; +enum class PowerState { + Initial, + On, + Off +}; + +#define DEBUG_POWER_STATES false + +/** Helper method to use the buzzer to signal the different power stages */ +static void powerInfoBuzz(uint8_t count) { + if (DEBUG_POWER_STATES) { + uint8_t index = 0; + while (index < count) { + unPhoneFeatures.setVibePower(true); + tt::kernel::delayMillis(50); + unPhoneFeatures.setVibePower(false); + + index++; + + if (index < count) { + tt::kernel::delayMillis(100); + } + } + } +} + static void updatePowerSwitch() { - static bool last_on_state = true; + static PowerState last_state = PowerState::Initial; if (!unPhoneFeatures.isPowerSwitchOn()) { - if (last_on_state) { + if (last_state != PowerState::Off) { + last_state = PowerState::Off; TT_LOG_W(TAG, "Power off"); } - unPhoneFeatures.turnPeripheralsOff(); - if (!unPhoneFeatures.isUsbPowerConnected()) { // and usb unplugged we go into shipping mode - if (last_on_state) { - TT_LOG_W(TAG, "Shipping mode until USB connects"); - unPhoneFeatures.setShipping(true); // tell BM to stop supplying power until USB connects - } - } else { // power switch off and usb plugged in we sleep + TT_LOG_W(TAG, "Shipping mode until USB connects"); + + unPhoneFeatures.setExpanderPower(true); + powerInfoBuzz(3); + unPhoneFeatures.setExpanderPower(false); + + unPhoneFeatures.turnPeripheralsOff(); + + unPhoneFeatures.setShipping(true); // tell BM to stop supplying power until USB connects + } else { // When power switch is off, but USB is plugged in, we wait (deep sleep) until USB is unplugged. + TT_LOG_W(TAG, "Waiting for USB disconnect to power off"); + + powerInfoBuzz(2); + unPhoneFeatures.turnPeripheralsOff(); + + // Deep sleep for 1 minute, then awaken to check power state again + // GPIO trigger from power switch also awakens the device unPhoneFeatures.wakeOnPowerSwitch(); - // Using UINT64_MAX leads to boot loops because of a bug in esp_sleep_start() converting it to int64_t before sleeping - esp_sleep_enable_timer_wakeup(UINT64_MAX / 2); // ea min: USB? else->shipping - esp_deep_sleep_start(); // deep sleep, wait for wakeup on GPIO + esp_sleep_enable_timer_wakeup(60000000); + esp_deep_sleep_start(); } - - last_on_state = false; } else { - if (!last_on_state) { + if (last_state != PowerState::On) { + last_state = PowerState::On; TT_LOG_W(TAG, "Power on"); + unPhoneFeatures.setShipping(false); + unPhoneFeatures.setExpanderPower(true); + powerInfoBuzz(1); } - last_on_state = true; } } @@ -65,22 +102,16 @@ static bool unPhonePowerOn() { unPhoneFeatures.printInfo(); - // Vibrate once - // Note: Do this before power switching logic, to detect silent boot loops - unPhoneFeatures.setVibePower(true); - tt::kernel::delayMillis(150); + unPhoneFeatures.setBacklightPower(false); unPhoneFeatures.setVibePower(false); + unPhoneFeatures.setIrPower(false); + unPhoneFeatures.setExpanderPower(false); // Turn off the device if power switch is on off state, // instead of waiting for the Thread to start and continue booting updatePowerSwitch(); startPowerSwitchThread(); - unPhoneFeatures.setBacklightPower(false); - unPhoneFeatures.setVibePower(false); - unPhoneFeatures.setIrPower(false); - unPhoneFeatures.setExpanderPower(false); - return true; } diff --git a/Boards/UnPhone/Source/UnPhoneFeatures.cpp b/Boards/UnPhone/Source/UnPhoneFeatures.cpp index 1c7b899f..6521a6d7 100644 --- a/Boards/UnPhone/Source/UnPhoneFeatures.cpp +++ b/Boards/UnPhone/Source/UnPhoneFeatures.cpp @@ -168,12 +168,28 @@ bool UnPhoneFeatures::initGpioExpander() { assert(ioExpander != nullptr); // Output pins + + /** + * Important: + * If you clear the pins too late, the display or vibration motor might briefly turn on. + */ + esp_io_expander_set_dir(ioExpander, expanderpin::BACKLIGHT, IO_EXPANDER_OUTPUT); + esp_io_expander_set_level(ioExpander, expanderpin::BACKLIGHT, 0); + esp_io_expander_set_dir(ioExpander, expanderpin::EXPANDER_POWER, IO_EXPANDER_OUTPUT); + esp_io_expander_set_dir(ioExpander, expanderpin::LED_GREEN, IO_EXPANDER_OUTPUT); + esp_io_expander_set_level(ioExpander, expanderpin::LED_GREEN, 0); + esp_io_expander_set_dir(ioExpander, expanderpin::LED_BLUE, IO_EXPANDER_OUTPUT); + esp_io_expander_set_level(ioExpander, expanderpin::LED_BLUE, 0); + esp_io_expander_set_dir(ioExpander, expanderpin::VIBE, IO_EXPANDER_OUTPUT); + esp_io_expander_set_level(ioExpander, expanderpin::VIBE, 0); + // Input pins + esp_io_expander_set_dir(ioExpander, expanderpin::USB_VSENSE, IO_EXPANDER_INPUT); return true; @@ -266,16 +282,12 @@ void UnPhoneFeatures::turnPeripheralsOff() const { bool UnPhoneFeatures::setShipping(bool on) const { if (on) { TT_LOG_W(TAG, "setShipping: on"); - uint8_t mask = (1 << 4) | (1 << 5); - // REG05[5:4] = 00 - batteryManagement.setWatchDogBitOff(mask); + batteryManagement.setWatchDogTimer(Bq24295::WatchDogTimer::Disabled); // Set bit 5 to disable batteryManagement.setOperationControlBitOn(1 << 5); } else { TT_LOG_W(TAG, "setShipping: off"); - // REG05[5:4] = 01 - batteryManagement.setWatchDogBitOff(1 << 5); - batteryManagement.setWatchDogBitOn(1 << 4); + batteryManagement.setWatchDogTimer(Bq24295::WatchDogTimer::Enabled40s); // Clear bit 5 to enable batteryManagement.setOperationControlBitOff(1 << 5); } @@ -287,10 +299,5 @@ void UnPhoneFeatures::wakeOnPowerSwitch() const { } bool UnPhoneFeatures::isUsbPowerConnected() const { - uint8_t status; - if (batteryManagement.getStatus(status)) { - return (status & 4U) != 0U; - } else { - return false; - } + return batteryManagement.isUsbPowerConnected(); } diff --git a/Boards/UnPhone/Source/bq24295/Bq24295.cpp b/Boards/UnPhone/Source/bq24295/Bq24295.cpp index 1d185f35..a75e638d 100644 --- a/Boards/UnPhone/Source/bq24295/Bq24295.cpp +++ b/Boards/UnPhone/Source/bq24295/Bq24295.cpp @@ -8,25 +8,56 @@ * https://gitlab.com/hamishcunningham/unphonelibrary/-/blob/main/unPhone.h?ref_type=heads */ namespace registers { - static const uint8_t WATCHDOG = 0x05U; // Datasheet page 35: Charge end/timer cntrl + static const uint8_t CHARGE_TERMINATION = 0x05U; // Datasheet page 35: Charge end/timer cntrl static const uint8_t OPERATION_CONTROL = 0x07U; // Datasheet page 37: Misc operation control static const uint8_t STATUS = 0x08U; // Datasheet page 38: System status static const uint8_t VERSION = 0x0AU; // Datasheet page 38: Vendor/part/revision status } // namespace registers -// region Watchdog -bool Bq24295::getWatchDog(uint8_t value) const { - return readRegister8(registers::WATCHDOG, value); +bool Bq24295::readChargeTermination(uint8_t& out) const { + return readRegister8(registers::CHARGE_TERMINATION, out); } -bool Bq24295::setWatchDogBitOn(uint8_t mask) const { - return bitOn(registers::WATCHDOG, mask); +// region Watchdog +bool Bq24295::getWatchDogTimer(WatchDogTimer& out) const { + uint8_t value; + if (readChargeTermination(value)) { + uint8_t relevant_bits = value & (BIT(4) | BIT(5)); + switch (relevant_bits) { + case 0b000000: + out = WatchDogTimer::Disabled; + return true; + case 0b010000: + out = WatchDogTimer::Enabled40s; + return true; + case 0b100000: + out = WatchDogTimer::Enabled80s; + return true; + case 0b110000: + out = WatchDogTimer::Enabled160s; + return true; + default: + return false; + } + } + + return false; } -bool Bq24295::setWatchDogBitOff(uint8_t mask) const { - return bitOff(registers::WATCHDOG, mask); +bool Bq24295::setWatchDogTimer(WatchDogTimer in) const { + uint8_t value; + if (readChargeTermination(value)) { + uint8_t bits_to_set = 0b00110000 & static_cast(in); + uint8_t value_cleared = value & 0b11001111; + uint8_t to_set = bits_to_set & value_cleared; + TT_LOG_I(TAG, "WatchDogTimer: %02x -> %02x", value, to_set); + return writeRegister8(registers::CHARGE_TERMINATION, to_set); + } + + return false; } + // endregoin // region Operation Control @@ -51,14 +82,23 @@ bool Bq24295::getStatus(uint8_t& value) const { return readRegister8(registers::STATUS, value); } +bool Bq24295::isUsbPowerConnected() const { + uint8_t status; + if (getStatus(status)) { + return (status & BIT(2)) != 0U; + } else { + return false; + } +} + bool Bq24295::getVersion(uint8_t& value) const { return readRegister8(registers::VERSION, value); } void Bq24295::printInfo() const { - uint8_t version, status; - if (getStatus(status) && getVersion(version)) { - TT_LOG_I(TAG, "Version %d, status %02x", version, status); + uint8_t version, status, charge_termination; + if (getStatus(status) && getVersion(version) && readChargeTermination(charge_termination)) { + TT_LOG_I(TAG, "Version %d, status %02x, charge termination %02x", version, status, charge_termination); } else { TT_LOG_E(TAG, "Failed to retrieve version and/or status"); } diff --git a/Boards/UnPhone/Source/bq24295/Bq24295.h b/Boards/UnPhone/Source/bq24295/Bq24295.h index 7f825b39..096924b4 100644 --- a/Boards/UnPhone/Source/bq24295/Bq24295.h +++ b/Boards/UnPhone/Source/bq24295/Bq24295.h @@ -6,13 +6,25 @@ class Bq24295 : I2cDevice { +private: + + bool readChargeTermination(uint8_t& out) const; + public: + enum class WatchDogTimer { + Disabled = 0b000000, + Enabled40s = 0b010000, + Enabled80s = 0b100000, + Enabled160s = 0b110000 + }; + explicit Bq24295(i2c_port_t port) : I2cDevice(port, BQ24295_ADDRESS) {} - bool getWatchDog(uint8_t value) const; - bool setWatchDogBitOn(uint8_t mask) const; - bool setWatchDogBitOff(uint8_t mask) const; + bool getWatchDogTimer(WatchDogTimer& out) const; + bool setWatchDogTimer(WatchDogTimer in) const; + + bool isUsbPowerConnected() const; bool getOperationControl(uint8_t value) const; bool setOperationControlBitOn(uint8_t mask) const;