sensor: bq274xx: Add BQ27421 driver
Add support for TI BQ27421 fuel gauge sensor Signed-off-by: Parthiban Nallathambi <parthiban@linumiz.com> Signed-off-by: NavinSankar Velliangiri <navin@linumiz.com>
This commit is contained in:
parent
4185da0a7e
commit
e65b14c2b6
10 changed files with 828 additions and 0 deletions
|
@ -15,6 +15,7 @@ add_subdirectory_ifdef(CONFIG_BME680 bme680)
|
|||
add_subdirectory_ifdef(CONFIG_BMG160 bmg160)
|
||||
add_subdirectory_ifdef(CONFIG_BMI160 bmi160)
|
||||
add_subdirectory_ifdef(CONFIG_BMM150 bmm150)
|
||||
add_subdirectory_ifdef(CONFIG_BQ274XX bq274xx)
|
||||
add_subdirectory_ifdef(CONFIG_CCS811 ccs811)
|
||||
add_subdirectory_ifdef(CONFIG_DHT dht)
|
||||
add_subdirectory_ifdef(CONFIG_ENS210 ens210)
|
||||
|
|
|
@ -59,6 +59,8 @@ source "drivers/sensor/bmi160/Kconfig"
|
|||
|
||||
source "drivers/sensor/bmm150/Kconfig"
|
||||
|
||||
source "drivers/sensor/bq274xx/Kconfig"
|
||||
|
||||
source "drivers/sensor/ccs811/Kconfig"
|
||||
|
||||
source "drivers/sensor/dht/Kconfig"
|
||||
|
|
7
drivers/sensor/bq274xx/CMakeLists.txt
Normal file
7
drivers/sensor/bq274xx/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Copyright (c) 2020 Linumiz
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_BQ274XX bq274xx.c)
|
9
drivers/sensor/bq274xx/Kconfig
Normal file
9
drivers/sensor/bq274xx/Kconfig
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2020 Linumiz
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config BQ274XX
|
||||
bool "BQ274xx Fuel Gauge"
|
||||
depends on (I2C && HAS_DTS_I2C)
|
||||
help
|
||||
Enable I2C-based driver for BQ274xx Fuel Gauge.
|
633
drivers/sensor/bq274xx/bq274xx.c
Normal file
633
drivers/sensor/bq274xx/bq274xx.c
Normal file
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Linumiz
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT ti_bq274xx
|
||||
|
||||
#include <drivers/i2c.h>
|
||||
#include <init.h>
|
||||
#include <drivers/sensor.h>
|
||||
#include <sys/__assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/byteorder.h>
|
||||
|
||||
#include "bq274xx.h"
|
||||
|
||||
#define BQ274XX_SUBCLASS_DELAY 5 /* subclass 64 & 82 needs 5ms delay */
|
||||
|
||||
static int bq274xx_command_reg_read(struct bq274xx_data *bq274xx, u8_t reg_addr,
|
||||
s16_t *val)
|
||||
{
|
||||
u8_t i2c_data[2];
|
||||
int status;
|
||||
|
||||
status = i2c_burst_read(bq274xx->i2c, DT_INST_REG_ADDR(0), reg_addr,
|
||||
i2c_data, 2);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to read register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*val = (i2c_data[1] << 8) | i2c_data[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq274xx_control_reg_write(struct bq274xx_data *bq274xx,
|
||||
u16_t subcommand)
|
||||
{
|
||||
u8_t i2c_data, reg_addr;
|
||||
int status = 0;
|
||||
|
||||
reg_addr = BQ274XX_COMMAND_CONTROL_LOW;
|
||||
i2c_data = (u8_t)((subcommand)&0x00FF);
|
||||
|
||||
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0), reg_addr,
|
||||
i2c_data);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to write into control low register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
k_msleep(BQ274XX_SUBCLASS_DELAY);
|
||||
|
||||
reg_addr = BQ274XX_COMMAND_CONTROL_HIGH;
|
||||
i2c_data = (u8_t)((subcommand >> 8) & 0x00FF);
|
||||
|
||||
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0), reg_addr,
|
||||
i2c_data);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to write into control high register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq274xx_command_reg_write(struct bq274xx_data *bq274xx, u8_t command,
|
||||
u8_t data)
|
||||
{
|
||||
u8_t i2c_data, reg_addr;
|
||||
int status = 0;
|
||||
|
||||
reg_addr = command;
|
||||
i2c_data = data;
|
||||
|
||||
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0), reg_addr,
|
||||
i2c_data);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to write into control register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq274xx_read_data_block(struct bq274xx_data *bq274xx, u8_t offset,
|
||||
u8_t *data, u8_t bytes)
|
||||
{
|
||||
u8_t i2c_data;
|
||||
int status = 0;
|
||||
|
||||
i2c_data = BQ274XX_EXTENDED_BLOCKDATA_START + offset;
|
||||
|
||||
status = i2c_burst_read(bq274xx->i2c, DT_INST_REG_ADDR(0), i2c_data,
|
||||
data, bytes);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read block");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
k_msleep(BQ274XX_SUBCLASS_DELAY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq274xx_get_device_type(struct bq274xx_data *bq274xx, u16_t *val)
|
||||
{
|
||||
int status;
|
||||
|
||||
status =
|
||||
bq274xx_control_reg_write(bq274xx, BQ274XX_CONTROL_DEVICE_TYPE);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to write control register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = bq274xx_command_reg_read(bq274xx, BQ274XX_COMMAND_CONTROL_LOW,
|
||||
val);
|
||||
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to read register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sensor value get
|
||||
*
|
||||
* @return -ENOTSUP for unsupported channels
|
||||
*/
|
||||
static int bq274xx_channel_get(struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct bq274xx_data *bq274xx = dev->driver_data;
|
||||
float int_temp;
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_GAUGE_VOLTAGE:
|
||||
val->val1 = ((bq274xx->voltage / 1000));
|
||||
val->val2 = ((bq274xx->voltage % 1000) * 1000U);
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_AVG_CURRENT:
|
||||
val->val1 = ((bq274xx->avg_current / 1000));
|
||||
val->val2 = ((bq274xx->avg_current % 1000) * 1000U);
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_STDBY_CURRENT:
|
||||
val->val1 = ((bq274xx->stdby_current / 1000));
|
||||
val->val2 = ((bq274xx->stdby_current % 1000) * 1000U);
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT:
|
||||
val->val1 = ((bq274xx->max_load_current / 1000));
|
||||
val->val2 = ((bq274xx->max_load_current % 1000) * 1000U);
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_TEMP:
|
||||
int_temp = (bq274xx->internal_temperature * 0.1);
|
||||
int_temp = int_temp - 273.15;
|
||||
val->val1 = (s32_t)int_temp;
|
||||
val->val2 = (int_temp - (s32_t)int_temp) * 1000000;
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
|
||||
val->val1 = bq274xx->state_of_charge;
|
||||
val->val2 = 0;
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_STATE_OF_HEALTH:
|
||||
val->val1 = bq274xx->state_of_health;
|
||||
val->val2 = 0;
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY:
|
||||
val->val1 = (bq274xx->full_charge_capacity / 1000);
|
||||
val->val2 = ((bq274xx->full_charge_capacity % 1000) * 1000U);
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY:
|
||||
val->val1 = (bq274xx->remaining_charge_capacity / 1000);
|
||||
val->val2 =
|
||||
((bq274xx->remaining_charge_capacity % 1000) * 1000U);
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY:
|
||||
val->val1 = (bq274xx->nom_avail_capacity / 1000);
|
||||
val->val2 = ((bq274xx->nom_avail_capacity % 1000) * 1000U);
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY:
|
||||
val->val1 = (bq274xx->full_avail_capacity / 1000);
|
||||
val->val2 = ((bq274xx->full_avail_capacity % 1000) * 1000U);
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_AVG_POWER:
|
||||
val->val1 = (bq274xx->avg_power / 1000);
|
||||
val->val2 = ((bq274xx->avg_power % 1000) * 1000U);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq274xx_sample_fetch(struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
struct bq274xx_data *bq274xx = dev->driver_data;
|
||||
int status = 0;
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_GAUGE_VOLTAGE:
|
||||
status = bq274xx_command_reg_read(
|
||||
bq274xx, BQ274XX_COMMAND_VOLTAGE, &bq274xx->voltage);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read voltage");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_AVG_CURRENT:
|
||||
status = bq274xx_command_reg_read(bq274xx,
|
||||
BQ274XX_COMMAND_AVG_CURRENT,
|
||||
&bq274xx->avg_current);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read average current ");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_TEMP:
|
||||
status = bq274xx_command_reg_read(
|
||||
bq274xx, BQ274XX_COMMAND_INT_TEMP,
|
||||
&bq274xx->internal_temperature);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read internal temperature");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_STDBY_CURRENT:
|
||||
status = bq274xx_command_reg_read(bq274xx,
|
||||
BQ274XX_COMMAND_STDBY_CURRENT,
|
||||
&bq274xx->stdby_current);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read standby current");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT:
|
||||
status = bq274xx_command_reg_read(bq274xx,
|
||||
BQ274XX_COMMAND_MAX_CURRENT,
|
||||
&bq274xx->max_load_current);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read maximum current");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
|
||||
status = bq274xx_command_reg_read(bq274xx, BQ274XX_COMMAND_SOC,
|
||||
&bq274xx->state_of_charge);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read state of charge");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY:
|
||||
status = bq274xx_command_reg_read(
|
||||
bq274xx, BQ274XX_COMMAND_FULL_CAPACITY,
|
||||
&bq274xx->full_charge_capacity);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read full charge capacity");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY:
|
||||
status = bq274xx_command_reg_read(
|
||||
bq274xx, BQ274XX_COMMAND_REM_CAPACITY,
|
||||
&bq274xx->remaining_charge_capacity);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read remaining charge capacity");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY:
|
||||
status = bq274xx_command_reg_read(bq274xx,
|
||||
BQ274XX_COMMAND_NOM_CAPACITY,
|
||||
&bq274xx->nom_avail_capacity);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read nominal available capacity");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY:
|
||||
status =
|
||||
bq274xx_command_reg_read(bq274xx,
|
||||
BQ274XX_COMMAND_AVAIL_CAPACITY,
|
||||
&bq274xx->full_avail_capacity);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read full available capacity");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_AVG_POWER:
|
||||
status = bq274xx_command_reg_read(bq274xx,
|
||||
BQ274XX_COMMAND_AVG_POWER,
|
||||
&bq274xx->avg_power);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read battery average power");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GAUGE_STATE_OF_HEALTH:
|
||||
status = bq274xx_command_reg_read(bq274xx, BQ274XX_COMMAND_SOH,
|
||||
&bq274xx->state_of_health);
|
||||
|
||||
bq274xx->state_of_health = (bq274xx->state_of_health) & 0x00FF;
|
||||
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read state of health");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief initialise the fuel gauge
|
||||
*
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int bq274xx_gauge_init(struct device *dev)
|
||||
{
|
||||
struct bq274xx_data *bq274xx = dev->driver_data;
|
||||
const struct bq274xx_config *const config = dev->config->config_info;
|
||||
int status = 0;
|
||||
u8_t tmp_checksum = 0, checksum_old = 0, checksum_new = 0;
|
||||
u16_t flags = 0, designenergy_mwh = 0, taperrate = 0, id;
|
||||
u8_t designcap_msb, designcap_lsb, designenergy_msb, designenergy_lsb,
|
||||
terminatevolt_msb, terminatevolt_lsb, taperrate_msb,
|
||||
taperrate_lsb;
|
||||
u8_t block[32];
|
||||
|
||||
designenergy_mwh = (u16_t)3.7 * config->design_capacity;
|
||||
taperrate =
|
||||
(u16_t)config->design_capacity / (0.1 * config->taper_current);
|
||||
|
||||
bq274xx->i2c = device_get_binding(config->bus_name);
|
||||
if (bq274xx == NULL) {
|
||||
LOG_ERR("Could not get pointer to %s device.",
|
||||
config->bus_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
status = bq274xx_get_device_type(bq274xx, &id);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to get device ID");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (id != BQ274XX_DEVICE_ID) {
|
||||
LOG_ERR("Invalid Device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/** Unseal the battery control register **/
|
||||
status = bq274xx_control_reg_write(bq274xx, BQ274XX_UNSEAL_KEY);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to unseal the battery");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = bq274xx_control_reg_write(bq274xx, BQ274XX_UNSEAL_KEY);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to unseal the battery");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Send CFG_UPDATE */
|
||||
status = bq274xx_control_reg_write(bq274xx,
|
||||
BQ274XX_CONTROL_SET_CFGUPDATE);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to set CFGUpdate");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/** Step to place the Gauge into CONFIG UPDATE Mode **/
|
||||
do {
|
||||
status = bq274xx_command_reg_read(
|
||||
bq274xx, BQ274XX_COMMAND_FLAGS, &flags);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to read flags");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!(flags & 0x0010)) {
|
||||
k_msleep(BQ274XX_SUBCLASS_DELAY * 10);
|
||||
}
|
||||
|
||||
} while (!(flags & 0x0010));
|
||||
|
||||
status = bq274xx_command_reg_write(bq274xx,
|
||||
BQ274XX_EXTENDED_DATA_CONTROL, 0x00);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to enable block data memory");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Access State subclass */
|
||||
status = bq274xx_command_reg_write(bq274xx, BQ274XX_EXTENDED_DATA_CLASS,
|
||||
0x52);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to update state subclass");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Write the block offset */
|
||||
status = bq274xx_command_reg_write(bq274xx, BQ274XX_EXTENDED_DATA_BLOCK,
|
||||
0x00);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to update block offset");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (u8_t i = 0; i < 32; i++) {
|
||||
block[i] = 0;
|
||||
}
|
||||
|
||||
status = bq274xx_read_data_block(bq274xx, 0x00, block, 32);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to read block data");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
tmp_checksum = 0;
|
||||
for (u8_t i = 0; i < 32; i++) {
|
||||
tmp_checksum += block[i];
|
||||
}
|
||||
tmp_checksum = 255 - tmp_checksum;
|
||||
|
||||
/* Read the block checksum */
|
||||
status = i2c_reg_read_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_CHECKSUM, &checksum_old);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to read block checksum");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
designcap_msb = config->design_capacity >> 8;
|
||||
designcap_lsb = config->design_capacity & 0x00FF;
|
||||
designenergy_msb = designenergy_mwh >> 8;
|
||||
designenergy_lsb = designenergy_mwh & 0x00FF;
|
||||
terminatevolt_msb = config->terminate_voltage >> 8;
|
||||
terminatevolt_lsb = config->terminate_voltage & 0x00FF;
|
||||
taperrate_msb = taperrate >> 8;
|
||||
taperrate_lsb = taperrate & 0x00FF;
|
||||
|
||||
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_BLOCKDATA_DESIGN_CAP_HIGH,
|
||||
designcap_msb);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to write designCAP MSB");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_BLOCKDATA_DESIGN_CAP_LOW,
|
||||
designcap_lsb);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to erite designCAP LSB");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_BLOCKDATA_DESIGN_ENR_HIGH,
|
||||
designenergy_msb);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to write designEnergy MSB");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_BLOCKDATA_DESIGN_ENR_LOW,
|
||||
designenergy_lsb);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to erite designEnergy LSB");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = i2c_reg_write_byte(
|
||||
bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_BLOCKDATA_TERMINATE_VOLT_HIGH,
|
||||
terminatevolt_msb);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to write terminateVolt MSB");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = i2c_reg_write_byte(
|
||||
bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_BLOCKDATA_TERMINATE_VOLT_LOW,
|
||||
terminatevolt_lsb);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to write terminateVolt LSB");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_BLOCKDATA_TAPERRATE_HIGH,
|
||||
taperrate_msb);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to write taperRate MSB");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = i2c_reg_write_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_BLOCKDATA_TAPERRATE_LOW,
|
||||
taperrate_lsb);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to erite taperRate LSB");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (u8_t i = 0; i < 32; i++) {
|
||||
block[i] = 0;
|
||||
}
|
||||
|
||||
status = bq274xx_read_data_block(bq274xx, 0x00, block, 32);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to read block data");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
checksum_new = 0;
|
||||
for (u8_t i = 0; i < 32; i++) {
|
||||
checksum_new += block[i];
|
||||
}
|
||||
checksum_new = 255 - checksum_new;
|
||||
|
||||
status = bq274xx_command_reg_write(bq274xx, BQ274XX_EXTENDED_CHECKSUM,
|
||||
checksum_new);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to update new checksum");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
tmp_checksum = 0;
|
||||
status = i2c_reg_read_byte(bq274xx->i2c, DT_INST_REG_ADDR(0),
|
||||
BQ274XX_EXTENDED_CHECKSUM, &tmp_checksum);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read checksum");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = bq274xx_control_reg_write(bq274xx, BQ274XX_CONTROL_BAT_INSERT);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to configure BAT Detect");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
status = bq274xx_control_reg_write(bq274xx, BQ274XX_CONTROL_SOFT_RESET);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to soft reset the gauge");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
/* Poll Flags */
|
||||
do {
|
||||
status = bq274xx_command_reg_read(
|
||||
bq274xx, BQ274XX_COMMAND_FLAGS, &flags);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Unable to read flags");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!(flags & 0x0010)) {
|
||||
k_msleep(BQ274XX_SUBCLASS_DELAY * 10);
|
||||
}
|
||||
} while ((flags & 0x0010));
|
||||
|
||||
/* Seal the gauge */
|
||||
status = bq274xx_control_reg_write(bq274xx, BQ274XX_CONTROL_SEALED);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to seal the gauge");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api bq274xx_battery_driver_api = {
|
||||
.sample_fetch = bq274xx_sample_fetch,
|
||||
.channel_get = bq274xx_channel_get,
|
||||
};
|
||||
|
||||
#define BQ274XX_INIT(index) \
|
||||
static struct bq274xx_data bq274xx_driver_##index; \
|
||||
\
|
||||
static const struct bq274xx_config bq274xx_config_##index = { \
|
||||
.bus_name = DT_INST_BUS_LABEL(index), \
|
||||
.design_voltage = DT_INST_PROP(index, design_voltage), \
|
||||
.design_capacity = DT_INST_PROP(index, design_capacity), \
|
||||
.taper_current = DT_INST_PROP(index, taper_current), \
|
||||
.terminate_voltage = DT_INST_PROP(index, terminate_voltage), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_AND_API_INIT(bq274xx_##index, DT_INST_LABEL(index), \
|
||||
&bq274xx_gauge_init, &bq274xx_driver_##index, \
|
||||
&bq274xx_config_##index, POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&bq274xx_battery_driver_api) \
|
||||
|
||||
DT_INST_FOREACH(BQ274XX_INIT)
|
105
drivers/sensor/bq274xx/bq274xx.h
Normal file
105
drivers/sensor/bq274xx/bq274xx.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright(c) 2020 Linumiz
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_BATTERY_BQ274XX_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_BATTERY_BQ274XX_H_
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(bq274xx, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
/*** General Constant ***/
|
||||
#define BQ274XX_UNSEAL_KEY 0x8000 /* Secret code to unseal the BQ27441-G1A */
|
||||
#define BQ274XX_DEVICE_ID 0x0421 /* Default device ID */
|
||||
|
||||
/*** Standard Commands ***/
|
||||
#define BQ274XX_COMMAND_CONTROL_LOW 0x00 /* Control() low register */
|
||||
#define BQ274XX_COMMAND_CONTROL_HIGH 0x01 /* Control() high register */
|
||||
#define BQ274XX_COMMAND_TEMP 0x02 /* Temperature() */
|
||||
#define BQ274XX_COMMAND_VOLTAGE 0x04 /* Voltage() */
|
||||
#define BQ274XX_COMMAND_FLAGS 0x06 /* Flags() */
|
||||
#define BQ274XX_COMMAND_NOM_CAPACITY 0x08 /* NominalAvailableCapacity() */
|
||||
#define BQ274XX_COMMAND_AVAIL_CAPACITY 0x0A /* FullAvailableCapacity() */
|
||||
#define BQ274XX_COMMAND_REM_CAPACITY 0x0C /* RemainingCapacity() */
|
||||
#define BQ274XX_COMMAND_FULL_CAPACITY 0x0E /* FullChargeCapacity() */
|
||||
#define BQ274XX_COMMAND_AVG_CURRENT 0x10 /* AverageCurrent() */
|
||||
#define BQ274XX_COMMAND_STDBY_CURRENT 0x12 /* StandbyCurrent() */
|
||||
#define BQ274XX_COMMAND_MAX_CURRENT 0x14 /* MaxLoadCurrent() */
|
||||
#define BQ274XX_COMMAND_AVG_POWER 0x18 /* AveragePower() */
|
||||
#define BQ274XX_COMMAND_SOC 0x1C /* StateOfCharge() */
|
||||
#define BQ274XX_COMMAND_INT_TEMP 0x1E /* InternalTemperature() */
|
||||
#define BQ274XX_COMMAND_SOH 0x20 /* StateOfHealth() */
|
||||
#define BQ274XX_COMMAND_REM_CAP_UNFL 0x28 /* RemainingCapacityUnfiltered() */
|
||||
#define BQ274XX_COMMAND_REM_CAP_FIL 0x2A /* RemainingCapacityFiltered() */
|
||||
#define BQ274XX_COMMAND_FULL_CAP_UNFL 0x2C /* FullChargeCapacityUnfiltered() */
|
||||
#define BQ274XX_COMMAND_FULL_CAP_FIL 0x2E /* FullChargeCapacityFiltered() */
|
||||
#define BQ274XX_COMMAND_SOC_UNFL 0x30 /* StateOfChargeUnfiltered() */
|
||||
|
||||
/*** Control Sub-Commands ***/
|
||||
#define BQ274XX_CONTROL_STATUS 0x0000
|
||||
#define BQ274XX_CONTROL_DEVICE_TYPE 0x0001
|
||||
#define BQ274XX_CONTROL_FW_VERSION 0x0002
|
||||
#define BQ274XX_CONTROL_DM_CODE 0x0004
|
||||
#define BQ274XX_CONTROL_PREV_MACWRITE 0x0007
|
||||
#define BQ274XX_CONTROL_CHEM_ID 0x0008
|
||||
#define BQ274XX_CONTROL_BAT_INSERT 0x000C
|
||||
#define BQ274XX_CONTROL_BAT_REMOVE 0x000D
|
||||
#define BQ274XX_CONTROL_SET_HIBERNATE 0x0011
|
||||
#define BQ274XX_CONTROL_CLEAR_HIBERNATE 0x0012
|
||||
#define BQ274XX_CONTROL_SET_CFGUPDATE 0x0013
|
||||
#define BQ274XX_CONTROL_SHUTDOWN_ENABLE 0x001B
|
||||
#define BQ274XX_CONTROL_SHUTDOWN 0x001C
|
||||
#define BQ274XX_CONTROL_SEALED 0x0020
|
||||
#define BQ274XX_CONTROL_PULSE_SOC_INT 0x0023
|
||||
#define BQ274XX_CONTROL_RESET 0x0041
|
||||
#define BQ274XX_CONTROL_SOFT_RESET 0x0042
|
||||
#define BQ274XX_CONTROL_EXIT_CFGUPDATE 0x0043
|
||||
#define BQ274XX_CONTROL_EXIT_RESIM 0x0044
|
||||
|
||||
/*** Extended Data Commands ***/
|
||||
#define BQ274XX_EXTENDED_OPCONFIG 0x3A /* OpConfig() */
|
||||
#define BQ274XX_EXTENDED_CAPACITY 0x3C /* DesignCapacity() */
|
||||
#define BQ274XX_EXTENDED_DATA_CLASS 0x3E /* DataClass() */
|
||||
#define BQ274XX_EXTENDED_DATA_BLOCK 0x3F /* DataBlock() */
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_START 0x40 /* BlockData_start() */
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_END 0x5F /* BlockData_end() */
|
||||
#define BQ274XX_EXTENDED_CHECKSUM 0x60 /* BlockDataCheckSum() */
|
||||
#define BQ274XX_EXTENDED_DATA_CONTROL 0x61 /* BlockDataControl() */
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_DESIGN_CAP_HIGH 0x4A /* BlockData */
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_DESIGN_CAP_LOW 0x4B
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_DESIGN_ENR_HIGH 0x4C
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_DESIGN_ENR_LOW 0x4D
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_TERMINATE_VOLT_HIGH 0x50
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_TERMINATE_VOLT_LOW 0x51
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_TAPERRATE_HIGH 0x5B
|
||||
#define BQ274XX_EXTENDED_BLOCKDATA_TAPERRATE_LOW 0x5C
|
||||
|
||||
#define BQ274XX_DELAY 1000
|
||||
|
||||
struct bq274xx_data {
|
||||
struct device *i2c;
|
||||
u16_t voltage;
|
||||
s16_t avg_current;
|
||||
s16_t stdby_current;
|
||||
s16_t max_load_current;
|
||||
s16_t avg_power;
|
||||
u16_t state_of_charge;
|
||||
s16_t state_of_health;
|
||||
u16_t internal_temperature;
|
||||
u16_t full_charge_capacity;
|
||||
u16_t remaining_charge_capacity;
|
||||
u16_t nom_avail_capacity;
|
||||
u16_t full_avail_capacity;
|
||||
};
|
||||
|
||||
struct bq274xx_config {
|
||||
char *bus_name;
|
||||
u16_t design_voltage;
|
||||
u16_t design_capacity;
|
||||
u16_t taper_current;
|
||||
u16_t terminate_voltage;
|
||||
};
|
||||
|
||||
#endif
|
36
dts/bindings/sensor/ti,bq274xx.yaml
Normal file
36
dts/bindings/sensor/ti,bq274xx.yaml
Normal file
|
@ -0,0 +1,36 @@
|
|||
#
|
||||
# Copyright (c) 2020 Linumiz
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: Texas Instruments BQ274xx Fuel Gauge
|
||||
|
||||
compatible: "ti,bq274xx"
|
||||
|
||||
include: i2c-device.yaml
|
||||
|
||||
properties:
|
||||
design-voltage:
|
||||
type: int
|
||||
required: false
|
||||
description: Battery Design Volatge in (3300 - 4400)mV
|
||||
default: 3700
|
||||
|
||||
design-capacity:
|
||||
type: int
|
||||
required: false
|
||||
description: Battery Design Capacity in mAh
|
||||
default: 1800
|
||||
|
||||
taper-current:
|
||||
type: int
|
||||
required: false
|
||||
description: Battery Taper current in mAh
|
||||
default: 45
|
||||
|
||||
terminate-voltage:
|
||||
type: int
|
||||
required: false
|
||||
description: Battery Terminate Voltage in mV
|
||||
default: 3000
|
|
@ -137,6 +137,31 @@ enum sensor_channel {
|
|||
/** Revolutions per minute, in RPM. */
|
||||
SENSOR_CHAN_RPM,
|
||||
|
||||
/** Voltage, in volts **/
|
||||
SENSOR_CHAN_GAUGE_VOLTAGE,
|
||||
/** Average current, in amps **/
|
||||
SENSOR_CHAN_GAUGE_AVG_CURRENT,
|
||||
/** Standy current, in amps **/
|
||||
SENSOR_CHAN_GAUGE_STDBY_CURRENT,
|
||||
/** Max load current, in amps **/
|
||||
SENSOR_CHAN_GAUGE_MAX_LOAD_CURRENT,
|
||||
/** Gauge temperature **/
|
||||
SENSOR_CHAN_GAUGE_TEMP,
|
||||
/** State of charge measurement in % **/
|
||||
SENSOR_CHAN_GAUGE_STATE_OF_CHARGE,
|
||||
/** Full Charge Capacity in mAh **/
|
||||
SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY,
|
||||
/** Remaining Charge Capacity in mAh **/
|
||||
SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY,
|
||||
/** Nominal Available Capacity in mAh **/
|
||||
SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY,
|
||||
/** Full Available Capacity in mAh **/
|
||||
SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY,
|
||||
/** Average power in mW **/
|
||||
SENSOR_CHAN_GAUGE_AVG_POWER,
|
||||
/** State of health measurement in % **/
|
||||
SENSOR_CHAN_GAUGE_STATE_OF_HEALTH,
|
||||
|
||||
/** All channels. */
|
||||
SENSOR_CHAN_ALL,
|
||||
|
||||
|
|
|
@ -508,3 +508,12 @@ test_i2c_tmp116: tmp116@46 {
|
|||
reg = <0x46>;
|
||||
};
|
||||
|
||||
test_i2c_bq274xx: bq27xx@47 {
|
||||
compatible = "ti,bq274xx";
|
||||
label = "BQ274XX";
|
||||
reg = <0x47>;
|
||||
design-voltage = <3700>;
|
||||
design-capacity = <1800>;
|
||||
taper-current = <45>;
|
||||
terminate-voltage = <3000>;
|
||||
};
|
||||
|
|
|
@ -20,6 +20,7 @@ CONFIG_BME280=y
|
|||
CONFIG_BMG160=y
|
||||
CONFIG_BMI160=y
|
||||
CONFIG_BMM150=y
|
||||
CONFIG_BQ274XX=y
|
||||
CONFIG_CCS811=y
|
||||
CONFIG_DHT=y
|
||||
CONFIG_ENS210=y
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue