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) {