summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builddefs/common_features.mk22
-rw-r--r--docs/_sidebar.json1
-rw-r--r--docs/drivers/battery.md51
-rw-r--r--drivers/battery/battery.c31
-rw-r--r--drivers/battery/battery.h34
-rw-r--r--drivers/battery/battery_adc.c55
-rw-r--r--drivers/battery/battery_driver.h29
-rw-r--r--keyboards/handwired/onekey/keymaps/battery/config.h6
-rw-r--r--keyboards/handwired/onekey/keymaps/battery/keymap.c28
-rw-r--r--keyboards/handwired/onekey/keymaps/battery/keymap.json7
-rw-r--r--keyboards/handwired/onekey/keymaps/battery/rules.mk1
-rw-r--r--quantum/keyboard.c10
12 files changed, 275 insertions, 0 deletions
diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
index c88ce36011..cbfbbcced5 100644
--- a/builddefs/common_features.mk
+++ b/builddefs/common_features.mk
@@ -934,6 +934,28 @@ ifeq ($(strip $(DIP_SWITCH_ENABLE)), yes)
934 endif 934 endif
935endif 935endif
936 936
937VALID_BATTERY_DRIVER_TYPES := adc custom vendor
938
939BATTERY_DRIVER ?= adc
940ifeq ($(strip $(BATTERY_DRIVER_REQUIRED)), yes)
941 ifeq ($(filter $(BATTERY_DRIVER),$(VALID_BATTERY_DRIVER_TYPES)),)
942 $(call CATASTROPHIC_ERROR,Invalid BATTERY_DRIVER,BATTERY_DRIVER="$(BATTERY_DRIVER)" is not a valid battery driver)
943 endif
944
945 OPT_DEFS += -DBATTERY_DRIVER
946 OPT_DEFS += -DBATTERY_$(strip $(shell echo $(BATTERY_DRIVER) | tr '[:lower:]' '[:upper:]'))
947
948 COMMON_VPATH += $(DRIVER_PATH)/battery
949
950 SRC += battery.c
951 SRC += battery_$(strip $(BATTERY_DRIVER)).c
952
953 # add extra deps
954 ifeq ($(strip $(BATTERY_DRIVER)), adc)
955 ANALOG_DRIVER_REQUIRED = yes
956 endif
957endif
958
937VALID_WS2812_DRIVER_TYPES := bitbang custom i2c pwm spi vendor 959VALID_WS2812_DRIVER_TYPES := bitbang custom i2c pwm spi vendor
938 960
939WS2812_DRIVER ?= bitbang 961WS2812_DRIVER ?= bitbang
diff --git a/docs/_sidebar.json b/docs/_sidebar.json
index ae4d8fe4a9..f504f6f900 100644
--- a/docs/_sidebar.json
+++ b/docs/_sidebar.json
@@ -227,6 +227,7 @@
227 { "text": "ADC Driver", "link": "/drivers/adc" }, 227 { "text": "ADC Driver", "link": "/drivers/adc" },
228 { "text": "APA102 Driver", "link": "/drivers/apa102" }, 228 { "text": "APA102 Driver", "link": "/drivers/apa102" },
229 { "text": "Audio Driver", "link": "/drivers/audio" }, 229 { "text": "Audio Driver", "link": "/drivers/audio" },
230 { "text": "Battery Driver", "link": "/drivers/battery" },
230 { "text": "EEPROM Driver", "link": "/drivers/eeprom" }, 231 { "text": "EEPROM Driver", "link": "/drivers/eeprom" },
231 { "text": "Flash Driver", "link": "/drivers/flash" }, 232 { "text": "Flash Driver", "link": "/drivers/flash" },
232 { "text": "I2C Driver", "link": "/drivers/i2c" }, 233 { "text": "I2C Driver", "link": "/drivers/i2c" },
diff --git a/docs/drivers/battery.md b/docs/drivers/battery.md
new file mode 100644
index 0000000000..b1f75c1156
--- /dev/null
+++ b/docs/drivers/battery.md
@@ -0,0 +1,51 @@
1# Battery Driver
2
3This driver provides support for sampling battery level.
4
5## Usage
6
7To use this driver, add the following to your `rules.mk`:
8
9```make
10BATTERY_DRIVER_REQUIRED = yes
11```
12
13## Basic Configuration {#basic-configuration}
14
15Add the following to your `config.h`:
16
17|Define |Default |Description |
18|--------------------------|--------|--------------------------------------------------|
19|`BATTERY_SAMPLE_INTERVAL` |`30000` |The time between battery samples in milliseconds. |
20
21## Driver Configuration {#driver-configuration}
22
23Driver 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.
24
25### ADC Driver {#adc-driver}
26
27This is the default battery driver. The default configuration assumes the battery is connected to a ADC capable pin through a voltage divider.
28
29```make
30BATTERY_DRIVER = adc
31```
32
33The following `#define`s apply only to the `adc` driver:
34
35|Define |Default |Description |
36|-----------------------------|--------------|--------------------------------------------------------------|
37|`BATTERY_PIN` |*Not defined* |The GPIO pin connected to the voltage divider. |
38|`BATTERY_REF_VOLTAGE_MV` |`3300` |The ADC reverence voltage, in millivolts. |
39|`BATTERY_VOLTAGE_DIVIDER_R1` |`100000` |The voltage divider resistance, in kOhm. Set to 0 to disable. |
40|`BATTERY_VOLTAGE_DIVIDER_R1` |`100000` |The voltage divider resistance, in kOhm. Set to 0 to disable. |
41|`BATTERY_ADC_RESOLUTION` |`10` |The ADC resolution configured for the ADC Driver. |
42
43## Functions
44
45### `uint8_t battery_get_percent(void)` {#api-battery-get-percent}
46
47Sample battery level.
48
49#### Return Value {#api-battery-get-percent-return}
50
51The battery percentage, in the range 0-100.
diff --git a/drivers/battery/battery.c b/drivers/battery/battery.c
new file mode 100644
index 0000000000..65497399e8
--- /dev/null
+++ b/drivers/battery/battery.c
@@ -0,0 +1,31 @@
1// Copyright 2025 QMK
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "battery_driver.h"
5#include "battery.h"
6#include "timer.h"
7
8#ifndef BATTERY_SAMPLE_INTERVAL
9# define BATTERY_SAMPLE_INTERVAL 30000
10#endif
11
12static uint8_t last_bat_level = 100;
13
14void battery_init(void) {
15 battery_driver_init();
16
17 last_bat_level = battery_driver_sample_percent();
18}
19
20void battery_task(void) {
21 static uint32_t bat_timer = 0;
22 if (timer_elapsed32(bat_timer) > BATTERY_SAMPLE_INTERVAL) {
23 last_bat_level = battery_driver_sample_percent();
24
25 bat_timer = timer_read32();
26 }
27}
28
29uint8_t battery_get_percent(void) {
30 return last_bat_level;
31}
diff --git a/drivers/battery/battery.h b/drivers/battery/battery.h
new file mode 100644
index 0000000000..831b1f07e5
--- /dev/null
+++ b/drivers/battery/battery.h
@@ -0,0 +1,34 @@
1// Copyright 2025 QMK
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <stdint.h>
7
8/**
9 * \file
10 *
11 * \defgroup battery Battery API
12 *
13 * \brief API to query battery status.
14 * \{
15 */
16
17/**
18 * \brief Initialize the battery driver.
19 */
20void battery_init(void);
21
22/**
23 * \brief Perform housekeeping tasks.
24 */
25void battery_task(void);
26
27/**
28 * \brief Sample battery level.
29 *
30 * \return The battery percentage, in the range 0-100.
31 */
32uint8_t battery_get_percent(void);
33
34/** \} */
diff --git a/drivers/battery/battery_adc.c b/drivers/battery/battery_adc.c
new file mode 100644
index 0000000000..cf0e69cb48
--- /dev/null
+++ b/drivers/battery/battery_adc.c
@@ -0,0 +1,55 @@
1// Copyright 2025 QMK
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "analog.h"
5#include "gpio.h"
6
7#ifndef BATTERY_PIN
8# error("BATTERY_PIN not configured!")
9#endif
10
11#ifndef BATTERY_REF_VOLTAGE_MV
12# define BATTERY_REF_VOLTAGE_MV 3300
13#endif
14
15#ifndef BATTERY_VOLTAGE_DIVIDER_R1
16# define BATTERY_VOLTAGE_DIVIDER_R1 100
17#endif
18
19#ifndef BATTERY_VOLTAGE_DIVIDER_R2
20# define BATTERY_VOLTAGE_DIVIDER_R2 100
21#endif
22
23// TODO: infer from adc config?
24#ifndef BATTERY_ADC_RESOLUTION
25# define BATTERY_ADC_RESOLUTION 10
26#endif
27
28void battery_driver_init(void) {
29 gpio_set_pin_input(BATTERY_PIN);
30}
31
32uint16_t battery_driver_get_mv(void) {
33 uint32_t raw = analogReadPin(BATTERY_PIN);
34
35 uint32_t bat_mv = raw * BATTERY_REF_VOLTAGE_MV / (1 << BATTERY_ADC_RESOLUTION);
36
37#if BATTERY_VOLTAGE_DIVIDER_R1 > 0 && BATTERY_VOLTAGE_DIVIDER_R2 > 0
38 bat_mv = bat_mv * (BATTERY_VOLTAGE_DIVIDER_R1 + BATTERY_VOLTAGE_DIVIDER_R2) / BATTERY_VOLTAGE_DIVIDER_R2;
39#endif
40
41 return bat_mv;
42}
43
44uint8_t battery_driver_sample_percent(void) {
45 uint16_t bat_mv = battery_driver_get_mv();
46
47 // https://github.com/zmkfirmware/zmk/blob/3f7c9d7cc4f46617faad288421025ea2a6b0bd28/app/module/drivers/sensor/battery/battery_common.c#L33
48 if (bat_mv >= 4200) {
49 return 100;
50 } else if (bat_mv <= 3450) {
51 return 0;
52 }
53
54 return bat_mv * 2 / 15 - 459;
55}
diff --git a/drivers/battery/battery_driver.h b/drivers/battery/battery_driver.h
new file mode 100644
index 0000000000..c2ee75e966
--- /dev/null
+++ b/drivers/battery/battery_driver.h
@@ -0,0 +1,29 @@
1// Copyright 2025 QMK
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <stdint.h>
7
8/**
9 * \file
10 *
11 * \defgroup battery Battery Driver API
12 *
13 * \brief API to query battery status.
14 * \{
15 */
16
17/**
18 * \brief Initialize the battery driver. This function must be called only once, before any of the below functions can be called.
19 */
20void battery_driver_init(void);
21
22/**
23 * \brief Sample battery level.
24 *
25 * \return The battery percentage, in the range 0-100.
26 */
27uint8_t battery_driver_sample_percent(void);
28
29/** \} */
diff --git a/keyboards/handwired/onekey/keymaps/battery/config.h b/keyboards/handwired/onekey/keymaps/battery/config.h
new file mode 100644
index 0000000000..8a1c05d436
--- /dev/null
+++ b/keyboards/handwired/onekey/keymaps/battery/config.h
@@ -0,0 +1,6 @@
1// Copyright 2024 QMK
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#define BATTERY_PIN ADC_PIN
diff --git a/keyboards/handwired/onekey/keymaps/battery/keymap.c b/keyboards/handwired/onekey/keymaps/battery/keymap.c
new file mode 100644
index 0000000000..74191e83fc
--- /dev/null
+++ b/keyboards/handwired/onekey/keymaps/battery/keymap.c
@@ -0,0 +1,28 @@
1// Copyright 2024 QMK
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include QMK_KEYBOARD_H
5#include "battery.h"
6
7const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
8 LAYOUT_ortho_1x1(KC_A)
9};
10
11void keyboard_post_init_user(void) {
12 // Customise these values to desired behaviour
13 debug_enable=true;
14// debug_matrix=false;
15// debug_keyboard=true;
16// debug_mouse=false;
17
18 battery_init();
19}
20
21void housekeeping_task_user(void) {
22 static uint32_t last = 0;
23 if (timer_elapsed32(last) > 2000) {
24 uprintf("Bat: %d!\n", battery_get_percent());
25
26 last = timer_read32();
27 }
28}
diff --git a/keyboards/handwired/onekey/keymaps/battery/keymap.json b/keyboards/handwired/onekey/keymaps/battery/keymap.json
new file mode 100644
index 0000000000..c641dfe773
--- /dev/null
+++ b/keyboards/handwired/onekey/keymaps/battery/keymap.json
@@ -0,0 +1,7 @@
1{
2 "config": {
3 "features": {
4 "console": true
5 }
6 }
7}
diff --git a/keyboards/handwired/onekey/keymaps/battery/rules.mk b/keyboards/handwired/onekey/keymaps/battery/rules.mk
new file mode 100644
index 0000000000..06908179ae
--- /dev/null
+++ b/keyboards/handwired/onekey/keymaps/battery/rules.mk
@@ -0,0 +1 @@
BATTERY_DRIVER_REQUIRED = yes
diff --git a/quantum/keyboard.c b/quantum/keyboard.c
index ad740de4b3..fccdaf2990 100644
--- a/quantum/keyboard.c
+++ b/quantum/keyboard.c
@@ -122,6 +122,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
122#ifdef SPLIT_KEYBOARD 122#ifdef SPLIT_KEYBOARD
123# include "split_util.h" 123# include "split_util.h"
124#endif 124#endif
125#ifdef BATTERY_DRIVER
126# include "battery.h"
127#endif
125#ifdef BLUETOOTH_ENABLE 128#ifdef BLUETOOTH_ENABLE
126# include "bluetooth.h" 129# include "bluetooth.h"
127#endif 130#endif
@@ -522,6 +525,9 @@ void keyboard_init(void) {
522 // init after split init 525 // init after split init
523 pointing_device_init(); 526 pointing_device_init();
524#endif 527#endif
528#ifdef BATTERY_DRIVER
529 battery_init();
530#endif
525#ifdef BLUETOOTH_ENABLE 531#ifdef BLUETOOTH_ENABLE
526 bluetooth_init(); 532 bluetooth_init();
527#endif 533#endif
@@ -782,6 +788,10 @@ void keyboard_task(void) {
782 joystick_task(); 788 joystick_task();
783#endif 789#endif
784 790
791#ifdef BATTERY_DRIVER
792 battery_task();
793#endif
794
785#ifdef BLUETOOTH_ENABLE 795#ifdef BLUETOOTH_ENABLE
786 bluetooth_task(); 796 bluetooth_task();
787#endif 797#endif