Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:d92f936cf10d, committed 2021-07-31
- Comitter:
- MPPT51
- Date:
- Sat Jul 31 09:12:21 2021 +0000
- Commit message:
- commit_bms
Changed in this revision
--- /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; +