Skip to content

Commit

Permalink
Add BQ27220 Data Memory Table settings
Browse files Browse the repository at this point in the history
  • Loading branch information
ShallowGreen123 committed Jan 8, 2025
1 parent 2c1dbfe commit f15d77d
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 58 deletions.
219 changes: 184 additions & 35 deletions examples/bq27xxx_test/bq27220.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@

#define BQ27220_ID (0x0220u)

/** Delay between 2 writes into Subclass/MAC area. Fails at ~120us. */
#define BQ27220_MAC_WRITE_DELAY_US (250u)

/** Delay between we ask chip to load data to MAC and it become valid. Fails at ~500us. */
#define BQ27220_SELECT_DELAY_US (1000u)

/** Delay between 2 control operations(like unseal or full access). Fails at ~2500us.*/
#define BQ27220_MAGIC_DELAY_US (5000u)

/** Delay before freshly written configuration can be read. Fails at ? */
#define BQ27220_CONFIG_DELAY_US (10000u)

/** Config apply delay. Must wait, or DM read returns garbage. */
#define BQ27220_CONFIG_APPLY_US (2000000u)

/** Timeout for common operations. */
#define BQ27220_TIMEOUT_COMMON_US (2000000u)

Expand All @@ -21,22 +30,113 @@
/** Timeout cycles count helper */
#define BQ27220_TIMEOUT(timeout_us) ((timeout_us) / (BQ27220_TIMEOUT_CYCLE_INTERVAL_US))

static uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) {
uint8_t ret = 0;
for(uint16_t i = 0; i < len; i++) {
ret += data[i];
}
return 0xFF - ret;
}

bool BQ27220::parameterCheck(uint16_t addr, uint32_t value, bool update)
bool BQ27220::parameterCheck(uint16_t address, uint32_t value, size_t size, bool update)
{
bool ret = false;
if(!(size == 1 || size == 2 || size == 4)) {
Serial.printf("(%d) Parameter size error\n", __LINE__);
return false;
}

bool ret = false;
uint8_t buffer[6] = {0};
uint8_t old_data[4] = {0};

do {
buffer[0] = address & 0xFF;
buffer[1] = (address >> 8) & 0xFF;

for(size_t i = 0; i < size; i++) {
buffer[1 + size - i] = (value >> (i * 8)) & 0xFF;
}

if(update) {
if(!i2cWriteBytes(CommandSelectSubclass, buffer, size + 2)) {
Serial.printf("(%d) DM write failed\n", __LINE__);
break;
}
// We must wait, otherwise write will fail
delayMicroseconds(BQ27220_MAC_WRITE_DELAY_US);

// Calculate the check sum: 0xFF - (sum of address and data) OR 0xFF
uint8_t checksum = bq27220_get_checksum(buffer, size + 2);
// Write the check sum to 0x60 and the total length of (address + parameter data + check sum + length) to 0x61
buffer[0] = checksum;
// 2 bytes address, `size` bytes data, 1 byte check sum, 1 byte length
buffer[1] = 2 + size + 1 + 1;
if(!i2cWriteBytes(CommandMACDataSum, buffer, size + 2)) {
Serial.printf("(%d) CRC write failed\n", __LINE__);
break;
}
// We must wait, otherwise write will fail
delayMicroseconds(BQ27220_CONFIG_DELAY_US);
ret = true;
} else {
if(!i2cWriteBytes(CommandSelectSubclass, buffer, 2)) {
Serial.printf("(%d) DM SelectSubclass for read failed\n", __LINE__);
break;
}
// bqstudio uses 15ms wait delay here
delayMicroseconds(BQ27220_SELECT_DELAY_US);

if(!i2cReadBytes(CommandMACData, old_data, size)) {
Serial.printf("(%d) DM read failed\n", __LINE__);
break;
}
// bqstudio uses burst reads with continue(CommandSelectSubclass without argument) and ~5ms between burst
delayMicroseconds(BQ27220_SELECT_DELAY_US);

if(*(uint32_t*)&(old_data[0]) != *(uint32_t*)&(buffer[2])) {
Serial.printf(
"(%d) Data at 0x%04x(%zu): 0x%08lx!=0x%08lx\n", __LINE__,
address,
size,
*(uint32_t*)&(old_data[0]),
*(uint32_t*)&(buffer[2]));
} else {
ret = true;
}
}
} while(0);

return ret;
}

