sensor: add MAX17262 fuel gauge driver and sample application
The MAX17262 is an ultra-low power fuel-gauge IC which implements the Maxim ModelGauge m5 algorithm. The IC monitors a single-cell battery pack and supports internal current sensing for up to 3.1A pulse current. The IC provides best performance for batteries with 100mAhr to 6Ahr capacity. Signed-off-by: Matija Tudan <mtudan@mobilisis.hr>
This commit is contained in:
parent
0500d75c3a
commit
d2c503f202
15 changed files with 678 additions and 0 deletions
|
@ -53,6 +53,7 @@ add_subdirectory_ifdef(CONFIG_LSM6DSO lsm6dso)
|
||||||
add_subdirectory_ifdef(CONFIG_LSM9DS0_GYRO lsm9ds0_gyro)
|
add_subdirectory_ifdef(CONFIG_LSM9DS0_GYRO lsm9ds0_gyro)
|
||||||
add_subdirectory_ifdef(CONFIG_LSM9DS0_MFD lsm9ds0_mfd)
|
add_subdirectory_ifdef(CONFIG_LSM9DS0_MFD lsm9ds0_mfd)
|
||||||
add_subdirectory_ifdef(CONFIG_MAX17055 max17055)
|
add_subdirectory_ifdef(CONFIG_MAX17055 max17055)
|
||||||
|
add_subdirectory_ifdef(CONFIG_MAX17262 max17262)
|
||||||
add_subdirectory_ifdef(CONFIG_MAX30101 max30101)
|
add_subdirectory_ifdef(CONFIG_MAX30101 max30101)
|
||||||
add_subdirectory_ifdef(CONFIG_MAX44009 max44009)
|
add_subdirectory_ifdef(CONFIG_MAX44009 max44009)
|
||||||
add_subdirectory_ifdef(CONFIG_MAX6675 max6675)
|
add_subdirectory_ifdef(CONFIG_MAX6675 max6675)
|
||||||
|
|
|
@ -144,6 +144,8 @@ source "drivers/sensor/lsm9ds0_mfd/Kconfig"
|
||||||
|
|
||||||
source "drivers/sensor/max17055/Kconfig"
|
source "drivers/sensor/max17055/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/sensor/max17262/Kconfig"
|
||||||
|
|
||||||
source "drivers/sensor/max30101/Kconfig"
|
source "drivers/sensor/max30101/Kconfig"
|
||||||
|
|
||||||
source "drivers/sensor/max44009/Kconfig"
|
source "drivers/sensor/max44009/Kconfig"
|
||||||
|
|
7
drivers/sensor/max17262/CMakeLists.txt
Normal file
7
drivers/sensor/max17262/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Copyright 2020 Matija Tudan
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
zephyr_library()
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_MAX17262 max17262.c)
|
11
drivers/sensor/max17262/Kconfig
Normal file
11
drivers/sensor/max17262/Kconfig
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright 2020 Matija Tudan
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config MAX17262
|
||||||
|
bool "MAX17262 Fuel Gauge"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Enable I2C-based driver for MAX17262 Fuel Gauge. This driver supports
|
||||||
|
reading various sensor settings including voltage, current, temperature,
|
||||||
|
time to full/empty and remaining capacity in mAh.
|
343
drivers/sensor/max17262/max17262.c
Normal file
343
drivers/sensor/max17262/max17262.c
Normal file
|
@ -0,0 +1,343 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Matija Tudan
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <drivers/i2c.h>
|
||||||
|
#include <drivers/sensor.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(max17262, CONFIG_SENSOR_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include "max17262.h"
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT maxim_max17262
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read a register value
|
||||||
|
*
|
||||||
|
* Registers have an address and a 16-bit value
|
||||||
|
*
|
||||||
|
* @param dev MAX17262 device to access
|
||||||
|
* @param reg_addr Register address to read
|
||||||
|
* @param valp Place to put the value on success
|
||||||
|
* @return 0 if successful, or negative error code from I2C API
|
||||||
|
*/
|
||||||
|
static int max17262_reg_read(const struct device *dev, uint8_t reg_addr,
|
||||||
|
int16_t *valp)
|
||||||
|
{
|
||||||
|
const struct max17262_config *cfg = dev->config;
|
||||||
|
uint8_t i2c_data[2];
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = i2c_burst_read(cfg->i2c, cfg->i2c_addr, reg_addr,
|
||||||
|
i2c_data, 2);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOG_ERR("Unable to read register");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
*valp = ((int16_t)i2c_data[1] << 8) | i2c_data[0];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write a register value
|
||||||
|
*
|
||||||
|
* Registers have an address and a 16-bit value
|
||||||
|
*
|
||||||
|
* @param dev MAX17262 device to access
|
||||||
|
* @param reg_addr Register address to write to
|
||||||
|
* @param val Register value to write
|
||||||
|
* @return 0 if successful, or negative error code from I2C API
|
||||||
|
*/
|
||||||
|
static int max17262_reg_write(const struct device *dev, uint8_t reg_addr,
|
||||||
|
int16_t val)
|
||||||
|
{
|
||||||
|
const struct max17262_config *cfg = dev->config;
|
||||||
|
uint8_t i2c_data[3] = {reg_addr, val & 0xFF, (uint16_t)val >> 8};
|
||||||
|
|
||||||
|
return i2c_write(cfg->i2c, i2c_data, sizeof(i2c_data),
|
||||||
|
cfg->i2c_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert sensor value from millis
|
||||||
|
*
|
||||||
|
* @param val Where to store converted value in sensor_value format
|
||||||
|
* @param val_millis Value in millis
|
||||||
|
*/
|
||||||
|
static void convert_millis(struct sensor_value *val, int32_t val_millis)
|
||||||
|
{
|
||||||
|
val->val1 = val_millis / 1000;
|
||||||
|
val->val2 = (val_millis % 1000) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert raw register values for specific channel
|
||||||
|
*
|
||||||
|
* @param dev MAX17262 device to access
|
||||||
|
* @param chan Channel number to read
|
||||||
|
* @param valp Returns the sensor value read on success
|
||||||
|
* @return 0 if successful
|
||||||
|
* @return -ENOTSUP for unsupported channels
|
||||||
|
*/
|
||||||
|
static int max17262_channel_get(const struct device *dev,
|
||||||
|
enum sensor_channel chan,
|
||||||
|
struct sensor_value *valp)
|
||||||
|
{
|
||||||
|
const struct max17262_config *const config = dev->config;
|
||||||
|
struct max17262_data *const data = dev->data;
|
||||||
|
int32_t tmp;
|
||||||
|
|
||||||
|
switch (chan) {
|
||||||
|
case SENSOR_CHAN_GAUGE_VOLTAGE:
|
||||||
|
/* Get voltage in uV */
|
||||||
|
tmp = data->voltage * VOLTAGE_MULTIPLIER_UV;
|
||||||
|
/* Convert to V */
|
||||||
|
valp->val1 = tmp / 1000000;
|
||||||
|
valp->val2 = tmp % 1000000;
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_AVG_CURRENT: {
|
||||||
|
int current;
|
||||||
|
/* Get avg current in nA */
|
||||||
|
current = data->avg_current * CURRENT_MULTIPLIER_NA;
|
||||||
|
/* Convert to mA */
|
||||||
|
valp->val1 = current / 1000000;
|
||||||
|
valp->val2 = current % 1000000;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
|
||||||
|
valp->val1 = data->state_of_charge / 256;
|
||||||
|
valp->val2 = data->state_of_charge % 256 * 1000000 / 256;
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_TEMP:
|
||||||
|
valp->val1 = data->internal_temp / 256;
|
||||||
|
valp->val2 = data->internal_temp % 256 * 1000000 / 256;
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY:
|
||||||
|
convert_millis(valp, data->full_cap);
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY:
|
||||||
|
convert_millis(valp, data->remaining_cap);
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_TIME_TO_EMPTY:
|
||||||
|
/* Get time in ms */
|
||||||
|
if (data->time_to_empty == 0xffff) {
|
||||||
|
valp->val1 = 0;
|
||||||
|
valp->val2 = 0;
|
||||||
|
} else {
|
||||||
|
tmp = data->time_to_empty * TIME_MULTIPLIER_MS;
|
||||||
|
convert_millis(valp, tmp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_TIME_TO_FULL:
|
||||||
|
/* Get time in ms */
|
||||||
|
if (data->time_to_full == 0xffff) {
|
||||||
|
valp->val1 = 0;
|
||||||
|
valp->val2 = 0;
|
||||||
|
} else {
|
||||||
|
tmp = data->time_to_full * TIME_MULTIPLIER_MS;
|
||||||
|
convert_millis(valp, tmp);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_CYCLE_COUNT:
|
||||||
|
valp->val1 = data->cycle_count / 100;
|
||||||
|
valp->val2 = data->cycle_count % 100 * 10000;
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY:
|
||||||
|
convert_millis(valp, data->design_cap);
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE:
|
||||||
|
convert_millis(valp, config->design_voltage);
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE:
|
||||||
|
convert_millis(valp, config->desired_voltage);
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT:
|
||||||
|
valp->val1 = data->ichg_term;
|
||||||
|
valp->val2 = 0;
|
||||||
|
break;
|
||||||
|
case MAX17262_COULOMB_COUNTER:
|
||||||
|
/* Get spent capacity in mAh */
|
||||||
|
data->coulomb_counter = 0xffff - data->coulomb_counter;
|
||||||
|
valp->val1 = data->coulomb_counter / 2;
|
||||||
|
valp->val2 = data->coulomb_counter % 2 * 10 / 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERR("Unsupported channel!");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read register values for supported channels
|
||||||
|
*
|
||||||
|
* @param dev MAX17262 device to access
|
||||||
|
* @return 0 if successful, or negative error code from I2C API
|
||||||
|
*/
|
||||||
|
static int max17262_sample_fetch(const struct device *dev,
|
||||||
|
enum sensor_channel chan)
|
||||||
|
{
|
||||||
|
struct max17262_data *data = dev->data;
|
||||||
|
struct {
|
||||||
|
int reg_addr;
|
||||||
|
int16_t *dest;
|
||||||
|
} regs[] = {
|
||||||
|
{ VCELL, &data->voltage },
|
||||||
|
{ AVG_CURRENT, &data->avg_current },
|
||||||
|
{ ICHG_TERM, &data->ichg_term },
|
||||||
|
{ REP_SOC, &data->state_of_charge },
|
||||||
|
{ INT_TEMP, &data->internal_temp },
|
||||||
|
{ REP_CAP, &data->remaining_cap },
|
||||||
|
{ FULL_CAP_REP, &data->full_cap },
|
||||||
|
{ TTE, &data->time_to_empty },
|
||||||
|
{ TTF, &data->time_to_full },
|
||||||
|
{ CYCLES, &data->cycle_count },
|
||||||
|
{ DESIGN_CAP, &data->design_cap },
|
||||||
|
{ COULOMB_COUNTER, &data->coulomb_counter },
|
||||||
|
};
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = max17262_reg_read(dev, regs[i].reg_addr, regs[i].dest);
|
||||||
|
if (rc != 0) {
|
||||||
|
LOG_ERR("Failed to read channel %d", chan);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialise the fuel gauge
|
||||||
|
*
|
||||||
|
* @param dev MAX17262 device to access
|
||||||
|
* @return 0 for success
|
||||||
|
* @return -EINVAL if the I2C controller could not be found
|
||||||
|
*/
|
||||||
|
static int max17262_gauge_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct max17262_config *const config = dev->config;
|
||||||
|
int16_t tmp, hibcfg;
|
||||||
|
|
||||||
|
if (!device_is_ready(config->i2c)) {
|
||||||
|
LOG_ERR("Could not get pointer to %s device", config->i2c->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read Status register */
|
||||||
|
max17262_reg_read(dev, STATUS, &tmp);
|
||||||
|
|
||||||
|
if (!(tmp & STATUS_POR)) {
|
||||||
|
/*
|
||||||
|
* Status.POR bit is set to 1 when MAX17262 detects that
|
||||||
|
* a software or hardware POR event has occurred and
|
||||||
|
* therefore a custom configuration needs to be set...
|
||||||
|
* If POR event did not happen (Status.POR == 0), skip
|
||||||
|
* init and continue with measurements.
|
||||||
|
*/
|
||||||
|
LOG_DBG("No POR event detected - skip device configuration");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
LOG_DBG("POR detected, setting custom device configuration...");
|
||||||
|
|
||||||
|
/** STEP 1 */
|
||||||
|
max17262_reg_read(dev, FSTAT, &tmp);
|
||||||
|
|
||||||
|
/* Do not continue until FSTAT.DNR bit is cleared */
|
||||||
|
while (tmp & FSTAT_DNR) {
|
||||||
|
k_sleep(K_MSEC(10));
|
||||||
|
max17262_reg_read(dev, FSTAT, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** STEP 2 */
|
||||||
|
/* Store original HibCFG value */
|
||||||
|
max17262_reg_read(dev, HIBCFG, &hibcfg);
|
||||||
|
|
||||||
|
/* Exit Hibernate Mode step 1 */
|
||||||
|
max17262_reg_write(dev, SOFT_WAKEUP, 0x0090);
|
||||||
|
/* Exit Hibernate Mode step 2 */
|
||||||
|
max17262_reg_write(dev, HIBCFG, 0x0000);
|
||||||
|
/* Exit Hibernate Mode step 3 */
|
||||||
|
max17262_reg_write(dev, SOFT_WAKEUP, 0x0000);
|
||||||
|
|
||||||
|
/** STEP 2.1 --> OPTION 1 EZ Config (No INI file is needed) */
|
||||||
|
/* Write DesignCap */
|
||||||
|
max17262_reg_write(dev, DESIGN_CAP, config->design_cap);
|
||||||
|
|
||||||
|
/* Write IChgTerm */
|
||||||
|
max17262_reg_write(dev, ICHG_TERM, config->desired_charging_current);
|
||||||
|
|
||||||
|
/* Write VEmpty */
|
||||||
|
max17262_reg_write(dev, VEMPTY, ((config->empty_voltage / 10) << 7) |
|
||||||
|
((config->recovery_voltage / 40) & 0x7F));
|
||||||
|
|
||||||
|
/* Write ModelCFG */
|
||||||
|
if (config->charge_voltage > 4275) {
|
||||||
|
max17262_reg_write(dev, MODELCFG, 0x8400);
|
||||||
|
} else {
|
||||||
|
max17262_reg_write(dev, MODELCFG, 0x8000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read ModelCFG.Refresh (highest bit),
|
||||||
|
* proceed to Step 3 when ModelCFG.Refresh == 0
|
||||||
|
*/
|
||||||
|
max17262_reg_read(dev, MODELCFG, &tmp);
|
||||||
|
|
||||||
|
/* Do not continue until ModelCFG.Refresh == 0 */
|
||||||
|
while (tmp & MODELCFG_REFRESH) {
|
||||||
|
k_sleep(K_MSEC(10));
|
||||||
|
max17262_reg_read(dev, MODELCFG, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore Original HibCFG value */
|
||||||
|
max17262_reg_write(dev, HIBCFG, hibcfg);
|
||||||
|
|
||||||
|
/** STEP 3 */
|
||||||
|
/* Read Status register */
|
||||||
|
max17262_reg_read(dev, STATUS, &tmp);
|
||||||
|
|
||||||
|
/* Clear PowerOnReset bit */
|
||||||
|
tmp &= ~STATUS_POR;
|
||||||
|
max17262_reg_write(dev, STATUS, tmp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sensor_driver_api max17262_battery_driver_api = {
|
||||||
|
.sample_fetch = max17262_sample_fetch,
|
||||||
|
.channel_get = max17262_channel_get,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX17262_INIT(n) \
|
||||||
|
static struct max17262_data max17262_data_##n; \
|
||||||
|
\
|
||||||
|
static const struct max17262_config max17262_config_##n = { \
|
||||||
|
.i2c = DEVICE_DT_GET(DT_BUS(DT_DRV_INST(n))), \
|
||||||
|
.i2c_addr = DT_INST_REG_ADDR(n), \
|
||||||
|
.design_voltage = DT_INST_PROP(n, design_voltage), \
|
||||||
|
.desired_voltage = DT_INST_PROP(n, desired_voltage), \
|
||||||
|
.desired_charging_current = \
|
||||||
|
DT_INST_PROP(n, desired_charging_current), \
|
||||||
|
.design_cap = DT_INST_PROP(n, design_cap), \
|
||||||
|
.empty_voltage = DT_INST_PROP(n, empty_voltage), \
|
||||||
|
.recovery_voltage = DT_INST_PROP(n, recovery_voltage), \
|
||||||
|
.charge_voltage = DT_INST_PROP(n, charge_voltage), \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
DEVICE_DT_INST_DEFINE(n, &max17262_gauge_init, \
|
||||||
|
device_pm_control_nop, \
|
||||||
|
&max17262_data_##n, \
|
||||||
|
&max17262_config_##n, POST_KERNEL, \
|
||||||
|
CONFIG_SENSOR_INIT_PRIORITY, \
|
||||||
|
&max17262_battery_driver_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(MAX17262_INIT)
|
96
drivers/sensor/max17262/max17262.h
Normal file
96
drivers/sensor/max17262/max17262.h
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Matija Tudan
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_DRIVERS_SENSOR_BATTERY_MAX17262_H_
|
||||||
|
#define ZEPHYR_DRIVERS_SENSOR_BATTERY_MAX17262_H_
|
||||||
|
|
||||||
|
#define VOLTAGE_MULTIPLIER_UV 1250 / 16
|
||||||
|
#define CURRENT_MULTIPLIER_NA 156250
|
||||||
|
#define TIME_MULTIPLIER_MS 5625
|
||||||
|
|
||||||
|
/* Register addresses */
|
||||||
|
enum {
|
||||||
|
STATUS = 0x00,
|
||||||
|
REP_CAP = 0x05,
|
||||||
|
REP_SOC = 0x06,
|
||||||
|
INT_TEMP = 0x08,
|
||||||
|
VCELL = 0x09,
|
||||||
|
AVG_CURRENT = 0x0b,
|
||||||
|
FULL_CAP_REP = 0x10,
|
||||||
|
TTE = 0x11,
|
||||||
|
CYCLES = 0x17,
|
||||||
|
DESIGN_CAP = 0x18,
|
||||||
|
ICHG_TERM = 0x1E,
|
||||||
|
TTF = 0x20,
|
||||||
|
VEMPTY = 0x3A,
|
||||||
|
FSTAT = 0x3D,
|
||||||
|
COULOMB_COUNTER = 0x4D,
|
||||||
|
SOFT_WAKEUP = 0x60,
|
||||||
|
HIBCFG = 0xBA,
|
||||||
|
MODELCFG = 0xDB,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Masks */
|
||||||
|
enum {
|
||||||
|
FSTAT_DNR = 0x01,
|
||||||
|
STATUS_POR = 0x02,
|
||||||
|
MODELCFG_REFRESH = 0x8000,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* MAX17262 specific channels */
|
||||||
|
enum max17262_channel {
|
||||||
|
MAX17262_COULOMB_COUNTER,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct max17262_data {
|
||||||
|
/* Current cell voltage in units of 1.25/16mV */
|
||||||
|
uint16_t voltage;
|
||||||
|
/* Average current in units of 156.25uA */
|
||||||
|
int16_t avg_current;
|
||||||
|
/* Desired charging current in mA */
|
||||||
|
uint16_t ichg_term;
|
||||||
|
/* Remaining capacity as a %age */
|
||||||
|
uint16_t state_of_charge;
|
||||||
|
/* Internal temperature in units of 1/256 degrees C */
|
||||||
|
int16_t internal_temp;
|
||||||
|
/* Full charge capacity in mAh */
|
||||||
|
uint16_t full_cap;
|
||||||
|
/* Remaining capacity in mAh */
|
||||||
|
uint16_t remaining_cap;
|
||||||
|
/* Time to empty in seconds */
|
||||||
|
uint16_t time_to_empty;
|
||||||
|
/* Time to full in seconds */
|
||||||
|
uint16_t time_to_full;
|
||||||
|
/* Cycle count in 1/100ths (number of charge/discharge cycles) */
|
||||||
|
uint16_t cycle_count;
|
||||||
|
/* Battery capacity in mAh */
|
||||||
|
uint16_t design_cap;
|
||||||
|
/* Spent capacity in mAh */
|
||||||
|
uint16_t coulomb_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct max17262_config {
|
||||||
|
const struct device *i2c;
|
||||||
|
uint16_t i2c_addr;
|
||||||
|
/* Value of Rsense resistor in milliohms (typicallly 5 or 10) */
|
||||||
|
uint16_t rsense_mohms;
|
||||||
|
/* Design voltage of cell in mV */
|
||||||
|
uint16_t design_voltage;
|
||||||
|
/* Desired voltage of cell in mV */
|
||||||
|
uint16_t desired_voltage;
|
||||||
|
/* Desired charging current in mA */
|
||||||
|
uint16_t desired_charging_current;
|
||||||
|
/* Battery capacity in mAh */
|
||||||
|
uint16_t design_cap;
|
||||||
|
/* Empty voltage detection in mV */
|
||||||
|
uint16_t empty_voltage;
|
||||||
|
/* Recovery voltage detection in mV */
|
||||||
|
uint16_t recovery_voltage;
|
||||||
|
/* Defined charge voltage value in mV */
|
||||||
|
uint16_t charge_voltage;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
47
dts/bindings/sensor/maxim,max17262.yaml
Normal file
47
dts/bindings/sensor/maxim,max17262.yaml
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#
|
||||||
|
# Copyright 2020 Matija Tudan
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
description: Maxim MAX17262 Fuel Gauge
|
||||||
|
|
||||||
|
compatible: "maxim,max17262"
|
||||||
|
|
||||||
|
include: i2c-device.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
design-voltage:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Battery Design Voltage in mV (3300 to 4400)
|
||||||
|
|
||||||
|
desired-voltage:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Battery Desired Voltage in mV (3300 to 4400)
|
||||||
|
|
||||||
|
desired-charging-current:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Battery Design Charging Current in mA (e.g. 2000)
|
||||||
|
|
||||||
|
design-cap:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Battery Capacity in mAh (default 3000)
|
||||||
|
|
||||||
|
empty-voltage:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Empty voltage target during load in mV (default 3300)
|
||||||
|
|
||||||
|
recovery-voltage:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: The voltage level for clearing empty detection in mV (default 3880)
|
||||||
|
|
||||||
|
charge-voltage:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Charge voltage in mV
|
8
samples/sensor/max17262/CMakeLists.txt
Normal file
8
samples/sensor/max17262/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13.1)
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(max17262)
|
||||||
|
|
||||||
|
FILE(GLOB app_sources src/*.c)
|
||||||
|
target_sources(app PRIVATE ${app_sources})
|
57
samples/sensor/max17262/README.rst
Normal file
57
samples/sensor/max17262/README.rst
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
.. _max17262:
|
||||||
|
|
||||||
|
MAX17262 Fuel Gauge Sensor
|
||||||
|
###################################
|
||||||
|
|
||||||
|
Overview
|
||||||
|
********
|
||||||
|
|
||||||
|
This sample application periodically reads voltage, current and temperature
|
||||||
|
data from the MAX17262 device that implements SENSOR_CHAN_GAUGE_VOLTAGE,
|
||||||
|
SENSOR_CHAN_GAUGE_AVG_CURRENT, and SENSOR_CHAN_GAUGE_TEMP.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
************
|
||||||
|
|
||||||
|
The MAX17262 is an ultra-low power fuel-gauge IC which implements the Maxim
|
||||||
|
ModelGauge m5 algorithm. The IC monitors a single-cell battery pack and
|
||||||
|
supports internal current sensing for up to 3.1A pulse current. The IC
|
||||||
|
provides best performance for batteries with 100mAhr to 6Ahr capacity.
|
||||||
|
|
||||||
|
This sample requires a board which provides a configuration for Arduino
|
||||||
|
connectors and defines node aliases for the I2C interface.
|
||||||
|
For more info about the node structure see
|
||||||
|
:zephyr_file:`samples/sensor/max17262/app.overlay`
|
||||||
|
|
||||||
|
Building and Running
|
||||||
|
********************
|
||||||
|
|
||||||
|
This sample application uses an MAX17262 sensor connected to a board via I2C.
|
||||||
|
Connect the sensor pins according to the connection diagram given in the
|
||||||
|
`max17262 datasheet`_.
|
||||||
|
|
||||||
|
.. zephyr-app-commands::
|
||||||
|
:zephyr-app: samples/sensor/max17262
|
||||||
|
:board: nrf52840dk_nrf52840
|
||||||
|
:goals: build flash
|
||||||
|
:compact:
|
||||||
|
|
||||||
|
Sample Output
|
||||||
|
=============
|
||||||
|
To check output of this sample , any serial console program can be used.
|
||||||
|
This example uses ``picocom`` on the serial port ``/dev/ttyUSB0``:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ sudo picocom -D /dev/ttyUSB0
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
V: 3.626406 V; I: -3.437500 mA; T: 28.011718 °C
|
||||||
|
V: 3.626406 V; I: -3.437500 mA; T: 28.011718 °C
|
||||||
|
V: 3.626406 V; I: -3.437500 mA; T: 28.011718 °C
|
||||||
|
|
||||||
|
References
|
||||||
|
***********
|
||||||
|
|
||||||
|
.. _max17262 datasheet: https://datasheets.maximintegrated.com/en/ds/MAX17262.pdf
|
23
samples/sensor/max17262/app.overlay
Normal file
23
samples/sensor/max17262/app.overlay
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Matija Tudan
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
&arduino_i2c {
|
||||||
|
status = "okay";
|
||||||
|
|
||||||
|
max17262@36 {
|
||||||
|
compatible = "maxim,max17262";
|
||||||
|
label = "MAX17262";
|
||||||
|
reg = <0x36>;
|
||||||
|
design-voltage = <3600>;
|
||||||
|
desired-voltage = <3600>;
|
||||||
|
desired-charging-current = <2000>;
|
||||||
|
design-cap = <17000>;
|
||||||
|
empty-voltage = <3300>;
|
||||||
|
recovery-voltage = <3880>;
|
||||||
|
charge-voltage = <3600>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
};
|
5
samples/sensor/max17262/prj.conf
Normal file
5
samples/sensor/max17262/prj.conf
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
CONFIG_I2C=y
|
||||||
|
CONFIG_LOG=y
|
||||||
|
CONFIG_SENSOR=y
|
||||||
|
CONFIG_MAX17262=y
|
||||||
|
CONFIG_CBPRINTF_FP_SUPPORT=y
|
15
samples/sensor/max17262/sample.yaml
Normal file
15
samples/sensor/max17262/sample.yaml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
sample:
|
||||||
|
description: MAX17262 sensor sample
|
||||||
|
name: MAX17262 sample
|
||||||
|
tests:
|
||||||
|
sample.sensor.max17262:
|
||||||
|
build_only: true
|
||||||
|
depends_on: arduino_i2c
|
||||||
|
harness: console
|
||||||
|
tags: sensors
|
||||||
|
platform_allow: nrf52840dk_nrf52840
|
||||||
|
harness_config:
|
||||||
|
type: one_line
|
||||||
|
regex:
|
||||||
|
- "V: (.*) V; I: (.*) mA; T: (.*) °C"
|
||||||
|
fixture: fixture_i2c_max17262
|
49
samples/sensor/max17262/src/main.c
Normal file
49
samples/sensor/max17262/src/main.c
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Matija Tudan
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <device.h>
|
||||||
|
#include <devicetree.h>
|
||||||
|
#include <drivers/sensor.h>
|
||||||
|
|
||||||
|
#define MAX17262 DT_INST(0, maxim_max17262)
|
||||||
|
|
||||||
|
#if DT_NODE_HAS_STATUS(MAX17262, okay)
|
||||||
|
#define MAX17262_LABEL DT_LABEL(MAX17262)
|
||||||
|
#else
|
||||||
|
#error Your devicetree has no enabled nodes with compatible "maxim,max17262"
|
||||||
|
#define MAX17262_LABEL "<none>"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
const struct device *dev = device_get_binding(MAX17262_LABEL);
|
||||||
|
|
||||||
|
if (dev == NULL) {
|
||||||
|
printk("No device %s found...\n", dev->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printk("Found device %s\n", dev->name);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct sensor_value voltage, avg_current, temperature;
|
||||||
|
float i_avg;
|
||||||
|
|
||||||
|
sensor_sample_fetch(dev);
|
||||||
|
sensor_channel_get(dev, SENSOR_CHAN_GAUGE_VOLTAGE, &voltage);
|
||||||
|
sensor_channel_get(dev, SENSOR_CHAN_GAUGE_AVG_CURRENT,
|
||||||
|
&avg_current);
|
||||||
|
sensor_channel_get(dev, SENSOR_CHAN_GAUGE_TEMP, &temperature);
|
||||||
|
|
||||||
|
i_avg = avg_current.val1 + (avg_current.val2 / 1000000.0);
|
||||||
|
|
||||||
|
printk("V: %d.%06d V; I: %f mA; T: %d.%06d °C\n",
|
||||||
|
voltage.val1, voltage.val2, i_avg,
|
||||||
|
temperature.val1, temperature.val2);
|
||||||
|
|
||||||
|
k_sleep(K_MSEC(1000));
|
||||||
|
}
|
||||||
|
}
|
|
@ -573,6 +573,19 @@ test_i2c_max17055: max17055@49 {
|
||||||
v-empty = <3300>;
|
v-empty = <3300>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test_i2c_max17262: max17262@36 {
|
||||||
|
compatible = "maxim,max17262";
|
||||||
|
label = "MAX17262";
|
||||||
|
reg = <0x36>;
|
||||||
|
design-voltage = <3600>;
|
||||||
|
desired-voltage = <3600>;
|
||||||
|
desired-charging-current = <2000>;
|
||||||
|
design-cap = <17000>;
|
||||||
|
empty-voltage = <3300>;
|
||||||
|
recovery-voltage = <3880>;
|
||||||
|
charge-voltage = <3600>;
|
||||||
|
};
|
||||||
|
|
||||||
test_i2c_vcnl4040: vcnl4040@60 {
|
test_i2c_vcnl4040: vcnl4040@60 {
|
||||||
compatible = "vishay,vcnl4040";
|
compatible = "vishay,vcnl4040";
|
||||||
label = "VCNL4040";
|
label = "VCNL4040";
|
||||||
|
|
|
@ -26,6 +26,7 @@ CONFIG_LSM9DS0_MFD=y
|
||||||
CONFIG_MAX30101=y
|
CONFIG_MAX30101=y
|
||||||
CONFIG_MAX44009=y
|
CONFIG_MAX44009=y
|
||||||
CONFIG_MAX17055=y
|
CONFIG_MAX17055=y
|
||||||
|
CONFIG_MAX17262=y
|
||||||
CONFIG_MAX6675=y
|
CONFIG_MAX6675=y
|
||||||
CONFIG_MCP9808=y
|
CONFIG_MCP9808=y
|
||||||
CONFIG_MPU6050=y
|
CONFIG_MPU6050=y
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue