bq769x0 BMS support (tested, but to be completed) Working: Alerts, config, and readin...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Sat, 1 May 2021 19:35:46 +0000 (21:35 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Fri, 10 Feb 2023 18:02:59 +0000 (19:02 +0100)
extdrv/bq769x0_bms.c [new file with mode: 0644]
include/extdrv/bq769x0_bms.h [new file with mode: 0644]

diff --git a/extdrv/bq769x0_bms.c b/extdrv/bq769x0_bms.c
new file mode 100644 (file)
index 0000000..de04a45
--- /dev/null
@@ -0,0 +1,543 @@
+/****************************************************************************
+ *   extdrv/bq769x0_bms.c
+ *
+ *
+ * Copyright 2020 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+#include "core/system.h"
+#include "lib/errno.h"
+#include "lib/string.h"
+#include "drivers/i2c.h"
+#include "extdrv/bq769x0_bms.h"
+
+
+/***************************************************************************** */
+/*          Support for TMP101 temperature bmss from Texas Instrument       */
+/***************************************************************************** */
+/* This driver is made for the TMP101 version of the chip, though there's few
+ * diferences between the TMP100 and TMP101 version.
+ * This driver does not handle the SMBus Alert command.
+ */
+
+
+enum bq769x0_internal_reg_numbers {
+       SYSTEM_STATUS = 0,
+       CELL_BALANCE_1,
+       CELL_BALANCE_2,
+       CELL_BALANCE_3,
+       SYS_CTRL_1,
+       SYS_CTRL_2,
+       PROTECT_1,
+       PROTECT_2,
+       PROTECT_3,
+       OVER_VOLTAGE_TRIP,
+       UNDER_VOLTAGE_TRIP,
+       CC_CFG, /* Coulomb Counter */
+       VC1_HIGH, /* 0x0C */
+       VC1_LOW,
+       /* VC2 to VC15 ... */
+       BAT_HIGH = 0x2A,
+       BAT_LOW,
+       TS1_HIGH,
+       TS1_LOW,
+       /* TS2 and TS3 */
+       CC_HIGH = 0x32,
+       CC_LOW,
+       ADC_GAIN_1 = 0x50,
+       ADC_OFFSET,
+       ADC_GAIN_2 = 0x59,
+       BQ769X0_LAST_REG,
+};
+
+
+/* Aditional defines, not exported to userspace */
+
+/* Sys Ctrl 1 */
+#define LOAD_PRESENT(x)  ((x) & 0x80)
+#define ADC_ENABLE  (0x01 << 4)
+#define TEMP_SEL_DIE (0 << 3)
+#define TEMP_SEL_TSX (1 << 3)
+#define SHUTDOWN_CMD_A  (0x01)
+#define SHUTDOWN_CMD_B  (0x02)
+
+/* Sys Ctrl 2 */
+#define DELAYS_OFF_FOR_TESTING  (0x01 << 7)
+#define CC_ENABLE  (0x01 << 6)
+#define CC_ONE_SHOT  (0x01 << 5)
+#define DISCHARGE_ON  (0x01 << 1)
+#define CHARGE_ON  (0x01 << 0)
+
+/* CC Config */
+/* The documentation asks for CC_CFG to be set to 0x19 upon startup */
+#define CC_CFG_DEFAULT  0x19
+
+
+/* Check the bms presence, return 1 if bms was found.
+ * This is a basic check, it could be anything with the same address ...
+ */
+int bq769x0_probe_bms(struct bq769x0_bms_conf* conf)
+{
+       char cmd_buf = (conf->addr | I2C_READ_BIT);
+
+       /* Did we already probe the bms ? */
+       /* 1 is the only valid value, all other values indicate an error state */
+       if (conf->probe_ok != 1) {
+               conf->probe_ok = i2c_read(conf->bus_num, &cmd_buf, 1, NULL, NULL, 0);
+               msleep(10);
+       }
+       return conf->probe_ok;
+}
+
+
+/* Read registers from the BMS
+ * Return value(s):
+ *   Upon successfull completion, returns 0 and the registers content is placed in the buffer.
+ *   On error, returns a negative integer equivalent to errors from glibc.
+ */
+#define CMD_BUF_SIZE 3
+int bq769x0_bms_get_regs(struct bq769x0_bms_conf* conf,
+                               uint8_t reg_start, uint8_t* values, uint8_t len)
+{
+       int ret = 0;
+       char cmd_buf[CMD_BUF_SIZE] = { conf->addr, 0, (conf->addr | I2C_READ_BIT), };
+       char ctrl_buf[CMD_BUF_SIZE] = { I2C_CONT, I2C_DO_REPEATED_START, I2C_CONT, };
+
+       if (bq769x0_probe_bms(conf) != 1) {
+               return -ENODEV;
+       }
+       if (values == NULL) {
+               return -EINVAL;
+       }
+       if ((reg_start + len - 1) > BQ769X0_LAST_REG) {
+               return -EINVAL;
+       }
+       cmd_buf[1] = reg_start;
+
+       ret = i2c_read(conf->bus_num, cmd_buf, CMD_BUF_SIZE, ctrl_buf, values, len);
+       if (ret != len) {
+               conf->probe_ok = 0;
+               return ret;
+       }
+       return 0;
+}
+
+#define REG_WR_CMD_SIZE 2
+#define MAX_WR_BUF_SIZE  (REG_WR_CMD_SIZE + CC_CFG) /* Maybe someone wants to write all regs at once ? */
+int bq769x0_bms_set_regs(struct bq769x0_bms_conf* conf,
+                               uint8_t reg_start, uint8_t* values, uint8_t len)
+{
+       int ret = 0;
+       char cmd_buf[MAX_WR_BUF_SIZE] = { conf->addr, };
+
+       if (bq769x0_probe_bms(conf) != 1) {
+               return -ENODEV;
+       }
+       if (values == NULL) {
+               return -EINVAL;
+       }
+       if ((reg_start + len - 1) > CC_CFG) { /* CC_CFG is the last RW reg */
+               return -EINVAL;
+       }
+       cmd_buf[1] = reg_start;
+
+       memcpy((cmd_buf + REG_WR_CMD_SIZE), values, len);
+       ret = i2c_write(conf->bus_num, cmd_buf, (REG_WR_CMD_SIZE + len), NULL);
+       if (ret != (REG_WR_CMD_SIZE + len)) {
+               conf->probe_ok = 0;
+               return ret;
+       }
+       return 0;
+}
+
+int bq769x0_bms_read_adc_gain(struct bq769x0_bms_conf* conf)
+{
+       int ret = 0;
+       uint8_t gain = 0, tmp = 0;
+
+       /* Get GAIN two MSB bits */
+       ret = bq769x0_bms_get_regs(conf, ADC_GAIN_1, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 6;
+               return -1;
+       }
+       msleep(2);
+       gain = (tmp & 0x0C) << 1;
+
+       /* Get GAIN three LSB bits */
+       ret = bq769x0_bms_get_regs(conf, ADC_GAIN_2, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 7;
+               return -1;
+       }
+       msleep(2);
+       gain |= ((tmp >> 5) & 0x07);
+
+       /* Store gain and gain in µV */
+       conf->adc_gain = gain;
+       conf->adc_gain_uV = 365 + gain;
+
+       /* Get ADC offset */
+       ret = bq769x0_bms_get_regs(conf, ADC_OFFSET, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 8;
+               return -1;
+       }
+       conf->adc_offset = (int8_t)tmp;
+
+       return 0;
+}
+
+/* Check load presence.
+ * This is valid only when the battery charging is disabled.
+ * Return 1 if load is present, or 0 if load is not present and charge is disabled.
+ * Returns -1 on error, or -2 if charge is enabled
+ */
+int bq769x0_bms_load_present(struct bq769x0_bms_conf* conf)
+{
+       int ret = 0;
+       uint8_t sys_ctrl[2];
+       
+       ret = bq769x0_bms_get_regs(conf, SYS_CTRL_1, sys_ctrl, 2);
+       if (ret != 0) {
+               conf->probe_ok = 9;
+               return -1;
+       }
+       if (sys_ctrl[1] & CHARGE_ON) {
+               return -2;
+       }
+       if (LOAD_PRESENT(sys_ctrl[0])) {
+               return 1;
+       }
+       return 0; /*  */
+}
+
+/* BMS Shut-Down.
+ * Disable both charge and discharge and turn off BMS by sending shutdown sequence.
+ * Only way out of sequence is power cycle (battery removal and re-insertion or button).
+ */
+int bq769x0_bms_shutdown(struct bq769x0_bms_conf* conf)
+{
+       int ret = 0;
+       uint8_t tmp = 0;
+
+       /* Turn off both charge and discharge */
+       tmp = 0;
+       ret = bq769x0_bms_set_regs(conf, SYS_CTRL_2, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 10;
+               return ret;
+       }
+       msleep(2);
+
+       /* Write shutdown sequence part 1 */
+       tmp = SHUTDOWN_CMD_A;
+       ret = bq769x0_bms_set_regs(conf, SYS_CTRL_1, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 11;
+               return ret;
+       }
+       msleep(2);
+       /* Write shutdown sequence part 2 */
+       tmp = SHUTDOWN_CMD_B;
+       ret = bq769x0_bms_set_regs(conf, SYS_CTRL_1, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 12;
+               return ret;
+       }
+       return 0; /* Shutdown OK ... should we reach this point ??? */
+}
+
+/* Start a conversion when the bms is not in Continuous CC mode. */
+int bq769x0_bms_start_conversion(struct bq769x0_bms_conf* conf)
+{
+       int ret = 0;
+       uint8_t tmp = 0;
+
+       /* Read actual config */
+       ret = bq769x0_bms_get_regs(conf, SYS_CTRL_2, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 13;
+               return ret;
+       }
+       msleep(2);
+       /* Set CC one-shot conversion start bit and write back */
+       tmp &= ~(CC_ENABLE);
+       tmp |= CC_ONE_SHOT;
+       ret = bq769x0_bms_set_regs(conf, SYS_CTRL_2, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 14;
+               return ret;
+       }
+       
+       return 0; /* Conversion start success */
+}
+
+/* Place the bms in continuous CC convertion mode */
+int bq769x0_bms_set_continuous_conversion(struct bq769x0_bms_conf* conf)
+{
+       int ret = 0;
+       uint8_t tmp = 0;
+
+       /* Read actual config */
+       ret = bq769x0_bms_get_regs(conf, SYS_CTRL_2, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 15;
+               return ret;
+       }
+       msleep(2);
+       /* Set CC continuous conversion bit and write back */
+       tmp |= CC_ENABLE;
+       ret = bq769x0_bms_set_regs(conf, SYS_CTRL_2, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 16;
+               return ret;
+       }
+       return 0; /* Configuration change success */
+}
+
+/* Enable/Disable charge and/or discharge
+ * Note that it is not possible to keep the old charge/discharge state and modify
+ *  only the other one with this function.
+ */
+int bq769x0_bms_change_state(struct bq769x0_bms_conf* conf, uint8_t charge, uint8_t discharge)
+{
+       int ret = 0;
+       uint8_t tmp = 0;
+
+       /* Read actual config */
+       ret = bq769x0_bms_get_regs(conf, SYS_CTRL_2, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 17;
+               return ret;
+       }
+       msleep(2);
+       /* Update required bits */
+       tmp &= ~(CHARGE_ON | DISCHARGE_ON);
+       if (charge) {
+               tmp |= CHARGE_ON;
+       }
+       if (discharge) {
+               tmp |= DISCHARGE_ON;
+       }
+       ret = bq769x0_bms_set_regs(conf, SYS_CTRL_2, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 18;
+               return ret;
+       }
+       return 0; /* Configuration change success */
+}
+
+
+/* Set the uvlo (under voltage lockout), ovp (overvoltage protection),
+ *  ocd (over curent in discharge) and scd (short circuit in discharge) values
+ * Note that timmings will be set to their minimum values.
+ * - scd is between 0 and 7, 0 is 22mV and 7 is 100mV with approx 11mV steps.
+ * - ocd is between 0 and 15, 0 is 8mV and 15 is 50mV with approx 3mV steps.
+ * - uvlo and ovp are the middle 8 bits of the 13 bits of ADC values with the corresponding mapping :
+ *   0b(10 <ovp> 1000) which is between 8200 and 12280
+ *   0b(01 <uvlo> 0000) which is between 4096 and 8176
+ * The uvlo and ovp values depend on ADC gain and ADC offset which are stored in
+ *   conf->adc_gain_uV and conf->adc_offset upon configuration.
+ */
+int bq769x0_bms_set_ranges(struct bq769x0_bms_conf* conf,
+                       uint8_t uvlo, uint8_t ovp, uint8_t ocd, uint8_t scd)
+{
+       int ret = 0;
+       uint8_t protect[5];
+
+       if ((scd > 0x07) || (ocd > 0x0F)) {
+               return -EINVAL;
+       }
+       protect[0] = scd & 0x07;
+       protect[1] = ocd & 0x0F;
+       protect[2] = 0;
+       protect[3] = ovp;
+       protect[4] = uvlo;
+       ret = bq769x0_bms_set_regs(conf, PROTECT_1, protect, 5);
+       if (ret != 0) {
+               conf->probe_ok = 19;
+               return ret;
+       }
+       return 0;
+}
+
+
+/* Read ADC values.
+ * type is one of :
+ *  - ALL : Read all values (up to nb_val)
+ *  - ALL_CELLS : Read all cells values. nb_val must equal conf->nb_cells (max 15)
+ *     (number of available cells depends on package)
+ *  - BATTERY : Read only the battery voltage (nb_val must be 1)
+ *  - THERMISTORS : Read only the Thermistor values. nb_val between 1 and 3 and
+ *     must equal conf->nb_thermistors
+ *     (number of available thermistors depends on package)
+ *  - CELL(x) : Read only cell "x" voltage
+ *  - CC_COUNT : Read the Coulomb counter value (nb_val must be 1)
+ */
+int bq769x0_bms_read_adc(struct bq769x0_bms_conf* conf, uint16_t* data, int type, int nb_val)
+{
+       int ret = 0, i = 0;
+       uint8_t offset = 0;
+
+       if ((nb_val == 0) || (data == NULL)) {
+               return -EINVAL;
+       }
+
+       switch (type) {
+               case BATTERY:
+                       offset = BAT_HIGH;
+                       if (nb_val != 1) {
+                               return -EINVAL;
+                       }
+                       break;
+               case ALL_CELLS:
+                       if (nb_val != conf->nb_cells) {
+                               return -EINVAL;
+                       }
+                       /* Breakthrough */
+               case ALL:
+                       offset = VC1_HIGH;
+                       break;
+               case THERMISTORS:
+                       offset = TS1_HIGH;
+                       if (nb_val > conf->nb_thermistors) {
+                               return -EINVAL;
+                       }
+                       break;
+               case CC_COUNT:
+                       offset = CC_HIGH;
+                       if (nb_val != 1) {
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       offset = VC1_HIGH + (2 * (type - 10));
+                       if (nb_val != 1) {
+                               return -EINVAL;
+                       }
+                       break;
+       }       
+       ret = bq769x0_bms_get_regs(conf, offset, (uint8_t*)data, (nb_val * 2));
+       if (ret != 0) {
+               return ret;
+       }
+
+       /* Change endianness */
+       for (i = 0; i < nb_val; i++) {
+               data[i] = (uint16_t)byte_swap_16(data[i]);
+       }
+       return 0;
+}
+
+int bq769x0_bms_get_and_erase_status(struct bq769x0_bms_conf* conf, uint8_t* status)
+{
+       int ret = 0;
+
+       if (status == NULL) {
+               return -EINVAL;
+       }
+       /* Read status */
+       ret = bq769x0_bms_get_regs(conf, SYSTEM_STATUS, status, 1);
+       if (ret != 0) {
+               return ret;
+       }
+       msleep(2);
+       /* Write back to clear all bits */
+       ret = bq769x0_bms_set_regs(conf, SYSTEM_STATUS, status, 1);
+       if (ret != 0) {
+               return ret;
+       }
+       return 0;
+}
+
+
+/* BMS config
+ * Performs default configuration of the bms.
+ * Return value :
+ *   Upon successfull completion, returns 0. On error, returns a negative integer
+ *   equivalent to errors from glibc.
+ */
+#define CONF_BUF_SIZE 4
+int bq769x0_bms_config(struct bq769x0_bms_conf* conf, uint8_t* old_status)
+{
+       int ret = 0;
+       uint8_t tmp = 0;
+
+       /* Probe BMS and get old status */
+       ret = bq769x0_bms_get_and_erase_status(conf, &tmp);
+       if (ret != 0) {
+               conf->probe_ok = 2;
+               return ret;
+       }
+       if (old_status != NULL) {
+               *old_status = tmp;
+       }
+       msleep(2);
+
+       /* Programm CC_CFG as requested in datasheet */
+       tmp = CC_CFG_DEFAULT;
+       ret = bq769x0_bms_set_regs(conf, CC_CFG, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 3;
+               return ret;
+       }
+       msleep(2);
+
+       /* Program first control register :
+        *  - enable ADC
+        *  - set temp readings to externel thermistor instead of Die temperature
+        */
+       tmp = ADC_ENABLE | TEMP_SEL_TSX;
+       ret = bq769x0_bms_set_regs(conf, SYS_CTRL_1, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 4;
+               return ret;
+       }
+
+       msleep(2);
+       /* Program second control register :
+        *  - Enable coulomb counter
+        *  - Enable discharge
+        *  - Enable charge
+        */
+       tmp = CC_ENABLE | DISCHARGE_ON | CHARGE_ON;
+       ret = bq769x0_bms_set_regs(conf, SYS_CTRL_2, &tmp, 1);
+       if (ret != 0) {
+               conf->probe_ok = 5;
+               return ret;
+       }
+       msleep(2);
+
+       /* Re-read (and discard) status so that any new status read reflects a fresh status */
+       ret = bq769x0_bms_get_and_erase_status(conf, &tmp);
+       if (ret != 0) {
+               return ret;
+       }
+       msleep(2);
+       
+       /* Get the ADC gain and offset once upon startup */
+       ret = bq769x0_bms_read_adc_gain(conf);
+       if (ret != 0) {
+               return ret;
+       }
+
+       return 0; /* Config success */
+}
+
diff --git a/include/extdrv/bq769x0_bms.h b/include/extdrv/bq769x0_bms.h
new file mode 100644 (file)
index 0000000..ad523cb
--- /dev/null
@@ -0,0 +1,162 @@
+/****************************************************************************
+ *   extdrv/bq769x0_bms.h
+ *
+ *
+ * Copyright 2020 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef EXTDRV_BQ769X0_H
+#define EXTDRV_BQ769X0_H
+
+#include "lib/stdint.h"
+
+
+/***************************************************************************** */
+/*          Support for BQ769X0 Battery Protection from Texas Instrument       */
+/***************************************************************************** */
+/* This driver is made for the BQ76920 version of the chip, though there's few
+ * diferences between the BQ76920, BQ76930 and BQ76950 versions.
+ * This driver does not handle the Alert command.
+ */
+
+/* BQ769X0 instance data.
+ * Use one of this for each BMS you want to access.
+ * - addr is the BMS address on most significant bits.
+ */
+struct bq769x0_bms_conf {
+       uint8_t addr;
+       uint8_t bus_num;
+       uint8_t probe_ok;
+       uint8_t crc_check; /* 0 : No CRC check */
+       uint8_t adc_gain;
+       int8_t adc_offset;
+       uint16_t adc_gain_uV; /* ADC gain converted to µV */
+       uint8_t nb_cells;
+       uint8_t nb_thermistors;
+};
+
+/* Check the bms presence, return 1 if found
+ * This is a basic check, it could be anything with the same address ...
+ */
+int bq769x0_probe_bms(struct bq769x0_bms_conf* conf);
+
+
+int bq769x0_bms_get_regs(struct bq769x0_bms_conf* conf,
+                uint8_t reg_start, uint8_t* values, uint8_t len);
+
+int bq769x0_bms_set_regs(struct bq769x0_bms_conf* conf,
+                               uint8_t reg_start, uint8_t* values, uint8_t len);
+
+
+/* Read the BMS status Word and erase it's content
+ * conf: the bms configuration structure.
+ * status: pointer to an uint8_t value to store the old status value. May NOT be NULL.
+ * Return value:
+ *   Upon successfull completion, returns 0. On error, returns a negative integer
+ *   equivalent to errors from glibc.
+ */
+int bq769x0_bms_get_and_erase_status(struct bq769x0_bms_conf* conf, uint8_t* status);
+
+/* SYS Status */
+#define CC_READY(x)  ((x) & 0x80)  /* New Coulomb Counter reading available */
+#define DEVICE_NOT_READY(x)  ((x) & 0x20)  /* Set means internal fault */
+#define EXT_OVERRIDE_ALERT(x)  ((x) & 0x10)
+#define UNDER_VOLTAGE(x)  ((x) & 0x08)  /* UV */
+#define OVER_VOLTAGE(x)  ((x) & 0x04)   /* OV */
+#define SHORT_CIRCUIT_DISCHARGE(x)  ((x) & 0x02) /* SCD */
+#define OVER_CURRENT_DISCHARGE(x)  ((x) & 0x01) /* OCD */
+
+
+
+/* Check load presence.
+ * This is valid only when the battery charging is disabled.
+ * Return 1 if load is present, or 0 if load is not present and charge is disabled.
+ * Returns -1 on error, or -2 if charge is enabled
+ */
+int bq769x0_bms_load_present(struct bq769x0_bms_conf* conf);
+
+/* BMS Shut-Down.
+ * Disable both charge and discharge and turn off BMS by sending shutdown sequence.
+ * Only way out of sequence is power cycle (battery removal and re-insertion or button).
+ */
+int bq769x0_bms_shutdown(struct bq769x0_bms_conf* conf);
+
+/* Start a conversion when the bms is not in Continuous CC mode. */
+int bq769x0_bms_start_conversion(struct bq769x0_bms_conf* conf);
+
+/* Place the bms in continuous CC convertion mode */
+int bq769x0_bms_set_continuous_conversion(struct bq769x0_bms_conf* conf);
+
+/* Enable/Disable charge and/or discharge
+ * Note that it is not possible to keep the old charge/discharge state and modify
+ *  only the other one with this function.
+ */
+int bq769x0_bms_change_state(struct bq769x0_bms_conf* conf, uint8_t charge, uint8_t discharge);
+
+/* Set the uvlo (under voltage lockout), ovp (overvoltage protection),
+ *  ocd (over curent in discharge) and scd (short circuit in discharge) values
+ * Note that timmings will be set to their minimum values.
+ * - scd is between 0 and 7, 0 is 22mV and 7 is 100mV with approx 11mV steps.
+ * - ocd is between 0 and 15, 0 is 8mV and 15 is 50mV with approx 3mV steps.
+ * - uvlo and ovp are the middle 8 bits of the 13 bits of ADC values with the corresponding mapping :
+ *   0b(10 <ovp> 1000) which is between 8200 and 12280
+ *   0b(01 <uvlo> 0000) which is between 4096 and 8176
+ * The uvlo and ovp values depend on ADC gain and ADC offset which are stored in
+ *   conf->adc_gain_uV and conf->adc_offset upon configuration.
+ */
+int bq769x0_bms_set_ranges(struct bq769x0_bms_conf* conf,
+            uint8_t uvlo, uint8_t ovp, uint8_t ocd, uint8_t scd);
+
+
+
+enum bq769x0_bms_adc_values {
+       ALL = 0,
+       BATTERY,
+       ALL_CELLS,
+       THERMISTORS,
+       CC_COUNT,
+};
+#define CELL(x) ((x) + 10)
+
+/* Read ADC values.
+ * type is one of :
+ *  - ALL : Read all values (up to nb_val)
+ *  - ALL_CELLS : Read all cells values. nb_val must equal conf->nb_cells (max 15)
+ *     (number of available cells depends on package)
+ *  - BATTERY : Read only the battery voltage (nb_val must be 1)
+ *  - THERMISTORS : Read only the Thermistor values. nb_val between 1 and 3 and
+ *     must equal conf->nb_thermistors
+ *     (number of available thermistors depends on package)
+ *  - CELL(x) : Read only cell "x" voltage
+ *  - CC_COUNT : Read the Coulomb counter value (nb_val must be 1)
+ */
+int bq769x0_bms_read_adc(struct bq769x0_bms_conf* conf, uint16_t* data, int type, int nb_val);
+
+
+/* BMS config
+ * Performs default configuration of the BMS.
+ * conf: the bms configuration structure.
+ * old_status: pointer to an uint8_t value to store the old status value. May be NULL.
+ * Return value:
+ *   Upon successfull completion, returns 0. On error, returns a negative integer
+ *   equivalent to errors from glibc.
+ */
+int bq769x0_bms_config(struct bq769x0_bms_conf* conf, uint8_t* old_status);
+
+#endif /* EXTDRV_BQ769X0_H */
+