bool BQ27220::dateMemoryCheck(const BQ27220DMData *data_memory, bool update)
{
if(update) {
const uint16_t cfg_request = Control_ENTER_CFG_UPDATE;
if(!i2cWriteBytes(CommandSelectSubclass, (uint8_t*)&cfg_request, sizeof(cfg_request))) {
Serial.printf("(%d) ENTER_CFG_UPDATE command failed", __LINE__);
return false;
}

// Wait for enter CFG update mode
uint32_t timeout = BQ27220_TIMEOUT(BQ27220_TIMEOUT_COMMON_US);
BQ27220OperationStatus operation_status;
while(--timeout > 0) {
if(!getOperationStatus(&operation_status)) {
Serial.printf("(%d) Failed to get operation status, retries left %lu", __LINE__, timeout);
} else if(operation_status.reg.CFGUPDATE) {
break;
};
delayMicroseconds(BQ27220_TIMEOUT_CYCLE_INTERVAL_US);
}

if(timeout == 0) {
Serial.printf(
"(%d) Enter CFGUPDATE mode failed, CFG %u, SEC %u", __LINE__,
operation_status.reg.CFGUPDATE,
operation_status.reg.SEC);
return false;
}
}

// Process data memory records
Expand All @@ -46,13 +146,56 @@ bool BQ27220::dateMemoryCheck(const BQ27220DMData *data_memory, bool update)
if(data_memory->type == BQ27220DMTypeWait) {
delayMicroseconds(data_memory->value.u32);
} else if(data_memory->type == BQ27220DMTypeU8) {

result &= parameterCheck(data_memory->address, data_memory->value.u8, 1, update);
} else if(data_memory->type == BQ27220DMTypeU16) {
result &= parameterCheck(data_memory->address, data_memory->value.u16, 2, update);
} else if(data_memory->type == BQ27220DMTypeU32) {
result &= parameterCheck(data_memory->address, data_memory->value.u32, 4, update);
} else if(data_memory->type == BQ27220DMTypeI8) {
result &= parameterCheck(data_memory->address, data_memory->value.i8, 1, update);
} else if(data_memory->type == BQ27220DMTypeI16) {
result &= parameterCheck(data_memory->address, data_memory->value.i16, 2, update);
} else if(data_memory->type == BQ27220DMTypeI32) {
result &= parameterCheck(data_memory->address, data_memory->value.i32, 4, update);
} else if(data_memory->type == BQ27220DMTypeF32) {
result &= parameterCheck(data_memory->address, data_memory->value.u32, 4, update);
} else if(data_memory->type == BQ27220DMTypePtr8) {
result &= parameterCheck(data_memory->address, *(uint8_t*)data_memory->value.u32, 1, update);
} else if(data_memory->type == BQ27220DMTypePtr16) {
result &= parameterCheck(data_memory->address, *(uint16_t*)data_memory->value.u32, 2, update);
} else if(data_memory->type == BQ27220DMTypePtr32) {
result &= parameterCheck(data_memory->address, *(uint32_t*)data_memory->value.u32, 4, update);
} else {
Serial.printf("(%d) Invalid DM Type\n", __LINE__);
}

data_memory++;
}

// Finalize configuration update
if(update && result) {
controlSubCmd(Control_EXIT_CFG_UPDATE_REINIT);

// Wait for gauge to apply new configuration
delayMicroseconds(BQ27220_CONFIG_APPLY_US);

// ensure that we exited config update mode
uint32_t timeout = BQ27220_TIMEOUT(BQ27220_TIMEOUT_COMMON_US);
BQ27220OperationStatus operation_status;
while(--timeout > 0) {
if(!getOperationStatus(&operation_status)) {
Serial.printf("(%d) Failed to get operation status, retries left %lu\n", __LINE__, timeout);
} else if(operation_status.reg.CFGUPDATE != true) {
break;
}
delayMicroseconds(BQ27220_TIMEOUT_CYCLE_INTERVAL_US);
}

// Check timeout
if(timeout == 0) {
Serial.printf("(%d) Exit CFGUPDATE mode failed\n", __LINE__);
return false;
}
}
return result;
}

