Ver código fonte

Implement battery level interface (#24666)

Co-authored-by: Nick Brassel <nick@tzarc.org>
Joel Challis 1 ano atrás
pai
commit
6ee806f376

+ 22 - 0
builddefs/common_features.mk

@@ -934,6 +934,28 @@ ifeq ($(strip $(DIP_SWITCH_ENABLE)), yes)
     endif
 endif
 
+VALID_BATTERY_DRIVER_TYPES := adc custom vendor
+
+BATTERY_DRIVER ?= adc
+ifeq ($(strip $(BATTERY_DRIVER_REQUIRED)), yes)
+    ifeq ($(filter $(BATTERY_DRIVER),$(VALID_BATTERY_DRIVER_TYPES)),)
+        $(call CATASTROPHIC_ERROR,Invalid BATTERY_DRIVER,BATTERY_DRIVER="$(BATTERY_DRIVER)" is not a valid battery driver)
+    endif
+
+    OPT_DEFS += -DBATTERY_DRIVER
+    OPT_DEFS += -DBATTERY_$(strip $(shell echo $(BATTERY_DRIVER) | tr '[:lower:]' '[:upper:]'))
+
+    COMMON_VPATH += $(DRIVER_PATH)/battery
+
+    SRC += battery.c
+    SRC += battery_$(strip $(BATTERY_DRIVER)).c
+
+    # add extra deps
+    ifeq ($(strip $(BATTERY_DRIVER)), adc)
+        ANALOG_DRIVER_REQUIRED = yes
+    endif
+endif
+
 VALID_WS2812_DRIVER_TYPES := bitbang custom i2c pwm spi vendor
 
 WS2812_DRIVER ?= bitbang

+ 1 - 0
docs/_sidebar.json

@@ -227,6 +227,7 @@
                             { "text": "ADC Driver", "link": "/drivers/adc" },
                             { "text": "APA102 Driver", "link": "/drivers/apa102" },
                             { "text": "Audio Driver", "link": "/drivers/audio" },
+                            { "text": "Battery Driver", "link": "/drivers/battery" },
                             { "text": "EEPROM Driver", "link": "/drivers/eeprom" },
                             { "text": "Flash Driver", "link": "/drivers/flash" },
                             { "text": "I2C Driver", "link": "/drivers/i2c" },

+ 51 - 0
docs/drivers/battery.md

@@ -0,0 +1,51 @@
+# Battery Driver
+
+This driver provides support for sampling battery level.
+
+## Usage
+
+To use this driver, add the following to your `rules.mk`:
+
+```make
+BATTERY_DRIVER_REQUIRED = yes
+```
+
+## Basic Configuration {#basic-configuration}
+
+Add the following to your `config.h`:
+
+|Define                    |Default |Description                                       |
+|--------------------------|--------|--------------------------------------------------|
+|`BATTERY_SAMPLE_INTERVAL` |`30000` |The time between battery samples in milliseconds. |
+
+## Driver Configuration {#driver-configuration}
+
+Driver selection can be configured in `rules.mk` as `BATTERY_DRIVER`. Valid values are `adc` (default), `vendor`, or `custom`. See below for information on individual drivers.
+
+### ADC Driver {#adc-driver}
+
+This is the default battery driver. The default configuration assumes the battery is connected to a ADC capable pin through a voltage divider.
+
+```make
+BATTERY_DRIVER = adc
+```
+
+The following `#define`s apply only to the `adc` driver:
+
+|Define                       |Default       |Description                                                   |
+|-----------------------------|--------------|--------------------------------------------------------------|
+|`BATTERY_PIN`                |*Not defined* |The GPIO pin connected to the voltage divider.                |
+|`BATTERY_REF_VOLTAGE_MV`     |`3300`        |The ADC reverence voltage, in millivolts.                     |
+|`BATTERY_VOLTAGE_DIVIDER_R1` |`100000`      |The voltage divider resistance, in kOhm. Set to 0 to disable. |
+|`BATTERY_VOLTAGE_DIVIDER_R1` |`100000`      |The voltage divider resistance, in kOhm. Set to 0 to disable. |
+|`BATTERY_ADC_RESOLUTION`     |`10`          |The ADC resolution configured for the ADC Driver.             |
+
+## Functions
+
+### `uint8_t battery_get_percent(void)` {#api-battery-get-percent}
+
+Sample battery level.
+
+#### Return Value {#api-battery-get-percent-return}
+
+The battery percentage, in the range 0-100.

+ 31 - 0
drivers/battery/battery.c

@@ -0,0 +1,31 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "battery_driver.h"
+#include "battery.h"
+#include "timer.h"
+
+#ifndef BATTERY_SAMPLE_INTERVAL
+#    define BATTERY_SAMPLE_INTERVAL 30000
+#endif
+
+static uint8_t last_bat_level = 100;
+
+void battery_init(void) {
+    battery_driver_init();
+
+    last_bat_level = battery_driver_sample_percent();
+}
+
+void battery_task(void) {
+    static uint32_t bat_timer = 0;
+    if (timer_elapsed32(bat_timer) > BATTERY_SAMPLE_INTERVAL) {
+        last_bat_level = battery_driver_sample_percent();
+
+        bat_timer = timer_read32();
+    }
+}
+
+uint8_t battery_get_percent(void) {
+    return last_bat_level;
+}

+ 34 - 0
drivers/battery/battery.h

@@ -0,0 +1,34 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <stdint.h>
+
+/**
+ * \file
+ *
+ * \defgroup battery Battery API
+ *
+ * \brief API to query battery status.
+ * \{
+ */
+
+/**
+ * \brief Initialize the battery driver.
+ */
+void battery_init(void);
+
+/**
+ * \brief Perform housekeeping tasks.
+ */
+void battery_task(void);
+
+/**
+ * \brief Sample battery level.
+ *
+ * \return The battery percentage, in the range 0-100.
+ */
+uint8_t battery_get_percent(void);
+
+/** \} */

+ 55 - 0
drivers/battery/battery_adc.c

@@ -0,0 +1,55 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "analog.h"
+#include "gpio.h"
+
+#ifndef BATTERY_PIN
+#    error("BATTERY_PIN not configured!")
+#endif
+
+#ifndef BATTERY_REF_VOLTAGE_MV
+#    define BATTERY_REF_VOLTAGE_MV 3300
+#endif
+
+#ifndef BATTERY_VOLTAGE_DIVIDER_R1
+#    define BATTERY_VOLTAGE_DIVIDER_R1 100
+#endif
+
+#ifndef BATTERY_VOLTAGE_DIVIDER_R2
+#    define BATTERY_VOLTAGE_DIVIDER_R2 100
+#endif
+
+// TODO: infer from adc config?
+#ifndef BATTERY_ADC_RESOLUTION
+#    define BATTERY_ADC_RESOLUTION 10
+#endif
+
+void battery_driver_init(void) {
+    gpio_set_pin_input(BATTERY_PIN);
+}
+
+uint16_t battery_driver_get_mv(void) {
+    uint32_t raw = analogReadPin(BATTERY_PIN);
+
+    uint32_t bat_mv = raw * BATTERY_REF_VOLTAGE_MV / (1 << BATTERY_ADC_RESOLUTION);
+
+#if BATTERY_VOLTAGE_DIVIDER_R1 > 0 && BATTERY_VOLTAGE_DIVIDER_R2 > 0
+    bat_mv = bat_mv * (BATTERY_VOLTAGE_DIVIDER_R1 + BATTERY_VOLTAGE_DIVIDER_R2) / BATTERY_VOLTAGE_DIVIDER_R2;
+#endif
+
+    return bat_mv;
+}
+
+uint8_t battery_driver_sample_percent(void) {
+    uint16_t bat_mv = battery_driver_get_mv();
+
+    // https://github.com/zmkfirmware/zmk/blob/3f7c9d7cc4f46617faad288421025ea2a6b0bd28/app/module/drivers/sensor/battery/battery_common.c#L33
+    if (bat_mv >= 4200) {
+        return 100;
+    } else if (bat_mv <= 3450) {
+        return 0;
+    }
+
+    return bat_mv * 2 / 15 - 459;
+}

+ 29 - 0
drivers/battery/battery_driver.h

@@ -0,0 +1,29 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <stdint.h>
+
+/**
+ * \file
+ *
+ * \defgroup battery Battery Driver API
+ *
+ * \brief API to query battery status.
+ * \{
+ */
+
+/**
+ * \brief Initialize the battery driver. This function must be called only once, before any of the below functions can be called.
+ */
+void battery_driver_init(void);
+
+/**
+ * \brief Sample battery level.
+ *
+ * \return The battery percentage, in the range 0-100.
+ */
+uint8_t battery_driver_sample_percent(void);
+
+/** \} */

+ 6 - 0
keyboards/handwired/onekey/keymaps/battery/config.h

@@ -0,0 +1,6 @@
+// Copyright 2024 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#define BATTERY_PIN ADC_PIN

+ 28 - 0
keyboards/handwired/onekey/keymaps/battery/keymap.c

@@ -0,0 +1,28 @@
+// Copyright 2024 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include QMK_KEYBOARD_H
+#include "battery.h"
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+    LAYOUT_ortho_1x1(KC_A)
+};
+
+void keyboard_post_init_user(void) {
+  // Customise these values to desired behaviour
+  debug_enable=true;
+//   debug_matrix=false;
+//   debug_keyboard=true;
+//   debug_mouse=false;
+
+  battery_init();
+}
+
+void housekeeping_task_user(void) {
+    static uint32_t last = 0;
+    if (timer_elapsed32(last) > 2000) {
+        uprintf("Bat: %d!\n", battery_get_percent());
+
+        last = timer_read32();
+    }
+}

