From b82be6b48536ef26704385a31a8c261a0b7a04f3 Mon Sep 17 00:00:00 2001 From: ReFil Date: Tue, 11 Oct 2022 15:56:16 +0100 Subject: [PATCH 1/5] feat(battery): Initial battery charging state setection feat(battery): Initial battery charging state setection --- .../drivers/sensor/battery/battery_charging.h | 21 +++++++++ app/include/zmk/battery.h | 1 + .../drivers/sensor/battery/CMakeLists.txt | 1 + .../drivers/sensor/battery/battery_common.c | 7 +++ .../drivers/sensor/battery/battery_common.h | 1 + .../drivers/sensor/battery/battery_nrf_vddh.c | 44 +++++++++++++++++-- .../sensor/battery/battery_voltage_divider.c | 31 ++++++++++++- .../bindings/sensor/zmk,battery-nrf-vddh.yaml | 6 +++ .../sensor/zmk,battery-voltage-divider.yaml | 6 +++ app/src/battery.c | 21 +++++++++ 10 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 app/include/drivers/sensor/battery/battery_charging.h 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 00000000000..aa507521690 --- /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 edc8fd7a070..11c86776996 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 1203e53a6be..2826569ac15 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 9afe2d5b4ca..4a2f3b2048f 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 3e16ceed145..0c187723a8f 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 32c7c61eb82..05cdea71274 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 62a02e9c3df..881449ff395 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 28b7541b819..f1ce9a461f8 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 d9e07b797e8..c831dcdc157 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 1295f822486..7fa3b4039f1 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; } From fee7faf809d5f625b7c4cbab712cdb70d98e66e7 Mon Sep 17 00:00:00 2001 From: ReFil <31960031+ReFil@users.noreply.github.com> Date: Fri, 20 Oct 2023 11:01:12 +0100 Subject: [PATCH 2/5] Document charging indication functionality Co-Authored-By: Cem Aksoylar --- docs/docs/config/battery.md | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/docs/config/battery.md b/docs/docs/config/battery.md index 463dc087b6e..9b1f3f0d327 100644 --- a/docs/docs/config/battery.md +++ b/docs/docs/config/battery.md @@ -49,22 +49,42 @@ Applies to: [`/chosen` node](https://docs.zephyrproject.org/3.5.0/build/dts/intr ## Battery Voltage Divider Sensor -Driver for reading the voltage of a battery using an ADC connected to a voltage divider. +Driver for reading the voltage of a battery using an ADC connected to a voltage divider. This driver can also read a GPIO pin to detect whether the battery is charging or not. This requires supported hardware (a battery charging IC with an output to indicate charging status). This functionality is optional, if the hardware doesn't support it the `chg-gpios` devicetree configuration does not have to be set. ### Devicetree Applies to: `compatible = "zmk,battery-voltage-divider"` -See [Zephyr's voltage divider documentation](https://docs.zephyrproject.org/3.5.0/build/dts/api/bindings/iio/afe/voltage-divider.html). +Definition file: [zmk/app/module/dts/bindings/sensor/zmk,battery-voltage-divider.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/module/dts/bindings/sensor/zmk,battery-voltage-divider.yaml) + +The ZMK battery voltage divider includes the [Zephyr voltage divider](https://docs.zephyrproject.org/latest/build/dts/api/bindings/adc/voltage-divider.html) and adds on additional functionality. + +| Property | Type | Description | Default | +| ----------- | ---------- | ------------------------------------------------ | ------- | +| `chg-gpios` | GPIO array | GPIO connected to the charging IC's charging pin | | + +:::note Charging indication + +The battery charging status is not consumed by any built-in indicators currently and it cannot be conveyed to the host over BLE. + +::: ## nRF VDDH Battery Sensor -Driver for reading the voltage of a battery using a Nordic nRF52's VDDH pin. +Driver for reading the voltage of a battery using a Nordic nRF52's VDDH pin. This driver can also read a GPIO pin to detect whether the battery is charging or not. This requires supported hardware (a battery charging IC with an output to indicate charging status). This functionality is optional, if the hardware doesn't support it the `chg-gpios` devicetree configuration does not have to be set. ### Devicetree Applies to: `compatible = "zmk,battery-nrf-vddh"` -Definition file: [zmk/app/module/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/module/dts/bindings/sensor/zmk%2Cbattery-nrf-vddh.yaml) +Definition file: [zmk/app/module/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/module/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml) + +| Property | Type | Description | Default | +| ----------- | ---------- | ------------------------------------------------ | ------- | +| `chg-gpios` | GPIO array | GPIO connected to the charging IC's charging pin | | + +:::note Charging indication -This driver has no configuration. +The battery charging status is not consumed by any built-in indicators currently and it cannot be conveyed to the host over BLE. + +::: From ef20c7f473dc7508e6c5966e628adb9168d504eb Mon Sep 17 00:00:00 2001 From: ReFil <31960031+ReFil@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:32:59 +0000 Subject: [PATCH 3/5] Add interrupt triggering to BVD and VDDH --- .../drivers/sensor/battery/battery_nrf_vddh.c | 67 ++++++++++++++++++- .../sensor/battery/battery_voltage_divider.c | 63 +++++++++++++++++ 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/app/module/drivers/sensor/battery/battery_nrf_vddh.c b/app/module/drivers/sensor/battery/battery_nrf_vddh.c index 05cdea71274..1103cc2df77 100644 --- a/app/module/drivers/sensor/battery/battery_nrf_vddh.c +++ b/app/module/drivers/sensor/battery/battery_nrf_vddh.c @@ -33,8 +33,47 @@ struct vddh_data { struct adc_channel_cfg acc; struct adc_sequence as; struct battery_value value; +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + const struct device *dev; + const struct sensor_trigger *data_ready_trigger; + struct gpio_callback gpio_cb; + sensor_trigger_handler_t data_ready_handler; + struct k_work work; +#endif }; +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) +static void set_int(const struct device *dev, const bool en) { + const struct vddh_config *drv_cfg = dev->config; + int ret = + gpio_pin_interrupt_configure_dt(&drv_cfg->chg, en ? GPIO_INT_EDGE_BOTH : GPIO_INT_DISABLE); + if (ret < 0) { + LOG_ERR("can't set interrupt"); + } +} + +static int vddh_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) { + struct vddh_data *drv_data = dev->data; + + set_int(dev, false); + if (trig->type != SENSOR_TRIG_DATA_READY) { + return -ENOTSUP; + } + drv_data->data_ready_trigger = trig; + drv_data->data_ready_handler = handler; + set_int(dev, true); + return 0; +} + +static void vddh_int_cb(const struct device *dev) { + struct vddh_data *drv_data = dev->data; + drv_data->data_ready_handler(dev, drv_data->data_ready_trigger); + LOG_DBG("Setting int on %d", 0); + set_int(dev, true); +} +#endif + 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 && @@ -44,7 +83,6 @@ static int vddh_sample_fetch(const struct device *dev, enum sensor_channel chan) } 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); @@ -69,6 +107,7 @@ static int vddh_sample_fetch(const struct device *dev, enum sensor_channel chan) drv_data->value.state_of_charge); #if DT_INST_NODE_HAS_PROP(0, chg_gpios) + const struct vddh_config *drv_cfg = dev->config; int raw = gpio_pin_get_dt(&drv_cfg->chg); if (raw == -EIO || raw == -EWOULDBLOCK) { LOG_DBG("Failed to read chg status: %d", raw); @@ -89,14 +128,28 @@ static int vddh_channel_get(const struct device *dev, enum sensor_channel chan, return battery_channel_get(&drv_data->value, chan, val); } +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) +static void vddh_work_cb(struct k_work *work) { + struct vddh_data *drv_data = CONTAINER_OF(work, struct vddh_data, work); + vddh_int_cb(drv_data->dev); +} +static void vddh_gpio_cb(const struct device *port, struct gpio_callback *cb, uint32_t pins) { + struct vddh_data *drv_data = CONTAINER_OF(cb, struct vddh_data, gpio_cb); + set_int(drv_data->dev, false); + k_work_submit(&drv_data->work); +} +#endif + static const struct sensor_driver_api vddh_api = { +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + .trigger_set = vddh_trigger_set, +#endif .sample_fetch = vddh_sample_fetch, .channel_get = vddh_channel_get, }; 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); @@ -128,6 +181,7 @@ static int vddh_init(const struct device *dev) { LOG_DBG("VDDHDIV5 setup returned %d", rc); #if DT_INST_NODE_HAS_PROP(0, chg_gpios) + const struct vddh_config *drv_cfg = dev->config; if (!device_is_ready(drv_cfg->chg.port)) { LOG_ERR("GPIO port for chg reading is not ready"); return -ENODEV; @@ -137,6 +191,15 @@ static int vddh_init(const struct device *dev) { LOG_ERR("Failed to set chg feed %u: %d", drv_cfg->chg.pin, rc); return rc; } + + drv_data->dev = dev; + gpio_init_callback(&drv_data->gpio_cb, vddh_gpio_cb, BIT(drv_cfg->chg.pin)); + int ret = gpio_add_callback(drv_cfg->chg.port, &drv_data->gpio_cb); + if (ret < 0) { + LOG_ERR("Failed to set chg callback: %d", ret); + return -EIO; + } + k_work_init(&drv_data->work, vddh_work_cb); #endif // DT_INST_NODE_HAS_PROP(0, chg_gpios) return rc; diff --git a/app/module/drivers/sensor/battery/battery_voltage_divider.c b/app/module/drivers/sensor/battery/battery_voltage_divider.c index 881449ff395..8b0d75e668f 100644 --- a/app/module/drivers/sensor/battery/battery_voltage_divider.c +++ b/app/module/drivers/sensor/battery/battery_voltage_divider.c @@ -35,8 +35,47 @@ struct bvd_data { struct adc_channel_cfg acc; struct adc_sequence as; struct battery_value value; +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + const struct device *dev; + const struct sensor_trigger *data_ready_trigger; + struct gpio_callback gpio_cb; + sensor_trigger_handler_t data_ready_handler; + struct k_work work; +#endif }; +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) +static void set_int(const struct device *dev, const bool en) { + const struct bvd_config *drv_cfg = dev->config; + int ret = + gpio_pin_interrupt_configure_dt(&drv_cfg->chg, en ? GPIO_INT_EDGE_BOTH : GPIO_INT_DISABLE); + if (ret < 0) { + LOG_ERR("can't set interrupt"); + } +} + +static int bvd_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) { + struct bvd_data *drv_data = dev->data; + + set_int(dev, false); + if (trig->type != SENSOR_TRIG_DATA_READY) { + return -ENOTSUP; + } + drv_data->data_ready_trigger = trig; + drv_data->data_ready_handler = handler; + set_int(dev, true); + return 0; +} + +static void bvd_int_cb(const struct device *dev) { + struct bvd_data *drv_data = dev->data; + drv_data->data_ready_handler(dev, drv_data->data_ready_trigger); + LOG_DBG("Setting int on %d", 0); + set_int(dev, true); +} +#endif + static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan) { struct bvd_data *drv_data = dev->data; const struct bvd_config *drv_cfg = dev->config; @@ -116,7 +155,22 @@ static int bvd_channel_get(const struct device *dev, enum sensor_channel chan, return battery_channel_get(&drv_data->value, chan, val); } +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) +static void bvd_work_cb(struct k_work *work) { + struct bvd_data *drv_data = CONTAINER_OF(work, struct bvd_data, work); + bvd_int_cb(drv_data->dev); +} +static void bvd_gpio_cb(const struct device *port, struct gpio_callback *cb, uint32_t pins) { + struct bvd_data *drv_data = CONTAINER_OF(cb, struct bvd_data, gpio_cb); + set_int(drv_data->dev, false); + k_work_submit(&drv_data->work); +} +#endif + static const struct sensor_driver_api bvd_api = { +#if DT_INST_NODE_HAS_PROP(0, chg_gpios) + .trigger_set = bvd_trigger_set, +#endif .sample_fetch = bvd_sample_fetch, .channel_get = bvd_channel_get, }; @@ -154,6 +208,15 @@ static int bvd_init(const struct device *dev) { LOG_ERR("Failed to set chg feed %u: %d", drv_cfg->chg.pin, rc); return rc; } + + drv_data->dev = dev; + gpio_init_callback(&drv_data->gpio_cb, bvd_gpio_cb, BIT(drv_cfg->chg.pin)); + int ret = gpio_add_callback(drv_cfg->chg.port, &drv_data->gpio_cb); + if (ret < 0) { + LOG_ERR("Failed to set chg callback: %d", ret); + return -EIO; + } + k_work_init(&drv_data->work, bvd_work_cb); #endif // DT_INST_NODE_HAS_PROP(0, chg_gpios) drv_data->as = (struct adc_sequence){ From ba810177d5527faf2535f7a1f4eb3cfef11b257c Mon Sep 17 00:00:00 2001 From: ReFil <31960031+ReFil@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:35:59 +0000 Subject: [PATCH 4/5] Add charging indication to battery event --- app/include/zmk/events/battery_state_changed.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/include/zmk/events/battery_state_changed.h b/app/include/zmk/events/battery_state_changed.h index 157490d9849..1a8839c8ca7 100644 --- a/app/include/zmk/events/battery_state_changed.h +++ b/app/include/zmk/events/battery_state_changed.h @@ -12,6 +12,7 @@ struct zmk_battery_state_changed { // TODO: Other battery channels uint8_t state_of_charge; + bool charging; }; ZMK_EVENT_DECLARE(zmk_battery_state_changed); @@ -19,6 +20,7 @@ ZMK_EVENT_DECLARE(zmk_battery_state_changed); struct zmk_peripheral_battery_state_changed { uint8_t source; // TODO: Other battery channels + // Charging state not broadcast over BAS so no need to have it in peripheral event uint8_t state_of_charge; }; From 4990e079dab8df0eab3af68b219ff54f33454fa0 Mon Sep 17 00:00:00 2001 From: ReFil <31960031+ReFil@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:36:20 +0000 Subject: [PATCH 5/5] Add interrupt trigger and charging event --- app/src/battery.c | 58 +++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/app/src/battery.c b/app/src/battery.c index 7fa3b4039f1..821183d3d6c 100644 --- a/app/src/battery.c +++ b/app/src/battery.c @@ -55,28 +55,12 @@ static int zmk_battery_update(const struct device *battery) { return rc; } - if (last_state_of_charge != state_of_charge.val1) { - last_state_of_charge = state_of_charge.val1; -#if IS_ENABLED(CONFIG_BT_BAS) - LOG_DBG("Setting BAS GATT battery level to %d.", last_state_of_charge); - - rc = bt_bas_set_battery_level(last_state_of_charge); - - if (rc != 0) { - LOG_WRN("Failed to set BAS GATT battery level (err %d)", rc); - return rc; - } -#endif - rc = raise_zmk_battery_state_changed( - (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); + LOG_DBG("Failed to fetch charging value: %d", rc); return rc; } struct sensor_value charging_state; @@ -85,9 +69,31 @@ static int zmk_battery_update(const struct device *battery) { LOG_DBG("Failed to get battery charging status: %d", rc); return rc; } - charging = charging_state.val1; #endif + if (last_state_of_charge != state_of_charge.val1 +#if DT_NODE_HAS_PROP(DT_CHOSEN(zmk_battery), chg_gpios) + || charging != charging_state.val1 +#endif + ) { + last_state_of_charge = state_of_charge.val1; +#if DT_NODE_HAS_PROP(DT_CHOSEN(zmk_battery), chg_gpios) + charging = charging_state.val1; +#endif +#if IS_ENABLED(CONFIG_BT_BAS) + LOG_DBG("Setting BAS GATT battery level to %d.", last_state_of_charge); + + rc = bt_bas_set_battery_level(last_state_of_charge); + + if (rc != 0) { + LOG_WRN("Failed to set BAS GATT battery level (err %d)", rc); + return rc; + } +#endif + rc = raise_zmk_battery_state_changed((struct zmk_battery_state_changed){ + .state_of_charge = last_state_of_charge, .charging = charging}); + } + return rc; } @@ -112,7 +118,11 @@ static void zmk_battery_start_reporting() { k_timer_start(&battery_timer, K_NO_WAIT, K_SECONDS(CONFIG_ZMK_BATTERY_REPORT_INTERVAL)); } } - +#if DT_NODE_HAS_PROP(DT_CHOSEN(zmk_battery), chg_gpios) +static void handle_chg_trig(const struct device *dev, const struct sensor_trigger *trig) { + zmk_battery_update(dev); +} +#endif static int zmk_battery_init(void) { #if !DT_HAS_CHOSEN(zmk_battery) battery = device_get_binding("BATTERY"); @@ -129,6 +139,16 @@ static int zmk_battery_init(void) { return -ENODEV; } +#if DT_NODE_HAS_PROP(DT_CHOSEN(zmk_battery), chg_gpios) + struct sensor_trigger trigger = { + .type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_ALL, + }; + if (sensor_trigger_set(battery, &trigger, handle_chg_trig) < 0) { + LOG_ERR("can't set batt chg trigger"); + }; +#endif + zmk_battery_start_reporting(); return 0; }