summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick <nick.win999@gmail.com>2020-09-19 19:14:59 -0500
committerNick <nick.win999@gmail.com>2020-09-19 19:14:59 -0500
commit844f2c76749cbc5ff611d8e69819af82b698089a (patch)
tree9e235ea8156772c61c0c7438051011e25c1fa3d4
parentceda57ddfd7b49dd61e8795c37cd678b69a4a76f (diff)
Voltage divider driver initial implementation
-rw-r--r--app/boards/arm/nice_nano/nice_nano.dts10
-rw-r--r--app/boards/arm/nice_nano/nice_nano_defconfig4
-rw-r--r--app/drivers/zephyr/CMakeLists.txt1
-rw-r--r--app/drivers/zephyr/battery_voltage_divider.c115
-rw-r--r--app/drivers/zephyr/dts/bindings/zmk,battery-voltage-divider.yaml9
5 files changed, 139 insertions, 0 deletions
diff --git a/app/boards/arm/nice_nano/nice_nano.dts b/app/boards/arm/nice_nano/nice_nano.dts
index 2e9556b..18312ec 100644
--- a/app/boards/arm/nice_nano/nice_nano.dts
+++ b/app/boards/arm/nice_nano/nice_nano.dts
@@ -29,6 +29,16 @@
};
};
+ vbatt {
+ compatible = "zmk,battery-voltage-divider";
+ io-channels = <&adc 2>;
+ output-ohms = <2000000>;
+ full-ohms = <(2000000 + 806000)>;
+ };
+};
+
+&adc {
+ status = "okay";
};
&gpio0 {
diff --git a/app/boards/arm/nice_nano/nice_nano_defconfig b/app/boards/arm/nice_nano/nice_nano_defconfig
index 393d61f..b727fe0 100644
--- a/app/boards/arm/nice_nano/nice_nano_defconfig
+++ b/app/boards/arm/nice_nano/nice_nano_defconfig
@@ -10,6 +10,10 @@ CONFIG_ARM_MPU=y
# enable GPIO
CONFIG_GPIO=y
+CONFIG_ADC=y
+
+CONFIG_NEWLIB_LIBC=y
+
CONFIG_USE_DT_CODE_PARTITION=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
diff --git a/app/drivers/zephyr/CMakeLists.txt b/app/drivers/zephyr/CMakeLists.txt
index 8778ded..0b1d18f 100644
--- a/app/drivers/zephyr/CMakeLists.txt
+++ b/app/drivers/zephyr/CMakeLists.txt
@@ -5,6 +5,7 @@ if(CONFIG_ZMK_KSCAN_GPIO_DRIVER)
zephyr_library_sources(
kscan_gpio_matrix.c
kscan_gpio_direct.c
+ battery_voltage_divider.c
)
zephyr_library_sources_ifdef(CONFIG_EC11 ec11.c)
diff --git a/app/drivers/zephyr/battery_voltage_divider.c b/app/drivers/zephyr/battery_voltage_divider.c
new file mode 100644
index 0000000..35fc799
--- /dev/null
+++ b/app/drivers/zephyr/battery_voltage_divider.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define DT_DRV_COMPAT zmk_battery_voltage_divider
+
+#include <device.h>
+#include <drivers/kscan.h>
+#include <drivers/gpio.h>
+#include <drivers/adc.h>
+#include <drivers/sensor.h>
+#include <logging/log.h>
+#include <math.h>
+
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
+
+#define VBATT DT_PATH(vbatt)
+
+struct battery_config {
+ struct device *adc;
+ struct adc_channel_cfg acc;
+ struct adc_sequence as;
+ int16_t adc_raw;
+};
+
+static struct battery_config battery_config;
+
+static int lithium_ion_mv_to_pct(int16_t bat_mv) {
+ // Magic function that maps mV to this discharge graph from adafruit: https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages
+ return round(106.818 + (-0.032685 - 106.818) / pow(1 + pow(bat_mv/3679.35, 58.979), 0.347386));
+}
+
+static void battery_read(struct k_work *workd) {
+ struct battery_config *cfg = &battery_config;
+ struct adc_sequence *as = &cfg->as;
+
+ int rc = adc_read(cfg->adc, as);
+ as->calibrate = false;
+ if (rc == 0) {
+ int32_t val = cfg->adc_raw;
+
+ adc_raw_to_millivolts(adc_ref_internal(cfg->adc), cfg->acc.gain, as->resolution, &val);
+
+ rc = val * (uint64_t)DT_PROP(VBATT, full_ohms) / DT_PROP(VBATT, output_ohms);
+ LOG_DBG("ADC raw %d ~ %d mV => %d mV\n", cfg->adc_raw, val, rc);
+ int percent = lithium_ion_mv_to_pct(rc);
+ LOG_DBG("Percent: %d", percent);
+ } else {
+ LOG_DBG("Failed to read ADC: %d", rc);
+ }
+}
+
+K_WORK_DEFINE(battery_work, battery_read);
+
+static void battery_handler(struct k_timer *timer)
+{
+ k_work_submit(&battery_work);
+}
+
+K_TIMER_DEFINE(battery_tick, battery_handler, NULL);
+
+static int battery_setup(struct device *_arg) {
+ struct battery_config *cfg = &battery_config;
+ struct adc_sequence *as = &cfg->as;
+ struct adc_channel_cfg *acc = &cfg->acc;
+
+ cfg->adc = device_get_binding(DT_IO_CHANNELS_LABEL(VBATT));
+
+ if (cfg->adc == NULL) {
+ LOG_ERR("ADC %s failed to retrieve", DT_IO_CHANNELS_LABEL(VBATT));
+ return -ENOENT;
+ }
+
+ *as = (struct adc_sequence){
+ .channels = BIT(0),
+ .buffer = &cfg->adc_raw,
+ .buffer_size = sizeof(cfg->adc_raw),
+ .oversampling = 4,
+ .calibrate = true,
+ };
+
+#ifdef CONFIG_ADC_NRFX_SAADC
+ *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_PSELP_PSELP_AnalogInput0 + DT_IO_CHANNELS_INPUT(VBATT),
+ };
+
+ as->resolution = 12;
+#else
+#error Unsupported ADC
+#endif
+
+ int adc_rc = adc_channel_setup(cfg->adc, acc);
+ LOG_DBG("AIN%u setup returned %d", DT_IO_CHANNELS_INPUT(VBATT), adc_rc);
+
+ if (adc_rc != 0) {
+ return adc_rc;
+ }
+
+ k_timer_start(&battery_tick, K_NO_WAIT, K_SECONDS(5));
+
+ return 0;
+}
+
+SYS_INIT(battery_setup,
+ APPLICATION,
+ CONFIG_APPLICATION_INIT_PRIORITY);
+
+#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ \ No newline at end of file
diff --git a/app/drivers/zephyr/dts/bindings/zmk,battery-voltage-divider.yaml b/app/drivers/zephyr/dts/bindings/zmk,battery-voltage-divider.yaml
new file mode 100644
index 0000000..f6e0642
--- /dev/null
+++ b/app/drivers/zephyr/dts/bindings/zmk,battery-voltage-divider.yaml
@@ -0,0 +1,9 @@
+# Copyright (c) 2020 The ZMK Contributors
+# SPDX-License-Identifier: MIT
+
+description: Battery SoC monitoring using voltage divider
+
+compatible: "zmk,battery-voltage-divider"
+
+include: voltage-divider.yaml
+ \ No newline at end of file