Utility library for testing and calibrating the BQ34Z100-G1 fuel gauge IC.

Dependencies:   BQ34Z100G1

Utils library for our BQ34Z100G1 driver. See https://os.mbed.com/users/MultipleMonomials/code/BQ34Z100G1/wiki/Setup-and-Calibration-Guide

New releases of this code have moved to GitHub: https://github.com/USCRPL/BQ34Z100G1-Utils

Revision:
0:fcd2c91c4626
diff -r 000000000000 -r fcd2c91c4626 BQ34Z100G1-Utils.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BQ34Z100G1-Utils.cpp	Sun Feb 07 14:21:20 2021 -0800
@@ -0,0 +1,439 @@
+/*
+    USC RPL HAMSTER v2.3 BQ34Z100 Test Suite
+    Contributors: Arpad Kovesdy
+*/
+#include "BQ34Z100G1-Utils.h"
+
+#include <cinttypes>
+
+namespace mbed
+{
+    FileHandle *mbed_override_console(int)
+    {
+        static BufferedSerial serial(USBTX, USBRX, 115200);
+        return &serial;
+    }
+}
+
+// helper function to print a bitfield prettily.
+void printBitfield(uint16_t value, const char* name, const char** bitDescriptions)
+{
+	printf("\n");
+	printf("%s: 0x%" PRIx16 " (0b", name, value);
+	for (int i = 15; i >= 0; i--)
+	{
+		printf("%i", (value >> i) & 1);
+	}
+	printf(")\n");
+
+	const size_t maxBit = sizeof(value) * 8 - 1;
+	for (int i = maxBit; i>=0; i--)
+	{
+		// Description array is in reverse order numerically
+		char const * description = bitDescriptions[maxBit - i];
+
+		uint8_t bitValue = (value >> i) & 1;
+		if(description != nullptr)
+		{
+			printf("- %s: %" PRIu8 "\n", description, bitValue);
+		}
+	}
+}
+
+void BQ34Utils::outputStatus()
+{
+    uint16_t status_code = soc.getStatus();
+
+    // Descriptions for each bit of the status bytes
+    const char* statusBitDescs[] = {
+    		nullptr,
+    		"Full Access Sealed (FAS)",
+    		"Sealed (SS)",
+    		"Calibration Enabled (CALEN)",
+    		"Coulomb Counter Calibrating (CCA)",
+    		"Board Calibration Active (BCA)",
+    		"Valid Data Flash Checksum (CSV)",
+    		nullptr,
+    		nullptr,
+    		nullptr,
+    		"Full Sleep Mode (FULLSLEEP)",
+    		"Sleep Mode (SLEEP)",
+    		"Impedance Track using Constant Power (LDMD)",
+    		"Ra Updates Disabled (RUP_DIS)",
+    		"Voltage OK for Qmax Updates (VOK)",
+    		"Qmax Updates Enabled (QEN)"
+    };
+
+    printBitfield(status_code, "Control Status", statusBitDescs);
+
+	// Descriptions for each bit of the flags bytes
+	const char* flagsBitDescs[] = {
+			"Overtemperature in Charge (OTC)",
+			"Overtemperature in Discharge (OTD)",
+			"High Battery Voltage (BATHI)",
+			"Low Battery Voltage (BATLOW)",
+			"Charge Inhibited (CHG_INH)",
+			"Charging Not Allowed (XCHG)",
+			"Full Charge (FC)",
+			"Charge Allowed (CHG)",
+			"Open Circuit Voltage Measurement Performed (OCVTAKEN)",
+			nullptr,
+			nullptr,
+			"Update Cycle Needed (CF)",
+			nullptr,
+			"SoC Threshold 1 Reached (SOC1)",
+			"SoC Threshold Final Reached (SOCF)",
+			"Discharge Detected (DSG)"
+	};
+	const char* flagsBBitDescs[] = {
+			"State of Health Calc Active (SOH)",
+			"LiFePO4 Relax Enabled (LIFE)",
+			"Waiting for Depth of Discharge Measurement (FIRSTDOD)",
+			nullptr,
+			nullptr,
+			"Depth of Discharge at End of Charge Updated (DODEOC)",
+			"Remaining Capacity Changed (DTRC)",
+			nullptr,
+			nullptr,
+			nullptr,
+			nullptr,
+			nullptr,
+			nullptr,
+			nullptr,
+			nullptr,
+			nullptr
+	};
+
+	std::pair<uint16_t, uint16_t> flags = soc.getFlags();
+	printBitfield(flags.first, "Flags", flagsBitDescs);
+	printBitfield(flags.second, "FlagsB", flagsBBitDescs);
+
+
+    uint8_t updateStatus = soc.getUpdateStatus();
+    printf("Update status: 0x%" PRIx8 "\n", updateStatus);
+}
+
+void BQ34Utils::sensorReset()
+{
+    printf("Resetting BQ34Z100 Sensor.\r\n");
+    soc.reset();
+
+    uint16_t deviceType = soc.readDeviceType();
+    if(deviceType == 0x100)
+    {
+        printf("BQ34Z100 detected\r\n");
+    }
+    else
+    {
+        printf("Error communicating with BQ34Z100.  Expected DEVICE_TYPE = 0x100, got 0x%" PRIx16 "\n", deviceType);
+        return;
+    }
+
+    printf("Chip reads as FW_VERSION 0x%" PRIx16 ", HW version 0x%" PRIx16 "\r\n", soc.readFWVersion(), soc.readHWVersion());
+}
+
+
+void BQ34Utils::displayData()
+{
+    ThisThread::sleep_for(10ms); //Let the device catch up
+    printf("SOC: %d%%\r\n", soc.getSOC());
+    printf("Voltage: %d mV\r\n", soc.getVoltage());
+    printf("Current: %d mA\r\n", soc.getCurrent());
+    printf("Remaining: %d mAh\r\n", soc.getRemaining());
+    printf("Temperature: %.1f C\r\n", soc.getTemperature());
+    printf("Max Error: %d%%\r\n", soc.getError());
+    printf("Serial No: %d\r\n", soc.getSerial());
+    printf("CHEM ID: %" PRIx16 "\r\n", soc.getChemID());
+}
+
+void BQ34Utils::testICConnection()
+{
+    printf("Testing Electrical Connection\r\n");
+    int status = soc.getStatus();
+    printf("Status: %d\r\n\r\n", status);
+}
+
+void BQ34Utils::startCal()
+{
+    printf("Starting calibration mode\r\n");
+    soc.enableCal();
+    soc.enterCal();
+}
+
+void BQ34Utils::stopCal()
+{
+    printf("Stopping calibration mode\r\n");
+    soc.exitCal();
+}
+
+void BQ34Utils::startIt()
+{
+    printf("Enabling Impedance Tracking\r\n");
+    soc.ITEnable();
+}
+
+//Outputs an integer of the length provided starting from the given index in flashBytes
+//Provide pointer to first element (array pointer to flashbytes)
+void BQ34Utils::outputFlashInt(uint8_t* flash, int index, int len)
+{
+    if (index > 31) index = index % 32;
+    unsigned int result = 0;
+    for (int i = 0; i<len; i++)
+    {
+        //result = result | ((uint32_t)flash[index+i] << 8*len);
+        result |= ((uint32_t)flash[index+i] << 8*(len-i-1));
+    }
+    printf("%d", result);
+}
+
+void BQ34Utils::writeSettings()
+{
+	if(soc.getVoltage() <= FLASH_UPDATE_OK_VOLT * CELLCOUNT)
+	{
+		printf("WARNING: Measured voltage is below FLASH_UPDATE_OK_VOLT, flash memory writes may not go through.  However this is expected if voltage has not been calibrated yet.");
+	}
+
+    soc.unseal();
+    printf("Starting overwrite of sensor settings\r\n");
+    uint8_t* flashStore = soc.getFlashBytes(); //Get address of array for later
+
+    //Page 48
+    soc.changePage(48, 0); //calls ChangePage from BQ34Z100 editing page 48 from datasheet
+    soc.readFlash();
+
+    //declares old subclass properties as per BQ34Z100 function commands
+    printf("Old design capacity:");
+    outputFlashInt(flashStore, 11, 2);
+    printf("\r\n");
+    printf("Old design energy:");
+    outputFlashInt(flashStore, 13, 2);
+    printf("\r\n");
+
+    //replaces the old subclass properties with new ones as per BQ34Z100 function commands
+    soc.changePage48();
+    printf("New design capacity:");
+    outputFlashInt(flashStore, 11, 2);
+    printf("\r\n");
+    printf("New design energy:");
+    outputFlashInt(flashStore, 13, 2);
+    printf("\r\n");
+
+    //Page 64
+    soc.changePage(64, 0); //calls ChangePage from BQ34Z100 editing page 48 from datasheet
+    soc.readFlash();
+
+    //declares old subclass properties as per BQ34Z100 function commands
+    printf("Old Pack Configuration:");
+    outputFlashInt(flashStore, 0, 2);
+    printf("\r\n");
+    printf("Old LED Config:");
+    outputFlashInt(flashStore, 4, 1);
+    printf("\r\n");
+    printf("Old Cell Count:");
+    outputFlashInt(flashStore, 7, 1);
+    printf("\r\n");
+
+    //replaces the old subclass properties with new ones as per BQ34Z100 function commands
+    soc.changePage64();
+    printf("New Pack Configuration:");
+    outputFlashInt(flashStore, 0, 2);
+    printf("\r\n");
+    printf("New LED Config:");
+    outputFlashInt(flashStore, 4, 1);
+    printf("\r\n");
+    printf("New Cell Count:");
+    outputFlashInt(flashStore, 7, 1);
+    printf("\r\n");
+
+    //Page 80
+
+    soc.changePage(80, 0); //calls ChangePage from BQ34Z100 editing page 48 from datasheet
+    soc.readFlash();
+    //declares old subclass properties as per BQ34Z100 function commands
+    printf("Old Load Select:");
+    outputFlashInt(flashStore, 0, 1);
+    printf("\r\n");
+    printf("Old Load Mode:");
+    outputFlashInt(flashStore, 1, 1);
+    printf("\r\n");
+
+    soc.changePage(80, 53);
+    soc.readFlash();
+    printf("Old Cell Terminate Voltage:");
+    outputFlashInt(flashStore, 53, 2);
+    printf("\r\n");
+
+    //replaces the old subclass properties with new ones as per BQ34Z100 function commands
+    soc.changePage80();
+    soc.changePage(80, 0);
+    soc.readFlash();
+    printf("New Load Select:");
+    outputFlashInt(flashStore, 0, 1);
+    printf("\r\n");
+    printf("New Load Mode:");
+    outputFlashInt(flashStore, 1, 1);
+    printf("\r\n");
+    printf("Res Current:");
+    outputFlashInt(flashStore, 10, 2);
+    printf("\r\n");
+    soc.changePage(80, 53);
+    soc.readFlash();
+    printf("New Cell Terminate Voltage:");
+    outputFlashInt(flashStore, 53, 2);
+    printf("\r\n");
+
+    //Page 82
+    soc.changePage(82, 0); //calls ChangePage from BQ34Z100 editing page 48 from datasheet
+    soc.readFlash();
+
+    //declares old subclass properties as per BQ34Z100 function commands
+    printf("Old QMax0:");
+    outputFlashInt(flashStore, 0, 2);
+    printf("\r\n");
+
+    //replaces the old subclass properties with new ones as per BQ34Z100 function commands
+    soc.changePage82();
+    printf("New QMax0:");
+    outputFlashInt(flashStore, 0, 2);
+    printf("\r\n");
+
+    //Print the updatestatus
+    //0x02 = Qmax and Ra data are learned, but Impedance Track is not enabled.
+    //This should be the standard setting for a Golden Image File
+    //0x04 = Impedance Track is enabled but Qmax and Ra data are not yet learned.
+    //0x05 = Impedance Track is enabled and only Qmax has been updated during a learning cycle.
+    //0x06 = Impedance Track is enabled. Qmax and Ra data are learned after a successful learning
+    //cycle. This should be the operation setting for end equipment.
+    printf("UPDATE STATUS:");
+    outputFlashInt(flashStore, 4, 1);
+    printf("\r\n");
+}
+
+void BQ34Utils::calibrateVoltage ()
+{
+
+    printf("Enter voltage across the pack: ");
+    float batVoltage=-1;
+    scanf("%f", &batVoltage);
+    printf("Hand measured voltage: %f\r\n\n", batVoltage);
+    //printf("Enter ACTUAL battery voltage in volts\r\n");
+    //pc.scanf("%f", &batVoltage);
+    uint16_t batVoltage_mv = (uint16_t)(batVoltage*1000.0f);
+    printf("\r\nCurrent Monitor Bus Voltage Battery Voltage (V): %f\r\n", batVoltage);
+
+    printf("New Flash Voltage: %d\r\n", soc.calibrateVoltage(batVoltage_mv));
+}
+
+//Input current in A
+void BQ34Utils::calibrateCurrent()
+{
+    // first reset back to theoretical value
+	soc.setSenseResistor();
+
+    // reset sensor so it picks up new settings
+	soc.reset();
+	ThisThread::sleep_for(200ms);
+
+    // Now calibrate more exactly using hand-measured value
+	printf("Enter current through the sense resistor in A: ");
+	float current=-1;
+	scanf("%f", &current);
+	printf("Hand measured current%f:\r\n\n", current);
+
+	printf("Calibrating current shunt\r\n\r\n");
+	int16_t current_int = (int16_t)(current*1000.0f);
+	soc.calibrateShunt(current_int);
+}
+
+
+void BQ34Utils::readVoltageCurrent()
+{
+    Timer chargeTimer;
+    chargeTimer.start();
+	
+    printf("Time (s),\tVoltage (V),\tCurrent (mA)\r\n");
+
+	while (true) {
+		int voltage = soc.getVoltage();
+		int current = soc.getCurrent();
+		printf("%f,\t%d,\t%d\r\n", std::chrono::duration_cast<std::chrono::duration<float>>(chargeTimer.elapsed_time()).count(), voltage, current);
+		ThisThread::sleep_for(100ms);
+	}
+}
+
+void BQ34Utils::resetVoltageCalibration()
+{
+    soc.setVoltageDivider(RESETVOLTAGE);
+    printf("\r\n\nVoltage divider calibration reset.\r\n");
+}
+
+void BQ34Utils::testFloatConversion()
+{
+	// test data from https://e2e.ti.com/support/power-management/f/196/p/551252/2020286?tisearch=e2e-quicksearch&keymatch=xemics#2020286
+	float valueFloat = .8335f;
+	uint32_t valueXemics = 0x80555E9E;
+
+	// try converting float to xemics
+	uint32_t convertedValue = BQ34Z100::floatToXemics(valueFloat);
+	printf("Converted value: 0x%" PRIx32 "\n", convertedValue);
+
+	// try converting xemics to float
+	float convertedFloat = BQ34Z100::xemicsToFloat(valueXemics);
+	printf("Converted float: %f\n", convertedFloat);
+
+	printf("Expected default CC Gain: 0x%" PRIx32 "\n", BQ34Z100::floatToXemics(0.4768));
+	printf("Expected default CC Delta: 0x%" PRIx32 "\n", BQ34Z100::floatToXemics(567744.56));
+
+}
+
+
+int main()
+{
+    //declare the test harness
+    BQ34Utils harness;
+
+    while(1){
+        int test=-1;
+        printf("\r\n\nBattery State of Charge Sensor Test Suite:\r\n");
+
+        //Menu for each test item
+        printf("Select a test: \n\r");
+        printf("1.  Reset Sensor (Restart)\r\n");
+        printf("2.  Write Settings to Flash\r\n");
+        printf("3.  Calibrate Voltage\r\n");
+        printf("4.  Calibrate Current\r\n");
+        printf("5.  Enable Calibration Mode\r\n");
+        printf("6.  Disable Calibration Mode\r\n");
+        printf("7.  Enable Impedance Tracking\r\n");
+        printf("8.  Output Status\r\n");
+        printf("9.  Display Data\r\n");
+        printf("10.  Test Connection to Sensor\r\n");
+        printf("11.  Reset Voltage Divider Calibration\r\n");
+	    printf("12.  Test Float Conversion\r\n");
+	    printf("13.  Read Voltage and Current Forever\r\n");
+
+        scanf("%d", &test);
+        printf("Running test %d:\r\n\n", test);
+
+        //SWITCH. ADD A CASE FOR EACH TEST.
+        switch(test) {
+            case 1:         harness.sensorReset();                           break;
+            case 2:         harness.writeSettings();                         break;
+            case 3:         harness.calibrateVoltage();                      break;
+            case 4:         harness.calibrateCurrent();                      break;
+            case 5:         harness.startCal();                              break;
+            case 6:         harness.stopCal();                               break;
+            case 7:         harness.startIt();                               break;
+            case 8:         harness.outputStatus();                          break;
+            case 9:         harness.displayData();                           break;
+            case 10:        harness.testICConnection();                      break;
+            case 11:        harness.resetVoltageCalibration();               break;
+	        case 12:        harness.testFloatConversion();                   break;
+	        case 13:        harness.readVoltageCurrent();                    break;
+            default:        printf("Invalid test number.\r\n");              break;
+        }
+
+        printf("done.\r\n");
+    }
+    return 0;
+}