From c9ba27e5a25426f8eb46cffd7f3538114b4df088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20J=C3=A4ger?= Date: Fri, 23 Feb 2024 08:29:34 +0100 Subject: [PATCH] drivers: bms_ic: rework balancing control 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. --- drivers/bms_ic/bq769x0/bq769x0.c | 39 ++++++----- drivers/bms_ic/bq769x2/bq769x2.c | 39 +++++------ drivers/bms_ic/bq769x2/bq769x2_priv.h | 1 + drivers/bms_ic/isl94202/isl94202.c | 90 ++++++++++++++----------- drivers/bms_ic/isl94202/isl94202_priv.h | 2 + include/drivers/bms_ic.h | 14 ++-- 6 files changed, 99 insertions(+), 86 deletions(-) diff --git a/drivers/bms_ic/bq769x0/bq769x0.c b/drivers/bms_ic/bq769x0/bq769x0.c index 0ca542e..3798575 100644 --- a/drivers/bms_ic/bq769x0/bq769x0.c +++ b/drivers/bms_ic/bq769x0/bq769x0.c @@ -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; @@ -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) @@ -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) @@ -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++) { @@ -838,6 +851,8 @@ static int bq769x0_set_balancing_switches(const struct device *dev, uint32_t cel } } + dev_data->balancing_status = cells; + return 0; } @@ -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) diff --git a/drivers/bms_ic/bq769x2/bq769x2.c b/drivers/bms_ic/bq769x2/bq769x2.c index b13de9f..b1a9216 100644 --- a/drivers/bms_ic/bq769x2/bq769x2.c +++ b/drivers/bms_ic/bq769x2/bq769x2.c @@ -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; /* @@ -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; @@ -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) diff --git a/drivers/bms_ic/bq769x2/bq769x2_priv.h b/drivers/bms_ic/bq769x2/bq769x2_priv.h index 6c05b14..5ed3dd9 100644 --- a/drivers/bms_ic/bq769x2/bq769x2_priv.h +++ b/drivers/bms_ic/bq769x2/bq769x2_priv.h @@ -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_ */ diff --git a/drivers/bms_ic/isl94202/isl94202.c b/drivers/bms_ic/isl94202/isl94202.c index 51b008a..2131e23 100644 --- a/drivers/bms_ic/isl94202/isl94202.c +++ b/drivers/bms_ic/isl94202/isl94202.c @@ -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 @@ -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, ®, 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, ®, 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; } @@ -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) @@ -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, ®, 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, ®, 1); @@ -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"); @@ -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; } diff --git a/drivers/bms_ic/isl94202/isl94202_priv.h b/drivers/bms_ic/isl94202/isl94202_priv.h index 85fc2f4..411ff1a 100644 --- a/drivers/bms_ic/isl94202/isl94202_priv.h +++ b/drivers/bms_ic/isl94202/isl94202_priv.h @@ -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_ */ diff --git a/include/drivers/bms_ic.h b/include/drivers/bms_ic.h index a334bb9..57d87e3 100644 --- a/include/drivers/bms_ic.h +++ b/include/drivers/bms_ic.h @@ -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 */ @@ -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 */ /** @@ -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) {