@@ 77,6 77,7 @@ namespace bsp::battery_charger
VEmpty_REG = 0x3A,
TIMER_REG = 0x3E,
SHDNTIMER_REG = 0x3F,
+ CURVE = 0xb9,
QRtable30_REG = 0x42,
dQ_acc_REG = 0x45,
@@ 84,8 85,22 @@ namespace bsp::battery_charger
ConvgCfg_REG = 0x49,
VFRemCap_REG = 0x4A,
QH_REG = 0x4D,
- CHG_INT_REG = 0xb0,
- CHG_INT_OK = 0xb2
+
+ CHG_INT_REG = 0xB0,
+ CHG_INT_OK = 0xB2,
+ CHG_CNFG_00 = 0xB7,
+ CHG_CNFG_01 = 0xB8,
+ CHG_CNFG_02 = 0xB9,
+ CHG_CNFG_03 = 0xBA,
+ CHG_CNFG_04 = 0xBB,
+ CHG_CNFG_05 = 0xBC,
+ CHG_CNFG_06 = 0xBD,
+ CHG_CNFG_07 = 0xBE,
+ CHG_CNFG_09 = 0xC0,
+ CHG_CNFG_10 = 0xC1,
+ CHG_CNFG_11 = 0xC2,
+ CHG_CNFG_12 = 0xC3
+
};
// STATUS register bits
@@ 11,6 11,8 @@
#include "drivers/i2c/DriverI2C.hpp"
#include <purefs/filesystem_paths.hpp>
#include <utility>
+#include <bitset>
+#include <fstream>
namespace bsp::battery_charger
{
@@ 18,32 20,63 @@ namespace bsp::battery_charger
{
constexpr std::uint32_t i2cSubaddresSize = 1;
- const auto cfgFile = purefs::dir::getCurrentOSPath() / "batteryAdjustementConfig.cfg";
- const auto cfgFilePrev = purefs::dir::getCurrentOSPath() / "batteryAdjustementConfig_old.cfg";
+ const auto cfgFile = purefs::dir::getCurrentOSPath() / "batteryFuelGaugeConfig.cfg";
+ constexpr auto registersToStore = 0xFF + 1;
- constexpr std::uint16_t BATT_SERVICE_AVG_CURRENT_PERIOD =
- 0x00; //< 0.1758 ms * 2^(2 + BATT_SERVICE_AVG_CURRENT_PERIOD) == 700ms
- constexpr std::uint16_t BATT_SERVICE_AVG_CELL_VOLTAGE_PERIOD =
- 0x00; //< 0.1758 ms * 2^(6 + BATT_SERVICE_AVG_CELL_VOLTAGE_PERIOD) == 11.25 s
- constexpr std::uint16_t BATT_SERVICE_AVG_MIXING_PERIOD =
- 0x0D; //< 0.1758 ms * 2^(5 + BATT_SERVICE_AVG_MIXING_PERIOD) == 12.8 h
- constexpr std::uint16_t BATT_SERVICE_AVG_TEMP_PERIOD =
- 0x01; //< 0.1758 ms * 2^(11 + BATT_SERVICE_AVG_TEMP_PERIOD) == 12 min
- constexpr std::uint16_t BATT_SERVICE_AVG_NEMPTY_PERIOD = 0x00;
+ constexpr std::uint16_t ENABLE_ALL_IRQ_MASK = 0xF8;
+ constexpr std::uint8_t UNLOCK_CHARGER = 0x3 << 2;
- constexpr auto ENABLE_ALL_IRQ_MASK = 0xf8;
+ constexpr std::uint8_t VSYS_MIN = 0x80; // 3.6V
+ constexpr std::uint8_t CHARGE_TARGET_VOLTAGE = 0x1D; // 4.35V
- constexpr std::uint16_t nominalCapacitymAh = 3000;
+ constexpr std::uint8_t MAX_CHARGE_CURRENT = 0x30; // 1600mA -> 1C
+ constexpr std::uint8_t FAST_CHARGE_CURRENT = 0x0A; // 500mA
- constexpr std::uint8_t fullyChargedPercent = 100;
- constexpr std::uint8_t DischargedPercent = 15;
+ constexpr std::uint16_t nominalCapacitymAh = 1600;
constexpr std::uint8_t maxTemperatureDegrees = 50;
constexpr std::uint8_t minTemperatureDegrees = 5;
- constexpr std::uint16_t maxVoltagemV = 4200;
+ constexpr std::uint16_t maxVoltagemV = 4350;
constexpr std::uint16_t minVoltagemV = 3600;
+ constexpr auto currentSenseGain = 0.15625; // mA
+ constexpr auto voltageSenseGain = 0.078125; // mV
+
+ // NTC calibration values
+ constexpr std::uint16_t temperatureConversionGain = 0xEE56;
+ constexpr std::uint16_t temperatureConversionOffset = 0x1DA4;
+
+ namespace fuel_gauge_params
+ {
+ /// Parameters calculated in in MAXIM EVKIT software
+ /// Initial parameters for fuel gauge battery model
+
+ constexpr std::uint16_t LearnCFG = 0x2602;
+ constexpr std::uint16_t FilterCFG = 0xCEA4;
+ constexpr std::uint16_t MiscCFG = 0x01D0;
+ constexpr std::uint16_t RelaxCFG = 0x2039;
+ constexpr std::uint16_t RCOMP0 = 0x0070;
+ constexpr std::uint16_t TempCo = 0x263D;
+ constexpr std::uint16_t QResidual00 = 0x2280;
+ constexpr std::uint16_t QResidual10 = 0x1000;
+ constexpr std::uint16_t QResidual20 = 0x0681;
+ constexpr std::uint16_t QResidual30 = 0x0682;
+ /// 1600mAh initial battery capacity
+ constexpr std::uint16_t FullCAPNom = 0x0C80;
+ constexpr std::uint16_t FullCAPRep = 0x0C80;
+ constexpr std::uint16_t DesignCap = 0x0C80;
+ constexpr std::uint16_t dQacc = 0x00C8;
+ constexpr std::uint16_t dPacc = 0x0C80;
+ /// Charge termination current = 150mA
+ constexpr std::uint16_t ICHGTerm = 0x03C0;
+ /// Empty battery = 3V
+ constexpr std::uint16_t V_empty = 0x965A;
+ /// Fully charged threshold = 90%
+ constexpr std::uint16_t FullSOCthr = 0x5A00;
+
+ } // namespace fuel_gauge_params
+
std::shared_ptr<drivers::DriverI2C> i2c;
std::shared_ptr<drivers::DriverGPIO> gpio;
@@ 125,97 158,121 @@ namespace bsp::battery_charger
return std::make_pair(status, value);
}
- batteryRetval loadConfiguration()
+ batteryRetval unlockProtectedChargerRegisters()
{
- auto fd = std::fopen(cfgFile.c_str(), "r");
- if (fd == nullptr) {
- LOG_WARN("Configuration file [%s] could not be opened. Trying to open file [%s]",
- cfgFile.c_str(),
- cfgFilePrev.c_str());
- fd = std::fopen(cfgFilePrev.c_str(), "r");
- if (fd == nullptr) {
- LOG_WARN("Configuration file [%s] could not be opened.", cfgFilePrev.c_str());
- return batteryRetval::ChargerError;
- }
+ if (chargerWrite(Registers::CHG_CNFG_06, UNLOCK_CHARGER) != kStatus_Success) {
+ LOG_ERROR("Charger registers unlock failed!");
+ return batteryRetval::ChargerError;
}
+ return batteryRetval::OK;
+ }
- std::uint16_t regValue = 0;
- for (auto i = 0; i < 0xff; ++i) {
- if (std::fread(®Value, sizeof(regValue), 1, fd) != sizeof(regValue)) {
- LOG_ERROR("Reading register 0x%x failed.", i);
- std::fclose(fd);
- return batteryRetval::ChargerError;
- }
-
- if (fuelGaugeWrite(static_cast<Registers>(i), regValue) != kStatus_Success) {
- LOG_ERROR("Writing register 0x%x failed.", i);
- std::fclose(fd);
- return batteryRetval::ChargerError;
- }
+ batteryRetval lockProtectedChargerRegisters()
+ {
+ if (chargerWrite(Registers::CHG_CNFG_06, 0) != kStatus_Success) {
+ LOG_ERROR("Charger registers lock failed!");
+ return batteryRetval::ChargerError;
}
-
- std::fclose(fd);
return batteryRetval::OK;
}
- batteryRetval storeConfiguration()
+ void configureBatteryCharger()
{
- // TODO:M.P procedure below seems to crash system, it should be fixed.
- if (ff_rename(cfgFile.c_str(), cfgFilePrev.c_str(), false) != 0) {
- LOG_ERROR("Could not move configuration file");
- return batteryRetval::ChargerError;
+ 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");
}
- auto fd = std::fopen(cfgFile.c_str(), "w");
- if (fd == nullptr) {
- LOG_ERROR("Could not open configuration file");
+ value = MAX_CHARGE_CURRENT;
+ if (chargerWrite(Registers::CHG_CNFG_09, value) != kStatus_Success) {
+ LOG_ERROR("Maximum charge current write fail");
+ }
+
+ value = FAST_CHARGE_CURRENT;
+ if (chargerWrite(Registers::CHG_CNFG_02, value) != kStatus_Success) {
+ LOG_ERROR("Fast charge current write fail");
+ }
+
+ lockProtectedChargerRegisters();
+ }
+
+ batteryRetval configureFuelGaugeBatteryModel()
+ {
+ int status = fuelGaugeWrite(Registers::LearnCFG_REG, fuel_gauge_params::LearnCFG);
+ status |= fuelGaugeWrite(Registers::FilterCFG_REG, fuel_gauge_params::FilterCFG);
+ status |= fuelGaugeWrite(Registers::MiscCFG_REG, fuel_gauge_params::MiscCFG);
+ status |= fuelGaugeWrite(Registers::RelaxCFG_REG, fuel_gauge_params::RelaxCFG);
+ status |= fuelGaugeWrite(Registers::RCOMP0_REG, fuel_gauge_params::RCOMP0);
+ status |= fuelGaugeWrite(Registers::TempCo_REG, fuel_gauge_params::TempCo);
+ status |= fuelGaugeWrite(Registers::QRtable00_REG, fuel_gauge_params::QResidual00);
+ status |= fuelGaugeWrite(Registers::QRtable10_REG, fuel_gauge_params::QResidual10);
+ status |= fuelGaugeWrite(Registers::QRtable20_REG, fuel_gauge_params::QResidual20);
+ status |= fuelGaugeWrite(Registers::QRtable30_REG, fuel_gauge_params::QResidual30);
+ status |= fuelGaugeWrite(Registers::FullCAPNom_REG, fuel_gauge_params::FullCAPNom);
+ status |= fuelGaugeWrite(Registers::FullCapRep_REG, fuel_gauge_params::FullCAPRep);
+ status |= fuelGaugeWrite(Registers::DesignCap_REG, fuel_gauge_params::DesignCap);
+ status |= fuelGaugeWrite(Registers::dQ_acc_REG, fuel_gauge_params::dQacc);
+ status |= fuelGaugeWrite(Registers::dP_acc_REG, fuel_gauge_params::dPacc);
+ status |= fuelGaugeWrite(Registers::ICHGTERM_REG, fuel_gauge_params::ICHGTerm);
+ status |= fuelGaugeWrite(Registers::VEmpty_REG, fuel_gauge_params::V_empty);
+ status |= fuelGaugeWrite(Registers::FullSOCthr_REG, fuel_gauge_params::FullSOCthr);
+
+ if (status != kStatus_Success) {
+ LOG_ERROR("configureFuelGaugeBatteryModel failed.");
return batteryRetval::ChargerError;
}
+ return batteryRetval::OK;
+ }
- for (unsigned int i = 0; i < 0xff; ++i) {
- auto regVal = fuelGaugeRead(static_cast<Registers>(i));
- if (regVal.first != kStatus_Success) {
- LOG_ERROR("Reading register 0x%x failed.", i);
- std::fclose(fd);
+ batteryRetval loadConfiguration()
+ {
+ std::ifstream file(cfgFile.c_str(), std::ios::binary | std::ios::in);
+ if (!file.is_open()) {
+ LOG_WARN("Configuration file [%s] could not be opened. Loading initial configuration.",
+ cfgFile.c_str());
+ if (configureFuelGaugeBatteryModel() == batteryRetval::OK) {
+ return batteryRetval::OK;
+ }
+ else {
return batteryRetval::ChargerError;
}
+ }
- if (std::fwrite(®Val.second, sizeof(regVal.second), 1, fd) != sizeof(regVal.second)) {
- LOG_ERROR("Storing register 0x%x failed.", i);
- std::fclose(fd);
- std::remove(cfgFile.c_str());
+ std::uint16_t regVal;
+ for (auto i = 0; i < registersToStore; ++i) {
+ file.read(reinterpret_cast<char *>(®Val), sizeof(std::uint16_t));
+ if (fuelGaugeWrite(static_cast<Registers>(i), regVal) != kStatus_Success) {
+ LOG_ERROR("Writing register 0x%x failed.", i);
+ file.close();
return batteryRetval::ChargerError;
}
}
+ file.close();
- std::fclose(fd);
return batteryRetval::OK;
}
- batteryRetval setAvgCalcPeriods()
+ batteryRetval storeConfiguration()
{
- std::uint16_t regVal = 0;
- regVal |= (BATT_SERVICE_AVG_CURRENT_PERIOD << 0);
- regVal |= (BATT_SERVICE_AVG_CELL_VOLTAGE_PERIOD << 4);
- regVal |= (BATT_SERVICE_AVG_MIXING_PERIOD << 7);
- regVal |= (BATT_SERVICE_AVG_TEMP_PERIOD << 11);
- regVal |= (BATT_SERVICE_AVG_NEMPTY_PERIOD << 14);
-
- if (fuelGaugeWrite(Registers::FilterCFG_REG, regVal) != kStatus_Success) {
- LOG_ERROR("setAvgCalcPeriods failed.");
+ std::ofstream file(cfgFile.c_str(), std::ios::binary | std::ios::out);
+ if (!file.is_open()) {
+ LOG_WARN("Configuration file [%s] could not be opened.", cfgFile.c_str());
return batteryRetval::ChargerError;
}
- return batteryRetval::OK;
- }
-
- batteryRetval setNominalBatteryCapacity(std::uint16_t capacity)
- {
- std::uint16_t regVal = capacity * 2;
- if (fuelGaugeWrite(Registers::DesignCap_REG, regVal) != kStatus_Success) {
- LOG_ERROR("setNominalBatteryCapacity failed.");
- return batteryRetval::ChargerError;
+ for (unsigned int i = 0; i < registersToStore; ++i) {
+ auto regVal = fuelGaugeRead(static_cast<Registers>(i));
+ if (regVal.first != kStatus_Success) {
+ LOG_ERROR("Reading register 0x%x failed.", i);
+ file.close();
+ return batteryRetval::ChargerError;
+ }
+ file.write(reinterpret_cast<const char *>(®Val.second), sizeof(std::uint16_t));
}
+ file.close();
+
return batteryRetval::OK;
}
@@ 254,24 311,38 @@ namespace bsp::battery_charger
return batteryRetval::OK;
}
- batteryRetval enableFuelGuageIRQs()
+ batteryRetval fillConfig2RegisterValue()
{
- std::uint16_t regVal = static_cast<std::uint16_t>(CONFIG2::dSOCen);
+ std::uint16_t regVal = static_cast<std::uint16_t>(CONFIG2::dSOCen) | // SOC 1% change alert
+ static_cast<std::uint16_t>(CONFIG2::OCVQen); // Enable automatic empty compensation
if (fuelGaugeWrite(Registers::CONFIG2_REG, regVal) != kStatus_Success) {
- LOG_ERROR("enableFuelGuageIRQs failed.");
+ LOG_ERROR("fillConfig2RegisterValue failed.");
return batteryRetval::ChargerError;
}
return batteryRetval::OK;
}
- batteryRetval configureAlerts()
+ batteryRetval fillConfigRegisterValue()
{
- std::uint16_t regVal = static_cast<std::uint16_t>(CONFIG::Aen);
+ std::uint16_t regVal = static_cast<std::uint16_t>(CONFIG::Aen) // Enable alerts
+ | static_cast<std::uint16_t>(CONFIG::Ten) // Enable tepreature conversion
+ | static_cast<std::uint16_t>(CONFIG::ETHRM); // External thermistor
if (fuelGaugeWrite(Registers::CONFIG_REG, regVal) != kStatus_Success) {
- LOG_ERROR("configureAlerts failed.");
+ LOG_ERROR("fillConfigRegisterValue failed.");
+ return batteryRetval::ChargerError;
+ }
+
+ return batteryRetval::OK;
+ }
+
+ batteryRetval configureTemperatureMeasurement()
+ {
+ if ((fuelGaugeWrite(Registers::TGAIN_REG, temperatureConversionGain) &
+ fuelGaugeWrite(Registers::TOFF_REG, temperatureConversionOffset)) != kStatus_Success) {
+ LOG_ERROR("configureTemperatureMeasurement failed.");
return batteryRetval::ChargerError;
}
@@ 313,6 384,45 @@ namespace bsp::battery_charger
gpio->EnableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::BATTERY_CHARGER_INOKB_PIN));
gpio->EnableInterrupt(1 << static_cast<std::uint32_t>(BoardDefinitions::BATTERY_CHARGER_INTB_PIN));
}
+
+ int getCellTemperature()
+ {
+ auto value = fuelGaugeRead(Registers::TEMP_REG);
+ int temperature = value.second >> 8;
+ if (value.second & 0x8000) {
+ temperature *= -1;
+ }
+ LOG_INFO("Battery cell temperature = %d Cdeg.", temperature);
+ return temperature;
+ }
+
+ int getCurrentMeasurement()
+ {
+ auto value = fuelGaugeRead(Registers::Current_REG);
+ int current;
+ // 2's compliment into decimal
+ if (value.second & 0x8000) {
+ // negative numbers
+ std::bitset<16> currentBitset = std::bitset<16>(value.second - 1);
+ currentBitset.flip();
+ current =
+ static_cast<int>((static_cast<std::uint16_t>(currentBitset.to_ulong()) * -1) * currentSenseGain);
+ }
+ else {
+ // positive numbers
+ current = static_cast<int>(value.second * currentSenseGain);
+ }
+ LOG_INFO("Battery current measurement = %d mA.", current);
+ return current;
+ }
+
+ int getCellVoltage()
+ {
+ auto value = fuelGaugeRead(Registers::VCELL_REG);
+ int voltage = value.second * voltageSenseGain;
+ LOG_INFO("Battery cell voltage measurement = %d mV.", voltage);
+ return voltage;
+ }
} // namespace
int init(xQueueHandle queueHandle)
@@ 324,21 434,22 @@ namespace bsp::battery_charger
IRQQueueHandle = queueHandle;
+ configureBatteryCharger();
+
// check Power-On reset bit
std::uint16_t status = fuelGaugeRead(Registers::STATUS_REG).second;
if (status & static_cast<std::uint16_t>(STATUS::POR)) {
- LOG_INFO("Initializing battery charger");
+ LOG_INFO("Initializing battery fuel gauge model.");
loadConfiguration();
- setAvgCalcPeriods();
- setNominalBatteryCapacity(nominalCapacitymAh);
- setChargingDischargingThresholds(fullyChargedPercent, DischargedPercent);
- setTemperatureThresholds(maxTemperatureDegrees, minTemperatureDegrees);
- setServiceVoltageThresholds(maxVoltagemV, minVoltagemV);
}
- configureAlerts();
- enableFuelGuageIRQs();
+ setTemperatureThresholds(maxTemperatureDegrees, minTemperatureDegrees);
+ setServiceVoltageThresholds(maxVoltagemV, minVoltagemV);
+
+ fillConfigRegisterValue();
+ fillConfig2RegisterValue();
+ configureTemperatureMeasurement();
StateOfCharge level = getBatteryLevel();
bool charging = getChargeStatus();
@@ 346,7 457,6 @@ namespace bsp::battery_charger
clearAllIRQs();
enableTopIRQs();
-
IRQPinsInit();
return 0;