drivers: sensors: smart-battery with SBS 1.1 compliant fuel gauge

Implementation of a SBS 1.1 compliant fuel gauge driver

Signed-off-by: MORGER Patrick <patrick.morger@leica-geosystems.com>
This commit is contained in:
MORGER Patrick 2021-04-08 08:46:34 +02:00 committed by Kumar Gala
commit 0e1d16bf03
17 changed files with 620 additions and 4 deletions

View file

@ -0,0 +1,3 @@
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_SBS_GAUGE sbs_gauge.c)

View file

@ -0,0 +1,8 @@
# Copyright (c) 2021 Leica Geosystems AG
# SPDX-License-Identifier: Apache-2.0
config SBS_GAUGE
bool "Smart Battery Fuel Gauge"
depends on I2C
help
Enable I2C-based/SMBus-based driver for a Smart Battery Fuel Gauge.

View file

@ -0,0 +1,272 @@
/*
* Copyright (c) 2021 Leica Geosystems AG
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT sbs_sbs_gauge
#include <drivers/i2c.h>
#include <drivers/sensor.h>
#include <sys/byteorder.h>
#include "sbs_gauge.h"
#include <logging/log.h>
LOG_MODULE_REGISTER(sbs_gauge, CONFIG_SENSOR_LOG_LEVEL);
static int sbs_cmd_reg_read(const struct device *dev,
uint8_t reg_addr,
uint16_t *val)
{
const struct sbs_gauge_config *cfg;
uint8_t i2c_data[2];
int status;
cfg = (struct sbs_gauge_config *)dev->config;
status = i2c_burst_read(cfg->i2c_dev, cfg->i2c_addr, reg_addr,
i2c_data, ARRAY_SIZE(i2c_data));
if (status < 0) {
LOG_ERR("Unable to read register");
return status;
}
*val = sys_get_le16(i2c_data);
return 0;
}
/**
* @brief sensor value get
*
* @return -ENOTSUP for unsupported channels
*/
static int sbs_gauge_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct sbs_gauge_data *data;
int32_t int_temp;
data = (struct sbs_gauge_data *)dev->data;
val->val2 = 0;
switch (chan) {
case SENSOR_CHAN_GAUGE_VOLTAGE:
val->val1 = data->voltage / 1000;
val->val2 = (data->voltage % 1000) * 1000;
break;
case SENSOR_CHAN_GAUGE_AVG_CURRENT:
val->val1 = data->avg_current / 1000;
val->val2 = (data->avg_current % 1000) * 1000;
break;
case SENSOR_CHAN_GAUGE_TEMP:
int_temp = (data->internal_temperature * 10);
int_temp = int_temp - 27315;
val->val1 = int_temp / 100;
val->val2 = (int_temp % 100) * 1000000;
break;
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
val->val1 = data->state_of_charge;
break;
case SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY:
val->val1 = data->full_charge_capacity;
break;
case SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY:
val->val1 = data->remaining_charge_capacity;
break;
case SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY:
val->val1 = data->nom_avail_capacity;
break;
case SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY:
val->val1 = data->full_avail_capacity;
break;
case SENSOR_CHAN_GAUGE_TIME_TO_EMPTY:
val->val1 = data->time_to_empty;
break;
case SENSOR_CHAN_GAUGE_TIME_TO_FULL:
val->val1 = data->time_to_full;
break;
case SENSOR_CHAN_GAUGE_CYCLE_COUNT:
val->val1 = data->cycle_count;
break;
default:
return -ENOTSUP;
}
return 0;
}
static int sbs_gauge_sample_fetch(const struct device *dev,
enum sensor_channel chan)
{
struct sbs_gauge_data *data;
int status = 0;
data = (struct sbs_gauge_data *)dev->data;
switch (chan) {
case SENSOR_CHAN_GAUGE_VOLTAGE:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_VOLTAGE,
&data->voltage);
if (status < 0) {
LOG_ERR("Failed to read voltage");
}
break;
case SENSOR_CHAN_GAUGE_AVG_CURRENT:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_AVG_CURRENT,
&data->avg_current);
if (status < 0) {
LOG_ERR("Failed to read average current ");
}
break;
case SENSOR_CHAN_GAUGE_TEMP:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_TEMP,
&data->internal_temperature);
if (status < 0) {
LOG_ERR("Failed to read internal temperature");
}
break;
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_ASOC,
&data->state_of_charge);
if (status < 0) {
LOG_ERR("Failed to read state of charge");
}
break;
case SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_FULL_CAPACITY,
&data->full_charge_capacity);
if (status < 0) {
LOG_ERR("Failed to read full charge capacity");
}
break;
case SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_REM_CAPACITY,
&data->remaining_charge_capacity);
if (status < 0) {
LOG_ERR("Failed to read remaining charge capacity");
}
break;
case SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_NOM_CAPACITY,
&data->nom_avail_capacity);
if (status < 0) {
LOG_ERR("Failed to read nominal available capacity");
}
break;
case SENSOR_CHAN_GAUGE_FULL_AVAIL_CAPACITY:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_FULL_CAPACITY,
&data->full_avail_capacity);
if (status < 0) {
LOG_ERR("Failed to read full available capacity");
}
break;
case SENSOR_CHAN_GAUGE_TIME_TO_EMPTY:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_AVG_TIME2EMPTY,
&data->time_to_empty);
data->time_to_empty = (data->time_to_empty) & 0x00FF;
if (status < 0) {
LOG_ERR("Failed to read time to empty");
}
break;
case SENSOR_CHAN_GAUGE_TIME_TO_FULL:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_AVG_TIME2FULL,
&data->time_to_full);
data->time_to_full = (data->time_to_full) & 0x00FF;
if (status < 0) {
LOG_ERR("Failed to read time to full");
}
break;
case SENSOR_CHAN_GAUGE_CYCLE_COUNT:
status = sbs_cmd_reg_read(dev,
SBS_GAUGE_CMD_CYCLE_COUNT,
&data->cycle_count);
data->cycle_count = (data->cycle_count) & 0x00FF;
if (status < 0) {
LOG_ERR("Failed to read cycle count");
}
break;
default:
return -ENOTSUP;
}
return status;
}
/**
* @brief initialize the fuel gauge
*
* @return 0 for success
*/
static int sbs_gauge_init(const struct device *dev)
{
struct sbs_gauge_config *cfg;
cfg = (struct sbs_gauge_config *)dev->config;
if (!device_is_ready(cfg->i2c_dev)) {
LOG_ERR("%s device is not ready", cfg->i2c_dev->name);
return -ENODEV;
}
return 0;
}
static const struct sensor_driver_api sbs_gauge_driver_api = {
.sample_fetch = sbs_gauge_sample_fetch,
.channel_get = sbs_gauge_channel_get,
};
#define SBS_GAUGE_INIT(index) \
static struct sbs_gauge_data sbs_gauge_driver_##index; \
\
static const struct sbs_gauge_config sbs_gauge_config_##index = { \
.i2c_dev = DEVICE_DT_GET(DT_INST_BUS(index)), \
.i2c_addr = DT_INST_REG_ADDR(index), \
}; \
\
DEVICE_DT_INST_DEFINE(index, \
&sbs_gauge_init, \
NULL, \
&sbs_gauge_driver_##index, \
&sbs_gauge_config_##index, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&sbs_gauge_driver_api);
DT_INST_FOREACH_STATUS_OKAY(SBS_GAUGE_INIT)

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2021 Leica Geosystems AG
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_SBS_GAUGE_H_
#define ZEPHYR_DRIVERS_SENSOR_SBS_GAUGE_H_
#include <stdint.h>
/*** Standard Commands ***/
#define SBS_GAUGE_CMD_MANUFACTURER_ACCESS 0x00 /* ManufacturerAccess */
#define SBS_GAUGE_CMD_REM_CAPACITY_ALARM 0x01 /* LowCapacityAlarmThreshold */
#define SBS_GAUGE_CMD_REM_TIME_ALARM 0x02 /* RemainingTimeToEmptyThreshold */
#define SBS_GAUGE_CMD_BATTERY_MODE 0x03 /* BatteryOperatingMode */
#define SBS_GAUGE_CMD_AR 0x04 /* AtRate */
#define SBS_GAUGE_CMD_ARTTF 0x05 /* AtRateTimeToFull */
#define SBS_GAUGE_CMD_ARTTE 0x06 /* AtRateTimeToEmpty */
#define SBS_GAUGE_CMD_AROK 0x07 /* AtRateOK */
#define SBS_GAUGE_CMD_TEMP 0x08 /* Temperature */
#define SBS_GAUGE_CMD_VOLTAGE 0x09 /* Voltage */
#define SBS_GAUGE_CMD_CURRENT 0x0A /* Current */
#define SBS_GAUGE_CMD_AVG_CURRENT 0x0B /* AverageCurrent */
#define SBS_GAUGE_CMD_MAX_ERROR 0x0C /* MaxError */
#define SBS_GAUGE_CMD_RSOC 0x0D /* RelativeStateOfCharge */
#define SBS_GAUGE_CMD_ASOC 0x0E /* AbsoluteStateOfCharge */
#define SBS_GAUGE_CMD_REM_CAPACITY 0x0F /* RemainingCapacity */
#define SBS_GAUGE_CMD_FULL_CAPACITY 0x10 /* FullChargeCapacity */
#define SBS_GAUGE_CMD_RUNTIME2EMPTY 0x11 /* RunTimeToEmpty */
#define SBS_GAUGE_CMD_AVG_TIME2EMPTY 0x12 /* AverageTimeToEmpty */
#define SBS_GAUGE_CMD_AVG_TIME2FULL 0x13 /* AverageTimeToFull */
#define SBS_GAUGE_CMD_CHG_CURRENT 0x14 /* ChargeCurrent */
#define SBS_GAUGE_CMD_CHG_VOLTAGE 0x15 /* ChargeVoltage */
#define SBS_GAUGE_CMD_FLAGS 0x16 /* BatteryStatus */
#define SBS_GAUGE_CMD_CYCLE_COUNT 0x17 /* CycleCount */
#define SBS_GAUGE_CMD_NOM_CAPACITY 0x18 /* DesignCapacity */
#define SBS_GAUGE_CMD_DESIGN_VOLTAGE 0x19 /* DesignVoltage */
#define SBS_GAUGE_CMD_SPECS_INFO 0x1A /* SpecificationInfo */
#define SBS_GAUGE_CMD_MANUFACTURER_DATE 0x1B /* ManufacturerDate */
#define SBS_GAUGE_CMD_SN 0x1C /* SerialNumber */
#define SBS_GAUGE_CMD_MANUFACTURER_NAME 0x20 /* ManufacturerName */
#define SBS_GAUGE_CMD_DEVICE_NAME 0x21 /* DeviceName */
#define SBS_GAUGE_CMD_DEVICE_CHEM 0x22 /* DeviceChemistry */
#define SBS_GAUGE_CMD_MANUFACTURER_DATA 0x23 /* ManufacturerData */
#define SBS_GAUGE_CMD_DESIGN_MAX_POWER 0x24 /* DesignMaxPower */
#define SBS_GAUGE_CMD_START_TIME 0x25 /* StartTime */
#define SBS_GAUGE_CMD_TOTAL_RUNTIME 0x26 /* TotalRuntime */
#define SBS_GAUGE_CMD_FC_TEMP 0x27 /* FCTemp */
#define SBS_GAUGE_CMD_FC_STATUS 0x28 /* FCStatus */
#define SBS_GAUGE_CMD_FC_MODE 0x29 /* FCMode */
#define SBS_GAUGE_CMD_AUTO_SOFT_OFF 0x2A /* AutoSoftOff */
#define SBS_GAUGE_CMD_AUTHENTICATE 0x2F /* Authenticate */
#define SBS_GAUGE_CMD_CELL_V4 0x3C /* CellVoltage4 */
#define SBS_GAUGE_CMD_CELL_V3 0x3D /* CellVoltage3 */
#define SBS_GAUGE_CMD_CELL_V2 0x3E /* CellVoltage2 */
#define SBS_GAUGE_CMD_CELL_V1 0x3F /* CellVoltage1 */
#define SBS_GAUGE_DELAY 1000
struct sbs_gauge_data {
uint16_t voltage;
int16_t avg_current;
uint16_t state_of_charge;
uint16_t internal_temperature;
uint16_t full_charge_capacity;
uint16_t remaining_charge_capacity;
uint16_t nom_avail_capacity;
uint16_t full_avail_capacity;
uint16_t time_to_empty;
uint16_t time_to_full;
uint16_t cycle_count;
};
struct sbs_gauge_config {
const struct device *i2c_dev;
uint8_t i2c_addr;
};
#endif