From 03e5f8d7148275d0db11ddd8248055e93d35da3a Mon Sep 17 00:00:00 2001 From: Wojtek Rzepecki Date: Tue, 13 Apr 2021 09:33:31 +0200 Subject: [PATCH] [EGD-5360] Add USB current selection Charging current selection algorithm depending on USB type. --- .../linux/battery-charger/battery_charger.cpp | 27 +++++++ .../rt1051/bsp/battery-charger/MAX77818.hpp | 16 +++- .../bsp/battery-charger/battery_charger.cpp | 75 +++++++++++++++---- .../bsp/battery-charger/battery_charger.hpp | 4 + .../service-evtmgr/WorkerEvent.cpp | 4 +- .../doc/USB_current_selection.puml | 14 ++++ .../doc/USB_current_selection.svg | 31 ++++++++ .../service-evtmgr/doc/battery_charging.md | 44 +++++++++++ .../doc/charger_temperature_cutoff.md | 9 --- 9 files changed, 199 insertions(+), 25 deletions(-) create mode 100644 module-services/service-evtmgr/doc/USB_current_selection.puml create mode 100644 module-services/service-evtmgr/doc/USB_current_selection.svg create mode 100644 module-services/service-evtmgr/doc/battery_charging.md delete mode 100644 module-services/service-evtmgr/doc/charger_temperature_cutoff.md diff --git a/module-bsp/board/linux/battery-charger/battery_charger.cpp b/module-bsp/board/linux/battery-charger/battery_charger.cpp index 6bd11741f44965eddfac2906d882d81ac1c2ffa8..efaf4addf4106181bd1f248caef6aaab33b4b3af 100644 --- a/module-bsp/board/linux/battery-charger/battery_charger.cpp +++ b/module-bsp/board/linux/battery-charger/battery_charger.cpp @@ -158,4 +158,31 @@ namespace bsp::battery_charger void checkTemperatureRange() {} + void setUSBCurrentLimit(batteryChargerType chargerType) + { + switch (chargerType) { + case batteryChargerType::DcdTimeOut: + [[fallthrough]]; + case batteryChargerType::DcdUnknownType: + [[fallthrough]]; + case batteryChargerType::DcdError: + [[fallthrough]]; + case batteryChargerType::DcdSDP: + LOG_INFO("USB current limit set to 500mA"); + break; + case batteryChargerType::DcdCDP: + [[fallthrough]]; + case batteryChargerType::DcdDCP: + LOG_INFO("USB current limit set to 1000mA"); + break; + } + } + + void actionIfChargerUnplugged() + { + if (Store::Battery::get().state == Store::Battery::State::Discharging) { + LOG_INFO("USB unplugged"); + } + } + } // namespace bsp::battery_charger diff --git a/module-bsp/board/rt1051/bsp/battery-charger/MAX77818.hpp b/module-bsp/board/rt1051/bsp/battery-charger/MAX77818.hpp index 9770e0f2fee88e422d0e70e05e05de44cd896903..da15a3b35117db3fb4fd59cf410ff15f8336f02e 100644 --- a/module-bsp/board/rt1051/bsp/battery-charger/MAX77818.hpp +++ b/module-bsp/board/rt1051/bsp/battery-charger/MAX77818.hpp @@ -154,7 +154,7 @@ namespace bsp::battery_charger }; /// CHG_DETAILS_01 register - enum CHG_DETAILS_01 + enum class CHG_DETAILS_01 { CHARGER_PREQUALIFICATION = 0x00, CHARGER_CC = 0x01, @@ -166,6 +166,20 @@ namespace bsp::battery_charger CHARGER_OFF = 0x08, }; + // CHG_CNFG_09 register + enum class USBCurrentLimit + { + lim500mA = 0x0F, + lim1000mA = 0x1E, + }; + + // CHG_CNFG_02 register + enum class ChargeCurrentLimit + { + lim450mA = 0x09, /// default + lim1600mA = 0x20, /// 1C of battery + }; + // CONFIG register bits enum class CONFIG { diff --git a/module-bsp/board/rt1051/bsp/battery-charger/battery_charger.cpp b/module-bsp/board/rt1051/bsp/battery-charger/battery_charger.cpp index 5d48c9cbd3930ce840ebd5e72b6482c3236c3029..6927f84e24bc3624941ce1b8fbba43cca7e9374c 100644 --- a/module-bsp/board/rt1051/bsp/battery-charger/battery_charger.cpp +++ b/module-bsp/board/rt1051/bsp/battery-charger/battery_charger.cpp @@ -32,9 +32,6 @@ namespace bsp::battery_charger constexpr std::uint8_t VSYS_MIN = 0x80; // 3.6V constexpr std::uint8_t CHARGE_TARGET_VOLTAGE = 0x1D; // 4.35V - constexpr std::uint8_t MAX_USB_CURRENT = 0x0F; // 500mA - constexpr std::uint8_t FAST_CHARGE_CURRENT = 0x09; // 450mA - constexpr std::uint16_t nominalCapacitymAh = 1600; constexpr std::uint16_t fullyChargedSOC = 100; @@ -206,26 +203,43 @@ namespace bsp::battery_charger } } - void configureBatteryCharger() + void setMaxBusCurrent(USBCurrentLimit limit) { - unlockProtectedChargerRegisters(); - - std::uint8_t value = CHARGE_TARGET_VOLTAGE | VSYS_MIN; - if (chargerWrite(Registers::CHG_CNFG_04, value) != kStatus_Success) { - LOG_ERROR("Charge target voltage write fail"); - } - - value = MAX_USB_CURRENT; + const auto value = static_cast(limit); if (chargerWrite(Registers::CHG_CNFG_09, value) != kStatus_Success) { LOG_ERROR("Maximum usb current write fail"); } + } - value = FAST_CHARGE_CURRENT; + void setMaxChargeCurrent(ChargeCurrentLimit limit) + { + unlockProtectedChargerRegisters(); + const auto value = static_cast(limit); if (chargerWrite(Registers::CHG_CNFG_02, value) != kStatus_Success) { LOG_ERROR("Fast charge current write fail"); } + lockProtectedChargerRegisters(); + } + + void resetUSBCurrrentLimit() + { + LOG_INFO("USB current limit set to 500mA"); + setMaxBusCurrent(USBCurrentLimit::lim500mA); + } + + void configureBatteryCharger() + { + unlockProtectedChargerRegisters(); + + std::uint8_t value = CHARGE_TARGET_VOLTAGE | VSYS_MIN; + if (chargerWrite(Registers::CHG_CNFG_04, value) != kStatus_Success) { + LOG_ERROR("Charge target voltage write fail"); + } lockProtectedChargerRegisters(); + + resetUSBCurrrentLimit(); + setMaxChargeCurrent(ChargeCurrentLimit::lim1600mA); } std::uint8_t getChargerDetails() @@ -595,7 +609,7 @@ namespace bsp::battery_charger Store::Battery::modify().state = Store::Battery::State::Discharging; } - switch (chargerDetails) { + switch (static_cast(chargerDetails)) { case CHG_DETAILS_01::CHARGER_DONE: Store::Battery::modify().state = Store::Battery::State::ChargingDone; chargingFinishedAction(); @@ -614,6 +628,10 @@ namespace bsp::battery_charger case CHG_DETAILS_01::CHARGER_TOPOFF: Store::Battery::modify().state = Store::Battery::State::Charging; break; + case CHG_DETAILS_01::CHARGER_TIMER_FAULT: + [[fallthrough]]; + case CHG_DETAILS_01::CHARGER_BATTERY_DETECT: + break; } return (Store::Battery::get().state == Store::Battery::State::ChargingDone || @@ -667,6 +685,35 @@ namespace bsp::battery_charger return value.second; } + void setUSBCurrentLimit(batteryChargerType chargerType) + { + switch (chargerType) { + case batteryChargerType::DcdTimeOut: + [[fallthrough]]; + case batteryChargerType::DcdUnknownType: + [[fallthrough]]; + case batteryChargerType::DcdError: + [[fallthrough]]; + case batteryChargerType::DcdSDP: + resetUSBCurrrentLimit(); + break; + case batteryChargerType::DcdCDP: + [[fallthrough]]; + case batteryChargerType::DcdDCP: + // TODO: Uncomment when temerature ranges protection implemented + // LOG_INFO("USB current limit set to 1000mA"); + // setMaxBusCurrent(USBCurrentLimit::lim1000mA); + break; + } + } + + void actionIfChargerUnplugged() + { + if (Store::Battery::get().state == Store::Battery::State::Discharging) { + resetUSBCurrrentLimit(); + } + } + BaseType_t INTB_IRQHandler() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; diff --git a/module-bsp/bsp/battery-charger/battery_charger.hpp b/module-bsp/bsp/battery-charger/battery_charger.hpp index 500418c5898015fb6407d75d2931d36d0ef6ca71..3433e2d5608fe147a09f6d23cf187659d2e66e30 100644 --- a/module-bsp/bsp/battery-charger/battery_charger.hpp +++ b/module-bsp/bsp/battery-charger/battery_charger.hpp @@ -71,6 +71,10 @@ namespace bsp::battery_charger std::uint8_t getTopControllerINTSource(); + void setUSBCurrentLimit(batteryChargerType chargerType); + + void actionIfChargerUnplugged(); + BaseType_t INTB_IRQHandler(); extern "C" diff --git a/module-services/service-evtmgr/WorkerEvent.cpp b/module-services/service-evtmgr/WorkerEvent.cpp index 7b578b05c71501cfb681c98d41293a0f83211ded..5614f5341866f57510de0923936fd5e768a9a51a 100644 --- a/module-services/service-evtmgr/WorkerEvent.cpp +++ b/module-services/service-evtmgr/WorkerEvent.cpp @@ -101,6 +101,7 @@ bool WorkerEvent::handleMessage(uint32_t queueID) auto topINT = bsp::battery_charger::getTopControllerINTSource(); if (topINT & static_cast(bsp::battery_charger::topControllerIRQsource::CHGR_INT)) { bsp::battery_charger::getChargeStatus(); + bsp::battery_charger::actionIfChargerUnplugged(); auto message = std::make_shared(); service->bus.sendUnicast(std::move(message), service::name::evt_manager); battery_level_check::checkBatteryLevel(); @@ -207,11 +208,12 @@ bool WorkerEvent::handleMessage(uint32_t queueID) } if (queueID == static_cast(WorkerEventQueues::queueChargerDetect)) { - uint8_t notification; + std::uint8_t notification; if (!queue->Dequeue(¬ification, 0)) { return false; } LOG_DEBUG("USB charger type: %d", notification); + bsp::battery_charger::setUSBCurrentLimit(static_cast(notification)); } return true; diff --git a/module-services/service-evtmgr/doc/USB_current_selection.puml b/module-services/service-evtmgr/doc/USB_current_selection.puml new file mode 100644 index 0000000000000000000000000000000000000000..d5d13c8cb8df1a97e6678634e46f9c1e57c46b6a --- /dev/null +++ b/module-services/service-evtmgr/doc/USB_current_selection.puml @@ -0,0 +1,14 @@ +@startuml +(*) -> [initialization] "Limit 500mA" + +"USB plugged" -> "USB type recognition" +If "Detected USB type" then + ---> [SDP] "Limit 500mA" + else + -> [CDP] "Limit 1500mA" + else + --> [DCP] "Limit 1500mA" +Endif + +"USB unplugged" --> "Limit 500mA" +@enduml diff --git a/module-services/service-evtmgr/doc/USB_current_selection.svg b/module-services/service-evtmgr/doc/USB_current_selection.svg new file mode 100644 index 0000000000000000000000000000000000000000..5795dafc3a94e5da55ff4806abb5465fff30960c --- /dev/null +++ b/module-services/service-evtmgr/doc/USB_current_selection.svg @@ -0,0 +1,31 @@ +Limit 500mAUSB pluggedUSB type recognitionLimit 1500mAUSB unpluggedinitializationDetected USB typeSDPCDPDCP \ No newline at end of file diff --git a/module-services/service-evtmgr/doc/battery_charging.md b/module-services/service-evtmgr/doc/battery_charging.md new file mode 100644 index 0000000000000000000000000000000000000000..817886d4863898fb0dc734c0a2a7022c2dfa600e --- /dev/null +++ b/module-services/service-evtmgr/doc/battery_charging.md @@ -0,0 +1,44 @@ +# Battery Charger operation + +Pure Phone utilize MAX77818 driver for battery charging and power conversion between phone, battery and USB power input. This chip is also capable of SOC (State Of Charge) measurement which outputs battery level in percentage. This part of charger is known as Fuel Gauge (FG). FG is equipped in mathematical model of the battery to correctly estimate SOC based on voltage and current measurement, mainly utilizing integral of the current flowing in/out. This model has numeorus parameters which are tuned automatically across the lifetime of the battery. Those parameters are held in the driver's registers in volatile memory and has to be dumped to non-volatile memory in the system in order to preserve them. + +Due to learning process of the FG algorithm and integral-based measurement of SOC, the sufficient amount of time is needed for FG to track SOC correctly. Due to those reasons, phone is intended to work with a single battery in order to have right SOC information. Changing battery requires at least one cycle of charging and discharging to obtain real SOC estimate. + +## Charger state information + +Charger informs about its state change through interrupts. System can recognize several events signalized by the charger: +- SOC 1% change +- Charger plug in/out +- End of charging process +- Cell temperature range violation +- Brownout voltage level threshold violation + +## Fuel gauge parameters + +Initial fuel gauge parameters has to be provided on battery charger initialization. First run will give rough estimate based on battery voltage level. Then the learning process starts. Pure OS is holding parameters of the FG in a file in the `/sys/user` directory of the filesystem. Parameters are stored in file in case of two events: +- Battery charging process finish. +- Phone shutdown (clean shutdown). + +In case of phone crash, recent parameters as well as last SOC readout will be not preserved. Due to volatile memory of the charger, SOC information also has to be preserved. Value saved to file will be used as initial at the next boot. + +## Charging current selection algorithm + +Battery charger has no capabilities to recognize USB thus this operation has to be done on the side of main processor. +Default current limit of a charger to draw from USB port is 500mA. Due to several USB source types additional algorithm was introduced to rise this limit and provide faster battery charging. Depending on current output capabilities, several USB types could be distinguished: +- Standard downstream port (SDP) +- Charging downstream port (CDP) +- Dedicated charging port (DCP) + +PureOS limits the bus current to 500mA, in case of SDP, and to 1500mA in case of CDP and DCP. USB type recognition is part of the USB stack. + +![](USB_current_selection.svg "Current selection algorithm") + +## Charger cutoff due to temperature + +In order to prevent fast battery cell degradation, charging action needs to be prohibited in specific temperature range. For given cell valid charging range is 0-45 Cdeg. + +Current implementation cuts off charging when cell temperature is outside this range. Mechanism is interrupt-driven. In interrupt handler temperature measurement is sampled and appropriate interrupt range is set. The same action is done at the time of initialization. This way no cyclic sampling of the temperature has to be done. Algorithm could be described by following graph: + +![](charger_temperature_cutoff.svg "Charging cutoff") + +Additional 1 Cdeg hysteresis was introduced to prevent rapid changes in charging state. \ No newline at end of file diff --git a/module-services/service-evtmgr/doc/charger_temperature_cutoff.md b/module-services/service-evtmgr/doc/charger_temperature_cutoff.md deleted file mode 100644 index a4a3776430d7f70ba31ee36ba36a1911e683d28b..0000000000000000000000000000000000000000 --- a/module-services/service-evtmgr/doc/charger_temperature_cutoff.md +++ /dev/null @@ -1,9 +0,0 @@ -# Charger cutoff due to temperature - -In order to prevent fast battery cell degradation, charging action needs to be prohibited in specific temperature range. For given cell valid charging range is 0-45 Cdeg. - -Current implementation cuts off charging when cell temperature is outside this range. Mechanism is interrupt-driven. In interrupt handler temperature measurement is sampled and appropriate interrupt range is set. The same action is done at the time of initialization. This way no cyclic sampling of the temperature has to be done. Algorithm could be described by following graph: - -![](charger_temperature_cutoff.svg "Charging cutoff") - -Additional 1 Cdeg hysteresis was introduced to prevent rapid changes in charging state. \ No newline at end of file