KIT Solar Car Project / Mbed 2 deprecated BMS_ver1

Dependencies:   mbed INA226gfg

Files at this revision

API Documentation at this revision

Comitter:
MPPT51
Date:
Sat Jul 31 09:12:21 2021 +0000
Commit message:
commit_bms

Changed in this revision

INA226.lib Show annotated file Show diff for this revision Revisions of this file
bq769x0.cpp Show annotated file Show diff for this revision Revisions of this file
bq769x0.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
registers.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/INA226.lib	Sat Jul 31 09:12:21 2021 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/MPPT51/code/INA226gfg/#e0ddd1dfa2cc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bq769x0.cpp	Sat Jul 31 09:12:21 2021 +0000
@@ -0,0 +1,1227 @@
+/* Battery management system based on bq769x0 for ARM mbed
+ * Copyright (c) 2015-2018 Martin Jäger (www.libre.solar)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <math.h>     // log for thermistor calculation
+
+#include "bq769x0.h"
+#include "registers.h"
+#include "mbed.h"
+
+DigitalOut bms1_on(p8);
+DigitalOut bms2_on(p12);
+
+const char *byte2char(int x)
+{
+    static char b[9];
+    b[0] = '\0';
+
+    int z;
+    for (z = 128; z > 0; z >>= 1)
+    {
+        strcat(b, ((x & z) == z) ? "1" : "0");
+    }
+
+    return b;
+}
+
+uint8_t _crc8_ccitt_update (uint8_t inCrc, uint8_t inData)
+{
+    uint8_t   i;
+    uint8_t   data;
+
+    data = inCrc ^ inData;
+
+    for ( i = 0; i < 8; i++ )
+    {
+        if (( data & 0x80 ) != 0 )
+        {
+            data <<= 1;
+            data ^= 0x07;
+        }
+        else
+        {
+            data <<= 1;
+        }
+    }
+    return data;
+}
+
+//----------------------------------------------------------------------------
+
+//bq769x0::bms1_output(PinName bms1_on) : _pin(LED1) {
+//    _Pin = 1;
+//}
+bq769x0::bq769x0(I2C& bqI2C, PinName alertPin, int bqType, int bqI2CAddress, bool crc):
+    _i2c(bqI2C), _alertInterrupt(alertPin)
+{
+    _timer.start();
+    _alertInterrupt.rise(callback(this, &bq769x0::setAlertInterruptFlag));
+
+    // set some safe default values
+    autoBalancingEnabled = false;
+    balancingMinIdleTime_s = 1800;    // default: 30 minutes
+    idleCurrentThreshold = 30; // mA
+
+    thermistorBetaValue = 3435;  // typical value for Semitec 103AT-5 thermistor
+
+    alertInterruptFlag = true;   // init with true to check and clear errors at start-up
+
+    type = bqType;
+    if (type == bq76920) {
+        numberOfCells = 5;
+    } else if (type == bq76930) {
+        numberOfCells = 10;
+    } else {
+        numberOfCells = 15;
+    }
+
+    // initialize variables
+    for (int i = 0; i < numberOfCells - 1; i++) {
+        cellVoltages[i] = 0;
+    }
+
+    //init1();
+    //init2();
+    
+    //crcEnabled = crc;
+    //I2CAddress = bqI2CAddress;
+    /*
+    if (determineAddressAndCrc())
+    {
+        // initial settings for bq769x0
+        writeRegister(SYS_CTRL1, 0b00011000);  // switch external thermistor and ADC on
+        writeRegister(SYS_CTRL2, 0b01000000);  // switch CC_EN on
+
+        // get ADC offset and gain
+        adcOffset = (signed int) readRegister(ADCOFFSET);  // convert from 2's complement
+        adcGain = 365 + (((readRegister(ADCGAIN1) & 0b00001100) << 1) |
+            ((readRegister(ADCGAIN2) & 0b11100000) >> 5)); // uV/LSB
+        
+        printf("BMS OK\n");
+    }
+    else {
+        // TODO: do something else... e.g. set error flag
+#if BQ769X0_DEBUG
+        printf("BMS communication error\n");
+#endif
+    }
+    */
+}
+
+void bq769x0::init1(void){
+    bms1_on = 1;
+    wait(1);
+    printf("BMS1 init\n");
+    _timer.reset();
+        
+    if (determineAddressAndCrc())
+    {
+        printf("BMS1 init11\n");
+        // initial settings for bq769x0
+        writeRegister(SYS_CTRL1, 0b00011000);  // switch external thermistor and ADC on
+        printf("BMS1 init111\n");
+        writeRegister(SYS_CTRL2, 0b01000000);  // switch CC_EN on
+        printf("BMS1 init111\n");
+
+        // get ADC offset and gain
+        adcOffset1 = (signed int) readRegister(ADCOFFSET);  // convert from 2's complement
+        adcGain1 = 365 + (((readRegister(ADCGAIN1) & 0b00001100) << 1) |
+            ((readRegister(ADCGAIN2) & 0b11100000) >> 5)); // uV/LSB
+        
+        printf("BMS1 OK\n");
+    }
+    else {
+        // TODO: do something else... e.g. set error flag
+#if BQ769X0_DEBUG
+        printf("BMS1 communication error\n");
+#endif
+    }
+    bms1_on = 0;
+}
+
+void bq769x0::init2(void){
+    bms2_on = 1;
+    wait(0.5);
+    
+    printf("BMS2 init\n");
+    _timer.reset();
+        
+    if (determineAddressAndCrc())
+    {
+        // initial settings for bq769x0
+        writeRegister(SYS_CTRL1, 0b00011000);  // switch external thermistor and ADC on
+        writeRegister(SYS_CTRL2, 0b01000000);  // switch CC_EN on
+        printf("BMS init 1\n");
+
+        // get ADC offset and gain
+        adcOffset2 = (signed int) readRegister(ADCOFFSET);  // convert from 2's complement
+        adcGain2 = 365 + (((readRegister(ADCGAIN1) & 0b00001100) << 1) |
+            ((readRegister(ADCGAIN2) & 0b11100000) >> 5)); // uV/LSB
+        
+        printf("BMS2 OK\n");
+    }
+    else {
+        // TODO: do something else... e.g. set error flag
+#if BQ769X0_DEBUG
+        printf("BMS1 communication error\n");
+#endif
+    }
+    bms2_on = 0;
+}
+//----------------------------------------------------------------------------
+// automatically find out address and CRC setting
+
+bool bq769x0::determineAddressAndCrc(void)
+{
+    I2CAddress = 0x08;
+    crcEnabled = true;
+    writeRegister(CC_CFG, 0x19);
+    if (readRegister(CC_CFG) == 0x19) {
+        return true;
+    }
+
+    I2CAddress = 0x18;
+    crcEnabled = true;
+    writeRegister(CC_CFG, 0x19);
+    if (readRegister(CC_CFG) == 0x19) {
+        return true;
+    }
+
+    I2CAddress = 0x08;
+    crcEnabled = false;
+    writeRegister(CC_CFG, 0x19);
+    if (readRegister(CC_CFG) == 0x19) {
+        return true;
+    }
+
+    I2CAddress = 0x18;
+    crcEnabled = false;
+    writeRegister(CC_CFG, 0x19);
+    if (readRegister(CC_CFG) == 0x19) {
+        return true;
+    }
+
+    return false;
+}
+
+//----------------------------------------------------------------------------
+// Boot IC by pulling the boot pin TS1 high for some ms
+
+void bq769x0::boot(PinName bootPin)
+{
+    DigitalInOut boot(bootPin);
+
+    boot = 1;
+    wait_ms(5);   // wait 5 ms for device to receive boot signal (datasheet: max. 2 ms)
+    boot.input();         // don't disturb temperature measurement
+    wait_ms(10);  // wait for device to boot up completely (datasheet: max. 10 ms)
+}
+
+
+//----------------------------------------------------------------------------
+// Fast function to check whether BMS has an error
+// (returns 0 if everything is OK)
+
+int bq769x0::checkStatus()
+{
+    //  printf("errorStatus: ");
+    //  printf(errorStatus);
+    if (alertInterruptFlag == false && errorStatus == 0) {
+        return 0;
+    } else {
+
+        regSYS_STAT_t sys_stat;
+        sys_stat.regByte = readRegister(SYS_STAT);
+
+        // first check, if only a new CC reading is available
+        if (sys_stat.bits.CC_READY == 1) {
+            //printf("Interrupt: CC ready");
+            updateCurrent();  // automatically clears CC ready flag
+        }
+
+        // Serious error occured
+        if (sys_stat.regByte & 0b00111111)
+        {
+            if (alertInterruptFlag == true) {
+                secSinceErrorCounter = 0;
+            }
+            errorStatus = sys_stat.regByte;
+
+            unsigned int secSinceInterrupt = (_timer.read_ms() - interruptTimestamp) / 1000;
+
+            // check for overrun of _timer.read_ms() or very slow running program
+            if (abs((long)(secSinceInterrupt - secSinceErrorCounter)) > 2) {
+                secSinceErrorCounter = secSinceInterrupt;
+            }
+
+            // called only once per second
+            if (secSinceInterrupt >= secSinceErrorCounter)
+            {
+                if (sys_stat.regByte & 0b00100000) { // XR error
+                    // datasheet recommendation: try to clear after waiting a few seconds
+                    if (secSinceErrorCounter % 3 == 0) {
+                        #if BQ769X0_DEBUG
+                        printf("Attempting to clear XR error");
+                        #endif
+                        writeRegister(SYS_STAT, 0b00100000);
+                        enableCharging();
+                        enableDischarging();
+                    }
+                }
+                if (sys_stat.regByte & 0b00010000) { // Alert error
+                    if (secSinceErrorCounter % 10 == 0) {
+                        #if BQ769X0_DEBUG
+                        printf("Attempting to clear Alert error");
+                        #endif
+                        writeRegister(SYS_STAT, 0b00010000);
+                        enableCharging();
+                        enableDischarging();
+                    }
+                }
+                if (sys_stat.regByte & 0b00001000) { // UV error
+                    updateVoltages();
+                    if (cellVoltages[idCellMinVoltage] > minCellVoltage) {
+                        #if BQ769X0_DEBUG
+                        printf("Attempting to clear UV error");
+                        #endif
+                        writeRegister(SYS_STAT, 0b00001000);
+                        enableDischarging();
+                    }
+                }
+                if (sys_stat.regByte & 0b00000100) { // OV error
+                    updateVoltages();
+                    if (cellVoltages[idCellMaxVoltage] < maxCellVoltage) {
+                        #if BQ769X0_DEBUG
+                        printf("Attempting to clear OV error");
+                        #endif
+                        writeRegister(SYS_STAT, 0b00000100);
+                        enableCharging();
+                    }
+                }
+                if (sys_stat.regByte & 0b00000010) { // SCD
+                    if (secSinceErrorCounter % 60 == 0) {
+                        #if BQ769X0_DEBUG
+                        printf("Attempting to clear SCD error");
+                        #endif
+                        writeRegister(SYS_STAT, 0b00000010);
+                        enableDischarging();
+                    }
+                }
+                if (sys_stat.regByte & 0b00000001) { // OCD
+                    if (secSinceErrorCounter % 60 == 0) {
+                        #if BQ769X0_DEBUG
+                        printf("Attempting to clear OCD error");
+                        #endif
+                        writeRegister(SYS_STAT, 0b00000001);
+                        enableDischarging();
+                    }
+                }
+                secSinceErrorCounter++;
+            }
+        }
+        else {
+            errorStatus = 0;
+        }
+
+        return errorStatus;
+    }
+}
+
+//----------------------------------------------------------------------------
+// checks if temperatures are within the limits, otherwise disables CHG/DSG FET
+
+void bq769x0::checkCellTemp()
+{
+    int numberOfThermistors = numberOfCells/5;
+    bool cellTempChargeError = 0;
+    bool cellTempDischargeError = 0;
+
+    for (int thermistor = 0; thermistor < numberOfThermistors; thermistor++) {
+        cellTempChargeError |=
+            temperatures[thermistor] > maxCellTempCharge - cellTempChargeErrorFlag ? cellTempHysteresis : 0 ||
+            temperatures[thermistor] < minCellTempCharge + cellTempChargeErrorFlag ? cellTempHysteresis : 0;
+
+        cellTempDischargeError |=
+            temperatures[thermistor] > maxCellTempDischarge - cellTempDischargeErrorFlag ? cellTempHysteresis : 0 ||
+            temperatures[thermistor] < minCellTempDischarge + cellTempDischargeErrorFlag ? cellTempHysteresis : 0;
+    }
+
+    if (cellTempChargeErrorFlag != cellTempChargeError) {
+        cellTempChargeErrorFlag = cellTempChargeError;
+        if (cellTempChargeError) {
+            disableCharging();
+            #if BQ769X0_DEBUG
+            printf("Temperature error (CHG)");
+            #endif
+        }
+        else {
+            enableCharging();
+            #if BQ769X0_DEBUG
+            printf("Clearing temperature error (CHG)");
+            #endif
+        }
+    }
+
+    if (cellTempDischargeErrorFlag != cellTempDischargeError) {
+        cellTempDischargeErrorFlag = cellTempDischargeError;
+        if (cellTempDischargeError) {
+            disableDischarging();
+            #if BQ769X0_DEBUG
+            printf("Temperature error (DSG)");
+            #endif
+        }
+        else {
+            enableDischarging();
+            #if BQ769X0_DEBUG
+            printf("Clearing temperature error (DSG)");
+            #endif
+        }
+    }
+}
+
+//----------------------------------------------------------------------------
+// should be called at least once every 250 ms to get correct coulomb counting
+
+void bq769x0::update()
+{
+    //updateCurrent();  // will only read new current value if alert was triggered
+    updateVoltages();
+    //updateTemperatures();
+    //updateBalancingSwitches();
+    //checkCellTemp();
+}
+void bq769x0::update1(){
+    updateVoltages1();
+}
+void bq769x0::update2(){
+    updateVoltages2();
+}
+//----------------------------------------------------------------------------
+// puts BMS IC into SHIP mode (i.e. switched off)
+
+void bq769x0::shutdown()
+{
+    writeRegister(SYS_CTRL1, 0x0);
+    writeRegister(SYS_CTRL1, 0x1);
+    writeRegister(SYS_CTRL1, 0x2);
+}
+
+//----------------------------------------------------------------------------
+
+bool bq769x0::enableCharging()
+{
+    #if BQ769X0_DEBUG
+    printf("checkStatus() = %d\n", checkStatus());
+    printf("Umax = %d\n", cellVoltages[idCellMaxVoltage]);
+    printf("temperatures[0] = %d\n", temperatures[0]);
+    #endif
+
+    int numberOfThermistors = numberOfCells/5;
+    bool cellTempChargeError = 0;
+
+    for (int thermistor = 0; thermistor < numberOfThermistors; thermistor++) {
+        cellTempChargeError |=
+            temperatures[thermistor] > maxCellTempCharge ||
+            temperatures[thermistor] < minCellTempCharge;
+    }
+
+    if (checkStatus() == 0 &&
+        cellVoltages[idCellMaxVoltage] < maxCellVoltage &&
+        cellTempChargeError == 0)
+    {
+        int sys_ctrl2;
+        sys_ctrl2 = readRegister(SYS_CTRL2);
+        writeRegister(SYS_CTRL2, sys_ctrl2 | 0b00000001);  // switch CHG on
+        #if BQ769X0_DEBUG
+        printf("Enabling CHG FET\n");
+        #endif
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::disableCharging()
+{
+    int sys_ctrl2;
+    sys_ctrl2 = readRegister(SYS_CTRL2);
+    writeRegister(SYS_CTRL2, sys_ctrl2 & ~0b00000001);  // switch CHG off
+    #if BQ769X0_DEBUG
+    printf("Disabling CHG FET\n");
+    #endif
+}
+
+//----------------------------------------------------------------------------
+
+bool bq769x0::enableDischarging()
+{
+    #if BQ769X0_DEBUG
+    printf("checkStatus() = %d\n", checkStatus());
+    printf("Umin = %d\n", cellVoltages[idCellMinVoltage]);
+    printf("temperatures[0] = %d\n", temperatures[0]);
+    #endif
+
+    int numberOfThermistors = numberOfCells/5;
+    bool cellTempDischargeError = 0;
+
+    for (int thermistor = 0; thermistor < numberOfThermistors; thermistor++) {
+        cellTempDischargeError |=
+            temperatures[thermistor] > maxCellTempDischarge ||
+            temperatures[thermistor] < minCellTempDischarge;
+    }
+
+    if (checkStatus() == 0 &&
+        cellVoltages[idCellMinVoltage] > minCellVoltage &&
+        cellTempDischargeError == 0)
+    {
+        int sys_ctrl2;
+        sys_ctrl2 = readRegister(SYS_CTRL2);
+        writeRegister(SYS_CTRL2, sys_ctrl2 | 0b00000010);  // switch DSG on
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::disableDischarging()
+{
+    int sys_ctrl2;
+    sys_ctrl2 = readRegister(SYS_CTRL2);
+    writeRegister(SYS_CTRL2, sys_ctrl2 & ~0b00000010);  // switch DSG off
+    #if BQ769X0_DEBUG
+    printf("Disabling DISCHG FET\n");
+    #endif
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::enableAutoBalancing(void)
+{
+    autoBalancingEnabled = true;
+}
+
+
+//----------------------------------------------------------------------------
+
+void bq769x0::setBalancingThresholds(int idleTime_min, int absVoltage_mV, int voltageDifference_mV)
+{
+    balancingMinIdleTime_s = idleTime_min * 60;
+    balancingMinCellVoltage_mV = absVoltage_mV;
+    balancingMaxVoltageDifference_mV = voltageDifference_mV;
+}
+
+//----------------------------------------------------------------------------
+// sets balancing registers if balancing is allowed
+// (sufficient idle time + voltage)
+
+void bq769x0::updateBalancingSwitches(void)
+{
+    long idleSeconds = (_timer.read_ms() - idleTimestamp) / 1000;
+    int numberOfSections = numberOfCells/5;
+
+    // check for _timer.read_ms() overflow
+    if (idleSeconds < 0) {
+        idleTimestamp = 0;
+        idleSeconds = _timer.read_ms() / 1000;
+    }
+
+    // check if balancing allowed
+    if (checkStatus() == 0 &&
+        idleSeconds >= balancingMinIdleTime_s &&
+        cellVoltages[idCellMaxVoltage] > balancingMinCellVoltage_mV &&
+        (cellVoltages[idCellMaxVoltage] - cellVoltages[idCellMinVoltage]) > balancingMaxVoltageDifference_mV)
+    {
+        //printf("Balancing enabled!");
+        balancingStatus = 0;  // current status will be set in following loop
+
+        //regCELLBAL_t cellbal;
+        int balancingFlags;
+        int balancingFlagsTarget;
+
+        for (int section = 0; section < numberOfSections; section++)
+        {
+            // find cells which should be balanced and sort them by voltage descending
+            int cellList[5];
+            int cellCounter = 0;
+            for (int i = 0; i < 5; i++)
+            {
+                if ((cellVoltages[section*5 + i] - cellVoltages[idCellMinVoltage]) > balancingMaxVoltageDifference_mV) {
+                    int j = cellCounter;
+                    while (j > 0 && cellVoltages[section*5 + cellList[j - 1]] < cellVoltages[section*5 + i])
+                    {
+                        cellList[j] = cellList[j - 1];
+                        j--;
+                    }
+                    cellList[j] = i;
+                    cellCounter++;
+                }
+            }
+
+            balancingFlags = 0;
+            for (int i = 0; i < cellCounter; i++)
+            {
+                // try to enable balancing of current cell
+                balancingFlagsTarget = balancingFlags | (1 << cellList[i]);
+
+                // check if attempting to balance adjacent cells
+                bool adjacentCellCollision =
+                    ((balancingFlagsTarget << 1) & balancingFlags) ||
+                    ((balancingFlags << 1) & balancingFlagsTarget);
+
+                if (adjacentCellCollision == false) {
+                    balancingFlags = balancingFlagsTarget;
+                }
+            }
+
+            #if BQ769X0_DEBUG
+            //printf("Setting CELLBAL%d register to: %s\n", section+1, byte2char(balancingFlags));
+            #endif
+
+            balancingStatus |= balancingFlags << section*5;
+
+            // set balancing register for this section
+            writeRegister(CELLBAL1+section, balancingFlags);
+
+        } // section loop
+    }
+    else if (balancingStatus > 0)
+    {
+        // clear all CELLBAL registers
+        for (int section = 0; section < numberOfSections; section++)
+        {
+            #if BQ769X0_DEBUG
+            printf("Clearing Register CELLBAL%d\n", section+1);
+            #endif
+
+            writeRegister(CELLBAL1+section, 0x0);
+        }
+
+        balancingStatus = 0;
+    }
+}
+
+//----------------------------------------------------------------------------
+
+int bq769x0::getBalancingStatus()
+{
+    return balancingStatus;
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::setShuntResistorValue(float res_mOhm)
+{
+    shuntResistorValue_mOhm = res_mOhm;
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::setThermistorBetaValue(int beta_K)
+{
+    thermistorBetaValue = beta_K;
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::setBatteryCapacity(long capacity_mAh)
+{
+    nominalCapacity = capacity_mAh * 3600;
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::setOCV(int voltageVsSOC[NUM_OCV_POINTS])
+{
+    OCV = voltageVsSOC;
+}
+
+//----------------------------------------------------------------------------
+
+float bq769x0::getSOC(void)
+{
+    return (double) coulombCounter / nominalCapacity * 100;
+}
+
+//----------------------------------------------------------------------------
+// SOC calculation based on average cell open circuit voltage
+
+void bq769x0::resetSOC(int percent)
+{
+    if (percent <= 100 && percent >= 0)
+    {
+        coulombCounter = nominalCapacity * (percent / 100.0);
+    }
+    else  // reset based on OCV
+    {
+        printf("NumCells: %d, voltage: %d V\n", getNumberOfConnectedCells(), getBatteryVoltage());
+        int voltage = getBatteryVoltage() / getNumberOfConnectedCells();
+
+        coulombCounter = 0;  // initialize with totally depleted battery (0% SOC)
+
+        for (int i = 0; i < NUM_OCV_POINTS; i++)
+        {
+            if (OCV[i] <= voltage) {
+                if (i == 0) {
+                    coulombCounter = nominalCapacity;  // 100% full
+                }
+                else {
+                    // interpolate between OCV[i] and OCV[i-1]
+                    coulombCounter = (double) nominalCapacity / (NUM_OCV_POINTS - 1.0) *
+                    (NUM_OCV_POINTS - 1.0 - i + ((float)voltage - OCV[i])/(OCV[i-1] - OCV[i]));
+                }
+                return;
+            }
+        }
+    }
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::setTemperatureLimits(int minDischarge_degC, int maxDischarge_degC,
+  int minCharge_degC, int maxCharge_degC, int hysteresis_degC)
+{
+    // Temperature limits (°C/10)
+    minCellTempDischarge = minDischarge_degC * 10;
+    maxCellTempDischarge = maxDischarge_degC * 10;
+    minCellTempCharge = minCharge_degC * 10;
+    maxCellTempCharge = maxCharge_degC * 10;
+    cellTempHysteresis = hysteresis_degC * 10;
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::setIdleCurrentThreshold(int current_mA)
+{
+    idleCurrentThreshold = current_mA;
+}
+
+//----------------------------------------------------------------------------
+
+long bq769x0::setShortCircuitProtection(long current_mA, int delay_us)
+{
+    regPROTECT1_t protect1;
+
+    // only RSNS = 1 considered
+    protect1.bits.RSNS = 1;
+
+    protect1.bits.SCD_THRESH = 0;
+    for (int i = sizeof(SCD_threshold_setting)-1; i > 0; i--) {
+        if (current_mA * shuntResistorValue_mOhm / 1000 >= SCD_threshold_setting[i]) {
+            protect1.bits.SCD_THRESH = i;
+            break;
+        }
+    }
+
+    protect1.bits.SCD_DELAY = 0;
+    for (int i = sizeof(SCD_delay_setting)-1; i > 0; i--) {
+        if (delay_us >= SCD_delay_setting[i]) {
+            protect1.bits.SCD_DELAY = i;
+            break;
+        }
+    }
+
+    writeRegister(PROTECT1, protect1.regByte);
+
+    // returns the actual current threshold value
+    return (long)SCD_threshold_setting[protect1.bits.SCD_THRESH] * 1000 /
+        shuntResistorValue_mOhm;
+}
+
+//----------------------------------------------------------------------------
+
+long bq769x0::setOvercurrentChargeProtection(long current_mA, int delay_ms)
+{
+    // ToDo: Software protection for charge overcurrent
+    return 0;
+}
+
+//----------------------------------------------------------------------------
+
+long bq769x0::setOvercurrentDischargeProtection(long current_mA, int delay_ms)
+{
+    regPROTECT2_t protect2;
+
+    // Remark: RSNS must be set to 1 in PROTECT1 register
+
+    protect2.bits.OCD_THRESH = 0;
+    for (int i = sizeof(OCD_threshold_setting)-1; i > 0; i--) {
+        if (current_mA * shuntResistorValue_mOhm / 1000 >= OCD_threshold_setting[i]) {
+            protect2.bits.OCD_THRESH = i;
+            break;
+        }
+    }
+
+    protect2.bits.OCD_DELAY = 0;
+    for (int i = sizeof(OCD_delay_setting)-1; i > 0; i--) {
+        if (delay_ms >= OCD_delay_setting[i]) {
+            protect2.bits.OCD_DELAY = i;
+            break;
+        }
+    }
+
+    writeRegister(PROTECT2, protect2.regByte);
+
+    // returns the actual current threshold value
+    return (long)OCD_threshold_setting[protect2.bits.OCD_THRESH] * 1000 /
+        shuntResistorValue_mOhm;
+}
+
+
+//----------------------------------------------------------------------------
+
+int bq769x0::setCellUndervoltageProtection(int voltage_mV, int delay_s)
+{
+    regPROTECT3_t protect3;
+    int uv_trip = 0;
+
+    minCellVoltage = voltage_mV;
+
+    protect3.regByte = readRegister(PROTECT3);
+
+    uv_trip = ((((long)voltage_mV - adcOffset) * 1000 / adcGain) >> 4) & 0x00FF;
+    uv_trip += 1;   // always round up for lower cell voltage
+    writeRegister(UV_TRIP, uv_trip);
+
+    protect3.bits.UV_DELAY = 0;
+    for (int i = sizeof(UV_delay_setting)-1; i > 0; i--) {
+        if (delay_s >= UV_delay_setting[i]) {
+            protect3.bits.UV_DELAY = i;
+            break;
+        }
+    }
+
+    writeRegister(PROTECT3, protect3.regByte);
+
+    // returns the actual current threshold value
+    return ((long)1 << 12 | uv_trip << 4) * adcGain / 1000 + adcOffset;
+}
+
+//----------------------------------------------------------------------------
+
+int bq769x0::setCellOvervoltageProtection(int voltage_mV, int delay_s)
+{
+    regPROTECT3_t protect3;
+    int ov_trip = 0;
+
+    maxCellVoltage = voltage_mV;
+
+    protect3.regByte = readRegister(PROTECT3);
+
+    ov_trip = ((((long)voltage_mV - adcOffset) * 1000 / adcGain) >> 4) & 0x00FF;
+    writeRegister(OV_TRIP, ov_trip);
+
+    protect3.bits.OV_DELAY = 0;
+    for (int i = sizeof(OV_delay_setting)-1; i > 0; i--) {
+        if (delay_s >= OV_delay_setting[i]) {
+            protect3.bits.OV_DELAY = i;
+            break;
+        }
+    }
+
+    writeRegister(PROTECT3, protect3.regByte);
+
+    // returns the actual current threshold value
+    return ((long)1 << 13 | ov_trip << 4) * adcGain / 1000 + adcOffset;
+}
+
+
+//----------------------------------------------------------------------------
+
+int bq769x0::getBatteryCurrent()
+{
+    return batCurrent;
+}
+
+//----------------------------------------------------------------------------
+
+int bq769x0::getBatteryVoltage()
+{
+    return batVoltage;
+}
+
+//----------------------------------------------------------------------------
+
+int bq769x0::getMaxCellVoltage()
+{
+    return cellVoltages[idCellMaxVoltage];
+}
+
+//----------------------------------------------------------------------------
+
+int bq769x0::getMinCellVoltage()
+{
+    return cellVoltages[idCellMinVoltage];
+}
+
+//----------------------------------------------------------------------------
+
+int bq769x0::getCellVoltage(int idCell)
+{
+    return cellVoltages[idCell-1];
+}
+
+//----------------------------------------------------------------------------
+
+int bq769x0::getNumberOfCells(void)
+{
+    return numberOfCells;
+}
+
+//----------------------------------------------------------------------------
+
+int bq769x0::getNumberOfConnectedCells(void)
+{
+    return connectedCells;
+}
+
+//----------------------------------------------------------------------------
+
+float bq769x0::getTemperatureDegC(int channel)
+{
+    if (channel >= 1 && channel <= 3) {
+        return (float)temperatures[channel-1] / 10.0;
+    }
+    else {
+        return -273.15;   // Error: Return absolute minimum temperature
+    }
+}
+
+//----------------------------------------------------------------------------
+
+float bq769x0::getTemperatureDegF(int channel)
+{
+    return getTemperatureDegC(channel) * 1.8 + 32;
+}
+
+
+//----------------------------------------------------------------------------
+
+void bq769x0::updateTemperatures()
+{
+    float tmp = 0;
+    int adcVal = 0;
+    int vtsx = 0;
+    unsigned long rts = 0;
+
+    // calculate R_thermistor according to bq769x0 datasheet
+    adcVal = (readRegister(TS1_HI_BYTE) & 0b00111111) << 8 | readRegister(TS1_LO_BYTE);
+    vtsx = adcVal * 0.382; // mV
+    rts = 10000.0 * vtsx / (3300.0 - vtsx); // Ohm
+
+    // Temperature calculation using Beta equation
+    // - According to bq769x0 datasheet, only 10k thermistors should be used
+    // - 25°C reference temperature for Beta equation assumed
+    tmp = 1.0/(1.0/(273.15+25) + 1.0/thermistorBetaValue*log(rts/10000.0)); // K
+    temperatures[0] = (tmp - 273.15) * 10.0;
+
+    if (type == bq76930 || type == bq76940) {
+        adcVal = (readRegister(TS2_HI_BYTE) & 0b00111111) << 8 | readRegister(TS2_LO_BYTE);
+        vtsx = adcVal * 0.382; // mV
+        rts = 10000.0 * vtsx / (3300.0 - vtsx); // Ohm
+        tmp = 1.0/(1.0/(273.15+25) + 1.0/thermistorBetaValue*log(rts/10000.0)); // K
+        temperatures[1] = (tmp - 273.15) * 10.0;
+    }
+
+    if (type == bq76940) {
+        adcVal = (readRegister(TS3_HI_BYTE) & 0b00111111) << 8 | readRegister(TS3_LO_BYTE);
+        vtsx = adcVal * 0.382; // mV
+        rts = 10000.0 * vtsx / (3300.0 - vtsx); // Ohm
+        tmp = 1.0/(1.0/(273.15+25) + 1.0/thermistorBetaValue*log(rts/10000.0)); // K
+        temperatures[2] = (tmp - 273.15) * 10.0;
+    }
+}
+
+
+//----------------------------------------------------------------------------
+
+void bq769x0::updateCurrent()
+{
+    int adcVal = 0;
+    regSYS_STAT_t sys_stat;
+    sys_stat.regByte = readRegister(SYS_STAT);
+
+    // check if new current reading available
+    if (sys_stat.bits.CC_READY == 1)
+    {
+        //printf("reading CC register...\n");
+        adcVal = (readRegister(CC_HI_BYTE) << 8) | readRegister(CC_LO_BYTE);
+        batCurrent = (int16_t) adcVal * 8.44 / shuntResistorValue_mOhm;  // mA
+
+        coulombCounter += batCurrent / 4;  // is read every 250 ms
+
+        // reduce resolution for actual current value
+        if (batCurrent > -10 && batCurrent < 10) {
+            batCurrent = 0;
+        }
+
+        // reset idleTimestamp
+        if (abs(batCurrent) > idleCurrentThreshold) {
+            idleTimestamp = _timer.read_ms();
+        }
+
+        // no error occured which caused alert
+        if (!(sys_stat.regByte & 0b00111111)) {
+            alertInterruptFlag = false;
+        }
+
+        writeRegister(SYS_STAT, 0b10000000);  // Clear CC ready flag
+    }
+}
+
+//----------------------------------------------------------------------------
+// reads all cell voltages to array cellVoltages[NUM_CELLS] and updates batVoltage
+
+void bq769x0::updateVoltages()
+{
+    long adcVal = 0;
+    char buf[4];
+    int connectedCellsTemp = 0;
+
+    uint8_t crc;
+
+    // read cell voltages
+    buf[0] = (char) VC1_HI_BYTE;
+    _i2c.write(I2CAddress << 1, buf, 1);
+    printf("Tx:%x, %x, %d, %d\n\r", buf[0], buf[1], buf[0], buf[1]);
+
+    idCellMaxVoltage = 0;
+    idCellMinVoltage = 0;
+    for (int i = 0; i < numberOfCells; i++)
+    {
+        if (crcEnabled == true) {
+            _i2c.read(I2CAddress << 1, buf, 4);
+            adcVal = (buf[0] & 0b00111111) << 8 | buf[2];
+
+            // CRC of first bytes includes slave address (including R/W bit) and data
+            crc = _crc8_ccitt_update(0, (I2CAddress << 1) | 1);
+            crc = _crc8_ccitt_update(crc, buf[0]);
+            if (crc != buf[1]) return; // don't save corrupted value
+
+            // CRC of subsequent bytes contain only data
+            crc = _crc8_ccitt_update(0, buf[2]);
+            if (crc != buf[3]) return; // don't save corrupted value
+        }
+        else {
+            //printf("XX:%d\n\r", i);
+            _i2c.read(I2CAddress << 1, buf, 2);
+            adcVal = (buf[0] & 0b00111111) << 8 | buf[1];
+            printf("Rx:%x, %x, %d, %d\n\r", buf[0], buf[1], buf[0], buf[1]);
+            //printf("%d, %d, %d, %d\n\r", cellVoltages[i], adcVal, adcGain, adcOffset);
+        }
+
+        cellVoltages[i] = adcVal * adcGain / 1000 + adcOffset;
+        printf("%d, %d, %d, %d\n\r", cellVoltages[i], adcVal, adcGain, adcOffset);
+        
+        if (cellVoltages[i] > 500) {
+            connectedCellsTemp++;
+        }
+        if (cellVoltages[i] > cellVoltages[idCellMaxVoltage]) {
+            idCellMaxVoltage = i;
+        }
+        if (cellVoltages[i] < cellVoltages[idCellMinVoltage] && cellVoltages[i] > 500) {
+            idCellMinVoltage = i;
+        }
+    }
+    connectedCells = connectedCellsTemp;
+    
+    // read battery pack voltage
+    adcVal = (readRegister(BAT_HI_BYTE) << 8) | readRegister(BAT_LO_BYTE);
+    batVoltage = 4.0 * adcGain * adcVal / 1000.0 + connectedCells * adcOffset;
+}
+
+void bq769x0::updateVoltages1()
+{
+    bms1_on = 1;
+    wait(0.3);
+    
+    long adcVal = 0;
+    char buf[4];
+    int connectedCellsTemp = 0;
+
+    // read cell voltages
+    buf[0] = (char) VC1_HI_BYTE;
+    _i2c.write(I2CAddress << 1, buf, 1);
+    //printf("Tx1:%x, %x, %d, %d\n\r", buf[0], buf[1], buf[0], buf[1]);
+
+    idCellMaxVoltage = 0;
+    idCellMinVoltage = 0;
+    for (int i = 0; i < numberOfCells; i++)
+    {
+        _i2c.read(I2CAddress << 1, buf, 2);
+        adcVal = (buf[0] & 0b00111111) << 8 | buf[1];
+        //printf("Rx1:%x, %x, %d, %d\n\r", buf[0], buf[1], buf[0], buf[1]);
+
+        cellVoltages[i] = adcVal * adcGain1 / 1000 + adcOffset1;
+        //printf("1:%d, %d, %d, %d\n\r", cellVoltages[i], adcVal, adcGain1, adcOffset1);
+        
+        if (cellVoltages[i] > 500) {
+            connectedCellsTemp++;
+        }
+        if (cellVoltages[i] > cellVoltages[idCellMaxVoltage]) {
+            idCellMaxVoltage = i;
+        }
+        if (cellVoltages[i] < cellVoltages[idCellMinVoltage] && cellVoltages[i] > 500) {
+            idCellMinVoltage = i;
+        }
+    }
+    connectedCells = connectedCellsTemp;
+    
+    // read battery pack voltage
+    adcVal = (readRegister(BAT_HI_BYTE) << 8) | readRegister(BAT_LO_BYTE);
+    batVoltage = 4.0 * adcGain * adcVal / 1000.0 + connectedCells * adcOffset;
+    
+    bms1_on = 0;
+}
+
+void bq769x0::updateVoltages2()
+{
+    bms2_on = 1;
+    wait(0.3);
+    long adcVal = 0;
+    char buf[4];
+    int connectedCellsTemp = 0;
+
+    // read cell voltages
+    buf[0] = (char) VC1_HI_BYTE;
+    _i2c.write(I2CAddress << 1, buf, 1);
+    //printf("Tx2:%x, %x, %d, %d\n\r", buf[0], buf[1], buf[0], buf[1]);
+
+    idCellMaxVoltage = 0;
+    idCellMinVoltage = 0;
+    for (int i = 0; i < numberOfCells; i++)
+    {
+        _i2c.read(I2CAddress << 1, buf, 2);
+        adcVal = (buf[0] & 0b00111111) << 8 | buf[1];
+        //printf("Rx2:%x, %x, %d, %d\n\r", buf[0], buf[1], buf[0], buf[1]);
+
+        cellVoltages[i] = adcVal * adcGain2 / 1000 + adcOffset2;
+        //printf("2:%d, %d, %d, %d\n\r", cellVoltages[i], adcVal, adcGain2, adcOffset2);
+        
+        if (cellVoltages[i] > 500) {
+            connectedCellsTemp++;
+        }
+        if (cellVoltages[i] > cellVoltages[idCellMaxVoltage]) {
+            idCellMaxVoltage = i;
+        }
+        if (cellVoltages[i] < cellVoltages[idCellMinVoltage] && cellVoltages[i] > 500) {
+            idCellMinVoltage = i;
+        }
+    }
+    connectedCells = connectedCellsTemp;
+    
+    // read battery pack voltage
+    adcVal = (readRegister(BAT_HI_BYTE) << 8) | readRegister(BAT_LO_BYTE);
+    batVoltage = 4.0 * adcGain * adcVal / 1000.0 + connectedCells * adcOffset;
+    
+    bms2_on = 0;
+}
+
+//----------------------------------------------------------------------------
+
+void bq769x0::writeRegister(int address, int data)
+{
+    uint8_t crc = 0;
+    char buf[3];
+
+    buf[0] = (char) address;
+    buf[1] = data;
+
+    if (crcEnabled == true) {
+        // CRC is calculated over the slave address (including R/W bit), register address, and data.
+        crc = _crc8_ccitt_update(crc, (I2CAddress << 1) | 0);
+        crc = _crc8_ccitt_update(crc, buf[0]);
+        crc = _crc8_ccitt_update(crc, buf[1]);
+        buf[2] = crc;
+        _i2c.write(I2CAddress << 1, buf, 3);
+    }
+    else {
+        _i2c.write(I2CAddress << 1, buf, 2);
+    }
+}
+
+//----------------------------------------------------------------------------
+
+int bq769x0::readRegister(int address)
+{
+    uint8_t crc = 0;
+    char buf[2];
+
+    #if BQ769X0_DEBUG
+    //printf("Read register: 0x%x \n", address);
+    #endif
+
+    buf[0] = (char)address;
+    _i2c.write(I2CAddress << 1, buf, 1);;
+
+    if (crcEnabled == true) {
+        do {
+            _i2c.read(I2CAddress << 1, buf, 2);
+            // CRC is calculated over the slave address (including R/W bit) and data.
+            crc = _crc8_ccitt_update(crc, (I2CAddress << 1) | 1);
+            crc = _crc8_ccitt_update(crc, buf[0]);
+        } while (crc != buf[1]);
+        return buf[0];
+    }
+    else {
+        _i2c.read(I2CAddress << 1, buf, 1);
+        return buf[0];
+    }
+}
+
+//----------------------------------------------------------------------------
+// The bq769x0 drives the ALERT pin high if the SYS_STAT register contains
+// a new value (either new CC reading or an error)
+
+void bq769x0::setAlertInterruptFlag()
+{
+    interruptTimestamp = _timer.read_ms();
+    alertInterruptFlag = true;
+}
+
+#if BQ769X0_DEBUG
+
+//----------------------------------------------------------------------------
+// for debug purposes
+
+void bq769x0::printRegisters()
+{
+    printf("0x00 SYS_STAT:  %s\n", byte2char(readRegister(SYS_STAT)));
+    printf("0x01 CELLBAL1:  %s\n", byte2char(readRegister(CELLBAL1)));
+    printf("0x04 SYS_CTRL1: %s\n", byte2char(readRegister(SYS_CTRL1)));
+    printf("0x05 SYS_CTRL2: %s\n", byte2char(readRegister(SYS_CTRL2)));
+    printf("0x06 PROTECT1:  %s\n", byte2char(readRegister(PROTECT1)));
+    printf("0x07 PROTECT2:  %s\n", byte2char(readRegister(PROTECT2)));
+    printf("0x08 PROTECT3:  %s\n", byte2char(readRegister(PROTECT3)));
+    printf("0x09 OV_TRIP:   %s\n", byte2char(readRegister(OV_TRIP)));
+    printf("0x0A UV_TRIP:   %s\n", byte2char(readRegister(UV_TRIP)));
+    printf("0x0B CC_CFG:    %s\n", byte2char(readRegister(CC_CFG)));
+    printf("0x32 CC_HI:     %s\n", byte2char(readRegister(CC_HI_BYTE)));
+    printf("0x33 CC_LO:     %s\n", byte2char(readRegister(CC_LO_BYTE)));
+    /*
+    printf("0x50 ADCGAIN1:  %s\n", byte2char(readRegister(ADCGAIN1)));
+    printf("0x51 ADCOFFSET: %s\n", byte2char(readRegister(ADCOFFSET)));
+    printf("0x59 ADCGAIN2:  %s\n", byte2char(readRegister(ADCGAIN2)));
+    */
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bq769x0.h	Sat Jul 31 09:12:21 2021 +0000
@@ -0,0 +1,202 @@
+/* Battery management system based on bq769x0 for ARM mbed
+ * Copyright (c) 2015-2018 Martin Jäger (www.libre.solar)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BQ769X0_H
+#define BQ769X0_H
+
+#include "mbed.h"
+
+#define MAX_NUMBER_OF_CELLS 15
+#define MAX_NUMBER_OF_THERMISTORS 3
+#define NUM_OCV_POINTS 21
+
+// IC type/size
+#define bq76920 1
+#define bq76930 2
+#define bq76940 3
+
+// output information to serial console for debugging
+#define BQ769X0_DEBUG 1
+
+class bq769x0 {
+
+public:
+    //bms1_output(PinName bms1_on)
+    // initialization, status update and shutdown
+    bq769x0(I2C& bqI2C, PinName alertPin, int bqType = bq76940, int bqI2CAddress = 0x18, bool crc = false);
+    //bq769x0(I2C& bqI2C, PinName alertPin, int bqType = bq76930, int bqI2CAddress = 0x08, bool crc = true);
+    //void init(); // kikkawa added
+    void init1(); // kikkawa added
+    void init2(); // kikkawa added
+
+    int checkStatus();  // returns 0 if everything is OK
+    
+    void update(void);
+    void update1(void); // added
+    void update2(void); // added
+
+    void boot(PinName bootPin);
+    void shutdown(void);
+
+    // charging control
+    bool enableCharging(void);
+    void disableCharging(void);
+    bool enableDischarging(void);
+    void disableDischarging(void);
+
+    // hardware settings
+    void setShuntResistorValue(float res_mOhm);
+    void setThermistorBetaValue(int beta_K);
+
+    void resetSOC(int percent = -1);    // 0-100 %, -1 for automatic reset based on OCV
+    void setBatteryCapacity(long capacity_mAh);
+    void setOCV(int voltageVsSOC[NUM_OCV_POINTS]);
+
+    int getNumberOfCells(void);
+    int getNumberOfConnectedCells(void);
+
+    // limit settings (for battery protection)
+    void setTemperatureLimits(int minDischarge_degC, int maxDischarge_degC, int minCharge_degC, int maxCharge_degC, int hysteresis_degC = 2);    // °C
+    long setShortCircuitProtection(long current_mA, int delay_us = 70);
+    long setOvercurrentChargeProtection(long current_mA, int delay_ms = 8);
+    long setOvercurrentDischargeProtection(long current_mA, int delay_ms = 8);
+    int setCellUndervoltageProtection(int voltage_mV, int delay_s = 1);
+    int setCellOvervoltageProtection(int voltage_mV, int delay_s = 1);
+
+    // balancing settings
+    void setBalancingThresholds(int idleTime_min = 30, int absVoltage_mV = 3400, int voltageDifference_mV = 20);
+    void setIdleCurrentThreshold(int current_mA);
+
+    // automatic balancing when battery is within balancing thresholds
+    void enableAutoBalancing(void);
+    void disableAutoBalancing(void);
+
+    // battery status
+    int  getBatteryCurrent(void);
+    int  getBatteryVoltage(void);
+    int  getCellVoltage(int idCell);    // from 1 to 15
+    
+    int  getCellVoltage1(int idCell);    // from 1 to 15
+    int  getCellVoltage2(int idCell);    // from 1 to 15
+
+    int  getMinCellVoltage(void);
+    int  getMaxCellVoltage(void);
+    int  getAvgCellVoltage(void);
+    float getTemperatureDegC(int channel = 1);
+    float getTemperatureDegF(int channel = 1);
+    float getSOC(void);
+    int getBalancingStatus(void);
+
+    // interrupt handling (not to be called manually!)
+    void setAlertInterruptFlag(void);
+    
+    //public kara idou
+
+    #if BQ769X0_DEBUG
+    void printRegisters(void);
+    #endif
+
+private:
+    //DigitalOut _bms1_on;
+
+    // Variables
+
+    I2C& _i2c;
+    Timer _timer;
+    InterruptIn _alertInterrupt;
+
+    int I2CAddress;
+    int type;
+    bool crcEnabled;
+
+    float shuntResistorValue_mOhm;
+    int thermistorBetaValue;  // typical value for Semitec 103AT-5 thermistor: 3435
+    int *OCV;  // Open Circuit Voltage of cell for SOC 100%, 95%, ..., 5%, 0%
+
+    // indicates if a new current reading or an error is available from BMS IC
+    bool alertInterruptFlag;
+
+    int numberOfCells;                      // number of cells allowed by IC
+    int connectedCells;                     // actual number of cells connected
+    int cellVoltages[MAX_NUMBER_OF_CELLS];          // mV
+    int idCellMaxVoltage;
+    int idCellMinVoltage;
+    long batVoltage;                                // mV
+    long batCurrent;                                // mA
+    int temperatures[MAX_NUMBER_OF_THERMISTORS];    // °C/10
+
+    long nominalCapacity;    // mAs, nominal capacity of battery pack, max. 1193 Ah possible
+    long coulombCounter;     // mAs (= milli Coulombs) for current integration
+
+    // Current limits (mA)
+    long maxChargeCurrent;
+    long maxDischargeCurrent;
+    int idleCurrentThreshold; // mA
+
+    // Temperature limits (°C/10)
+    int minCellTempCharge;
+    int minCellTempDischarge;
+    int maxCellTempCharge;
+    int maxCellTempDischarge;
+    int cellTempHysteresis;
+
+    // Cell voltage limits (mV)
+    int maxCellVoltage;
+    int minCellVoltage;
+    int balancingMinCellVoltage_mV;
+    int balancingMaxVoltageDifference_mV;
+
+    int adcGain;    // uV/LSB
+    int adcOffset;  // mV
+    int adcGain1;    // uV/LSB      // added
+    int adcOffset1;  // mV          // added
+    int adcGain2;    // uV/LSB      // added
+    int adcOffset2;  // mV          // added
+
+    int errorStatus;
+    bool autoBalancingEnabled;
+    unsigned int balancingStatus;     // holds on/off status of balancing switches
+    int balancingMinIdleTime_s;
+    unsigned long idleTimestamp;
+
+    unsigned int secSinceErrorCounter;
+    unsigned long interruptTimestamp;
+
+    bool cellTempChargeErrorFlag;
+    bool cellTempDischargeErrorFlag;
+
+    // Methods
+
+    bool determineAddressAndCrc(void);
+
+    void  updateVoltages(void);
+    void  updateVoltages1(void);    // added
+    void  updateVoltages2(void);    // added
+    
+    void  updateCurrent(void);
+    void  updateTemperatures(void);
+
+    void updateBalancingSwitches(void);
+
+    void checkCellTemp(void);
+
+    int  readRegister(int address);
+    void writeRegister(int address, int data);
+    
+};
+
+#endif // BQ769X0_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sat Jul 31 09:12:21 2021 +0000
@@ -0,0 +1,140 @@
+#include "mbed.h"
+#include "bq769x0.h"
+#include "INA226.hpp"
+
+Timer timer1;
+
+Serial pc(USBTX,USBRX);
+I2C i2c(p9, p10);                              // SDA SCL
+bq769x0 bms( i2c, p26, 3, 0x18, false);
+I2C i2c_ina226(p28,p27);
+INA226 VCmonitor(i2c_ina226);
+CAN can(p30,p29);
+char counte=0;
+CANMessage CANbuf;
+
+AnalogIn tempSens1(p16);                         // set p15 to analog input to read LM61 sensor's voltage output
+AnalogIn tempSens2(p17);                         // set p15 to analog input to read LM61 sensor's voltage output
+AnalogIn tempSens3(p18);                         // set p15 to analog input to read LM61 sensor's voltage output
+AnalogIn tempSens4(p19);                         // set p15 to analog input to read LM61 sensor's voltage output
+AnalogIn tempSens5(p20);                         // set p15 to analog input to read LM61 sensor's voltage output
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+
+float temp1, temp2, temp3, temp4, temp5;
+int cellVolt1[15];                              //int cellVolt1[15]; /mv
+int cellVolt2[15];                              //int cellVolt1[15]; /mv
+int cellMax1;
+int cellMin1;
+int cellAve1;
+int cellMax2;
+int cellMin2;
+int cellAve2;
+int cellMax0;
+int cellMin0;
+int cellAve0;
+
+int sum1 = 0;
+int sum2 = 0;
+
+void readTemp(void);
+
+int main(){
+    pc.printf("main program start\n\r");
+    bms.init1();
+    bms.init2();
+    wait(1);
+    
+    CANbuf.id = 0x30; //CAN送信側(slave)のIDを決定
+
+    //INA226の処理
+    unsigned short val=0;
+    double C;
+    int count = 1;
+    if(!VCmonitor.isExist()){
+        pc.printf("VCmonitor NOT FOUND\n");
+        while(1){;}
+    }
+    pc.printf("VCmonitor FOUND\n\r");
+    if(VCmonitor.rawRead(0x00,&val) != 0){  //configResisterの値を読み取れるか確認(通信できてるか確認)
+        pc.printf("VCmonitor READ ERROR\n");
+    }
+    VCmonitor.setConfigResister();
+    wait(0.1);
+    pc.printf("VCmonitor Reg 0x00 : 0x%04x\n\r",val);  //configResisterの値表示
+    VCmonitor.setCurrentCalibration();
+    
+    //無限ループ開始
+    while(1){
+        bms.update1();                              //1-15セル電圧の測定
+        //wait(0.5);
+        sum1 = 0;
+        for( int i=1; i<=15; i++ ){                 //1-15セルの電圧を格納する処理
+            cellVolt1[i-1] = bms.getCellVoltage(i); //cellVolt1配列に値を格納
+            sum1 = sum1 + cellVolt1[i-1];           //合計を算出(バッテリ電圧・セル平均電圧の算出に使用)
+        }
+        cellMax1 = bms.getMaxCellVoltage();         //1-15セルの最大セル電圧を格納
+        cellMin1 = bms.getMinCellVoltage();         //1-15セルの最小セル電圧を格納
+        cellAve1 = sum1/15;                         //1-15セルの平均セル電圧を格納
+        pc.printf("max1:%d, ave1:%d, min1:%d\n\r", cellMax1, cellAve1, cellMin1 );
+        pc.printf("cell1:%d,%d,%d,%d,%d %d,%d,%d,%d,%d %d,%d,%d,%d,%d\n\r",
+            cellVolt1[0],cellVolt1[1],cellVolt1[2],cellVolt1[3],cellVolt1[4], cellVolt1[5],cellVolt1[6],cellVolt1[7],cellVolt1[8],cellVolt1[9], cellVolt1[10],cellVolt1[11],cellVolt1[12],cellVolt1[13],cellVolt1[14]);
+        //pc.printf("\n\r");
+        bms.update2();                              //16-27セル電圧の測定
+        //wait(0.5);
+        sum2 = 0;
+        for( int i=1; i<=15; i++ ){                 //16-27セルの電圧を格納する処理
+            cellVolt2[i-1] = bms.getCellVoltage(i); //
+            sum2 = sum2 + cellVolt2[i-1];
+        }
+        sum2 = sum2 -(cellVolt2[3]+cellVolt2[8]+cellVolt2[13]);     //4セル,9セル,14セルは未接続のため値を引く  
+        cellAve2 = sum2/12;                                         //16-27セルの平均セル電圧を格納
+        cellMax2 = bms.getMaxCellVoltage();                         //16-27セルの最大セル電圧を格納
+        cellMin2 = bms.getMinCellVoltage();                         //16-27セルの最小セル電圧を格納
+        pc.printf("max2:%d, ave2:%d, min2:%d\n\r", cellMax2, cellAve2, cellMin2 );
+        pc.printf("cell2:%d,%d,%d,%d,%d %d,%d,%d,%d,%d %d,%d,%d,%d,%d\n\r",
+            cellVolt2[0],cellVolt2[1],cellVolt2[2],cellVolt2[3],cellVolt2[4], cellVolt2[5],cellVolt2[6],cellVolt2[7],cellVolt2[8],cellVolt2[9], cellVolt2[10],cellVolt2[11],cellVolt2[12],cellVolt2[13],cellVolt2[14]);
+        
+        if(cellMax1 >= cellMax2){
+            cellMax0 = cellMax1;            //27セル中の最大セル電圧を格納
+        }else if(cellMax1 < cellMax2){
+            cellMax0 = cellMax2;            //27セル中の最大セル電圧を格納
+        }
+        if(cellMin1 <= cellMin2){
+            cellMin0 = cellMin1;            //27セル中の最小セル電圧を格納
+        }else if(cellMin1 > cellMin2){
+            cellMin0 = cellMin2;            //27セル中の最小セル電圧を格納
+        }
+        cellAve0 = (sum1 + sum2) / 27;      //27セルの平均セル電圧を格納
+        pc.printf("%d, %d, %d\n\r", sum1, sum2, sum1+sum2);
+        //温度を測定する処理
+        temp1 = ((tempSens1*3.3)-0.600)*100.0;      //conversion to degrees C - from sensor output voltage per LM61 data sheet
+        temp2 = ((tempSens2*3.3)-0.600)*100.0;      //conversion to degrees C - from sensor output voltage per LM61 data sheet
+        temp3 = ((tempSens3*3.3)-0.600)*100.0;      //conversion to degrees C - from sensor output voltage per LM61 data sheet
+        temp4 = ((tempSens4*3.3)-0.600)*100.0;      //conversion to degrees C - from sensor output voltage per LM61 data sheet
+        temp5 = ((tempSens5*3.3)-0.600)*100.0;      //conversion to degrees C - from sensor output voltage per LM61 data sheet
+        pc.printf("temp:%4.1f,%4.1f,%4.1f,%4.1f,%4.1f\n\r",temp1,temp2,temp3,temp4,temp5);     //print current temp
+        //pc.printf("\n\r");
+        
+        //バッテリ電流を測定する処理
+        //VCmonitor.getVoltage(&V);
+        //V = V * 14.825;
+        VCmonitor.getCurrent(&C);
+        pc.printf("%d,C,%f\n\r",count,C);
+        pc.printf("\n\r");
+
+        CANbuf.data[0] = ((sum1+sum2)/100) / 100;
+        CANbuf.data[1] = ((sum1+sum2)/100) % 100;
+        CANbuf.data[2] = ((int)(C/10) / 100);
+        CANbuf.data[3] = ((int)(C/10) % 100);
+        CANbuf.data[4] = cellMin0 / 1000;
+        CANbuf.data[5] = (cellMin0 % 1000) / 10;
+        CANbuf.data[6] = cellMax0 / 1000;
+        CANbuf.data[7] = (cellMax0 % 1000) / 10;
+        if(can.write(CANbuf)){              //格納したデータを送信する
+            led1 = !led1;
+        }
+    }   
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sat Jul 31 09:12:21 2021 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/mbed_official/code/mbed/builds/65be27845400
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/registers.h	Sat Jul 31 09:12:21 2021 +0000
@@ -0,0 +1,210 @@
+/*
+    registers.h - Battery management system based on bq769x0 for ARM mbed
+    Copyright (C) 2015-2016  Martin Jäger (http://libre.solar)
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this program. If not, see
+    <http://www.gnu.org/licenses/>.
+*/
+
+#include "mbed.h"
+
+// register map
+#define SYS_STAT        0x00
+#define CELLBAL1        0x01
+#define CELLBAL2        0x02
+#define CELLBAL3        0x03
+#define SYS_CTRL1       0x04
+#define SYS_CTRL2       0x05
+#define PROTECT1        0x06
+#define PROTECT2        0x07
+#define PROTECT3        0x08
+#define OV_TRIP         0x09
+#define UV_TRIP         0x0A
+#define CC_CFG          0x0B
+
+#define VC1_HI_BYTE     0x0C
+#define VC1_LO_BYTE     0x0D
+#define VC2_HI_BYTE     0x0E
+#define VC2_LO_BYTE     0x0F
+#define VC3_HI_BYTE     0x10
+#define VC3_LO_BYTE     0x11
+#define VC4_HI_BYTE     0x12
+#define VC4_LO_BYTE     0x13
+#define VC5_HI_BYTE     0x14
+#define VC5_LO_BYTE     0x15
+#define VC6_HI_BYTE     0x16
+#define VC6_LO_BYTE     0x17
+#define VC7_HI_BYTE     0x18
+#define VC7_LO_BYTE     0x19
+#define VC8_HI_BYTE     0x1A
+#define VC8_LO_BYTE     0x1B
+#define VC9_HI_BYTE     0x1C
+#define VC9_LO_BYTE     0x1D
+#define VC10_HI_BYTE    0x1E
+#define VC10_LO_BYTE    0x1F
+#define VC11_HI_BYTE    0x20
+#define VC11_LO_BYTE    0x21
+#define VC12_HI_BYTE    0x22
+#define VC12_LO_BYTE    0x23
+#define VC13_HI_BYTE    0x24
+#define VC13_LO_BYTE    0x25
+#define VC14_HI_BYTE    0x26
+#define VC14_LO_BYTE    0x27
+#define VC15_HI_BYTE    0x28
+#define VC15_LO_BYTE    0x29
+
+#define BAT_HI_BYTE     0x2A
+#define BAT_LO_BYTE     0x2B
+
+#define TS1_HI_BYTE     0x2C
+#define TS1_LO_BYTE     0x2D
+#define TS2_HI_BYTE     0x2E
+#define TS2_LO_BYTE     0x2F
+#define TS3_HI_BYTE     0x30
+#define TS3_LO_BYTE     0x31
+
+#define CC_HI_BYTE      0x32
+#define CC_LO_BYTE      0x33
+
+#define ADCGAIN1        0x50
+#define ADCOFFSET       0x51
+#define ADCGAIN2        0x59
+
+// function from TI reference design
+#define LOW_BYTE(Data)			(uint8_t)(0xff & Data)
+#define HIGH_BYTE(Data)			(uint8_t)(0xff & (Data >> 8))
+
+// for bit clear operations of the SYS_STAT register
+#define STAT_CC_READY           (0x80)
+#define STAT_DEVICE_XREADY      (0x20)
+#define STAT_OVRD_ALERT         (0x10)
+#define STAT_UV                 (0x08)
+#define STAT_OV                 (0x04)
+#define STAT_SCD                (0x02)
+#define STAT_OCD                (0x01)
+#define STAT_FLAGS              (0x3F)
+
+// maps for settings in protection registers
+
+const int SCD_delay_setting [4] =
+  { 70, 100, 200, 400 }; // us
+const int SCD_threshold_setting [8] =
+  { 44, 67, 89, 111, 133, 155, 178, 200 }; // mV
+
+const int OCD_delay_setting [8] =
+  { 8, 20, 40, 80, 160, 320, 640, 1280 }; // ms
+const int OCD_threshold_setting [16] =
+  { 17, 22, 28, 33, 39, 44, 50, 56, 61, 67, 72, 78, 83, 89, 94, 100 };  // mV
+
+const int UV_delay_setting [4] = { 1, 4, 8, 16 };  // s
+const int OV_delay_setting [4] = { 1, 2, 4, 8 };   // s
+
+typedef union regSYS_STAT {
+  struct
+  {
+    uint8_t OCD            :1;
+    uint8_t SCD            :1;
+    uint8_t OV             :1;
+    uint8_t UV             :1;
+    uint8_t OVRD_ALERT     :1;
+    uint8_t DEVICE_XREADY  :1;
+    uint8_t WAKE           :1;
+    uint8_t CC_READY       :1;
+  } bits;
+  uint8_t regByte;
+} regSYS_STAT_t;
+
+typedef union regSYS_CTRL1 {
+  struct
+  {
+    uint8_t SHUT_B        :1;
+    uint8_t SHUT_A        :1;
+    uint8_t RSVD1         :1;
+    uint8_t TEMP_SEL      :1;
+    uint8_t ADC_EN        :1;
+    uint8_t RSVD2         :2;
+    uint8_t LOAD_PRESENT  :1;
+  } bits;
+  uint8_t regByte;
+} regSYS_CTRL1_t;
+
+typedef union regSYS_CTRL2 {
+  struct
+  {
+    uint8_t CHG_ON      :1;
+    uint8_t DSG_ON      :1;
+    uint8_t WAKE_T      :2;
+    uint8_t WAKE_EN     :1;
+    uint8_t CC_ONESHOT  :1;
+    uint8_t CC_EN       :1;
+    uint8_t DELAY_DIS   :1;
+  } bits;
+  uint8_t regByte;
+} regSYS_CTRL2_t;
+
+typedef union regPROTECT1 {
+  struct
+  {
+      uint8_t SCD_THRESH      :3;
+      uint8_t SCD_DELAY       :2;
+      uint8_t RSVD            :2;
+      uint8_t RSNS            :1;
+  } bits;
+  uint8_t regByte;
+} regPROTECT1_t;
+
+typedef union regPROTECT2 {
+  struct
+  {
+    uint8_t OCD_THRESH      :4;
+    uint8_t OCD_DELAY       :3;
+    uint8_t RSVD            :1;
+  } bits;
+  uint8_t regByte;
+} regPROTECT2_t;
+
+typedef union regPROTECT3 {
+  struct
+  {
+    uint8_t RSVD            :4;
+    uint8_t OV_DELAY        :2;
+    uint8_t UV_DELAY        :2;
+  } bits;
+  uint8_t regByte;
+} regPROTECT3_t;
+
+typedef union regCELLBAL
+{
+  struct
+  {
+      uint8_t RSVD        :3;
+      uint8_t CB5         :1;
+      uint8_t CB4         :1;
+      uint8_t CB3         :1;
+      uint8_t CB2         :1;
+      uint8_t CB1         :1;
+  } bits;
+  uint8_t regByte;
+} regCELLBAL_t;
+
+typedef union regVCELL
+{
+    struct
+    {
+        uint8_t VC_HI;
+        uint8_t VC_LO;
+    } bytes;
+    uint16_t regWord;
+} regVCELL_t;
+