From 6c49a3487e9711a3757229406d04ee4ba81df590 Mon Sep 17 00:00:00 2001 From: Philipp Steiner Date: Thu, 24 Apr 2025 14:09:27 +0200 Subject: [PATCH 1/6] drivers: fuelgauge: Add additional properties Adds properties for current direction, charge and voltage alarms Signed-off-by: Philipp Steiner --- include/zephyr/drivers/fuel_gauge.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/zephyr/drivers/fuel_gauge.h b/include/zephyr/drivers/fuel_gauge.h index 387b3fb436756..61e8960ab8b27 100644 --- a/include/zephyr/drivers/fuel_gauge.h +++ b/include/zephyr/drivers/fuel_gauge.h @@ -1,6 +1,7 @@ /* * Copyright 2022 Google LLC * Copyright 2023 Microsoft Corporation + * Copyright (c) 2025 Philipp Steiner * * SPDX-License-Identifier: Apache-2.0 */ @@ -100,6 +101,12 @@ enum fuel_gauge_prop_type { FUEL_GAUGE_DEVICE_NAME, /** Chemistry (1 byte length + 4 bytes data) */ FUEL_GAUGE_DEVICE_CHEMISTRY, + /** Battery current direction (flags)*/ + FUEL_GAUGE_CURRENT_DIRECTION, + /** Remaining state of charge alarm (percent, 0-100) */ + FUEL_GAUGE_STATE_OF_CHARGE_ALARM, + /** Low Cell Voltage Alarm (uV)*/ + FUEL_GAUGE_LOW_VOLTAGE_ALARM, /** Reserved to demark end of common fuel gauge properties */ FUEL_GAUGE_COMMON_COUNT, @@ -178,6 +185,12 @@ union fuel_gauge_prop_val { uint16_t sbs_remaining_capacity_alarm; /** FUEL_GAUGE_SBS_REMAINING_TIME_ALARM */ uint16_t sbs_remaining_time_alarm; + /** FUEL_GAUGE_CURRENT_DIRECTION */ + uint16_t current_direction; + /** FUEL_GAUGE_STATE_OF_CHARGE_ALARM */ + uint8_t state_of_charge_alarm; + /** FUEL_GAUGE_LOW_VOLTAGE_ALARM */ + uint32_t low_voltage_alarm; }; /** From 06d6f747c797bc04ebdf2e29e66c2c50ba42a7fa Mon Sep 17 00:00:00 2001 From: Philipp Steiner Date: Thu, 24 Apr 2025 14:09:27 +0200 Subject: [PATCH 2/6] drivers: fuelgauge: format fuel_gauge.h format fuel_gauge.h according to the clang-format settings Signed-off-by: Philipp Steiner --- include/zephyr/drivers/fuel_gauge.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/zephyr/drivers/fuel_gauge.h b/include/zephyr/drivers/fuel_gauge.h index 61e8960ab8b27..6ad5332f6b845 100644 --- a/include/zephyr/drivers/fuel_gauge.h +++ b/include/zephyr/drivers/fuel_gauge.h @@ -240,8 +240,8 @@ typedef int (*fuel_gauge_set_property_t)(const struct device *dev, fuel_gauge_pr * See fuel_gauge_get_buffer_property() for argument description */ typedef int (*fuel_gauge_get_buffer_property_t)(const struct device *dev, - fuel_gauge_prop_t prop_type, - void *dst, size_t dst_len); + fuel_gauge_prop_t prop_type, void *dst, + size_t dst_len); /** * @typedef fuel_gauge_battery_cutoff_t @@ -392,8 +392,8 @@ __syscall int fuel_gauge_get_buffer_prop(const struct device *dev, fuel_gauge_pr void *dst, size_t dst_len); static inline int z_impl_fuel_gauge_get_buffer_prop(const struct device *dev, - fuel_gauge_prop_t prop_type, - void *dst, size_t dst_len) + fuel_gauge_prop_t prop_type, void *dst, + size_t dst_len) { const struct fuel_gauge_driver_api *api = (const struct fuel_gauge_driver_api *)dev->api; From d4e13a60a0d952d39d01b418467c6f877d575007 Mon Sep 17 00:00:00 2001 From: Philipp Steiner Date: Thu, 24 Apr 2025 14:09:27 +0200 Subject: [PATCH 3/6] drivers: fuelgauge: Add Onsemi LC709203F driver Add driver for the Onsemi LC709203F fuel gauge Signed-off-by: Philipp Steiner --- drivers/fuel_gauge/CMakeLists.txt | 1 + drivers/fuel_gauge/Kconfig | 1 + drivers/fuel_gauge/lc709203f/CMakeLists.txt | 6 + drivers/fuel_gauge/lc709203f/Kconfig | 21 + drivers/fuel_gauge/lc709203f/emul_lc709203f.c | 269 ++++++++ drivers/fuel_gauge/lc709203f/lc709203f.c | 633 ++++++++++++++++++ drivers/fuel_gauge/lc709203f/lc709203f.h | 32 + dts/bindings/fuel-gauge/onnn,lc709203f.yaml | 85 +++ 8 files changed, 1048 insertions(+) create mode 100644 drivers/fuel_gauge/lc709203f/CMakeLists.txt create mode 100644 drivers/fuel_gauge/lc709203f/Kconfig create mode 100644 drivers/fuel_gauge/lc709203f/emul_lc709203f.c create mode 100644 drivers/fuel_gauge/lc709203f/lc709203f.c create mode 100644 drivers/fuel_gauge/lc709203f/lc709203f.h create mode 100644 dts/bindings/fuel-gauge/onnn,lc709203f.yaml diff --git a/drivers/fuel_gauge/CMakeLists.txt b/drivers/fuel_gauge/CMakeLists.txt index d8fa11f6a3398..532eb21998c28 100644 --- a/drivers/fuel_gauge/CMakeLists.txt +++ b/drivers/fuel_gauge/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory_ifdef(CONFIG_FUEL_GAUGE_COMPOSITE composite) add_subdirectory_ifdef(CONFIG_MAX17048 max17048) add_subdirectory_ifdef(CONFIG_BQ27Z746 bq27z746) add_subdirectory_ifdef(CONFIG_FUEL_GAUGE_AXP2101 axp2101) +add_subdirectory_ifdef(CONFIG_LC709203F lc709203f) zephyr_library_sources_ifdef(CONFIG_USERSPACE fuel_gauge_syscall_handlers.c) diff --git a/drivers/fuel_gauge/Kconfig b/drivers/fuel_gauge/Kconfig index c0eab696880c0..817bd94d0e785 100644 --- a/drivers/fuel_gauge/Kconfig +++ b/drivers/fuel_gauge/Kconfig @@ -24,5 +24,6 @@ source "drivers/fuel_gauge/sbs_gauge/Kconfig" source "drivers/fuel_gauge/bq27z746/Kconfig" source "drivers/fuel_gauge/composite/Kconfig" source "drivers/fuel_gauge/axp2101/Kconfig" +source "drivers/fuel_gauge/lc709203f/Kconfig" endif # FUEL_GAUGE diff --git a/drivers/fuel_gauge/lc709203f/CMakeLists.txt b/drivers/fuel_gauge/lc709203f/CMakeLists.txt new file mode 100644 index 0000000000000..4944c62a33bd6 --- /dev/null +++ b/drivers/fuel_gauge/lc709203f/CMakeLists.txt @@ -0,0 +1,6 @@ +zephyr_library() + +zephyr_library_sources(lc709203f.c) + +zephyr_include_directories_ifdef(CONFIG_EMUL_LC709203F .) +zephyr_library_sources_ifdef(CONFIG_EMUL_LC709203F ./emul_lc709203f.c) diff --git a/drivers/fuel_gauge/lc709203f/Kconfig b/drivers/fuel_gauge/lc709203f/Kconfig new file mode 100644 index 0000000000000..5be62ff9f1cfc --- /dev/null +++ b/drivers/fuel_gauge/lc709203f/Kconfig @@ -0,0 +1,21 @@ +# Copyright (c) 2025 Philipp Steiner +# +# SPDX-License-Identifier: Apache-2.0 + +config LC709203F + bool "LC709203F Fuel Gauge" + default y + depends on DT_HAS_ONNN_LC709203F_ENABLED + select CRC + select I2C + help + Enable I2C-based driver for LC709203F Fuel Gauge. + +config EMUL_LC709203F + bool "Emulate an LC709203F fuel gauge" + default y + depends on EMUL + depends on LC709203F + help + It provides readings which follow a simple sequence, thus allowing + test code to check that things are working as expected. diff --git a/drivers/fuel_gauge/lc709203f/emul_lc709203f.c b/drivers/fuel_gauge/lc709203f/emul_lc709203f.c new file mode 100644 index 0000000000000..4bee60209715b --- /dev/null +++ b/drivers/fuel_gauge/lc709203f/emul_lc709203f.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2025 Philipp Steiner + * + * SPDX-License-Identifier: Apache-2.0 + * + * Emulator for lc709203f fuel gauge + */ + +#include +#define DT_DRV_COMPAT onnn_lc709203f + +#include +LOG_MODULE_REGISTER(EMUL_LC709203F); + +#include +#include +#include +#include +#include +#include + +#include "lc709203f.h" + +/* You can store as many registers as you need. + * Note: The LC709203F typically uses 16-bit registers. + */ +struct lc709203f_emul_data { + /* This emulator object (required for i2c_emul). */ + struct i2c_emul emul; + /* The I2C emulation config (pointer to our dev_config). */ + const struct i2c_emul_api *api; + /* A backing store for registers in the device. */ + uint16_t regs[0x1B]; /* or enough to hold all used registers */ +}; + +struct lc709203f_emul_cfg { + /** I2C address of emulator */ + uint16_t addr; +}; + +/* Polynomial to calculate CRC-8-ATM */ +#define LC709203F_CRC_POLYNOMIAL 0x07 + +/* Reset handler (optional). You can reset internal state here if desired. */ +static int lc709203f_emul_reset(const struct emul *target) +{ + struct lc709203f_emul_data *data = (struct lc709203f_emul_data *)target->data; + + memset(data->regs, 0, sizeof(data->regs)); + + /* Set default values for registers that your real hardware starts with */ + data->regs[LC709203F_REG_BEFORE_RSOC] = 0x0000; /* - */ + data->regs[LC709203F_REG_THERMISTOR_B] = 0x0D34; /* B -constant */ + data->regs[LC709203F_REG_INITIAL_RSOC] = 0x0000; /* - */ + data->regs[LC709203F_REG_CELL_TEMPERATURE] = 0x0BA6; /* 25.0 °C 298.2 °K -> */ + data->regs[LC709203F_REG_CELL_VOLTAGE] = 3700; /* 3.7 V in mV */ + data->regs[LC709203F_REG_CURRENT_DIRECTION] = 0x0000; /* Auto mode */ + data->regs[LC709203F_REG_APA] = 0x0000; /* - */ + data->regs[LC709203F_REG_APT] = 0x001E; /* initial value */ + data->regs[LC709203F_REG_RSOC] = 50; /* 50% battery level */ + data->regs[LC709203F_REG_CELL_ITE] = 500; /* 50.0% battery level */ + data->regs[LC709203F_REG_IC_VERSION] = 0x1234; /* Example chip ID */ + data->regs[LC709203F_REG_BAT_PROFILE] = 0x0000; /* - */ + data->regs[LC709203F_REG_ALARM_LOW_RSOC] = 0x0008; /* 8% */ + data->regs[LC709203F_REG_ALARM_LOW_VOLTAGE] = 0x0000; /* initial value */ + data->regs[LC709203F_REG_IC_POWER_MODE] = 0x0002; /* - */ + data->regs[LC709203F_REG_STATUS_BIT] = 0x0000; /* initial value */ + data->regs[LC709203F_REG_NUM_PARAMETER] = 0x0301; /* - */ + + return 0; +} + +static int emul_lc709203f_reg_write(const struct emul *target, uint8_t *buf, size_t len) +{ + struct lc709203f_emul_data *data = target->data; + const struct lc709203f_emul_cfg *lc709203f_emul_cfg = target->cfg; + const uint8_t reg = buf[0]; + const uint16_t value = sys_get_le16(&buf[1]); + const uint8_t crc = buf[3]; + uint8_t crc_buf[4]; + + crc_buf[0] = lc709203f_emul_cfg->addr << 1; + crc_buf[1] = reg; + crc_buf[2] = buf[1]; + crc_buf[3] = buf[2]; + + const uint8_t crc_calc = crc8(crc_buf, sizeof(crc_buf), LC709203F_CRC_POLYNOMIAL, 0, false); + + if (crc != crc_calc) { + LOG_ERR("CRC mismatch on reg 0x%02x", reg); + return -EIO; + } + + switch (reg) { + case LC709203F_REG_RSOC: + data->regs[LC709203F_REG_RSOC] = value; + data->regs[LC709203F_REG_CELL_ITE] = value * 10; + break; + case LC709203F_REG_BEFORE_RSOC: + case LC709203F_REG_THERMISTOR_B: + case LC709203F_REG_INITIAL_RSOC: + case LC709203F_REG_CELL_TEMPERATURE: + case LC709203F_REG_CURRENT_DIRECTION: + case LC709203F_REG_APA: + case LC709203F_REG_APT: + case LC709203F_REG_BAT_PROFILE: + case LC709203F_REG_ALARM_LOW_RSOC: + case LC709203F_REG_ALARM_LOW_VOLTAGE: + case LC709203F_REG_IC_POWER_MODE: + case LC709203F_REG_STATUS_BIT: + data->regs[reg] = value; + break; + default: + LOG_ERR("Unknown or read only register 0x%x write", reg); + return -EIO; + } + return 0; +} + +static int emul_lc709203f_reg_read(const struct emul *target, int reg, uint8_t *buf, size_t len) +{ + struct lc709203f_emul_data *data = target->data; + const struct lc709203f_emul_cfg *lc709203f_emul_cfg = target->cfg; + uint16_t val = 0; + + switch (reg) { + case LC709203F_REG_CELL_TEMPERATURE: + if (data->regs[LC709203F_REG_STATUS_BIT] == 0x0000) { + LOG_ERR("Temperature obtaining method is not set to Thermistor mode, " + "instead its set to I2C mode"); + return -EIO; + } + case LC709203F_REG_THERMISTOR_B: + case LC709203F_REG_CELL_VOLTAGE: + case LC709203F_REG_CURRENT_DIRECTION: + case LC709203F_REG_APA: + case LC709203F_REG_APT: + case LC709203F_REG_RSOC: + case LC709203F_REG_CELL_ITE: + case LC709203F_REG_IC_VERSION: + case LC709203F_REG_BAT_PROFILE: + case LC709203F_REG_ALARM_LOW_RSOC: + case LC709203F_REG_ALARM_LOW_VOLTAGE: + case LC709203F_REG_IC_POWER_MODE: + case LC709203F_REG_STATUS_BIT: + case LC709203F_REG_NUM_PARAMETER: + val = data->regs[reg]; + break; + default: + LOG_ERR("Unknown or write only register 0x%x read", reg); + return -EIO; + } + + sys_put_le16(val, buf); + + uint8_t crc_buf[5]; + + /* Build buffer for CRC calculation */ + crc_buf[0] = lc709203f_emul_cfg->addr << 1; + crc_buf[1] = reg; + crc_buf[2] = (lc709203f_emul_cfg->addr << 1) | 0x01; + crc_buf[3] = buf[0]; /* LSB */ + crc_buf[4] = buf[1]; /* MSB */ + + /* Calculate CRC and write it into the receive buffer */ + buf[2] = crc8(crc_buf, sizeof(crc_buf), LC709203F_CRC_POLYNOMIAL, 0, false); + + return 0; +} + +static int lc709203f_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs, + int num_msgs, int addr) +{ + int reg; + + __ASSERT_NO_MSG(msgs && num_msgs); + + i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false); + switch (num_msgs) { + case 1: + if (msgs->flags & I2C_MSG_READ) { + LOG_ERR("Unexpected read"); + return -EIO; + } + + if (msgs->len == 4) { + return emul_lc709203f_reg_write(target, msgs->buf, msgs->len); + } + + LOG_ERR("Unexpected msg length %d", msgs->len); + return -EIO; + + case 2: + if (msgs->flags & I2C_MSG_READ) { + LOG_ERR("Unexpected read"); + return -EIO; + } + if (msgs->len != 1) { + LOG_ERR("Unexpected msg0 length %d", msgs->len); + return -EIO; + } + reg = msgs->buf[0]; + + /* Now process the 'read' part of the message */ + msgs++; + if (msgs->flags & I2C_MSG_READ) { + if (msgs->len == 3) { + return emul_lc709203f_reg_read(target, reg, msgs->buf, msgs->len); + } + + LOG_ERR("Unexpected msg length %d", msgs->len); + return -EIO; + } + LOG_ERR("Second message must be an I2C write"); + return -EIO; + default: + LOG_ERR("Invalid number of messages: %d", num_msgs); + return -EIO; + } + + return 0; +} +/* The I2C emulator API required by Zephyr. */ +static const struct i2c_emul_api lc709203f_emul_api_i2c = { + .transfer = lc709203f_emul_transfer_i2c, +}; + +#ifdef CONFIG_ZTEST +#include + +/* Add test reset handlers in when using emulators with tests */ +#define LC709203F_EMUL_RESET_RULE_BEFORE(inst) lc709203f_emul_reset(EMUL_DT_GET(DT_DRV_INST(inst))); + +static void lc709203f_gauge_reset_rule_after(const struct ztest_unit_test *test, void *data) +{ + ARG_UNUSED(test); + ARG_UNUSED(data); + + DT_INST_FOREACH_STATUS_OKAY(LC709203F_EMUL_RESET_RULE_BEFORE) +} +ZTEST_RULE(lc709203f_gauge_reset, NULL, lc709203f_gauge_reset_rule_after); +#endif /* CONFIG_ZTEST */ + +/** + * Set up a new emulator (I2C) + * + * @param emul Emulation information + * @param parent Device to emulate + * @return 0 indicating success (always) + */ +static int lc709203f_emul_init(const struct emul *target, const struct device *parent) +{ + ARG_UNUSED(parent); + lc709203f_emul_reset(target); + return 0; +} + +/* + * Main instantiation macro. + */ +#define DEFINE_LC709203F_EMUL(n) \ + static struct lc709203f_emul_data lc709203f_emul_data_##n; \ + static const struct lc709203f_emul_cfg lc709203f_emul_cfg_##n = { \ + .addr = DT_INST_REG_ADDR(n), \ + }; \ + EMUL_DT_INST_DEFINE(n, lc709203f_emul_init, &lc709203f_emul_data_##n, \ + &lc709203f_emul_cfg_##n, &lc709203f_emul_api_i2c, NULL) + +DT_INST_FOREACH_STATUS_OKAY(DEFINE_LC709203F_EMUL); diff --git a/drivers/fuel_gauge/lc709203f/lc709203f.c b/drivers/fuel_gauge/lc709203f/lc709203f.c new file mode 100644 index 0000000000000..4fc5226390092 --- /dev/null +++ b/drivers/fuel_gauge/lc709203f/lc709203f.c @@ -0,0 +1,633 @@ +/* + * Copyright (c) 2025 Philipp Steiner + * + * SPDX-License-Identifier: Apache-2.0 + * + * Zephyr driver for LC709203F Battery Monitor + * + * This driver implements the sensor API for the LC709203F battery monitor, + * providing battery voltage, state-of-charge (SOC), and temperature measurements. + * + * Note: + * - The LC709203F is assumed to be connected via I2C. + * - The register addresses and conversion factors used here are based on + * common LC709203F implementations. Consult your datasheet and adjust as needed. + * - To use this driver, create a matching device tree node (with a "compatible" + * string, I2C bus, and register address) so that the DT_INST_* macros can pick it up. + * - The LC chip works best when queried every few seconds at the fastest. Don't disconnect the LiPo + * battery, it is used to power the LC chip! + */ + +#define DT_DRV_COMPAT onnn_lc709203f + +#include "lc709203f.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(lc709203f); + +/* Battery temperature source */ +enum lc709203f_temp_mode { + LC709203F_TEMPERATURE_I2C = 0x0000, + LC709203F_TEMPERATURE_THERMISTOR = 0x0001, +}; + +/* Chip power state */ +enum lc709203f_power_mode { + LC709203F_POWER_MODE_OPERATIONAL = 0x0001, + LC709203F_POWER_MODE_SLEEP = 0x0002, +}; + +/* Current Direction Auto/Charge/Discharge mode */ +enum lc709203f_current_direction { + LC709203F_DIRECTION_AUTO = 0x0000, + LC709203F_DIRECTION_CHARGE = 0x0001, + LC709203F_DIRECTION_DISCHARGE = 0xFFFF, +}; + +/* Selects a battery profile */ +enum lc709203f_battery_profile { + LC709203F_BATTERY_PROFILE_0 = 0x0000, + LC709203F_BATTERY_PROFILE_1 = 0x0001, +}; + +/* Approx battery pack size. Pick the closest of the following values for your battery size. */ +enum lc709203f_battery_apa { + LC709203F_APA_100MAH = 0x08, + LC709203F_APA_200MAH = 0x0B, + LC709203F_APA_500MAH = 0x10, + LC709203F_APA_1000MAH = 0x19, + LC709203F_APA_2000MAH = 0x2D, + LC709203F_APA_3000MAH = 0x36, +}; + +struct lc709203f_config { + struct i2c_dt_spec i2c; + bool initial_rsoc; + char *battery_apa; + enum lc709203f_battery_profile battery_profile; + bool thermistor; + int thermistor_b_value; + int thermistor_apt; + enum lc709203f_temp_mode thermistor_mode; +}; + +#define LC709203F_INIT_RSOC_VAL 0xAA55 /* RSOC initialization value */ +#define LC709203F_CRC_POLYNOMIAL 0x07 /* Polynomial to calculate CRC-8-ATM */ + +static int lc709203f_read_word(const struct device *dev, uint8_t reg, uint16_t *value); +static int lc709203f_write_word(const struct device *dev, uint8_t reg, uint16_t value); + +static int lc709203f_get_alarm_low_rsoc(const struct device *dev, uint8_t *rsoc); +static int lc709203f_get_alarm_low_voltage(const struct device *dev, uint16_t *voltage); +static int lc709203f_get_apa(const struct device *dev, enum lc709203f_battery_apa *apa); +static int lc709203f_get_cell_temperature(const struct device *dev, uint16_t *temperature); +static int lc709203f_get_cell_voltage(const struct device *dev, uint16_t *voltage); +static int lc709203f_get_current_direction(const struct device *dev, + enum lc709203f_current_direction *direction); +static int lc709203f_get_power_mode(const struct device *dev, enum lc709203f_power_mode *mode); +static int lc709203f_get_rsoc(const struct device *dev, uint8_t *rsoc); + +static int lc709203f_set_initial_rsoc(const struct device *dev); +static int lc709203f_set_alarm_low_rsoc(const struct device *dev, uint8_t rsoc); +static int lc709203f_set_alarm_low_voltage(const struct device *dev, uint16_t voltage); +static int lc709203f_set_apa(const struct device *dev, enum lc709203f_battery_apa apa); +static int lc709203f_set_apt(const struct device *dev, uint16_t apt); +static int lc709203f_set_battery_profile(const struct device *dev, + enum lc709203f_battery_profile profile); +static int lc709203f_set_current_direction(const struct device *dev, + enum lc709203f_current_direction direction); +static int lc709203f_set_power_mode(const struct device *dev, enum lc709203f_power_mode mode); +static int lc709203f_set_temp_mode(const struct device *dev, enum lc709203f_temp_mode mode); +static int lc709203f_set_thermistor_b(const struct device *dev, uint16_t value); + +/* + * Read a 16-bit register value (with CRC check). + * + * The LC709203F expects the following transaction: + * Write: [reg] + * Read: [LSB, MSB, CRC] + * + * The CRC is computed over: + * [I2C_addr (write), reg, I2C_addr (read), LSB, MSB] + */ +static int lc709203f_read_word(const struct device *dev, uint8_t reg, uint16_t *value) +{ + const struct lc709203f_config *config = dev->config; + uint8_t buf[3]; + int ret; + + ret = i2c_write_read_dt(&config->i2c, ®, sizeof(reg), buf, sizeof(buf)); + if (ret) { + LOG_ERR("i2c_write_read failed (reg 0x%02x): %d", reg, ret); + return ret; + } + + uint8_t crc_buf[5]; + + /* Build buffer for CRC calculation */ + crc_buf[0] = config->i2c.addr << 1; + crc_buf[1] = reg; + crc_buf[2] = (config->i2c.addr << 1) | 0x01; + crc_buf[3] = buf[0]; /* LSB */ + crc_buf[4] = buf[1]; /* MSB */ + + uint8_t crc = crc8(crc_buf, sizeof(crc_buf), LC709203F_CRC_POLYNOMIAL, 0, false); + + if (crc != buf[2]) { + LOG_ERR("CRC mismatch on reg 0x%02x", reg); + return -EIO; + } + + if (value) { + *value = sys_get_le16(buf); /* LSB, MSB */ + } + + return 0; +} + +/* + * Write a 16-bit word to a register (with CRC appended). + * + * The transaction is: + * Write: [reg, LSB, MSB, CRC] + * + * The CRC is computed over: + * [I2C_addr (write), reg, LSB, MSB] + */ +static int lc709203f_write_word(const struct device *dev, uint8_t reg, uint16_t value) +{ + const struct lc709203f_config *config = dev->config; + uint8_t crc_buf[4]; + uint8_t write_buf[4]; + + crc_buf[0] = config->i2c.addr << 1; + crc_buf[1] = reg; + sys_put_le16(value, &crc_buf[2]); /* LSB, MSB */ + + write_buf[0] = reg; + sys_put_le16(value, &write_buf[1]); /* LSB, MSB */ + write_buf[3] = crc8(crc_buf, sizeof(crc_buf), LC709203F_CRC_POLYNOMIAL, 0, false); + + return i2c_write_dt(&config->i2c, write_buf, sizeof(write_buf)); +} + +static int lc709203f_get_alarm_low_rsoc(const struct device *dev, uint8_t *rsoc) +{ + uint16_t tmp; + int ret; + + if (!dev || !rsoc) { + return -EINVAL; + } + + ret = lc709203f_read_word(dev, LC709203F_REG_ALARM_LOW_RSOC, &tmp); + if (ret) { + return ret; + } + + *rsoc = (uint8_t)tmp; + return 0; +} + +static int lc709203f_get_alarm_low_voltage(const struct device *dev, uint16_t *voltage) +{ + if (!dev || !voltage) { + return -EINVAL; + } + return lc709203f_read_word(dev, LC709203F_REG_ALARM_LOW_VOLTAGE, voltage); +} + +static int lc709203f_get_apa(const struct device *dev, enum lc709203f_battery_apa *apa) +{ + uint16_t tmp; + int ret; + + if (!dev || !apa) { + return -EINVAL; + } + + ret = lc709203f_read_word(dev, LC709203F_REG_APA, &tmp); + if (ret) { + return ret; + } + + *apa = (enum lc709203f_battery_apa)tmp; + return 0; +} + +static int lc709203f_get_cell_temperature(const struct device *dev, uint16_t *temperature) +{ + if (!dev || !temperature) { + return -EINVAL; + } + return lc709203f_read_word(dev, LC709203F_REG_CELL_TEMPERATURE, temperature); +} + +static int lc709203f_get_cell_voltage(const struct device *dev, uint16_t *voltage) +{ + if (!dev || !voltage) { + return -EINVAL; + } + return lc709203f_read_word(dev, LC709203F_REG_CELL_VOLTAGE, voltage); +} + +static int lc709203f_get_current_direction(const struct device *dev, + enum lc709203f_current_direction *direction) +{ + uint16_t tmp; + int ret; + + if (!dev || !direction) { + return -EINVAL; + } + + ret = lc709203f_read_word(dev, LC709203F_REG_CURRENT_DIRECTION, &tmp); + if (ret) { + return ret; + } + + *direction = (enum lc709203f_current_direction)tmp; + return 0; +} + +static int lc709203f_get_power_mode(const struct device *dev, enum lc709203f_power_mode *mode) +{ + uint16_t tmp; + int ret; + + if (!dev || !mode) { + return -EINVAL; + } + + ret = lc709203f_read_word(dev, LC709203F_REG_IC_POWER_MODE, &tmp); + if (ret) { + return ret; + } + + *mode = (enum lc709203f_power_mode)tmp; + return 0; +} + +static int lc709203f_get_rsoc(const struct device *dev, uint8_t *rsoc) +{ + uint16_t tmp; + int ret; + + if (!dev || !rsoc) { + return -EINVAL; + } + + ret = lc709203f_read_word(dev, LC709203F_REG_RSOC, &tmp); + if (ret) { + return ret; + } + + *rsoc = (uint8_t)tmp; + return 0; +} + +static int lc709203f_set_initial_rsoc(const struct device *dev) +{ + if (!dev) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_INITIAL_RSOC, LC709203F_INIT_RSOC_VAL); +} + +static int lc709203f_set_alarm_low_rsoc(const struct device *dev, uint8_t rsoc) +{ + if (!dev) { + return -EINVAL; + } + if (rsoc > 100) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_ALARM_LOW_RSOC, rsoc); +} + +static int lc709203f_set_alarm_low_voltage(const struct device *dev, uint16_t voltage) +{ + if (!dev) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_ALARM_LOW_VOLTAGE, voltage); +} + +static int lc709203f_set_apa(const struct device *dev, enum lc709203f_battery_apa apa) +{ + if (!dev) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_APA, (uint16_t)apa); +} + +static int lc709203f_set_apt(const struct device *dev, uint16_t apt) +{ + if (!dev) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_APT, apt); +} + +static int lc709203f_set_battery_profile(const struct device *dev, + enum lc709203f_battery_profile profile) +{ + if (!dev) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_BAT_PROFILE, (uint16_t)profile); +} + +static int lc709203f_set_current_direction(const struct device *dev, + enum lc709203f_current_direction direction) +{ + if (!dev) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_CURRENT_DIRECTION, (uint16_t)direction); +} + +static int lc709203f_set_power_mode(const struct device *dev, enum lc709203f_power_mode mode) +{ + if (!dev) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_IC_POWER_MODE, (uint16_t)mode); +} + +static int lc709203f_set_temp_mode(const struct device *dev, enum lc709203f_temp_mode mode) +{ + if (!dev) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_STATUS_BIT, (uint16_t)mode); +} + +static int lc709203f_set_thermistor_b(const struct device *dev, uint16_t value) +{ + if (!dev) { + return -EINVAL; + } + return lc709203f_write_word(dev, LC709203F_REG_THERMISTOR_B, value); +} + +enum lc709203f_battery_apa lc709203f_string_to_apa(const char *apa_string) +{ + static const char *const apas[] = {"100mAh", "200mAh", "500mAh", + "1000mAh", "2000mAh", "3000mAh"}; + + static const enum lc709203f_battery_apa apa_values[] = { + LC709203F_APA_100MAH, LC709203F_APA_200MAH, LC709203F_APA_500MAH, + LC709203F_APA_1000MAH, LC709203F_APA_2000MAH, LC709203F_APA_3000MAH}; + + /* Check if the string is NULL or empty */ + for (size_t i = 0; i < ARRAY_SIZE(apas); i++) { + if (strncmp(apa_string, apas[i], strlen(apas[i])) == 0) { + return apa_values[i]; + } + } + LOG_ERR("Invalid apa_string: %s, returning default: %d", apa_string, LC709203F_APA_100MAH); + return LC709203F_APA_100MAH; +} + +enum lc709203f_power_mode lc709203f_num_to_power_mode(uint16_t num) +{ + switch (num) { + case 1: + return LC709203F_POWER_MODE_OPERATIONAL; + case 2: + return LC709203F_POWER_MODE_SLEEP; + default: + LOG_ERR("Invalid power mode: %d", num); + return LC709203F_POWER_MODE_OPERATIONAL; + } +} + +enum lc709203f_current_direction lc709203f_num_to_current_direction(uint16_t num) +{ + switch (num) { + case 0: + return LC709203F_DIRECTION_AUTO; + case 1: + return LC709203F_DIRECTION_CHARGE; + case 0xFFFF: + return LC709203F_DIRECTION_DISCHARGE; + default: + LOG_ERR("Invalid current direction: %d", num); + return LC709203F_DIRECTION_AUTO; + } +} + +/* + * Device initialization function. + */ +static int lc709203f_init(const struct device *dev) +{ + const struct lc709203f_config *config = dev->config; + int ret = 0; + + if (!device_is_ready(config->i2c.bus)) { + LOG_ERR("I2C bus not ready"); + return -ENODEV; + } + + enum lc709203f_power_mode mode = LC709203F_POWER_MODE_OPERATIONAL; + + LOG_DBG("Get power mode"); + ret = lc709203f_get_power_mode(dev, &mode); + if (ret) { + LOG_ERR("Failed to get power mode: %d", ret); + } + + LOG_DBG("Power mode: %d", mode); + if (mode == LC709203F_POWER_MODE_SLEEP) { + LOG_DBG("Set Power mode"); + ret = lc709203f_set_power_mode(dev, LC709203F_POWER_MODE_OPERATIONAL); + + if (ret) { + LOG_ERR("Failed to set power mode: %d", ret); + } + } + + LOG_DBG("Set battery pack: %s", config->battery_apa); + ret = lc709203f_set_apa(dev, lc709203f_string_to_apa(config->battery_apa)); + + if (ret) { + LOG_ERR("Failed to set battery pack: %d", ret); + } + + LOG_DBG("Set battery profile: %d", config->battery_profile); + ret = lc709203f_set_battery_profile(dev, config->battery_profile); + + if (ret) { + LOG_ERR("Failed to set battery profile: %d", ret); + } + + if (config->thermistor) { + LOG_DBG("Set temperature mode: %d", config->thermistor_mode); + lc709203f_set_temp_mode(dev, config->thermistor_mode); + if (ret) { + LOG_ERR("Failed to set temperature mode: %d", ret); + } + + LOG_DBG("Set thermistor B value: %d", config->thermistor_b_value); + ret = lc709203f_set_thermistor_b(dev, config->thermistor_b_value); + + if (ret) { + LOG_ERR("Failed to set thermistor B value: %d", ret); + } + + LOG_DBG("Set thermistor APT: %d", config->thermistor_apt); + ret = lc709203f_set_apt(dev, config->thermistor_apt); + + if (ret) { + LOG_ERR("Failed to set thermistor APT: %d", ret); + } + } + + if (config->initial_rsoc) { + LOG_DBG("lc709203f_set_initial_rsoc"); + ret = lc709203f_set_initial_rsoc(dev); + + if (ret) { + LOG_ERR("Quickstart failed: %d", ret); + return ret; + } + } + + LOG_DBG("initialized"); + return 0; +} + +static int lc709203f_get_prop(const struct device *dev, fuel_gauge_prop_t prop, + union fuel_gauge_prop_val *val) +{ + int rc = 0; + uint16_t tmp_val = 0; + const struct lc709203f_config *config = dev->config; + + switch (prop) { + case FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE: + rc = lc709203f_get_rsoc(dev, &val->relative_state_of_charge); + break; + case FUEL_GAUGE_TEMPERATURE: + if (!config->thermistor) { + LOG_ERR("Thermistor not enabled"); + return -ENOTSUP; + } + rc = lc709203f_get_cell_temperature(dev, &val->temperature); + break; + case FUEL_GAUGE_VOLTAGE: + rc = lc709203f_get_cell_voltage(dev, &tmp_val); + val->voltage = tmp_val * 1000; + break; + case FUEL_GAUGE_SBS_MODE: + rc = lc709203f_get_power_mode(dev, (enum lc709203f_power_mode *)&val->sbs_mode); + break; + case FUEL_GAUGE_DESIGN_CAPACITY: + enum lc709203f_battery_apa apa = LC709203F_APA_100MAH; + + rc = lc709203f_get_apa(dev, &apa); + + switch (apa) { + case LC709203F_APA_100MAH: + val->design_cap = 100; + break; + case LC709203F_APA_200MAH: + val->design_cap = 200; + break; + case LC709203F_APA_500MAH: + val->design_cap = 500; + break; + case LC709203F_APA_1000MAH: + val->design_cap = 1000; + break; + case LC709203F_APA_2000MAH: + val->design_cap = 2000; + break; + case LC709203F_APA_3000MAH: + val->design_cap = 3000; + break; + default: + LOG_ERR("Invalid battery capacity: %d", apa); + return -EINVAL; + } + break; + case FUEL_GAUGE_CURRENT_DIRECTION: + rc = lc709203f_get_current_direction( + dev, (enum lc709203f_current_direction *)&val->current_direction); + break; + case FUEL_GAUGE_STATE_OF_CHARGE_ALARM: + rc = lc709203f_get_alarm_low_rsoc(dev, &val->state_of_charge_alarm); + break; + case FUEL_GAUGE_LOW_VOLTAGE_ALARM: + rc = lc709203f_get_alarm_low_voltage(dev, &tmp_val); + val->low_voltage_alarm = tmp_val * 1000; + break; + default: + rc = -ENOTSUP; + break; + } + + return rc; +} + +static int lc709203f_set_prop(const struct device *dev, fuel_gauge_prop_t prop, + union fuel_gauge_prop_val val) +{ + int rc = 0; + + switch (prop) { + case FUEL_GAUGE_SBS_MODE: + rc = lc709203f_set_power_mode(dev, lc709203f_num_to_power_mode(val.sbs_mode)); + break; + case FUEL_GAUGE_CURRENT_DIRECTION: + rc = lc709203f_set_current_direction( + dev, lc709203f_num_to_current_direction(val.current_direction)); + break; + case FUEL_GAUGE_STATE_OF_CHARGE_ALARM: + rc = lc709203f_set_alarm_low_rsoc(dev, val.state_of_charge_alarm); + break; + case FUEL_GAUGE_LOW_VOLTAGE_ALARM: + rc = lc709203f_set_alarm_low_voltage(dev, val.low_voltage_alarm / 1000); + break; + default: + rc = -ENOTSUP; + break; + } + + return rc; +} + +static DEVICE_API(fuel_gauge, lc709203f_driver_api) = { + .get_property = &lc709203f_get_prop, + .set_property = &lc709203f_set_prop, +}; + +#define LC709203F_INIT(inst) \ + \ + static const struct lc709203f_config lc709203f_config_##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + .initial_rsoc = DT_INST_PROP(inst, initial_rsoc), \ + .battery_apa = DT_INST_PROP(inst, apa), \ + .battery_profile = DT_INST_PROP(inst, battery_profile), \ + .thermistor = DT_INST_PROP(inst, thermistor), \ + .thermistor_b_value = DT_INST_PROP(inst, thermistor_b_value), \ + .thermistor_apt = DT_INST_PROP(inst, apt), \ + .thermistor_mode = DT_INST_PROP(inst, thermistor_mode), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, &lc709203f_init, NULL, NULL, &lc709203f_config_##inst, \ + POST_KERNEL, CONFIG_FUEL_GAUGE_INIT_PRIORITY, \ + &lc709203f_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(LC709203F_INIT) diff --git a/drivers/fuel_gauge/lc709203f/lc709203f.h b/drivers/fuel_gauge/lc709203f/lc709203f.h new file mode 100644 index 0000000000000..36d880c9c4bec --- /dev/null +++ b/drivers/fuel_gauge/lc709203f/lc709203f.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Philipp Steiner + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_LC709203F_LC709203F_H_ +#define ZEPHYR_DRIVERS_SENSOR_LC709203F_LC709203F_H_ + +#include + +enum lc709203f_regs { + LC709203F_REG_BEFORE_RSOC = 0x04, /* Initialize before RSOC */ + LC709203F_REG_THERMISTOR_B = 0x06, /* Read/write thermistor B */ + LC709203F_REG_INITIAL_RSOC = 0x07, /* Initialize RSOC calculation */ + LC709203F_REG_CELL_TEMPERATURE = 0x08, /* Read/write cell temperature */ + LC709203F_REG_CELL_VOLTAGE = 0x09, /* Read batt voltage */ + LC709203F_REG_CURRENT_DIRECTION = 0x0A, /* Read/write current direction */ + LC709203F_REG_APA = 0x0B, /* Adjustment Pack Application */ + LC709203F_REG_APT = 0x0C, /* Read/write Adjustment Pack Thermistor */ + LC709203F_REG_RSOC = 0x0D, /* Read state of charge; 1% 0−100 scale */ + LC709203F_REG_CELL_ITE = 0x0F, /* Read batt indicator to empty */ + LC709203F_REG_IC_VERSION = 0x11, /* Read IC version */ + LC709203F_REG_BAT_PROFILE = 0x12, /* Set the battery profile */ + LC709203F_REG_ALARM_LOW_RSOC = 0x13, /* Alarm on percent threshold */ + LC709203F_REG_ALARM_LOW_VOLTAGE = 0x14, /* Alarm on voltage threshold */ + LC709203F_REG_IC_POWER_MODE = 0x15, /* Sets sleep/power mode */ + LC709203F_REG_STATUS_BIT = 0x16, /* Temperature obtaining method */ + LC709203F_REG_NUM_PARAMETER = 0x1A /* Batt profile code */ +}; + +#endif /* ZEPHYR_DRIVERS_SENSOR_LC709203F_LC709203F_H_ */ diff --git a/dts/bindings/fuel-gauge/onnn,lc709203f.yaml b/dts/bindings/fuel-gauge/onnn,lc709203f.yaml new file mode 100644 index 0000000000000..c3f458719da8a --- /dev/null +++ b/dts/bindings/fuel-gauge/onnn,lc709203f.yaml @@ -0,0 +1,85 @@ +# Copyright (c) 2025 Philipp Steiner +# SPDX-License-Identifier: Apache-2.0 + +description: | + ON Semiconductor LC709203F fuel gauge. For more info visit + https://www.onsemi.com/products/power-management/battery-management/battery-fuel-gauges/LC709204F + +compatible: "onnn,lc709203f" + +include: [i2c-device.yaml, fuel-gauge.yaml] + +properties: + initial-rsoc: + type: boolean + description: | + The LSI can be forced to initialize RSOC by sending the he Initial RSOC Command. + The LSI initializes RSOC by the measured voltage at that time when the Initial + RSOC command is written + + apa: + type: string + required: true + description: | + Adjustment Pack Application: Value for a battery type to improve the RSOC precision. + Typical values are provided and should be selected approximately. + enum: + - "100mAh" + - "200mAh" + - "500mAh" + - "1000mAh" + - "2000mAh" + - "3000mAh" + + battery-profile: + type: int + description: | + The LSI contains a data file comprised of two battery profiles. + This register is used to select the battery profile to be used. + Register Number of the Parameter (0x1A) contains identity of the data file. + The Data file is loaded during final test depending on the part number ordered. + To decide which battery-profile should be used please refer to the LC709203F data sheet. + enum: + - 0x0 + - 0x1 + + thermistor: + type: boolean + description: | + Specifies if the device is used with a thermistor or not. + + thermistor-b-value: + type: int + description: | + Sets B-constant of the thermistor to be measured. Refer to + the specification sheet of the thermistor for the set value to used. + The default is set to 0x0D34 which is the initial value of the IC. + Note: This value will only be set if a thermistor is used. + default: 0x0D34 + + apt: + type: int + description: | + This is used to compensate for the delay of the thermistor + measurement caused by a capacitor across the thermistor. + The default value has been found to meet most of circuits + where a capacitor is not put. For details please refer to the + LC709203F data sheet. + Note: This value will only be set if a thermistor is used. + default: 0x1E + + thermistor-mode: + type: int + description: | + This selects the Thermistor mode. + Thermistor mode (0x1): The LSI measures the attached thermistor and + loads the temperature into the Cell Temperature register. + I2C mode (0x0): the temperature is provided by the host processor. + The default is set to 0x1, because if a thermistor is used, the + thermistor mode should be selected. + Note: This value will only be necessary if a thermistor is used, + otherwise the initial value of the IC is 0x0 and setting is not necessary. + enum: + - 0x0 + - 0x1 + default: 0x1 From 2aba776bd747d6c719cb4022f0613a677f2ac077 Mon Sep 17 00:00:00 2001 From: Philipp Steiner Date: Thu, 24 Apr 2025 14:09:27 +0200 Subject: [PATCH 4/6] tests: fuelgauge: Add Onsemi LC709203F driver Add test for the Onsemi LC709203F fuel gauge Signed-off-by: Philipp Steiner --- .../fuel_gauge/lc709203f/CMakeLists.txt | 7 + .../lc709203f/boards/native_sim.conf | 3 + .../lc709203f/boards/native_sim.overlay | 14 ++ tests/drivers/fuel_gauge/lc709203f/prj.conf | 6 + .../fuel_gauge/lc709203f/src/test_lc709203f.c | 195 ++++++++++++++++++ .../fuel_gauge/lc709203f/testcase.yaml | 7 + 6 files changed, 232 insertions(+) create mode 100644 tests/drivers/fuel_gauge/lc709203f/CMakeLists.txt create mode 100644 tests/drivers/fuel_gauge/lc709203f/boards/native_sim.conf create mode 100644 tests/drivers/fuel_gauge/lc709203f/boards/native_sim.overlay create mode 100644 tests/drivers/fuel_gauge/lc709203f/prj.conf create mode 100644 tests/drivers/fuel_gauge/lc709203f/src/test_lc709203f.c create mode 100644 tests/drivers/fuel_gauge/lc709203f/testcase.yaml diff --git a/tests/drivers/fuel_gauge/lc709203f/CMakeLists.txt b/tests/drivers/fuel_gauge/lc709203f/CMakeLists.txt new file mode 100644 index 0000000000000..84b71bcfcbc8b --- /dev/null +++ b/tests/drivers/fuel_gauge/lc709203f/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(device) + +target_sources(app PRIVATE src/test_lc709203f.c) diff --git a/tests/drivers/fuel_gauge/lc709203f/boards/native_sim.conf b/tests/drivers/fuel_gauge/lc709203f/boards/native_sim.conf new file mode 100644 index 0000000000000..022a71dd0f0a6 --- /dev/null +++ b/tests/drivers/fuel_gauge/lc709203f/boards/native_sim.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_EMUL=y diff --git a/tests/drivers/fuel_gauge/lc709203f/boards/native_sim.overlay b/tests/drivers/fuel_gauge/lc709203f/boards/native_sim.overlay new file mode 100644 index 0000000000000..7df1b59a5f4ed --- /dev/null +++ b/tests/drivers/fuel_gauge/lc709203f/boards/native_sim.overlay @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +&i2c0 { + lc709203f: lc709203f@0b { + compatible = "onnn,lc709203f"; + status = "okay"; + reg = <0x0b>; + apa = "500mAh"; + battery-profile = <0x01>; + thermistor; + }; +}; diff --git a/tests/drivers/fuel_gauge/lc709203f/prj.conf b/tests/drivers/fuel_gauge/lc709203f/prj.conf new file mode 100644 index 0000000000000..ce11abb435a4a --- /dev/null +++ b/tests/drivers/fuel_gauge/lc709203f/prj.conf @@ -0,0 +1,6 @@ +CONFIG_ZTEST=y +CONFIG_I2C=y +CONFIG_TEST_USERSPACE=y +CONFIG_LOG=y + +CONFIG_FUEL_GAUGE=y diff --git a/tests/drivers/fuel_gauge/lc709203f/src/test_lc709203f.c b/tests/drivers/fuel_gauge/lc709203f/src/test_lc709203f.c new file mode 100644 index 0000000000000..d67c4cdfceeb8 --- /dev/null +++ b/tests/drivers/fuel_gauge/lc709203f/src/test_lc709203f.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2025 Philipp Steiner + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct lc709203f_fixture { + const struct device *dev; + const struct fuel_gauge_driver_api *api; +}; + +static void *lc709203f_setup(void) +{ + static ZTEST_DMEM struct lc709203f_fixture fixture; + + fixture.dev = DEVICE_DT_GET_ANY(onnn_lc709203f); + k_object_access_all_grant(fixture.dev); + + zassert_true(device_is_ready(fixture.dev), "Fuel Gauge not found"); + + return &fixture; +} + +ZTEST_USER_F(lc709203f, test_get_some_props_failed__returns_bad_status) +{ + fuel_gauge_prop_t props[] = { + /* First invalid property */ + FUEL_GAUGE_PROP_MAX, + /* Second invalid property */ + FUEL_GAUGE_PROP_MAX, + /* Valid property */ + FUEL_GAUGE_VOLTAGE, + }; + union fuel_gauge_prop_val vals[ARRAY_SIZE(props)]; + + int ret = fuel_gauge_get_props(fixture->dev, props, vals, ARRAY_SIZE(props)); + + zassert_equal(ret, -ENOTSUP, "Getting bad property has a good status."); +} + +ZTEST_USER_F(lc709203f, test_set_all_props_failed__returns_err) +{ + fuel_gauge_prop_t prop_types[] = { + /* Invalid property */ + FUEL_GAUGE_PROP_MAX, + }; + union fuel_gauge_prop_val props[ARRAY_SIZE(prop_types)] = {0}; + + int ret = fuel_gauge_set_props(fixture->dev, prop_types, props, ARRAY_SIZE(props)); + + zassert_equal(ret, -ENOTSUP); +} + +ZTEST_USER_F(lc709203f, test_set_some_props_failed__returns_err) +{ + fuel_gauge_prop_t prop_types[] = { + /* First invalid property */ + FUEL_GAUGE_PROP_MAX, + /* Second invalid property */ + FUEL_GAUGE_PROP_MAX, + /* Valid property */ + FUEL_GAUGE_STATE_OF_CHARGE_ALARM, + /* Set Manufacturer's Access to arbitrary word */ + + }; + + union fuel_gauge_prop_val props[] = { + /* First invalid property */ + {0}, + /* Second invalid property */ + {0}, + /* Valid property */ + /* Sets state of charge threshold to generate Alarm signal*/ + {.state_of_charge_alarm = 10}, + }; + + int ret = fuel_gauge_set_props(fixture->dev, prop_types, props, ARRAY_SIZE(props)); + + zassert_equal(ret, -ENOTSUP); +} + +ZTEST_USER_F(lc709203f, test_set_prop_can_be_get) +{ + uint16_t sbs_mode = 0x0002; + uint16_t current_direction = 0x0001; + uint8_t state_of_charge_alarm = 20; + uint32_t low_voltage_alarm = 3000 * 1000; + + fuel_gauge_prop_t prop_types[] = { + FUEL_GAUGE_SBS_MODE, + FUEL_GAUGE_CURRENT_DIRECTION, + FUEL_GAUGE_STATE_OF_CHARGE_ALARM, + FUEL_GAUGE_LOW_VOLTAGE_ALARM, + }; + + union fuel_gauge_prop_val set_props[] = { + { + .sbs_mode = sbs_mode, + }, + { + .current_direction = current_direction, + }, + { + .state_of_charge_alarm = state_of_charge_alarm, + }, + { + .low_voltage_alarm = low_voltage_alarm, + }, + }; + + union fuel_gauge_prop_val get_props[ARRAY_SIZE(prop_types)]; + + zassert_ok( + fuel_gauge_set_props(fixture->dev, prop_types, set_props, ARRAY_SIZE(set_props))); + + zassert_ok( + fuel_gauge_get_props(fixture->dev, prop_types, get_props, ARRAY_SIZE(get_props))); + + zassert_equal(get_props[0].sbs_mode, sbs_mode); + zassert_equal(get_props[1].current_direction, current_direction); + zassert_equal(get_props[2].state_of_charge_alarm, state_of_charge_alarm); + zassert_equal(get_props[3].low_voltage_alarm, low_voltage_alarm); +} + +ZTEST_USER_F(lc709203f, test_get_props__returns_ok) +{ + fuel_gauge_prop_t props[] = { + FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE, + FUEL_GAUGE_TEMPERATURE, + FUEL_GAUGE_VOLTAGE, + FUEL_GAUGE_SBS_MODE, + FUEL_GAUGE_DESIGN_CAPACITY, + FUEL_GAUGE_CURRENT_DIRECTION, + FUEL_GAUGE_STATE_OF_CHARGE_ALARM, + FUEL_GAUGE_LOW_VOLTAGE_ALARM, + }; + union fuel_gauge_prop_val vals[ARRAY_SIZE(props)]; + + int ret = fuel_gauge_get_props(fixture->dev, props, vals, ARRAY_SIZE(props)); + +#if CONFIG_EMUL + zassert_equal(vals[0].relative_state_of_charge, 50); + zassert_equal(vals[1].temperature, 0x0BA6); + zassert_equal(vals[2].voltage, 3700 * 1000); + zassert_equal(vals[3].sbs_mode, 0x0001); + zassert_equal(vals[4].design_cap, 500); + zassert_true(((vals[5].current_direction == 0x0000) || + (vals[5].current_direction == 0x0001) || + (vals[5].current_direction == 0xFFFF))); + zassert_equal(vals[6].state_of_charge_alarm, 0x0008); + zassert_equal(vals[7].low_voltage_alarm, 0x0000); +#else + zassert_between_inclusive(vals[0].relative_state_of_charge, 0, 100); + zassert_between_inclusive(vals[1].temperature, 0x09E4, 0x0D04); + zassert_between_inclusive(vals[2].voltage, 0, 0xFFFF * 1000); + zassert_between_inclusive(vals[3].sbs_mode, 0x0001, 0x0002); + zassert_true(((vals[4].design_cap == 100) || (vals[4].design_cap == 200) || + (vals[4].design_cap == 500) || (vals[4].design_cap == 1000) || + (vals[4].design_cap == 3000))); + zassert_true(((vals[5].current_direction == 0x0000) || + (vals[5].current_direction == 0x0001) || + (vals[5].current_direction == 0xFFFF))); + zassert_between_inclusive(vals[6].state_of_charge_alarm, 0, 100); + zassert_between_inclusive(vals[7].low_voltage_alarm, 0, 0xFFFF * 1000); +#endif + + zassert_equal(ret, 0, "Getting bad property has a good status."); +} + +ZTEST_USER_F(lc709203f, test_set_get_single_prop) +{ + uint8_t test_value = 5; + + union fuel_gauge_prop_val state_of_charge_alarm_set = { + .state_of_charge_alarm = test_value, + }; + union fuel_gauge_prop_val state_of_charge_alarm_get; + + zassert_ok(fuel_gauge_set_prop(fixture->dev, FUEL_GAUGE_STATE_OF_CHARGE_ALARM, + state_of_charge_alarm_set)); + zassert_ok(fuel_gauge_get_prop(fixture->dev, FUEL_GAUGE_STATE_OF_CHARGE_ALARM, + &state_of_charge_alarm_get)); + zassert_equal(state_of_charge_alarm_get.state_of_charge_alarm, test_value); +} + +ZTEST_SUITE(lc709203f, NULL, lc709203f_setup, NULL, NULL, NULL); diff --git a/tests/drivers/fuel_gauge/lc709203f/testcase.yaml b/tests/drivers/fuel_gauge/lc709203f/testcase.yaml new file mode 100644 index 0000000000000..317307c492bab --- /dev/null +++ b/tests/drivers/fuel_gauge/lc709203f/testcase.yaml @@ -0,0 +1,7 @@ +tests: + drivers.fuel_gauge.lc709203f: + tags: + - fuel_gauge + filter: dt_compat_enabled("onnn,lc709203f") + platform_allow: + - native_sim From 185fab5d24bb98c9c4be52a4dcb005bfc2574670 Mon Sep 17 00:00:00 2001 From: Philipp Steiner Date: Tue, 29 Apr 2025 11:56:37 +0200 Subject: [PATCH 5/6] boards: adafruit: esp32s2_feather: update documentation update because of LC709203F driver was added Signed-off-by: Philipp Steiner --- .../doc/adafruit_feather_esp32s2.rst | 67 +++++++++++++++---- .../doc/adafruit_feather_esp32s2_tft.rst | 55 +++++++++++---- .../adafruit_feather_esp32s2_tft_reverse.rst | 12 ++-- 3 files changed, 102 insertions(+), 32 deletions(-) diff --git a/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2.rst b/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2.rst index a5fb76324d985..00779bc87e3f0 100644 --- a/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2.rst +++ b/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2.rst @@ -16,7 +16,7 @@ Hardware - 320KB SRAM, 4MB flash + 2MB PSRAM - USB-C directly connected to the ESP32-S2 for USB - LiPo connector and built-in battery charging when powered via USB-C -- LC709203 or MAX17048 fuel gauge for battery voltage and state-of-charge reporting +- LC709203F or MAX17048 fuel gauge for battery voltage and state-of-charge reporting - Built-in NeoPixel indicator RGB LED - STEMMA QT connector for I2C devices, with switchable power for low-power mode @@ -28,12 +28,10 @@ Hardware overlay. All boards, except the `Adafruit ESP32-S2 Feather with BME280 Sensor`_ have a space for it, but will not be shipped with. - As of May 31, 2023 - Adafruit has changed the battery monitor chip from the - now-discontinued LC709203 to the MAX17048. Check the back silkscreen of your Feather to + now-discontinued LC709203F to the MAX17048. Check the back silkscreen of your Feather to see which chip you have. - - For the MAX17048 a driver in zephyr exists and is supported, but needs to be added via - a devicetree overlay. - - For the LC709203 a driver does'nt exists yet and the fuel gauge for boards with this IC - is not available. + - For the MAX17048 and LC709203F a driver in zephyr exists and is supported, but needs to be + added via a devicetree overlay. - For the `Adafruit ESP32-S2 Feather`_ there are two different Revisions ``rev B`` and ``rev C``. The ``rev C`` board has revised the power circuitry for the NeoPixel and I2C QT port. Instead of a transistor the ``rev C`` has a LDO regulator. To enable the @@ -304,19 +302,64 @@ functioning correctly with Zephyr: :board: adafruit_feather_esp32s2@C :goals: build flash -Testing the Fuel Gauge (MAX17048) -********************************* +Testing the Fuel Gauge +********************** -There is a sample available to verify that the MAX17048 fuel gauge on the board are -functioning correctly with Zephyr: +There is a sample available to verify that the MAX17048 or LC709203F fuel gauge on the board are +functioning correctly with Zephyr .. note:: - As of May 31, 2023 Adafruit changed the battery monitor chip from the now-discontinued LC709203 + As of May 31, 2023 Adafruit changed the battery monitor chip from the now-discontinued LC709203F to the MAX17048. +**Rev B** + +For the Rev B a devicetree overlay for the LC709203F fuel gauge already exists in the +``samples/drivers/fuel_gauge/boards`` folder. + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/fuel_gauge + :board: adafruit_feather_esp32s2@B + :goals: build flash + +**Rev C** + +For the Rev C a devicetree overlay for the MAX17048 fuel gauge already exists in the +``samples/drivers/fuel_gauge/boards`` folder. + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/fuel_gauge + :board: adafruit_feather_esp32s2@C + :goals: build flash + +For the LC709203F a devicetree overlay needs to be added to the build. +The overlay can be added via the ``--extra-dtc-overlay`` argument and should most likely includes +the following: + +.. code-block:: devicetree + + / { + aliases { + fuel-gauge0 = &lc709203f; + }; + }; + + &i2c0 { + lc709203f: lc709203f@0b { + compatible = "onnn,lc709203f"; + status = "okay"; + reg = <0x0b>; + power-domains = <&i2c_reg>; + apa = "500mAh"; + battery-profile = <0x01>; + }; + }; + + .. zephyr-app-commands:: - :zephyr-app: samples/fuel_gauge/max17048/ + :zephyr-app: samples/drivers/fuel_gauge :board: adafruit_feather_esp32s2@C + :west-args: --extra-dtc-overlay="boards/name_of_your.overlay" :goals: build flash Testing Wi-Fi diff --git a/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2_tft.rst b/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2_tft.rst index 6f44363ca4b59..6629b57feee6d 100644 --- a/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2_tft.rst +++ b/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2_tft.rst @@ -16,7 +16,7 @@ Hardware - 320KB SRAM, 4MB flash + 2MB PSRAM - USB-C directly connected to the ESP32-S2 for USB - LiPo connector and built-in battery charging when powered via USB-C -- LC709203 or MAX17048 fuel gauge for battery voltage and state-of-charge reporting +- LC709203F or MAX17048 fuel gauge for battery voltage and state-of-charge reporting - Charging indicator LED, user LED, reset and boot buttons. - Built-in NeoPixel indicator RGB LED - STEMMA QT connector for I2C devices, with switchable power for low-power mode @@ -26,12 +26,10 @@ Hardware - The board has a space for a BME280, but will not be shipped with. - As of May 31, 2023 - Adafruit has changed the battery monitor chip from the - now-discontinued LC709203 to the MAX17048. Check the back silkscreen of your Feather to + now-discontinued LC709203F to the MAX17048. Check the back silkscreen of your Feather to see which chip you have. - - For the MAX17048 a driver in zephyr exists and is supported, but needs to be added via - a devicetree overlay. - - For the LC709203 a driver does'nt exists yet and the fuel gauge for boards with this IC - is not available. + - For the MAX17048 and LC709203F a driver in zephyr exists and is supported, but needs to be + added via a devicetree overlay. Supported Features ================== @@ -252,19 +250,52 @@ Testing the TFT :board: adafruit_feather_esp32s2_tft :goals: build flash -Testing the Fuel Gauge (MAX17048) -********************************* +Testing the Fuel Gauge +********************** -There is a sample available to verify that the MAX17048 fuel gauge on the board are -functioning correctly with Zephyr: +There is a sample available to verify that the MAX17048 or LC709203F fuel gauge on the board are +functioning correctly with Zephyr. .. note:: - As of May 31, 2023 Adafruit changed the battery monitor chip from the now-discontinued LC709203 + As of May 31, 2023 Adafruit changed the battery monitor chip from the now-discontinued LC709203F to the MAX17048. +**LC709203F Fuel Gauge** + +For the LC709203F a devicetree overlay already exists in the ``samples/drivers/fuel_gauge/boards`` folder. + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/fuel_gauge + :board: adafruit_feather_esp32s2_tft + :goals: build flash + +**MAX17048 Fuel Gauge** + +For the MAX17048 a devicetree overlay needs to be added to the build. +The overlay can be added via the ``--extra-dtc-overlay`` argument and should most likely includes +the following: + +.. code-block:: devicetree + + / { + aliases { + fuel-gauge0 = &max17048; + }; + }; + + &i2c0 { + max17048: max17048@36 { + compatible = "maxim,max17048"; + status = "okay"; + reg = <0x36 >; + power-domains = <&i2c_reg>; + }; + }; + .. zephyr-app-commands:: - :zephyr-app: samples/fuel_gauge/max17048/ + :zephyr-app: samples/drivers/fuel_gauge :board: adafruit_feather_esp32s2_tft + :west-args: --extra-dtc-overlay="boards/name_of_your.overlay" :goals: build flash Testing Wi-Fi diff --git a/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2_tft_reverse.rst b/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2_tft_reverse.rst index fcbb635220591..ad90a07858204 100644 --- a/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2_tft_reverse.rst +++ b/boards/adafruit/feather_esp32s2/doc/adafruit_feather_esp32s2_tft_reverse.rst @@ -16,7 +16,7 @@ Hardware - 320KB SRAM, 4MB flash + 2MB PSRAM - USB-C directly connected to the ESP32-S2 for USB - LiPo connector and built-in battery charging when powered via USB-C -- LC709203 or MAX17048 fuel gauge for battery voltage and state-of-charge reporting +- MAX17048 fuel gauge for battery voltage and state-of-charge reporting - Charging indicator LED, user LED, reset and boot buttons and has 2 additional buttons. - Built-in NeoPixel indicator RGB LED - 240x135 pixel IPS TFT color display with 1.14" diagonal and ST7789 chipset. @@ -249,18 +249,14 @@ functioning correctly with Zephyr: :board: adafruit_feather_esp32s2_tft_reverse :goals: build flash -Testing the Fuel Gauge (MAX17048) -********************************* +Testing the Fuel Gauge +********************** There is a sample available to verify that the MAX17048 fuel gauge on the board are functioning correctly with Zephyr: -.. note:: - As of May 31, 2023 Adafruit changed the battery monitor chip from the now-discontinued LC709203 - to the MAX17048. - .. zephyr-app-commands:: - :zephyr-app: samples/fuel_gauge/max17048/ + :zephyr-app: samples/drivers/fuel_gauge :board: adafruit_feather_esp32s2_tft_reverse :goals: build flash From e2f29201f85e9dea0de72d291d441d45e6abd50d Mon Sep 17 00:00:00 2001 From: Philipp Steiner Date: Thu, 5 Jun 2025 07:39:07 +0200 Subject: [PATCH 6/6] samples: fuelgauge: unify fuel gauge sample Replace IC specific fuel gauge example with a generic fuel gauge example and move the fuel gauge sample to sample/drivers folder Signed-off-by: Philipp Steiner --- .../adafruit_feather_esp32s2_tft_reverse.dts | 1 + .../adafruit_feather_esp32s3_procpu.dts | 1 + .../adafruit_feather_esp32s3_tft_procpu.dts | 1 + .../fuel_gauge}/CMakeLists.txt | 2 +- samples/drivers/fuel_gauge/README.rst | 86 ++++ .../boards/adafruit_feather_esp32s2.conf} | 0 .../boards/adafruit_feather_esp32s2_B.overlay | 22 + .../boards/adafruit_feather_esp32s2_C.overlay | 20 + .../boards/adafruit_feather_esp32s2_tft.conf | 0 .../adafruit_feather_esp32s2_tft.overlay | 22 + .../adafruit_feather_esp32s2_tft_reverse.conf | 0 ...afruit_feather_esp32s2_tft_reverse.overlay | 11 + .../adafruit_feather_esp32s3_procpu.conf | 0 .../boards/nrf52840dk_nrf52840.overlay | 6 + samples/drivers/fuel_gauge/prj.conf | 4 + samples/drivers/fuel_gauge/sample.yaml | 20 + samples/drivers/fuel_gauge/src/main.c | 379 ++++++++++++++++++ samples/fuel_gauge/fuel_gauge.rst | 5 - samples/fuel_gauge/max17048/README.rst | 51 --- .../boards/adafruit_feather_esp32s2_C.overlay | 8 - .../adafruit_feather_esp32s2_tft.overlay | 8 - samples/fuel_gauge/max17048/prj.conf | 1 - samples/fuel_gauge/max17048/sample.yaml | 9 - samples/fuel_gauge/max17048/src/main.c | 67 ---- 24 files changed, 574 insertions(+), 150 deletions(-) rename samples/{fuel_gauge/max17048 => drivers/fuel_gauge}/CMakeLists.txt (91%) create mode 100644 samples/drivers/fuel_gauge/README.rst rename samples/{fuel_gauge/max17048/boards/adafruit_feather_esp32s2_C.conf => drivers/fuel_gauge/boards/adafruit_feather_esp32s2.conf} (100%) create mode 100644 samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_B.overlay create mode 100644 samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_C.overlay rename samples/{fuel_gauge/max17048 => drivers/fuel_gauge}/boards/adafruit_feather_esp32s2_tft.conf (100%) create mode 100644 samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft.overlay rename samples/{fuel_gauge/max17048 => drivers/fuel_gauge}/boards/adafruit_feather_esp32s2_tft_reverse.conf (100%) create mode 100644 samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft_reverse.overlay rename samples/{fuel_gauge/max17048 => drivers/fuel_gauge}/boards/adafruit_feather_esp32s3_procpu.conf (100%) rename samples/{fuel_gauge/max17048 => drivers/fuel_gauge}/boards/nrf52840dk_nrf52840.overlay (87%) create mode 100644 samples/drivers/fuel_gauge/prj.conf create mode 100644 samples/drivers/fuel_gauge/sample.yaml create mode 100644 samples/drivers/fuel_gauge/src/main.c delete mode 100644 samples/fuel_gauge/fuel_gauge.rst delete mode 100644 samples/fuel_gauge/max17048/README.rst delete mode 100644 samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_C.overlay delete mode 100644 samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_tft.overlay delete mode 100644 samples/fuel_gauge/max17048/prj.conf delete mode 100644 samples/fuel_gauge/max17048/sample.yaml delete mode 100644 samples/fuel_gauge/max17048/src/main.c diff --git a/boards/adafruit/feather_esp32s2/adafruit_feather_esp32s2_tft_reverse.dts b/boards/adafruit/feather_esp32s2/adafruit_feather_esp32s2_tft_reverse.dts index be28841df3a4d..fa46fe93646bc 100644 --- a/boards/adafruit/feather_esp32s2/adafruit_feather_esp32s2_tft_reverse.dts +++ b/boards/adafruit/feather_esp32s2/adafruit_feather_esp32s2_tft_reverse.dts @@ -19,6 +19,7 @@ aliases { backlight = &led1; + fuel-gauge0 = &max17048; }; leds { diff --git a/boards/adafruit/feather_esp32s3/adafruit_feather_esp32s3_procpu.dts b/boards/adafruit/feather_esp32s3/adafruit_feather_esp32s3_procpu.dts index b745820ddaa09..a782536176bc6 100644 --- a/boards/adafruit/feather_esp32s3/adafruit_feather_esp32s3_procpu.dts +++ b/boards/adafruit/feather_esp32s3/adafruit_feather_esp32s3_procpu.dts @@ -38,6 +38,7 @@ sw0 = &button0; led0 = &led0; led-strip = &led_strip; + fuel-gauge0 = &max17048; }; buttons { diff --git a/boards/adafruit/feather_esp32s3_tft/adafruit_feather_esp32s3_tft_procpu.dts b/boards/adafruit/feather_esp32s3_tft/adafruit_feather_esp32s3_tft_procpu.dts index b09b6262aa1e9..ab7659eef4676 100644 --- a/boards/adafruit/feather_esp32s3_tft/adafruit_feather_esp32s3_tft_procpu.dts +++ b/boards/adafruit/feather_esp32s3_tft/adafruit_feather_esp32s3_tft_procpu.dts @@ -22,6 +22,7 @@ aliases { i2c-0 = &i2c0; watchdog0 = &wdt0; + fuel-gauge0 = &max17048; }; chosen { diff --git a/samples/fuel_gauge/max17048/CMakeLists.txt b/samples/drivers/fuel_gauge/CMakeLists.txt similarity index 91% rename from samples/fuel_gauge/max17048/CMakeLists.txt rename to samples/drivers/fuel_gauge/CMakeLists.txt index 68b9bc1d7d771..55844f6f10114 100644 --- a/samples/fuel_gauge/max17048/CMakeLists.txt +++ b/samples/drivers/fuel_gauge/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(max17048) +project(fuel_gauge) FILE(GLOB app_sources src/*.c) target_sources(app PRIVATE ${app_sources}) diff --git a/samples/drivers/fuel_gauge/README.rst b/samples/drivers/fuel_gauge/README.rst new file mode 100644 index 0000000000000..445bd7e001a8d --- /dev/null +++ b/samples/drivers/fuel_gauge/README.rst @@ -0,0 +1,86 @@ +.. zephyr:code-sample:: fuel_gauge + :name: Fuel Gauge + + Use fuel gauge API to access fuel gauge properties and get charge information. + +Overview +******** + +This sample shows how to use the Zephyr :ref:`fuel_gauge_api` API driver for. + +First, the sample tries to read all public fuel gauge properties to verify which are supported by +the used fuel gauge driver. +Second, the sample then reads the battery percentage and voltage proper periodically using the +``FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE`` and ``FUEL_GAUGE_VOLTAGE`` properties. + +.. note:: + The sample does not set/write any properties to avoid misconfiguration the IC. + +Building and Running +******************** + +The sample can be configured to support a fuel gauge. + +Features +******** +By using this fuel gauge you can get the following information: + + * Read all public fuel gauge properties except ``FUEL_GAUGE_BATTERY_CUTOFF`` + * Battery charge status as percentage (periodically) + * Battery voltage (periodically) + +Sample output +************* + +.. code-block:: console + + *** Booting Zephyr OS build f95fd665ad26 *** + [00:00:00.116,000] app: Found device "lc709203f@0b" + [00:00:00.116,000] app: Test-Read generic fuel gauge properties to verify which are supported + [00:00:00.116,000] app: Info: not all properties are supported by all fuel gauges! + [00:00:00.116,000] app: Property "FUEL_GAUGE_AVG_CURRENT" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_CURRENT" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_CHARGE_CUTOFF" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_CYCLE_COUNT" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_CONNECT_STATE" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_FLAGS" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_FULL_CHARGE_CAPACITY" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_PRESENT_STATE" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_REMAINING_CAPACITY" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_RUNTIME_TO_EMPTY" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_RUNTIME_TO_FULL" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_SBS_MFR_ACCESS" is not supported + [00:00:00.116,000] app: Property "FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE" is not supported + [00:00:00.122,000] app: Property "FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE" is supported + [00:00:00.122,000] app: Relative state of charge: 100 + [00:00:00.122,000] lc709203f: Thermistor not enabled + [00:00:00.122,000] app: Property "FUEL_GAUGE_TEMPERATURE" is not supported + [00:00:00.123,000] app: Property "FUEL_GAUGE_VOLTAGE" is supported + [00:00:00.123,000] app: Voltage: 4190000 + [00:00:00.124,000] app: Property "FUEL_GAUGE_SBS_MODE" is supported + [00:00:00.124,000] app: SBS mode: 1 + [00:00:00.124,000] app: Property "FUEL_GAUGE_CHARGE_CURRENT" is not supported + [00:00:00.124,000] app: Property "FUEL_GAUGE_CHARGE_VOLTAGE" is not supported + [00:00:00.124,000] app: Property "FUEL_GAUGE_STATUS" is not supported + [00:00:00.124,000] app: Property "FUEL_GAUGE_DESIGN_CAPACITY" is supported + [00:00:00.124,000] app: Design capacity: 500 + [00:00:00.124,000] app: Property "FUEL_GAUGE_DESIGN_VOLTAGE" is not supported + [00:00:00.124,000] app: Property "FUEL_GAUGE_SBS_ATRATE" is not supported + [00:00:00.124,000] app: Property "FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL" is not supported + [00:00:00.124,000] app: Property "FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY" is not supported + [00:00:00.124,000] app: Property "FUEL_GAUGE_SBS_ATRATE_OK" is not supported + [00:00:00.125,000] app: Property "FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM" is not supported + [00:00:00.125,000] app: Property "FUEL_GAUGE_SBS_REMAINING_TIME_ALARM" is not supported + [00:00:00.125,000] app: Error: cannot get property "FUEL_GAUGE_MANUFACTURER_NAME": -88 + [00:00:00.125,000] app: Error: cannot get property "FUEL_GAUGE_DEVICE_NAME": -88 + [00:00:00.125,000] app: Error: cannot get property "FUEL_GAUGE_DEVICE_CHEMISTRY": -88 + [00:00:00.125,000] app: Property "FUEL_GAUGE_CURRENT_DIRECTION" is supported + [00:00:00.125,000] app: Current direction: 0 + [00:00:00.126,000] app: Property "FUEL_GAUGE_STATE_OF_CHARGE_ALARM" is supported + [00:00:00.126,000] app: State of charge alarm: 8 + [00:00:00.127,000] app: Property "FUEL_GAUGE_LOW_VOLTAGE_ALARM" is supported + [00:00:00.127,000] app: Low voltage alarm: 0 + [00:00:00.127,000] app: Polling fuel gauge data 'FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE' & 'FUEL_GAUGE_VOLTAGE' + [00:00:00.128,000] app: Fuel gauge data: Charge: 100%, Voltage: 4190mV + [00:00:05.130,000] app: Fuel gauge data: Charge: 100%, Voltage: 4190mV + [00:00:10.131,000] app: Fuel gauge data: Charge: 100%, Voltage: 4190mV diff --git a/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_C.conf b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2.conf similarity index 100% rename from samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_C.conf rename to samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2.conf diff --git a/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_B.overlay b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_B.overlay new file mode 100644 index 0000000000000..fbbd280652ab5 --- /dev/null +++ b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_B.overlay @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 Philipp Steiner + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + fuel-gauge0 = &lc709203f; + }; +}; + +&i2c0 { + lc709203f: lc709203f@0b { + compatible = "onnn,lc709203f"; + status = "okay"; + reg = <0x0b>; + power-domains = <&i2c_reg>; + apa = "500mAh"; + battery-profile = <0x01>; + }; +}; diff --git a/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_C.overlay b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_C.overlay new file mode 100644 index 0000000000000..0d37a8063ffd3 --- /dev/null +++ b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_C.overlay @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Philipp Steiner + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + fuel-gauge0 = &max17048; + }; +}; + +&i2c0 { + max17048: max17048@36 { + compatible = "maxim,max17048"; + status = "okay"; + reg = <0x36 >; + power-domains = <&i2c_reg>; + }; +}; diff --git a/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_tft.conf b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft.conf similarity index 100% rename from samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_tft.conf rename to samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft.conf diff --git a/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft.overlay b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft.overlay new file mode 100644 index 0000000000000..fbbd280652ab5 --- /dev/null +++ b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft.overlay @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 Philipp Steiner + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + fuel-gauge0 = &lc709203f; + }; +}; + +&i2c0 { + lc709203f: lc709203f@0b { + compatible = "onnn,lc709203f"; + status = "okay"; + reg = <0x0b>; + power-domains = <&i2c_reg>; + apa = "500mAh"; + battery-profile = <0x01>; + }; +}; diff --git a/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_tft_reverse.conf b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft_reverse.conf similarity index 100% rename from samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_tft_reverse.conf rename to samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft_reverse.conf diff --git a/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft_reverse.overlay b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft_reverse.overlay new file mode 100644 index 0000000000000..ed974697f7420 --- /dev/null +++ b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s2_tft_reverse.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2025 Philipp Steiner + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&i2c0 { + max17048: max17048@36 { + status = "okay"; + }; +}; diff --git a/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s3_procpu.conf b/samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s3_procpu.conf similarity index 100% rename from samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s3_procpu.conf rename to samples/drivers/fuel_gauge/boards/adafruit_feather_esp32s3_procpu.conf diff --git a/samples/fuel_gauge/max17048/boards/nrf52840dk_nrf52840.overlay b/samples/drivers/fuel_gauge/boards/nrf52840dk_nrf52840.overlay similarity index 87% rename from samples/fuel_gauge/max17048/boards/nrf52840dk_nrf52840.overlay rename to samples/drivers/fuel_gauge/boards/nrf52840dk_nrf52840.overlay index 52d6b9ad598c7..e166affe1c260 100644 --- a/samples/fuel_gauge/max17048/boards/nrf52840dk_nrf52840.overlay +++ b/samples/drivers/fuel_gauge/boards/nrf52840dk_nrf52840.overlay @@ -4,6 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +/ { + aliases { + fuel-gauge0 = &max17048; + }; +}; + &i2c0 { status = "okay"; compatible = "nordic,nrf-twim"; diff --git a/samples/drivers/fuel_gauge/prj.conf b/samples/drivers/fuel_gauge/prj.conf new file mode 100644 index 0000000000000..24d8fd7fa4552 --- /dev/null +++ b/samples/drivers/fuel_gauge/prj.conf @@ -0,0 +1,4 @@ +CONFIG_FUEL_GAUGE=y +CONFIG_I2C=y +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=4096 diff --git a/samples/drivers/fuel_gauge/sample.yaml b/samples/drivers/fuel_gauge/sample.yaml new file mode 100644 index 0000000000000..04a89b8954fbe --- /dev/null +++ b/samples/drivers/fuel_gauge/sample.yaml @@ -0,0 +1,20 @@ +sample: + name: Fuel Gauge Sensor Sample +tests: + sample.sensor.fuel_gauge: + build_only: true + platform_allow: + - nrf52840dk/nrf52840 + - adafruit_feather_esp32s2@B + - adafruit_feather_esp32s2@C + - adafruit_feather_esp32s2_tft + - adafruit_feather_esp32s2_tft_reverse/esp32s2 + - adafruit_feather_esp32s3/esp32s3/procpu + integration_platforms: + - nrf52840dk/nrf52840 + - adafruit_feather_esp32s2@B + - adafruit_feather_esp32s2@C + - adafruit_feather_esp32s2_tft + - adafruit_feather_esp32s2_tft_reverse/esp32s2 + - adafruit_feather_esp32s3/esp32s3/procpu + tags: fuel_gauge diff --git a/samples/drivers/fuel_gauge/src/main.c b/samples/drivers/fuel_gauge/src/main.c new file mode 100644 index 0000000000000..5e0eb0216065d --- /dev/null +++ b/samples/drivers/fuel_gauge/src/main.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2023 Alvaro Garcia Gomez + * Copyright (c) 2025 Philipp Steiner + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "zephyr/sys/util.h" +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#include +LOG_MODULE_REGISTER(app); + +const char *fuel_gauge_prop_to_str(enum fuel_gauge_prop_type prop) +{ + switch (prop) { + case FUEL_GAUGE_AVG_CURRENT: + return "FUEL_GAUGE_AVG_CURRENT"; + case FUEL_GAUGE_BATTERY_CUTOFF: + return "FUEL_GAUGE_BATTERY_CUTOFF"; + case FUEL_GAUGE_CURRENT: + return "FUEL_GAUGE_CURRENT"; + case FUEL_GAUGE_CHARGE_CUTOFF: + return "FUEL_GAUGE_CHARGE_CUTOFF"; + case FUEL_GAUGE_CYCLE_COUNT: + return "FUEL_GAUGE_CYCLE_COUNT"; + case FUEL_GAUGE_CONNECT_STATE: + return "FUEL_GAUGE_CONNECT_STATE"; + case FUEL_GAUGE_FLAGS: + return "FUEL_GAUGE_FLAGS"; + case FUEL_GAUGE_FULL_CHARGE_CAPACITY: + return "FUEL_GAUGE_FULL_CHARGE_CAPACITY"; + case FUEL_GAUGE_PRESENT_STATE: + return "FUEL_GAUGE_PRESENT_STATE"; + case FUEL_GAUGE_REMAINING_CAPACITY: + return "FUEL_GAUGE_REMAINING_CAPACITY"; + case FUEL_GAUGE_RUNTIME_TO_EMPTY: + return "FUEL_GAUGE_RUNTIME_TO_EMPTY"; + case FUEL_GAUGE_RUNTIME_TO_FULL: + return "FUEL_GAUGE_RUNTIME_TO_FULL"; + case FUEL_GAUGE_SBS_MFR_ACCESS: + return "FUEL_GAUGE_SBS_MFR_ACCESS"; + case FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE: + return "FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE"; + case FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE: + return "FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE"; + case FUEL_GAUGE_TEMPERATURE: + return "FUEL_GAUGE_TEMPERATURE"; + case FUEL_GAUGE_VOLTAGE: + return "FUEL_GAUGE_VOLTAGE"; + case FUEL_GAUGE_SBS_MODE: + return "FUEL_GAUGE_SBS_MODE"; + case FUEL_GAUGE_CHARGE_CURRENT: + return "FUEL_GAUGE_CHARGE_CURRENT"; + case FUEL_GAUGE_CHARGE_VOLTAGE: + return "FUEL_GAUGE_CHARGE_VOLTAGE"; + case FUEL_GAUGE_STATUS: + return "FUEL_GAUGE_STATUS"; + case FUEL_GAUGE_DESIGN_CAPACITY: + return "FUEL_GAUGE_DESIGN_CAPACITY"; + case FUEL_GAUGE_DESIGN_VOLTAGE: + return "FUEL_GAUGE_DESIGN_VOLTAGE"; + case FUEL_GAUGE_SBS_ATRATE: + return "FUEL_GAUGE_SBS_ATRATE"; + case FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL: + return "FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL"; + case FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY: + return "FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY"; + case FUEL_GAUGE_SBS_ATRATE_OK: + return "FUEL_GAUGE_SBS_ATRATE_OK"; + case FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM: + return "FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM"; + case FUEL_GAUGE_SBS_REMAINING_TIME_ALARM: + return "FUEL_GAUGE_SBS_REMAINING_TIME_ALARM"; + case FUEL_GAUGE_MANUFACTURER_NAME: + return "FUEL_GAUGE_MANUFACTURER_NAME"; + case FUEL_GAUGE_DEVICE_NAME: + return "FUEL_GAUGE_DEVICE_NAME"; + case FUEL_GAUGE_DEVICE_CHEMISTRY: + return "FUEL_GAUGE_DEVICE_CHEMISTRY"; + case FUEL_GAUGE_CURRENT_DIRECTION: + return "FUEL_GAUGE_CURRENT_DIRECTION"; + case FUEL_GAUGE_STATE_OF_CHARGE_ALARM: + return "FUEL_GAUGE_STATE_OF_CHARGE_ALARM"; + case FUEL_GAUGE_LOW_VOLTAGE_ALARM: + return "FUEL_GAUGE_LOW_VOLTAGE_ALARM"; + default: + return "Unknown fuel gauge property"; + } +} + +int main(void) +{ + const struct device *const dev = DEVICE_DT_GET(DT_ALIAS(fuel_gauge0)); + int ret = 0; + + if (dev == NULL) { + LOG_ERR("no device found."); + return 0; + } + + if (!device_is_ready(dev)) { + LOG_ERR("Error: Device \"%s\" is not ready; check the driver initialization logs " + "for errors.", + dev->name); + return 0; + } + + LOG_INF("Found device \"%s\"", dev->name); + + { + LOG_INF("Test-Read generic fuel gauge properties to verify which are supported"); + LOG_INF("Info: not all properties are supported by all fuel gauges!"); + + /* + * FUEL_GAUGE_BATTERY_CUTOFF will not be tested because this is a special property + * and is intended to be used to cutoff the battery from the system - useful for + * storage/shipping of devices + */ + + fuel_gauge_prop_t test_props[] = { + FUEL_GAUGE_AVG_CURRENT, + FUEL_GAUGE_CURRENT, + FUEL_GAUGE_CHARGE_CUTOFF, + FUEL_GAUGE_CYCLE_COUNT, + FUEL_GAUGE_CONNECT_STATE, + FUEL_GAUGE_FLAGS, + FUEL_GAUGE_FULL_CHARGE_CAPACITY, + FUEL_GAUGE_PRESENT_STATE, + FUEL_GAUGE_REMAINING_CAPACITY, + FUEL_GAUGE_RUNTIME_TO_EMPTY, + FUEL_GAUGE_RUNTIME_TO_FULL, + FUEL_GAUGE_SBS_MFR_ACCESS, + FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE, + FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE, + FUEL_GAUGE_TEMPERATURE, + FUEL_GAUGE_VOLTAGE, + FUEL_GAUGE_SBS_MODE, + FUEL_GAUGE_CHARGE_CURRENT, + FUEL_GAUGE_CHARGE_VOLTAGE, + FUEL_GAUGE_STATUS, + FUEL_GAUGE_DESIGN_CAPACITY, + FUEL_GAUGE_DESIGN_VOLTAGE, + FUEL_GAUGE_SBS_ATRATE, + FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL, + FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY, + FUEL_GAUGE_SBS_ATRATE_OK, + FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM, + FUEL_GAUGE_SBS_REMAINING_TIME_ALARM, + FUEL_GAUGE_MANUFACTURER_NAME, + FUEL_GAUGE_DEVICE_NAME, + FUEL_GAUGE_DEVICE_CHEMISTRY, + FUEL_GAUGE_CURRENT_DIRECTION, + FUEL_GAUGE_STATE_OF_CHARGE_ALARM, + FUEL_GAUGE_LOW_VOLTAGE_ALARM, + }; + + union fuel_gauge_prop_val test_vals[ARRAY_SIZE(test_props)]; + + for (size_t i = 0; i < ARRAY_SIZE(test_props); i++) { + + if (test_props[i] == FUEL_GAUGE_MANUFACTURER_NAME) { + struct sbs_gauge_manufacturer_name mfg_name; + + ret = fuel_gauge_get_buffer_prop(dev, FUEL_GAUGE_MANUFACTURER_NAME, + &mfg_name, sizeof(mfg_name)); + if (ret == -ENOTSUP) { + LOG_INF("Property \"%s\" is not supported", + fuel_gauge_prop_to_str(test_props[i])); + } else if (ret < 0) { + LOG_ERR("Error: cannot get property \"%s\": %d", + fuel_gauge_prop_to_str(test_props[i]), ret); + } else { + LOG_INF("Property \"%s\" is supported", + fuel_gauge_prop_to_str(test_props[i])); + LOG_INF("Manufacturer name: %s", + mfg_name.manufacturer_name); + } + } else if (test_props[i] == FUEL_GAUGE_DEVICE_NAME) { + struct sbs_gauge_device_name dev_name; + + ret = fuel_gauge_get_buffer_prop(dev, FUEL_GAUGE_DEVICE_NAME, + &dev_name, sizeof(dev_name)); + if (ret == -ENOTSUP) { + LOG_INF("Property \"%s\" is not supported", + fuel_gauge_prop_to_str(test_props[i])); + } else if (ret < 0) { + LOG_ERR("Error: cannot get property \"%s\": %d", + fuel_gauge_prop_to_str(test_props[i]), ret); + } else { + LOG_INF("Property \"%s\" is supported", + fuel_gauge_prop_to_str(test_props[i])); + LOG_INF("Device name: %s", dev_name.device_name); + } + } else if (test_props[i] == FUEL_GAUGE_DEVICE_CHEMISTRY) { + struct sbs_gauge_device_chemistry device_chemistry; + + ret = fuel_gauge_get_buffer_prop(dev, FUEL_GAUGE_DEVICE_CHEMISTRY, + &device_chemistry, + sizeof(device_chemistry)); + if (ret == -ENOTSUP) { + LOG_INF("Property \"%s\" is not supported", + fuel_gauge_prop_to_str(test_props[i])); + } else if (ret < 0) { + LOG_ERR("Error: cannot get property \"%s\": %d", + fuel_gauge_prop_to_str(test_props[i]), ret); + } else { + LOG_INF("Property \"%s\" is supported", + fuel_gauge_prop_to_str(test_props[i])); + LOG_INF("Device chemistry: %s", + device_chemistry.device_chemistry); + } + } else { + /* For all other properties, use the generic get_props function */ + + ret = fuel_gauge_get_props(dev, &test_props[i], &test_vals[i], 1); + + if (ret == -ENOTSUP) { + LOG_INF("Property \"%s\" is not supported", + fuel_gauge_prop_to_str(test_props[i])); + } else if (ret < 0) { + LOG_ERR("Error: cannot get property \"%s\": %d", + fuel_gauge_prop_to_str(test_props[i]), ret); + } else { + LOG_INF("Property \"%s\" is supported", + fuel_gauge_prop_to_str(test_props[i])); + + switch (test_props[i]) { + case FUEL_GAUGE_AVG_CURRENT: + LOG_INF(" Avg current: %d", + test_vals[i].avg_current); + break; + case FUEL_GAUGE_CURRENT: + LOG_INF(" Current: %d", test_vals[i].current); + break; + case FUEL_GAUGE_CYCLE_COUNT: + LOG_INF(" Cycle count: %" PRIu32, + test_vals[i].cycle_count); + break; + case FUEL_GAUGE_CONNECT_STATE: + LOG_INF(" Connect state: 0x%" PRIx32, + test_vals[i].connect_state); + break; + case FUEL_GAUGE_FLAGS: + LOG_INF(" Flags: 0x%" PRIx32, test_vals[i].flags); + break; + case FUEL_GAUGE_FULL_CHARGE_CAPACITY: + LOG_INF(" Full charge capacity: %" PRIu32, + test_vals[i].full_charge_capacity); + break; + case FUEL_GAUGE_PRESENT_STATE: + LOG_INF(" Present state: %d", + test_vals[i].present_state); + break; + case FUEL_GAUGE_REMAINING_CAPACITY: + LOG_INF(" Remaining capacity: %" PRIu32, + test_vals[i].remaining_capacity); + break; + case FUEL_GAUGE_RUNTIME_TO_EMPTY: + LOG_INF(" Runtime to empty: %" PRIu32, + test_vals[i].runtime_to_empty); + break; + case FUEL_GAUGE_RUNTIME_TO_FULL: + LOG_INF(" Runtime to full: %" PRIu32, + test_vals[i].runtime_to_full); + break; + case FUEL_GAUGE_SBS_MFR_ACCESS: + LOG_INF(" SBS MFR access: %" PRIu16, + test_vals[i].sbs_mfr_access_word); + break; + case FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE: + LOG_INF(" Absolute state of charge: %" PRIu8, + test_vals[i].absolute_state_of_charge); + break; + case FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE: + LOG_INF(" Relative state of charge: %" PRIu8, + test_vals[i].relative_state_of_charge); + break; + case FUEL_GAUGE_TEMPERATURE: + LOG_INF(" Temperature: %" PRIu16, + test_vals[i].temperature); + break; + case FUEL_GAUGE_VOLTAGE: + LOG_INF(" Voltage: %d", test_vals[i].voltage); + break; + case FUEL_GAUGE_SBS_MODE: + LOG_INF(" SBS mode: %" PRIu16, + test_vals[i].sbs_mode); + break; + case FUEL_GAUGE_CHARGE_CURRENT: + LOG_INF(" Charge current: %" PRIu32, + test_vals[i].chg_current); + break; + case FUEL_GAUGE_CHARGE_VOLTAGE: + LOG_INF(" Charge voltage: %" PRIu32, + test_vals[i].chg_voltage); + break; + case FUEL_GAUGE_STATUS: + LOG_INF(" Status: 0x%" PRIx16, + test_vals[i].fg_status); + break; + case FUEL_GAUGE_DESIGN_CAPACITY: + LOG_INF(" Design capacity: %" PRIu16, + test_vals[i].design_cap); + break; + case FUEL_GAUGE_DESIGN_VOLTAGE: + LOG_INF(" Design voltage: %" PRIx16, + test_vals[i].design_volt); + break; + case FUEL_GAUGE_SBS_ATRATE: + LOG_INF(" SBS at rate: %" PRIi16, + test_vals[i].sbs_at_rate); + break; + case FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL: + LOG_INF(" SBS at rate time to full: %" PRIu16, + test_vals[i].sbs_at_rate_time_to_full); + break; + case FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY: + LOG_INF(" SBS at rate time to empty: %" PRIu16, + test_vals[i].sbs_at_rate_time_to_empty); + break; + case FUEL_GAUGE_SBS_ATRATE_OK: + LOG_INF(" SBS at rate ok: %d", + test_vals[i].sbs_at_rate_ok); + break; + case FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM: + LOG_INF(" SBS remaining capacity alarm: %" PRIu16, + test_vals[i].sbs_remaining_capacity_alarm); + break; + case FUEL_GAUGE_SBS_REMAINING_TIME_ALARM: + LOG_INF(" SBS remaining time alarm: %" PRIu16, + test_vals[i].sbs_remaining_time_alarm); + break; + case FUEL_GAUGE_CURRENT_DIRECTION: + LOG_INF(" Current direction: %" PRIu16, + test_vals[i].current_direction); + break; + case FUEL_GAUGE_STATE_OF_CHARGE_ALARM: + LOG_INF(" State of charge alarm: %" PRIu8, + test_vals[i].state_of_charge_alarm); + break; + case FUEL_GAUGE_LOW_VOLTAGE_ALARM: + LOG_INF(" Low voltage alarm: %" PRIu32, + test_vals[i].low_voltage_alarm); + break; + } + } + } + } + } + + LOG_INF("Polling fuel gauge data 'FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE' & " + "'FUEL_GAUGE_VOLTAGE'"); + + while (1) { + fuel_gauge_prop_t poll_props[] = { + FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE, + FUEL_GAUGE_VOLTAGE, + }; + + union fuel_gauge_prop_val poll_vals[ARRAY_SIZE(poll_props)]; + + ret = fuel_gauge_get_props(dev, poll_props, poll_vals, ARRAY_SIZE(poll_props)); + + if (ret < 0) { + LOG_ERR("Error: cannot get properties"); + } else { + LOG_INF("Fuel gauge data: Charge: %d%%, Voltage: %dmV", + poll_vals[0].relative_state_of_charge, poll_vals[1].voltage / 1000); + } + + k_sleep(K_MSEC(5000)); + } + return 0; +} diff --git a/samples/fuel_gauge/fuel_gauge.rst b/samples/fuel_gauge/fuel_gauge.rst deleted file mode 100644 index 01df07c8aaa16..0000000000000 --- a/samples/fuel_gauge/fuel_gauge.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. zephyr:code-sample-category:: fuel_gauge - :name: Fuel Gauge - :show-listing: - - Samples that show how to interact with :ref:`fuel_gauge_api`. diff --git a/samples/fuel_gauge/max17048/README.rst b/samples/fuel_gauge/max17048/README.rst deleted file mode 100644 index cdb08e1e4ec65..0000000000000 --- a/samples/fuel_gauge/max17048/README.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. zephyr:code-sample:: max17048 - :name: MAX17048 Li-Ion battery fuel gauge - - Read battery percentage and power status using MAX17048 fuel gauge. - -Overview -******** - -This sample shows how to use the Zephyr :ref:`fuel_gauge_api` API driver for the MAX17048 fuel gauge. - -.. _MAX17048: https://www.maximintegrated.com/en/products/power/battery-management/MAX17048.html - -The sample periodically reads battery percentage and power status - -Building and Running -******************** - -The sample can be configured to support MAX17048 fuel gauge connected via either I2C. It only needs -an I2C pin configuration - -Features -******** -By using this fuel gauge you can get the following information: - * Battery charge status as percentage - * Total time until battery is fully charged or discharged - * Battery voltage - * Charging state: if charging or discharging - - -Notes -***** -The charging state and the time to full/empty are estimated and based on the last consumption average. That means that -if you plug/unplug a charger it will take some time until it is actually detected by the chip. Don't try to plug/unplug -to see in real time the charging status change because it will not work. If you really need to know exactly the moment -when the battery is being charged you will need other method. - -Sample output -************* - -``` -*** Booting Zephyr OS build 16043f62a40a *** -Found device "max17048@36", getting fuel gauge data -Time to empty 1911 -Time to full 0 -Charge 72% -Voltage 3968 -Time to empty 1911 -Time to full 0 -Charge 72% -Voltage 3968 -``` diff --git a/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_C.overlay b/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_C.overlay deleted file mode 100644 index 66298554285cb..0000000000000 --- a/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_C.overlay +++ /dev/null @@ -1,8 +0,0 @@ -&i2c0 { - max17048: max17048@36 { - compatible = "maxim,max17048"; - status = "okay"; - reg = <0x36 >; - power-domains = <&i2c_reg>; - }; -}; diff --git a/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_tft.overlay b/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_tft.overlay deleted file mode 100644 index 66298554285cb..0000000000000 --- a/samples/fuel_gauge/max17048/boards/adafruit_feather_esp32s2_tft.overlay +++ /dev/null @@ -1,8 +0,0 @@ -&i2c0 { - max17048: max17048@36 { - compatible = "maxim,max17048"; - status = "okay"; - reg = <0x36 >; - power-domains = <&i2c_reg>; - }; -}; diff --git a/samples/fuel_gauge/max17048/prj.conf b/samples/fuel_gauge/max17048/prj.conf deleted file mode 100644 index aeb0cb44b209b..0000000000000 --- a/samples/fuel_gauge/max17048/prj.conf +++ /dev/null @@ -1 +0,0 @@ -CONFIG_FUEL_GAUGE=y diff --git a/samples/fuel_gauge/max17048/sample.yaml b/samples/fuel_gauge/max17048/sample.yaml deleted file mode 100644 index 82d2b537b8199..0000000000000 --- a/samples/fuel_gauge/max17048/sample.yaml +++ /dev/null @@ -1,9 +0,0 @@ -sample: - name: MAX17048 Sensor sample -tests: - sample.sensor.max17048: - build_only: true - platform_allow: nrf52dk/nrf52832 - integration_platforms: - - nrf52dk/nrf52832 - tags: fuel_gauge diff --git a/samples/fuel_gauge/max17048/src/main.c b/samples/fuel_gauge/max17048/src/main.c deleted file mode 100644 index d5f6ab1e33781..0000000000000 --- a/samples/fuel_gauge/max17048/src/main.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2023 Alvaro Garcia Gomez - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "zephyr/sys/util.h" -#include -#include -#include -#include - - - -int main(void) -{ - const struct device *const dev = DEVICE_DT_GET_ANY(maxim_max17048); - int ret = 0; - - if (dev == NULL) { - printk("\nError: no device found.\n"); - return 0; - } - - if (!device_is_ready(dev)) { - printk("\nError: Device \"%s\" is not ready; " - "check the driver initialization logs for errors.\n", - dev->name); - return 0; - } - - - - printk("Found device \"%s\", getting fuel gauge data\n", dev->name); - - if (dev == NULL) { - return 0; - } - - while (1) { - - fuel_gauge_prop_t props[] = { - FUEL_GAUGE_RUNTIME_TO_EMPTY, - FUEL_GAUGE_RUNTIME_TO_FULL, - FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE, - FUEL_GAUGE_VOLTAGE, - }; - - union fuel_gauge_prop_val vals[ARRAY_SIZE(props)]; - - ret = fuel_gauge_get_props(dev, props, vals, ARRAY_SIZE(props)); - if (ret < 0) { - printk("Error: cannot get properties\n"); - } else { - printk("Time to empty %d minutes\n", vals[0].runtime_to_empty); - - printk("Time to full %d minutes\n", vals[1].runtime_to_full); - - printk("Charge %d%%\n", vals[2].relative_state_of_charge); - - printk("Voltage %d\n uV", vals[3].voltage); - } - - k_sleep(K_MSEC(5000)); - } - return 0; -}