+ 7 - 0
keyboards/handwired/onekey/keymaps/battery/keymap.json

@@ -0,0 +1,7 @@
+{
+    "config": {
+        "features": {
+            "console": true
+        }
+    }
+}

+ 1 - 0
keyboards/handwired/onekey/keymaps/battery/rules.mk

@@ -0,0 +1 @@
+BATTERY_DRIVER_REQUIRED = yes

+ 10 - 0
quantum/keyboard.c

@@ -122,6 +122,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #ifdef SPLIT_KEYBOARD
 #    include "split_util.h"
 #endif
+#ifdef BATTERY_DRIVER
+#    include "battery.h"
+#endif
 #ifdef BLUETOOTH_ENABLE
 #    include "bluetooth.h"
 #endif
@@ -522,6 +525,9 @@ void keyboard_init(void) {
     // init after split init
     pointing_device_init();
 #endif
+#ifdef BATTERY_DRIVER
+    battery_init();
+#endif
 #ifdef BLUETOOTH_ENABLE
     bluetooth_init();
 #endif
@@ -782,6 +788,10 @@ void keyboard_task(void) {
     joystick_task();
 #endif
 
+#ifdef BATTERY_DRIVER
+    battery_task();
+#endif
+
 #ifdef BLUETOOTH_ENABLE
     bluetooth_task();
 #endif