Skip to content

Commit

Permalink
drivers: bms_ic: rework balancing control
Browse files Browse the repository at this point in the history
Switching between automatic and manual balancing requires to enter
config update mode for the bq769x2. During config update mode, all
measurements and protections are disabled. As also stated in the
datasheet, this mode should only be activated for initial configuration
(before actual operation of the battery).

This means that we have to decide regarding automatic or manual
balancing operation at the very beginning and only allow manually
setting the switches if we are not in automatic balancing mode.

This behavior makes sense also for other drivers than the bq769x2,
as an application firmware would either control balancing itself
or let the chip/driver control balancing autonomously. It is not
required to switch between automatic and manual balancing while the
BMS is in operation.
  • Loading branch information
martinjaeger committed Feb 24, 2024
1 parent 58d2384 commit c9ba27e
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 86 deletions.
39 changes: 19 additions & 20 deletions drivers/bms_ic/bq769x0/bq769x0.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ struct bms_ic_bq769x0_data
float bal_cell_voltage_min;
float bal_idle_current;
uint16_t bal_idle_delay;
bool auto_balancing;

uint32_t alert_mask;
} ic_conf;
Expand All @@ -88,6 +89,8 @@ struct bms_ic_bq769x0_data
bool crc_enabled;
};

static int bq769x0_set_balancing_switches(const struct device *dev, uint32_t cells);

/*
* The bq769x0 drives the ALERT pin high if the SYS_STAT register contains
* a new value (either new CC reading or an error)
Expand Down Expand Up @@ -400,13 +403,22 @@ static int bq769x0_configure_dis_scp(const struct device *dev, struct bms_ic_con
static int bq769x0_configure_balancing(const struct device *dev, struct bms_ic_conf *ic_conf)
{
struct bms_ic_bq769x0_data *dev_data = dev->data;
struct k_work_sync work_sync;

dev_data->ic_conf.bal_cell_voltage_diff = ic_conf->bal_cell_voltage_diff;
dev_data->ic_conf.bal_cell_voltage_min = ic_conf->bal_cell_voltage_min;
dev_data->ic_conf.bal_idle_current = ic_conf->bal_idle_current;
dev_data->ic_conf.bal_idle_delay = ic_conf->bal_idle_delay;
dev_data->ic_conf.auto_balancing = ic_conf->auto_balancing;

return 0;
if (ic_conf->auto_balancing) {
k_work_schedule(&dev_data->balancing_work, K_NO_WAIT);
return 0;
}
else {
k_work_cancel_delayable_sync(&dev_data->balancing_work, &work_sync);
return bq769x0_set_balancing_switches(dev, 0x0);
}
}

static int bq769x0_configure_alerts(const struct device *dev, struct bms_ic_conf *ic_conf)
Expand Down Expand Up @@ -823,6 +835,7 @@ static int bms_ic_bq769x0_set_switches(const struct device *dev, uint8_t switche
static int bq769x0_set_balancing_switches(const struct device *dev, uint32_t cells)
{
const struct bms_ic_bq769x0_config *dev_config = dev->config;
struct bms_ic_bq769x0_data *dev_data = dev->data;
int err;

for (int section = 0; section < dev_config->num_sections; section++) {
Expand All @@ -838,6 +851,8 @@ static int bq769x0_set_balancing_switches(const struct device *dev, uint32_t cel
}
}

dev_data->balancing_status = cells;

return 0;
}

Expand Down Expand Up @@ -929,28 +944,12 @@ static void bq769x0_balancing_work_handler(struct k_work *work)
static int bms_ic_bq769x0_balance(const struct device *dev, uint32_t cells)
{
struct bms_ic_bq769x0_data *dev_data = dev->data;
struct k_work_sync work_sync;
int err = 0;

if (cells == BMS_IC_BALANCING_OFF) {
k_work_cancel_delayable_sync(&dev_data->balancing_work, &work_sync);
err = bq769x0_set_balancing_switches(dev, 0x0);
if (err == 0) {
dev_data->balancing_status = 0x0;
}
}
else if (cells == BMS_IC_BALANCING_AUTO) {
k_work_schedule(&dev_data->balancing_work, K_NO_WAIT);
}
else {
k_work_cancel_delayable_sync(&dev_data->balancing_work, &work_sync);
err = bq769x0_set_balancing_switches(dev, cells);
if (err == 0) {
dev_data->balancing_status = cells;
}
if (dev_data->auto_balancing) {
return -EBUSY;
}

return err;
return bq769x0_set_balancing_switches(dev, cells);
}

static int bq769x0_activate(const struct device *dev)
Expand Down
39 changes: 20 additions & 19 deletions drivers/bms_ic/bq769x2/bq769x2.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ static int bq769x2_configure_dis_scp(const struct device *dev, struct bms_ic_con

static int bq769x2_configure_balancing(const struct device *dev, struct bms_ic_conf *ic_conf)
{
struct bms_ic_bq769x2_data *dev_data = dev->data;
int err = 0;

/*
Expand Down Expand Up @@ -394,8 +395,14 @@ static int bq769x2_configure_balancing(const struct device *dev, struct bms_ic_c
/* allow balancing of up to 4 cells (instead of only 1 by default) */
err |= bq769x2_datamem_write_u1(dev, BQ769X2_SET_CBAL_MAX_CELLS, 4);

