M module-bsp/board/linux/battery-charger/battery_charger.cpp => module-bsp/board/linux/battery-charger/battery_charger.cpp +27 -0
@@ 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
M module-bsp/board/rt1051/bsp/battery-charger/MAX77818.hpp => module-bsp/board/rt1051/bsp/battery-charger/MAX77818.hpp +15 -1
@@ 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
{
M module-bsp/board/rt1051/bsp/battery-charger/battery_charger.cpp => module-bsp/board/rt1051/bsp/battery-charger/battery_charger.cpp +61 -14
@@ 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<std::uint8_t>(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<std::uint8_t>(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<CHG_DETAILS_01>(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;
M module-bsp/bsp/battery-charger/battery_charger.hpp => module-bsp/bsp/battery-charger/battery_charger.hpp +4 -0
@@ 71,6 71,10 @@ namespace bsp::battery_charger
std::uint8_t getTopControllerINTSource();
+ void setUSBCurrentLimit(batteryChargerType chargerType);
+
+ void actionIfChargerUnplugged();
+
BaseType_t INTB_IRQHandler();
extern "C"
M module-services/service-evtmgr/WorkerEvent.cpp => module-services/service-evtmgr/WorkerEvent.cpp +3 -1
@@ 101,6 101,7 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
auto topINT = bsp::battery_charger::getTopControllerINTSource();
if (topINT & static_cast<std::uint8_t>(bsp::battery_charger::topControllerIRQsource::CHGR_INT)) {
bsp::battery_charger::getChargeStatus();
+ bsp::battery_charger::actionIfChargerUnplugged();
auto message = std::make_shared<sevm::BatteryStatusChangeMessage>();
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<uint32_t>(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<bsp::battery_charger::batteryChargerType>(notification));
}
return true;
A module-services/service-evtmgr/doc/USB_current_selection.puml => module-services/service-evtmgr/doc/USB_current_selection.puml +14 -0
@@ 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
A module-services/service-evtmgr/doc/USB_current_selection.svg => module-services/service-evtmgr/doc/USB_current_selection.svg +31 -0
@@ 0,0 1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="282px" preserveAspectRatio="none" style="width:455px;height:282px;" version="1.1" viewBox="0 0 455 282" width="455px" zoomAndPan="magnify"><defs><filter height="300%" id="f12kntt60xghcw" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><ellipse cx="124" cy="252" fill="#000000" filter="url(#f12kntt60xghcw)" rx="10" ry="10" style="stroke:none;stroke-width:1.0;"/><rect fill="#FEFECE" filter="url(#f12kntt60xghcw)" height="33.9688" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="95" x="221.5" y="235"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="75" x="231.5" y="256.1387">Limit 500mA</text><rect fill="#FEFECE" filter="url(#f12kntt60xghcw)" height="33.9688" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="100" x="7" y="7"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="80" x="17" y="28.1387">USB plugged</text><rect fill="#FEFECE" filter="url(#f12kntt60xghcw)" height="33.9688" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="151" x="127.5" y="7"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="131" x="137.5" y="28.1387">USB type recognition</text><polygon fill="#FEFECE" filter="url(#f12kntt60xghcw)" points="203,82,215,94,203,106,191,94,203,82" style="stroke:#A80036;stroke-width:1.5;"/><rect fill="#FEFECE" filter="url(#f12kntt60xghcw)" height="33.9688" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="103" x="201.5" y="160"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="83" x="211.5" y="181.1387">Limit 1500mA</text><rect fill="#FEFECE" filter="url(#f12kntt60xghcw)" height="33.9688" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="116" x="325" y="160"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="96" x="335" y="181.1387">USB unplugged</text><!--MD5=[4efa0ceb8d869b0f02c9d84720c80e93]
+link start to Limit 500mA--><path d="M134.43,252 C151.43,252 186.24,252 216.2,252 " fill="none" id="start-to-Limit 500mA" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="221.34,252,212.34,248,216.34,252,212.34,256,221.34,252" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="66" x="144.75" y="246.2104">initialization</text><!--MD5=[04d55dba39d9a0eb7d067c791539fcd8]
+link USB plugged to USB type recognition--><path d="M107.19,24 C112.24,24 117.29,24 122.35,24 " fill="none" id="USB plugged-to-USB type recognition" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="127.4,24,118.4,20,122.4,24,118.4,28,127.4,24" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[36317cad5e94ba9bb427f52905fd2925]
+link USB type recognition to #8--><path d="M203,41.12 C203,51.92 203,66.08 203,76.88 " fill="none" id="USB type recognition-to-#8" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="203,81.89,207,72.89,203,76.89,199,72.89,203,81.89" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="106" x="76.7938" y="73.3494">Detected USB type</text><!--MD5=[8b72c5e5407284ef9afe374b980984ef]
+link #8 to Limit 500mA--><path d="M197.54,100.67 C183.16,116.32 146.62,161.12 166,194 C177.15,212.91 197.07,225.98 216.44,234.79 " fill="none" id="#8-to-Limit 500mA" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="221.35,236.94,214.6995,229.6756,216.7669,234.9412,211.5014,237.0085,221.35,236.94" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="23" x="167" y="181.2104">SDP</text><!--MD5=[b5dd0bbf25620b4138b920210523f638]
+link #8 to Limit 1500mA--><path d="M200.74,103.84 C198.8,113.34 197.16,128.6 203,140 C206.33,146.5 211.44,152.09 217.14,156.8 " fill="none" id="#8-to-Limit 1500mA" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="221.16,159.91,216.4833,151.2423,217.2033,156.8532,211.5924,157.5731,221.16,159.91" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="23" x="204" y="137.2104">CDP</text><!--MD5=[b5dd0bbf25620b4138b920210523f638]
+link #8 to Limit 1500mA--><path d="M208.66,100.82 C214.28,106.81 222.91,116.55 229,126 C234.87,135.1 240.21,145.84 244.35,155.04 " fill="none" id="#8-to-Limit 1500mA-1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="246.41,159.69,246.4415,149.8412,244.3939,155.1145,239.1206,153.0669,246.41,159.69" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="23" x="238" y="137.2104">DCP</text><!--MD5=[96c530ca5284f63feb7182093e557ed9]
+link USB unplugged to Limit 500mA--><path d="M357.98,194.02 C340.58,205.16 317.3,220.07 298.92,231.84 " fill="none" id="USB unplugged-to-Limit 500mA" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="294.35,234.76,304.0859,233.2729,298.5601,232.0627,299.7703,226.5368,294.35,234.76" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[83154e0bea18ea5a1fea1a60710e90e5]
+@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
+
+PlantUML version 1.2021.00(Sun Jan 10 11:25:05 CET 2021)
+(GPL source distribution)
+Java Runtime: OpenJDK Runtime Environment
+JVM: OpenJDK 64-Bit Server VM
+Default Encoding: UTF-8
+Language: pl
+Country: PL
+--></g></svg><
\ No newline at end of file
A module-services/service-evtmgr/doc/battery_charging.md => module-services/service-evtmgr/doc/battery_charging.md +44 -0
@@ 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.
+
+
+
+## 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:
+
+
+
+Additional 1 Cdeg hysteresis was introduced to prevent rapid changes in charging state.<
\ No newline at end of file
D module-services/service-evtmgr/doc/charger_temperature_cutoff.md => module-services/service-evtmgr/doc/charger_temperature_cutoff.md +0 -9
@@ 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:
-
-
-
-Additional 1 Cdeg hysteresis was introduced to prevent rapid changes in charging state.>
\ No newline at end of file