diff --git a/app/include/drivers/sensor/battery/battery_charging.h b/app/include/drivers/sensor/battery/battery_charging.h new file mode 100644 index 00000000..aa507521 --- /dev/null +++ b/app/include/drivers/sensor/battery/battery_charging.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_BATTERY_BATTERY_CHARGING_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_BATTERY_BATTERY_CHARGING_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +enum sensor_channel_bvd { + /** Charging state, bool **/ + SENSOR_CHAN_CHARGING = SENSOR_CHAN_PRIV_START, +}; + +#endif \ No newline at end of file diff --git a/app/include/zmk/battery.h b/app/include/zmk/battery.h index edc8fd7a..11c86776 100644 --- a/app/include/zmk/battery.h +++ b/app/include/zmk/battery.h @@ -7,3 +7,4 @@ #pragma once uint8_t zmk_battery_state_of_charge(void); +bool zmk_battery_charging(void); diff --git a/app/module/drivers/sensor/battery/CMakeLists.txt b/app/module/drivers/sensor/battery/CMakeLists.txt index 1203e53a..2826569a 100644 --- a/app/module/drivers/sensor/battery/CMakeLists.txt +++ b/app/module/drivers/sensor/battery/CMakeLists.txt @@ -2,6 +2,7 @@ # SPDX-License-Identifier: MIT zephyr_include_directories(.) +zephyr_include_directories(${CMAKE_SOURCE_DIR}/include) zephyr_library() diff --git a/app/module/drivers/sensor/battery/battery_common.c b/app/module/drivers/sensor/battery/battery_common.c index 9afe2d5b..4a2f3b20 100644 --- a/app/module/drivers/sensor/battery/battery_common.c +++ b/app/module/drivers/sensor/battery/battery_common.c @@ -7,6 +7,8 @@ #include #include +#include + #include "battery_common.h" int battery_channel_get(const struct battery_value *value, enum sensor_channel chan, @@ -22,6 +24,11 @@ int battery_channel_get(const struct battery_value *value, enum sensor_channel c val_out->val2 = 0; break; + case SENSOR_CHAN_CHARGING: + val_out->val1 = value->charging; + val_out->val2 = 0; + break; + default: return -ENOTSUP; } diff --git a/app/module/drivers/sensor/battery/battery_common.h b/app/module/drivers/sensor/battery/battery_common.h index 3e16ceed..0c187723 100644 --- a/app/module/drivers/sensor/battery/battery_common.h +++ b/app/module/drivers/sensor/battery/battery_common.h @@ -13,6 +13,7 @@ struct battery_value { uint16_t adc_raw; uint16_t millivolts; uint8_t state_of_charge; + bool charging; }; int battery_channel_get(const struct battery_value *value, enum sensor_channel chan, diff --git a/app/module/drivers/sensor/battery/battery_nrf_vddh.c b/app/module/drivers/sensor/battery/battery_nrf_vddh.c index 32c7c61e..05cdea71 100644 --- a/app/module/drivers/sensor/battery/battery_nrf_vddh.c +++ b/app/module/drivers/sensor/battery/battery_nrf_vddh.c @@ -11,10 +11,12 @@ #include #include +#include #include #include #include +#include #include "battery_common.h" LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -23,6 +25,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); static const struct device *adc = DEVICE_DT_GET(DT_NODELABEL(adc)); +struct vddh_config { + struct gpio_dt_spec chg; +}; + struct vddh_data { struct adc_channel_cfg acc; struct adc_sequence as; @@ -32,12 +38,13 @@ struct vddh_data { static int vddh_sample_fetch(const struct device *dev, enum sensor_channel chan) { // Make sure selected channel is supported if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE && - chan != SENSOR_CHAN_ALL) { + (enum sensor_channel_bvd)chan != SENSOR_CHAN_CHARGING && chan != SENSOR_CHAN_ALL) { LOG_DBG("Selected channel is not supported: %d.", chan); return -ENOTSUP; } struct vddh_data *drv_data = dev->data; + const struct vddh_config *drv_cfg = dev->config; struct adc_sequence *as = &drv_data->as; int rc = adc_read(adc, as); @@ -61,6 +68,18 @@ static int vddh_sample_fetch(const struct device *dev, enum sensor_channel chan) LOG_DBG("ADC raw %d ~ %d mV => %d%%", drv_data->value.adc_raw, drv_data->value.millivolts, drv_data->value.state_of_charge); +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + int raw = gpio_pin_get_dt(&drv_cfg->chg); + if (raw == -EIO || raw == -EWOULDBLOCK) { + LOG_DBG("Failed to read chg status: %d", raw); + return raw; + } else { + bool charging = raw; + LOG_DBG("Charging state: %d", raw); + drv_data->value.charging = charging; + } +#endif + return rc; } @@ -77,6 +96,7 @@ static const struct sensor_driver_api vddh_api = { static int vddh_init(const struct device *dev) { struct vddh_data *drv_data = dev->data; + const struct vddh_config *drv_cfg = dev->config; if (!device_is_ready(adc)) { LOG_ERR("ADC device is not ready %s", adc->name); @@ -104,13 +124,31 @@ static int vddh_init(const struct device *dev) { #error Unsupported ADC #endif - const int rc = adc_channel_setup(adc, &drv_data->acc); + int rc = adc_channel_setup(adc, &drv_data->acc); LOG_DBG("VDDHDIV5 setup returned %d", rc); +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + if (!device_is_ready(drv_cfg->chg.port)) { + LOG_ERR("GPIO port for chg reading is not ready"); + return -ENODEV; + } + rc = gpio_pin_configure_dt(&drv_cfg->chg, GPIO_INPUT); + if (rc != 0) { + LOG_ERR("Failed to set chg feed %u: %d", drv_cfg->chg.pin, rc); + return rc; + } +#endif // DT_INST_NODE_HAS_PROP(0, chg_gpios) + return rc; } static struct vddh_data vddh_data; -DEVICE_DT_INST_DEFINE(0, &vddh_init, NULL, &vddh_data, NULL, POST_KERNEL, +static const struct vddh_config vddh_cfg = { +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + .chg = GPIO_DT_SPEC_INST_GET(0, chg_gpios), +#endif +}; + +DEVICE_DT_INST_DEFINE(0, &vddh_init, NULL, &vddh_data, &vddh_cfg, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &vddh_api); diff --git a/app/module/drivers/sensor/battery/battery_voltage_divider.c b/app/module/drivers/sensor/battery/battery_voltage_divider.c index 62a02e9c..881449ff 100644 --- a/app/module/drivers/sensor/battery/battery_voltage_divider.c +++ b/app/module/drivers/sensor/battery/battery_voltage_divider.c @@ -14,6 +14,7 @@ #include #include "battery_common.h" +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -24,6 +25,7 @@ struct io_channel_config { struct bvd_config { struct io_channel_config io_channel; struct gpio_dt_spec power; + struct gpio_dt_spec chg; uint32_t output_ohm; uint32_t full_ohm; }; @@ -42,7 +44,7 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan) // Make sure selected channel is supported if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE && - chan != SENSOR_CHAN_ALL) { + (enum sensor_channel_bvd)chan != SENSOR_CHAN_CHARGING && chan != SENSOR_CHAN_ALL) { LOG_DBG("Selected channel is not supported: %d.", chan); return -ENOTSUP; } @@ -93,6 +95,18 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan) } #endif // DT_INST_NODE_HAS_PROP(0, power_gpios) +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + int raw = gpio_pin_get_dt(&drv_cfg->chg); + if (raw == -EIO || raw == -EWOULDBLOCK) { + LOG_DBG("Failed to read chg status: %d", raw); + return raw; + } else { + bool charging = raw; + LOG_DBG("Charging state: %d", raw); + drv_data->value.charging = charging; + } + +#endif return rc; } @@ -130,6 +144,18 @@ static int bvd_init(const struct device *dev) { } #endif // DT_INST_NODE_HAS_PROP(0, power_gpios) +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + if (!device_is_ready(drv_cfg->chg.port)) { + LOG_ERR("GPIO port for chg reading is not ready"); + return -ENODEV; + } + rc = gpio_pin_configure_dt(&drv_cfg->chg, GPIO_INPUT); + if (rc != 0) { + LOG_ERR("Failed to set chg feed %u: %d", drv_cfg->chg.pin, rc); + return rc; + } +#endif // DT_INST_NODE_HAS_PROP(0, chg_gpios) + drv_data->as = (struct adc_sequence){ .channels = BIT(0), .buffer = &drv_data->value.adc_raw, @@ -166,6 +192,9 @@ static const struct bvd_config bvd_cfg = { }, #if DT_INST_NODE_HAS_PROP(0, power_gpios) .power = GPIO_DT_SPEC_INST_GET(0, power_gpios), +#endif +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + .chg = GPIO_DT_SPEC_INST_GET(0, chg_gpios), #endif .output_ohm = DT_INST_PROP(0, output_ohms), .full_ohm = DT_INST_PROP(0, full_ohms), diff --git a/app/module/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml b/app/module/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml index 28b7541b..f1ce9a46 100644 --- a/app/module/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml +++ b/app/module/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml @@ -4,3 +4,9 @@ description: Battery SoC monitoring using nRF VDDH compatible: "zmk,battery-nrf-vddh" + +properties: + chg-gpios: + required: false + type: phandle-array + description: "A GPIO pin to report charging state to" diff --git a/app/module/dts/bindings/sensor/zmk,battery-voltage-divider.yaml b/app/module/dts/bindings/sensor/zmk,battery-voltage-divider.yaml index d9e07b79..c831dcdc 100644 --- a/app/module/dts/bindings/sensor/zmk,battery-voltage-divider.yaml +++ b/app/module/dts/bindings/sensor/zmk,battery-voltage-divider.yaml @@ -6,3 +6,9 @@ description: Battery SoC monitoring using voltage divider compatible: "zmk,battery-voltage-divider" include: voltage-divider.yaml + +properties: + chg-gpios: + required: false + type: phandle-array + description: "A GPIO pin to report charging state to" diff --git a/app/src/battery.c b/app/src/battery.c index 1295f822..7fa3b403 100644 --- a/app/src/battery.c +++ b/app/src/battery.c @@ -22,9 +22,13 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include +#include + static uint8_t last_state_of_charge = 0; +static bool charging = 0; uint8_t zmk_battery_state_of_charge(void) { return last_state_of_charge; } +bool zmk_battery_charging(void) { return charging; } #if DT_HAS_CHOSEN(zmk_battery) static const struct device *const battery = DEVICE_DT_GET(DT_CHOSEN(zmk_battery)); @@ -67,6 +71,23 @@ static int zmk_battery_update(const struct device *battery) { (struct zmk_battery_state_changed){.state_of_charge = last_state_of_charge}); } +#if DT_NODE_HAS_PROP(DT_CHOSEN(zmk_battery), chg_gpios) + + rc = sensor_sample_fetch_chan(battery, SENSOR_CHAN_CHARGING); + + if (rc != 0) { + LOG_DBG("Failed to fetch battery values: %d", rc); + return rc; + } + struct sensor_value charging_state; + rc = sensor_channel_get(battery, SENSOR_CHAN_CHARGING, &charging_state); + if (rc != 0) { + LOG_DBG("Failed to get battery charging status: %d", rc); + return rc; + } + charging = charging_state.val1; +#endif + return rc; }