diff options
| author | Joel Spadin <joelspadin@gmail.com> | 2021-04-03 01:00:51 -0500 | 
|---|---|---|
| committer | Nick Winans <nick@winans.codes> | 2021-07-24 23:39:13 -0500 | 
| commit | a6de43e665ded4831d79159495660ac3a7d48e99 (patch) | |
| tree | e2a3c4d4b79dd2e18723a564c29f7eb07b169daf | |
| parent | 824d605c2218ff01ece4391711d69f623fcc75c0 (diff) | |
feat: Add nrf VDDH battery driver
Added a driver which uses the nRF52's ADC channel on the VDDH pin to
read the battery voltage when using high voltage mode.
| -rw-r--r-- | app/drivers/sensor/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | app/drivers/sensor/Kconfig | 2 | ||||
| -rw-r--r-- | app/drivers/sensor/battery/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | app/drivers/sensor/battery/Kconfig | 21 | ||||
| -rw-r--r-- | app/drivers/sensor/battery/battery_common.c | 43 | ||||
| -rw-r--r-- | app/drivers/sensor/battery/battery_common.h | 21 | ||||
| -rw-r--r-- | app/drivers/sensor/battery/battery_nrf_vddh.c | 116 | ||||
| -rw-r--r-- | app/drivers/sensor/battery/battery_voltage_divider.c (renamed from app/drivers/sensor/battery_voltage_divider/battery_voltage_divider.c) | 50 | ||||
| -rw-r--r-- | app/drivers/sensor/battery_voltage_divider/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | app/drivers/sensor/battery_voltage_divider/Kconfig | 8 | ||||
| -rw-r--r-- | app/drivers/zephyr/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml | 11 | 
11 files changed, 236 insertions, 56 deletions
diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index a4c2ba8..b549320 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2020 The ZMK Contributors +# Copyright (c) 2020-2021 The ZMK Contributors  # SPDX-License-Identifier: MIT -add_subdirectory_ifdef(CONFIG_ZMK_BATTERY_VOLTAGE_DIVIDER battery_voltage_divider) +add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery)  add_subdirectory_ifdef(CONFIG_EC11 ec11)
\ No newline at end of file diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index 7b6a0d0..a828f6c 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -1,5 +1,5 @@  # Copyright (c) 2020 The ZMK Contributors  # SPDX-License-Identifier: MIT -rsource "battery_voltage_divider/Kconfig" +rsource "battery/Kconfig"  rsource "ec11/Kconfig"
\ No newline at end of file diff --git a/app/drivers/sensor/battery/CMakeLists.txt b/app/drivers/sensor/battery/CMakeLists.txt new file mode 100644 index 0000000..1203e53 --- /dev/null +++ b/app/drivers/sensor/battery/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2020-2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_include_directories(.) + +zephyr_library() + +zephyr_library_sources(battery_common.c) +zephyr_library_sources_ifdef(CONFIG_ZMK_BATTERY_NRF_VDDH battery_nrf_vddh.c) +zephyr_library_sources_ifdef(CONFIG_ZMK_BATTERY_VOLTAGE_DIVIDER battery_voltage_divider.c)
\ No newline at end of file diff --git a/app/drivers/sensor/battery/Kconfig b/app/drivers/sensor/battery/Kconfig new file mode 100644 index 0000000..fd8cd26 --- /dev/null +++ b/app/drivers/sensor/battery/Kconfig @@ -0,0 +1,21 @@ +# Copyright (c) 2020-2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config ZMK_BATTERY +	bool "ZMK battery monitoring" +	help +		Enable battery monitoring + +config ZMK_BATTERY_NRF_VDDH +	bool "ZMK nRF VDDH battery monitoring" +	select ADC +	select ZMK_BATTERY +	help +		Enable ZMK nRF VDDH voltage driver for battery monitoring. + +config ZMK_BATTERY_VOLTAGE_DIVIDER +	bool "ZMK battery voltage divider" +	select ADC +	select ZMK_BATTERY +	help +		Enable ZMK battery voltage divider driver for battery monitoring. diff --git a/app/drivers/sensor/battery/battery_common.c b/app/drivers/sensor/battery/battery_common.c new file mode 100644 index 0000000..36e98af --- /dev/null +++ b/app/drivers/sensor/battery/battery_common.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include <errno.h> +#include <drivers/sensor.h> + +#include "battery_common.h" + +int battery_channel_get(const struct battery_value *value, enum sensor_channel chan, +                        struct sensor_value *val_out) { +    switch (chan) { +    case SENSOR_CHAN_GAUGE_VOLTAGE: +        val_out->val1 = value->millivolts / 1000; +        val_out->val2 = (value->millivolts % 1000) * 1000U; +        break; + +    case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE: +        val_out->val1 = value->state_of_charge; +        val_out->val2 = 0; +        break; + +    default: +        return -ENOTSUP; +    } + +    return 0; +} + +uint8_t lithium_ion_mv_to_pct(int16_t bat_mv) { +    // Simple linear approximation of a battery based off adafruit's discharge graph: +    // https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages + +    if (bat_mv >= 4200) { +        return 100; +    } else if (bat_mv <= 3450) { +        return 0; +    } + +    return bat_mv * 2 / 15 - 459; +}
\ No newline at end of file diff --git a/app/drivers/sensor/battery/battery_common.h b/app/drivers/sensor/battery/battery_common.h new file mode 100644 index 0000000..d81c39e --- /dev/null +++ b/app/drivers/sensor/battery/battery_common.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include <drivers/sensor.h> +#include <stdint.h> + +struct battery_value { +    uint16_t adc_raw; +    uint16_t millivolts; +    uint8_t state_of_charge; +}; + +int battery_channel_get(const struct battery_value *value, enum sensor_channel chan, +                        struct sensor_value *val_out); + +uint8_t lithium_ion_mv_to_pct(int16_t bat_mv); diff --git a/app/drivers/sensor/battery/battery_nrf_vddh.c b/app/drivers/sensor/battery/battery_nrf_vddh.c new file mode 100644 index 0000000..9089034 --- /dev/null +++ b/app/drivers/sensor/battery/battery_nrf_vddh.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + * + * This is a simplified version of battery_voltage_divider.c which always reads + * the VDDHDIV5 channel of the &adc node and multiplies it by 5. + */ + +#define DT_DRV_COMPAT zmk_battery_nrf_vddh + +#include <device.h> +#include <devicetree.h> +#include <drivers/adc.h> +#include <drivers/sensor.h> +#include <logging/log.h> + +#include "battery_common.h" + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#define VDDHDIV (5) + +static const struct device *adc = DEVICE_DT_GET(DT_NODELABEL(adc)); + +struct vddh_data { +    struct adc_channel_cfg acc; +    struct adc_sequence as; +    struct battery_value value; +}; + +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) { +        LOG_DBG("Selected channel is not supported: %d.", chan); +        return -ENOTSUP; +    } + +    struct vddh_data *drv_data = dev->data; +    struct adc_sequence *as = &drv_data->as; + +    int rc = adc_read(adc, as); +    as->calibrate = false; + +    if (rc != 0) { +        LOG_ERR("Failed to read ADC: %d", rc); +        return rc; +    } + +    int32_t val = drv_data->value.adc_raw; +    rc = adc_raw_to_millivolts(adc_ref_internal(adc), drv_data->acc.gain, as->resolution, &val); +    if (rc != 0) { +        LOG_ERR("Failed to convert raw ADC to mV: %d", rc); +        return rc; +    } + +    drv_data->value.millivolts = val * VDDHDIV; +    drv_data->value.state_of_charge = lithium_ion_mv_to_pct(drv_data->value.millivolts); + +    LOG_DBG("ADC raw %d ~ %d mV => %d%%", drv_data->value.adc_raw, drv_data->value.millivolts, +            drv_data->value.state_of_charge); + +    return rc; +} + +static int vddh_channel_get(const struct device *dev, enum sensor_channel chan, +                            struct sensor_value *val) { +    struct vddh_data const *drv_data = dev->data; +    return battery_channel_get(&drv_data->value, chan, val); +} + +static const struct sensor_driver_api vddh_api = { +    .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; + +    if (!device_is_ready(adc)) { +        LOG_ERR("ADC device is not ready %s", adc->name); +        return -ENODEV; +    } + +    drv_data->as = (struct adc_sequence){ +        .channels = BIT(0), +        .buffer = &drv_data->value.adc_raw, +        .buffer_size = sizeof(drv_data->value.adc_raw), +        .oversampling = 4, +        .calibrate = true, +    }; + +#ifdef CONFIG_ADC_NRFX_SAADC +    drv_data->acc = (struct adc_channel_cfg){ +        .gain = ADC_GAIN_1_5, +        .reference = ADC_REF_INTERNAL, +        .acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40), +        .input_positive = SAADC_CH_PSELN_PSELN_VDDHDIV5, +    }; + +    drv_data->as.resolution = 12; +#else +#error Unsupported ADC +#endif + +    const int rc = adc_channel_setup(adc, &drv_data->acc); +    LOG_DBG("VDDHDIV5 setup returned %d", rc); + +    return rc; +} + +static struct vddh_data vddh_data; + +DEVICE_DT_INST_DEFINE(0, &vddh_init, device_pm_control_nop, &vddh_data, NULL, POST_KERNEL, +                      CONFIG_SENSOR_INIT_PRIORITY, &vddh_api); diff --git a/app/drivers/sensor/battery_voltage_divider/battery_voltage_divider.c b/app/drivers/sensor/battery/battery_voltage_divider.c index 9efd7fb..8981fb3 100644 --- a/app/drivers/sensor/battery_voltage_divider/battery_voltage_divider.c +++ b/app/drivers/sensor/battery/battery_voltage_divider.c @@ -7,11 +7,14 @@  #define DT_DRV_COMPAT zmk_battery_voltage_divider  #include <device.h> +#include <devicetree.h>  #include <drivers/gpio.h>  #include <drivers/adc.h>  #include <drivers/sensor.h>  #include <logging/log.h> +#include "battery_common.h" +  LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);  struct io_channel_config { @@ -37,24 +40,9 @@ struct bvd_data {      const struct device *gpio;      struct adc_channel_cfg acc;      struct adc_sequence as; -    uint16_t adc_raw; -    uint16_t voltage; -    uint8_t state_of_charge; +    struct battery_value value;  }; -static uint8_t lithium_ion_mv_to_pct(int16_t bat_mv) { -    // Simple linear approximation of a battery based off adafruit's discharge graph: -    // https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages - -    if (bat_mv >= 4200) { -        return 100; -    } else if (bat_mv <= 3450) { -        return 0; -    } - -    return bat_mv * 2 / 15 - 459; -} -  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; @@ -87,18 +75,18 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan)      as->calibrate = false;      if (rc == 0) { -        int32_t val = drv_data->adc_raw; +        int32_t val = drv_data->value.adc_raw;          adc_raw_to_millivolts(adc_ref_internal(drv_data->adc), drv_data->acc.gain, as->resolution,                                &val);          uint16_t millivolts = val * (uint64_t)drv_cfg->full_ohm / drv_cfg->output_ohm; -        LOG_DBG("ADC raw %d ~ %d mV => %d mV", drv_data->adc_raw, val, millivolts); +        LOG_DBG("ADC raw %d ~ %d mV => %d mV", drv_data->value.adc_raw, val, millivolts);          uint8_t percent = lithium_ion_mv_to_pct(millivolts);          LOG_DBG("Percent: %d", percent); -        drv_data->voltage = millivolts; -        drv_data->state_of_charge = percent; +        drv_data->value.millivolts = millivolts; +        drv_data->value.state_of_charge = percent;      } else {          LOG_DBG("Failed to read ADC: %d", rc);      } @@ -119,23 +107,7 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan)  static int bvd_channel_get(const struct device *dev, enum sensor_channel chan,                             struct sensor_value *val) {      struct bvd_data *drv_data = dev->data; - -    switch (chan) { -    case SENSOR_CHAN_GAUGE_VOLTAGE: -        val->val1 = drv_data->voltage / 1000; -        val->val2 = (drv_data->voltage % 1000) * 1000U; -        break; - -    case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE: -        val->val1 = drv_data->state_of_charge; -        val->val2 = 0; -        break; - -    default: -        return -ENOTSUP; -    } - -    return 0; +    return battery_channel_get(&drv_data->value, chan, val);  }  static const struct sensor_driver_api bvd_api = { @@ -173,8 +145,8 @@ static int bvd_init(const struct device *dev) {      drv_data->as = (struct adc_sequence){          .channels = BIT(0), -        .buffer = &drv_data->adc_raw, -        .buffer_size = sizeof(drv_data->adc_raw), +        .buffer = &drv_data->value.adc_raw, +        .buffer_size = sizeof(drv_data->value.adc_raw),          .oversampling = 4,          .calibrate = true,      }; diff --git a/app/drivers/sensor/battery_voltage_divider/CMakeLists.txt b/app/drivers/sensor/battery_voltage_divider/CMakeLists.txt deleted file mode 100644 index 4b7f042..0000000 --- a/app/drivers/sensor/battery_voltage_divider/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) 2020 The ZMK Contributors -# SPDX-License-Identifier: MIT - -zephyr_library() - -zephyr_library_sources(battery_voltage_divider.c)
\ No newline at end of file diff --git a/app/drivers/sensor/battery_voltage_divider/Kconfig b/app/drivers/sensor/battery_voltage_divider/Kconfig deleted file mode 100644 index 18c4ea3..0000000 --- a/app/drivers/sensor/battery_voltage_divider/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2020 The ZMK Contributors -# SPDX-License-Identifier: MIT - -config ZMK_BATTERY_VOLTAGE_DIVIDER -	bool "ZMK battery voltage divider" -	select ADC -	help -		Enable ZMK battery voltage divider driver for battery monitoring.
\ No newline at end of file diff --git a/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml b/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml new file mode 100644 index 0000000..a890436 --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/sensor/zmk,battery-nrf-vddh.yaml @@ -0,0 +1,11 @@ +# Copyright (c) 2021 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Battery SoC monitoring using nRF VDDH + +compatible: "zmk,battery-nrf-vddh" + +properties: +  label: +    required: true +    type: string  | 