Expand Down Expand Up @@ -84,12 +227,25 @@ bool BQ27220::init(const BQ27220DMData *data_memory)
}

// Ensure correct profile is selected

Serial.printf("(%d) Checking chosen profile\n", __LINE__);
BQ27220ControlStatus control_status;
if(!getControlStatus(&control_status)) {
Serial.printf("(%d) Failed to get control status\n", __LINE__);
break;
}
if(control_status.reg.BATT_ID != 0) {
Serial.printf("(%d) Incorrect profile, reset needed\n", __LINE__);
reset_and_provisioning_required = true;
}

// Ensure correct configuration loaded into gauge DataMemory
// Only if reset is not required, otherwise we don't
if(!reset_and_provisioning_required) {

Serial.printf("(%d) Checking data memory\n", __LINE__);
if(!dateMemoryCheck(data_memory, false)) {
Serial.printf("(%d) Incorrect configuration data, reset needed\n", __LINE__);
reset_and_provisioning_required = true;
}
}

// Reset needed
Expand All @@ -105,7 +261,12 @@ bool BQ27220::init(const BQ27220DMData *data_memory)
}

// Update memory
// TODO
Serial.printf("(%d) Updating data memory\n", __LINE__);
dateMemoryCheck(data_memory, true);
if(!dateMemoryCheck(data_memory, false)) {
Serial.printf("(%d) Data memory update failed\n", __LINE__);
break;
}
}

if(!sealAccess()) {
Expand Down Expand Up @@ -139,7 +300,7 @@ bool BQ27220::reset(void)
Serial.println("INITCOMP timeout after reset");
break;
}
Serial.printf("Cycles left: %lu\n", timeout);
Serial.printf("(%d) Cycles left: %lu\n", __LINE__, timeout);
result = true;
} while(0);
return result;
Expand Down Expand Up @@ -231,7 +392,7 @@ bool BQ27220::fullAccess(void)
}
// Must be unsealed to get full access
if(operat.reg.SEC != Bq27220OperationStatusSecUnsealed){
Serial.println("Not in unsealed state");
Serial.printf("(%d) Not in unsealed state\n", __LINE__);
break;
}

