Files
cardboy/Firmware/main/src/bat_mon.cpp

118 lines
4.5 KiB
C++

//
// Created by Stepan Usatiuk on 02.03.2025.
//
#include "bat_mon.hpp"
#include <power_helper.hpp>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "i2c_global.hpp"
#include "shutdowner.hpp"
static i2c_master_dev_handle_t dev_handle;
BatMon& BatMon::get() {
static BatMon bat_mon;
return bat_mon;
}
static void start_pooler(void* arg) { static_cast<BatMon*>(arg)->pooler(); }
void WriteRegister(uint8_t reg, uint16_t value) {
uint8_t buf2[3];
buf2[0] = reg;
buf2[1] = value & 0xFF;
buf2[2] = value >> 8;
ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, buf2, sizeof(buf2), -1));
}
uint16_t ReadRegister(uint8_t reg) {
uint16_t buffer;
ESP_ERROR_CHECK(
i2c_master_transmit_receive(dev_handle, &reg, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 2, -1));
return buffer;
}
void WriteAndVerifyRegister(char RegisterAddress, int RegisterValueToWrite) {
int attempt = 0;
uint16_t RegisterValueRead;
do {
WriteRegister(RegisterAddress, RegisterValueToWrite);
vTaskDelay(1 / portTICK_PERIOD_MS);
RegisterValueRead = ReadRegister(RegisterAddress);
} while (RegisterValueToWrite != RegisterValueRead && attempt++ < 3);
}
static constexpr float RSense = 0.1; // 100mOhm
static constexpr uint16_t DesignCapMah = 180; // 100mOhm
constexpr float mahToCap(float mah) { return mah * (1000.0 / 5.0) * RSense; }
constexpr float capToMah(uint16_t cap) { return cap * (5.0 / 1000.0) / RSense; }
constexpr float regToCurrent(uint16_t reg) {
return static_cast<float>(static_cast<int16_t>(reg)) * 0.0015625f / RSense; // Convert to mA
}
constexpr uint16_t currentToReg(float current) { return static_cast<uint16_t>(current * RSense / 0.0015625f); }
constexpr float regToVoltage(uint16_t reg) {
return reg * 0.078125f * 0.001f; // Convert to volts
}
constexpr uint16_t voltageToReg(float voltage) {
return static_cast<uint16_t>(voltage / (0.078125f * 0.001f)); // Convert to register value
}
static constexpr uint16_t DesignCap = mahToCap(DesignCapMah);
static constexpr uint16_t IchgTerm = currentToReg(10);
static constexpr uint16_t VEmpty = 0b1001011001100001; // (3V/3.88V)
static constexpr uint16_t dQAcc = (DesignCap / 32);
BatMon::BatMon() {
ESP_ERROR_CHECK(i2c_master_bus_add_device(I2cGlobal::get().get_bus_handle(), &_dev_cfg, &dev_handle));
bool StatusPOR = ReadRegister(0x00) & 0x0002;
if (StatusPOR) // POR reset
{
printf("Gas gauge reset!\n");
while (ReadRegister(0x3D) & 1)
vTaskDelay(10 / portTICK_PERIOD_MS);
uint16_t HibCFG = ReadRegister(0xBA); // Store original HibCFG value
WriteRegister(0x60, 0x90); // Exit Hibernate Mode step 1
WriteRegister(0xBA, 0x0); // Exit Hibernate Mode step 2
WriteRegister(0x60, 0x0); // Exit Hibernate Mode step 3
WriteRegister(0x18, DesignCap); // Write DesignCap
WriteRegister(0x45, DesignCap / 32); // Write dQAcc
WriteRegister(0x1E, IchgTerm); // Write IchgTerm
WriteRegister(0x3A, VEmpty); // Write VEmpty
WriteRegister(0x46, dQAcc * 44138 / DesignCap); // Write dPAcc
WriteRegister(0xDB, 0x8000); // Write ModelCFG
// Poll ModelCFG.Refresh(highest bit), proceed to Step 4 when ModelCFG.Refresh = 0.
while (ReadRegister(0xDB) & 0x8000)
vTaskDelay(10 / portTICK_PERIOD_MS); // 10ms wait loop. Do not continue until ModelCFG.Refresh == 0.
WriteRegister(0xBA, HibCFG); // Restore Original HibCFG value
uint16_t Status = ReadRegister(0x00); // Read Status
WriteAndVerifyRegister(0x00, Status & 0xFFFD); // Write and Verify Status with POR bit cleared
}
xTaskCreate(&start_pooler, "BatMon", 2048, this, tskIDLE_PRIORITY, &_pooler_task);
}
void BatMon::pooler() {
while (true) {
uint8_t reg = 8;
uint16_t buffer;
_charge = capToMah(ReadRegister(0x05));
_current = regToCurrent(ReadRegister(0x0B));
_voltage = regToVoltage(ReadRegister(0x09));
PowerHelper::get().delay(10000, 1000);
if (_voltage < 3.0f) {
Shutdowner::get().shutdown();
}
}
}
float BatMon::get_voltage() const { return _voltage; }
float BatMon::get_charge() const { return _charge; }
float BatMon::get_current() const { return _current; }