/* enable CB_RLX and CB_CHG */
err |= bq769x2_datamem_write_u1(dev, BQ769X2_SET_CBAL_CONF, 0x03);
if (ic_conf->auto_balancing) {
/* enable CB_RLX and CB_CHG */
err |= bq769x2_datamem_write_u1(dev, BQ769X2_SET_CBAL_CONF, 0x03);
}
else {
err |= bq769x2_datamem_write_u1(dev, BQ769X2_SET_CBAL_CONF, 0x00);
}
dev_data->auto_balancing = ic_conf->auto_balancing;

ic_conf->bal_cell_voltage_min = ic_conf->bal_cell_voltage_min;
ic_conf->bal_cell_voltage_diff = ic_conf->bal_cell_voltage_diff;
Expand Down Expand Up @@ -863,25 +870,19 @@ static int bms_ic_bq769x2_set_switches(const struct device *dev, uint8_t switche

static int bms_ic_bq769x2_balance(const struct device *dev, uint32_t cells)
{
if (cells == BMS_IC_BALANCING_OFF) {
return bq769x2_datamem_write_u1(dev, BQ769X2_SET_CBAL_CONF, 0x00);
}
else if (cells == BMS_IC_BALANCING_AUTO) {
/* enable CB_RLX and CB_CHG */
return bq769x2_datamem_write_u1(dev, BQ769X2_SET_CBAL_CONF, 0x03);
struct bms_ic_bq769x2_data *dev_data = dev->data;

if (dev_data->auto_balancing) {
return -EBUSY;
}
else {
if (((cells << 1) & cells) || ((cells >> 1) & cells)) {
/* balancing of adjacent cells not allowed */
return -EINVAL;
}
int err = bq769x2_datamem_write_u1(dev, BQ769X2_SET_CBAL_CONF, 0x00);
if (err != 0) {
return err;
}
/* ToDo: Consider bq chip number and gaps in CB_ACTIVE_CELLS */
return bq769x2_subcmd_write_u2(dev, BQ769X2_SUBCMD_CB_ACTIVE_CELLS, (uint16_t)cells);

if (((cells << 1) & cells) || ((cells >> 1) & cells)) {
/* balancing of adjacent cells not allowed */
return -EINVAL;
}

/* ToDo: Consider bq chip number and gaps in CB_ACTIVE_CELLS */
return bq769x2_subcmd_write_u2(dev, BQ769X2_SUBCMD_CB_ACTIVE_CELLS, (uint16_t)cells);
}

static int bq769x2_activate(const struct device *dev)
Expand Down
1 change: 1 addition & 0 deletions drivers/bms_ic/bq769x2/bq769x2_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct bms_ic_bq769x2_data
{
struct bms_ic_data *ic_data;
bool config_update_mode_enabled;
bool auto_balancing;
};

#endif /* DRIVERS_BMS_IC_BMS_IC_BQ769X2_PRIV_H_ */
90 changes: 50 additions & 40 deletions drivers/bms_ic/isl94202/isl94202.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ static int isl94202_configure_temp_limits(const struct device *dev, struct bms_i

static int isl94202_configure_balancing(const struct device *dev, struct bms_ic_conf *ic_conf)
{
struct bms_ic_isl94202_data *dev_data = dev->data;
struct k_work_sync work_sync;
uint8_t reg;
int err = 0;

// also apply balancing thresholds here
Expand All @@ -193,6 +196,21 @@ static int isl94202_configure_balancing(const struct device *dev, struct bms_ic_
// enable balancing during idle
err |= isl94202_write_voltage(dev, ISL94202_EOC, ic_conf->bal_cell_voltage_min, 0);

if (ic_conf->auto_balancing) {
// Enable automatic balancing during charging and EOC conditions
reg = ISL94202_SETUP1_CBDC_Msk | ISL94202_SETUP1_CB_EOC_Msk;
err |= isl94202_write_bytes(dev, ISL94202_SETUP1, &reg, 1);
// Start work handler to adjust balancing timings depending on operation mode
k_work_schedule(&dev_data->balancing_work, K_NO_WAIT);
}
else {
// Disable balancing
reg = 0;
err |= isl94202_write_bytes(dev, ISL94202_SETUP1, &reg, 1);
k_work_cancel_delayable_sync(&dev_data->balancing_work, &work_sync);
}
dev_data->auto_balancing = ic_conf->auto_balancing;

return err == 0 ? 0 : -EIO;
}

Expand Down Expand Up @@ -528,43 +546,40 @@ static int bms_ic_isl94202_debug_print_mem(const struct device *dev)
return 0;
}

static int bms_ic_isl94202_balance(const struct device *dev, uint32_t cells)
static void isl94202_balancing_work_handler(struct k_work *work)
{
int err = 0;
struct k_work_delayable *dwork = k_work_delayable_from_work(work);

if (cells == BMS_IC_BALANCING_OFF) {
}
else if (cells == BMS_IC_BALANCING_AUTO) {
uint8_t stat3;
isl94202_read_bytes(dev, ISL94202_STAT3, &stat3, 1);

/*
* System scans for voltage, current and temperature measurements happen in different
* intervals depending on the mode. Cell balancing should be off during voltage scans.
*
* Each scan takes max. 1.7 ms. Choosing 16 ms off-time for voltages to settle.
*/
if (stat3 & ISL94202_STAT3_INIDLE_Msk) {
/* IDLE mode: Scan every 256 ms */
isl94202_write_delay(dev, ISL94202_CBONT, ISL94202_DELAY_MS, 240, 0);
isl94202_write_delay(dev, ISL94202_CBOFFT, ISL94202_DELAY_MS, 16, 0);
}
else if (stat3 & ISL94202_STAT3_INDOZE_Msk) {
/* DOZE mode: Scan every 512 ms */
isl94202_write_delay(dev, ISL94202_CBONT, ISL94202_DELAY_MS, 496, 0);
isl94202_write_delay(dev, ISL94202_CBOFFT, ISL94202_DELAY_MS, 16, 0);
}
else if (!(stat3 & ISL94202_STAT3_INSLEEP_Msk)) {
/* NORMAL mode: Scan every 32 ms */
isl94202_write_delay(dev, ISL94202_CBONT, ISL94202_DELAY_MS, 16, 0);
isl94202_write_delay(dev, ISL94202_CBOFFT, ISL94202_DELAY_MS, 16, 0);
}
uint8_t stat3;
isl94202_read_bytes(dev, ISL94202_STAT3, &stat3, 1);

/*
* System scans for voltage, current and temperature measurements happen in different
* intervals depending on the mode. Cell balancing should be off during voltage scans.
*
* Each scan takes max. 1.7 ms. Choosing 16 ms off-time for voltages to settle.
*/
if (stat3 & ISL94202_STAT3_INIDLE_Msk) {
/* IDLE mode: Scan every 256 ms */
isl94202_write_delay(dev, ISL94202_CBONT, ISL94202_DELAY_MS, 240, 0);
}
else {
return -ENOTSUP;
else if (stat3 & ISL94202_STAT3_INDOZE_Msk) {
/* DOZE mode: Scan every 512 ms */
isl94202_write_delay(dev, ISL94202_CBONT, ISL94202_DELAY_MS, 496, 0);
}
else if (!(stat3 & ISL94202_STAT3_INSLEEP_Msk)) {
/* NORMAL mode: Scan every 32 ms */
isl94202_write_delay(dev, ISL94202_CBONT, ISL94202_DELAY_MS, 16, 0);
}
isl94202_write_delay(dev, ISL94202_CBOFFT, ISL94202_DELAY_MS, 16, 0);

return err;
k_work_reschedule(dwork, K_SECONDS(1));
}

static int bms_ic_isl94202_balance(const struct device *dev, uint32_t cells)
{
/* manual balancing not yet supported */
return -ENOTSUP;
}

static int isl94202_activate(const struct device *dev)
Expand Down Expand Up @@ -593,14 +608,6 @@ static int isl94202_activate(const struct device *dev)
return err;
}

// Enable balancing during charging and EOC conditions
reg = ISL94202_SETUP1_CBDC_Msk | ISL94202_SETUP1_CB_EOC_Msk;
err = isl94202_write_bytes(dev, ISL94202_SETUP1, &reg, 1);
if (err) {
LOG_ERR("Failed to set balancing setup: %d", err);
return err;
}

// Enable FET control via microcontroller
reg = ISL94202_CTRL2_UCFET_Msk;
err = isl94202_write_bytes(dev, ISL94202_CTRL2, &reg, 1);
Expand Down Expand Up @@ -636,6 +643,7 @@ static int bms_ic_isl94202_set_mode(const struct device *dev, enum bms_ic_mode m
static int isl94202_init(const struct device *dev)
{
const struct bms_ic_isl94202_config *dev_config = dev->config;
struct bms_ic_isl94202_data *dev_data = dev->data;

if (!i2c_is_ready_dt(&dev_config->i2c)) {
LOG_ERR("I2C device not ready");
Expand All @@ -646,6 +654,8 @@ static int isl94202_init(const struct device *dev)
return -ENODEV;
}

k_work_init_delayable(&dev_data->balancing_work, isl94202_balancing_work_handler);

return 0;
}

Expand Down
2 changes: 2 additions & 0 deletions drivers/bms_ic/isl94202/isl94202_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ struct bms_ic_isl94202_config
struct bms_ic_isl94202_data
{
struct bms_ic_data *ic_data;
struct k_work_delayable balancing_work;
uint8_t fet_state;
bool auto_balancing;
};

#endif /* DRIVERS_BMS_IC_BMS_IC_ISL94202_PRIV_H_ */
14 changes: 7 additions & 7 deletions include/drivers/bms_ic.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ extern "C" {
#define BMS_IC_DATA_ERROR_FLAGS BIT(5)
#define BMS_IC_DATA_ALL GENMASK(5, 0)

#define BMS_IC_BALANCING_OFF (0)
#define BMS_IC_BALANCING_AUTO (UINT32_MAX)

/**
* BMS IC operation modes
*/
Expand Down Expand Up @@ -119,6 +116,8 @@ struct bms_ic_conf
float bal_idle_current;
/** Minimum idle duration before balancing (s) */
uint16_t bal_idle_delay;
/** Enable/disable automatic balancing (controlled by the IC or driver) */
bool auto_balancing;

/* Built-in voltage regulator settings */
/**
Expand Down Expand Up @@ -318,13 +317,14 @@ static inline int bms_ic_set_switches(const struct device *dev, uint8_t switches
#endif

/**
* @brief Update the balancing operation of the IC.
* @brief Manually set balancing switches of the IC.
*
* @param dev Pointer to the device structure for the driver instance.
* @param cells Bitset defining the cell(s) to be balanced. Set to BMS_IC_BALANCING_OFF to disable
* balancing and BMS_IC_BALANCING_AUTO to enable automatic balancing.
* @param cells Bitset defining the cell(s) to be balanced. Set to 0 to disable balancing.
*
* @return 0 for success or negative error code otherwise.
* @retval 0 for success
* @retval -EBUSY if automatic balancing was enabled through bms_ic_configure
* @return -EINVAL if an invalid set of cells is requested to be balanced
*/
static inline int bms_ic_balance(const struct device *dev, uint32_t cells)
{
Expand Down

0 comments on commit c9ba27e

Please sign in to comment.