Expand Down Expand Up @@ -278,40 +439,28 @@ int16_t BQ27220::getCurrent(void)
}
bool BQ27220::getControlStatus(BQ27220ControlStatus *ctrl_sta)
{
bool result = false;
uint16_t data = readRegU16(CommandControl);
if(data != 0)
{
(*ctrl_sta).full = data;
result = true;
}
return result;
(*ctrl_sta).full = readRegU16(CommandControl);
return true;
}
bool BQ27220::getBatteryStatus(BQ27220BatteryStatus *batt_sta)
{
bool result = false;
uint16_t data = readRegU16(CommandBatteryStatus);
if(data != 0)
{
(*batt_sta).full = data;
result = true;
}
return result;
(*batt_sta).full = readRegU16(CommandBatteryStatus);
return true;
}
bool BQ27220::getOperationStatus(BQ27220OperationStatus *oper_sta)
{
bool result = false;
uint16_t data = readRegU16(CommandOperationStatus);
if(data != 0)
{
(*oper_sta).full = data;
result = true;
}
return result;
(*oper_sta).full = readRegU16(CommandOperationStatus);
return true;
}
bool BQ27220::getGaugingStatus(void)
bool BQ27220::getGaugingStatus(BQ27220GaugingStatus *gauging_sta)
{
return 0;
// Request gauging data to be loaded to MAC
controlSubCmd(Control_GAUGING_STATUS);
// Wait for data being loaded to MAC
delayMicroseconds(BQ27220_SELECT_DELAY_US);
// Read id data from MAC scratch space
(*gauging_sta).full = readRegU16(CommandMACData);
return true;
}
uint16_t BQ27220::getTemperature(void)
{
Expand Down
31 changes: 27 additions & 4 deletions examples/bq27xxx_test/bq27220.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#define DEFAULT_SCL 18
#define DEFAULT_SDA 8

typedef union {
typedef union ControlStatus{
struct __reg
{
// Low byte, Low bit first
Expand Down Expand Up @@ -76,6 +76,29 @@ typedef union OperationStatus{
uint16_t full;
} BQ27220OperationStatus;

typedef union GaugingStatus{
struct __reg
{
// Low byte, Low bit first
bool FD : 1; /**< Full Discharge */
bool FC : 1; /**< Full Charge */
bool TD : 1; /**< Terminate Discharge */
bool TC : 1; /**< Terminate Charge */
bool RSVD0 : 1; /**< Reserved */
bool EDV : 1; /**< Cell voltage is above or below EDV0 threshold */
bool DSG : 1; /**< DISCHARGE or RELAXATION */
bool CF : 1; /**< Battery conditioning is needed */
// High byte, Low bit first
uint8_t RSVD1 : 2; /**< Reserved */
bool FCCX : 1; /**< fcc1hz clock going into CC: 0 = 1 Hz, 1 = 16 Hz*/
uint8_t RSVD2 : 2; /**< Reserved */
bool EDV1 : 1; /**< Cell voltage is above or below EDV1 threshold */
bool EDV2 : 1; /**< Cell voltage is above or below EDV2 threshold */
bool VDQ : 1; /**< Charge cycle FCC update qualification */
} reg;
uint16_t full;
} BQ27220GaugingStatus;

class BQ27220{
public:
BQ27220() : addr{BQ27220_I2C_ADDRESS}, wire(&Wire), scl(DEFAULT_SCL), sda(DEFAULT_SDA)
Expand All @@ -94,10 +117,10 @@ class BQ27220{
return false;
}

bool parameterCheck(uint16_t addr, uint32_t value, bool update);
bool parameterCheck(uint16_t address, uint32_t value, size_t size, bool update);
bool dateMemoryCheck(const BQ27220DMData *data_memory, bool update);

bool init(const BQ27220DMData *data_memory);
bool init(const BQ27220DMData *data_memory = gauge_data_memory);
bool reset(void);

// Sealed Access
Expand All @@ -112,7 +135,7 @@ class BQ27220{
bool getControlStatus(BQ27220ControlStatus *ctrl_sta);
bool getBatteryStatus(BQ27220BatteryStatus *batt_sta);
bool getOperationStatus(BQ27220OperationStatus *oper_sta);
bool getGaugingStatus(void);
bool getGaugingStatus(BQ27220GaugingStatus *gauging_sta);
uint16_t getTemperature(void);
uint16_t getFullChargeCapacity(void);
uint16_t getDesignCapacity(void);
Expand Down
20 changes: 1 addition & 19 deletions examples/bq27xxx_test/bq27xxx.ino
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,7 @@ void setup()

Serial.printf("device number:0x%x\n", bq.getDeviceNumber());

BQ27220OperationStatus opr_st;
bq.getOperationStatus(&opr_st);
Serial.printf("1 OperationStatus.SEC: %d\n", opr_st.reg.SEC);

delay(2000);
bq.fullAccess();
bq.getOperationStatus(&opr_st);
Serial.printf("full access: %d\n", opr_st.reg.SEC);

delay(2000);
bq.sealAccess();
bq.getOperationStatus(&opr_st);
Serial.printf("sealAccess: %d\n", opr_st.reg.SEC);

delay(2000);
bq.unsealAccess();
bq.getOperationStatus(&opr_st);
Serial.printf("unsealAccess: %d\n", opr_st.reg.SEC);
// bq.reset();
bq.init();
}

void loop()
Expand Down

0 comments on commit f15d77d

Please sign in to comment.