Skip to content

Commit

Permalink
Initial potentiometer support
Browse files Browse the repository at this point in the history
  • Loading branch information
zvecr committed Nov 13, 2023
1 parent e884e42 commit d8e97ab
Show file tree
Hide file tree
Showing 17 changed files with 194 additions and 37 deletions.
4 changes: 4 additions & 0 deletions builddefs/common_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,10 @@ ifeq ($(strip $(ENCODER_ENABLE)), yes)
endif
endif

ifeq ($(strip $(POTENTIOMETER_ENABLE)), yes)
ANALOG_DRIVER_REQUIRED = yes
endif

VALID_WS2812_DRIVER_TYPES := bitbang custom i2c pwm spi vendor

WS2812_DRIVER ?= bitbang
Expand Down
1 change: 1 addition & 0 deletions builddefs/generic_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ GENERIC_FEATURES = \
MOUSEKEY \
MUSIC \
OS_DETECTION \
POTENTIOMETER \
PROGRAMMABLE_BUTTON \
REPEAT_KEY \
SECURE \
Expand Down
3 changes: 3 additions & 0 deletions data/mappings/info_config.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
"ONESHOT_TIMEOUT": {"info_key": "oneshot.timeout", "value_type": "int"},
"ONESHOT_TAP_TOGGLE": {"info_key": "oneshot.tap_toggle", "value_type": "int"},

// Potentiometer
"POTENTIOMETER_PINS": {"info_key": "potentiometer.pins", "value_type": "array"},

// PS/2
"PS2_CLOCK_PIN": {"info_key": "ps2.clock_pin"},
"PS2_DATA_PIN": {"info_key": "ps2.data_pin"},
Expand Down
1 change: 1 addition & 0 deletions data/mappings/info_rules.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"NO_USB_STARTUP_CHECK": {"info_key": "usb.no_startup_check", "value_type": "bool"},
"PIN_COMPATIBLE": {"info_key": "pin_compatible"},
"PLATFORM_KEY": {"info_key": "platform_key", "to_json": false},
"POTENTIOMETER_ENABLE": {"info_key": "potentiometer.enabled", "value_type": "bool"},
"PS2_DRIVER": {"info_key": "ps2.driver"},
"PS2_ENABLE": {"info_key": "ps2.enabled", "value_type": "bool"},
"PS2_MOUSE_ENABLE": {"info_key": "ps2.mouse_enabled", "value_type": "bool"},
Expand Down
7 changes: 7 additions & 0 deletions data/schemas/keyboard.jsonschema
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,13 @@
"timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
}
},
"potentiometer": {
"type": "object",
"properties": {
"enabled": {"type": "boolean"},
"pins": {"$ref": "qmk.definitions.v1#/mcu_pin_array"}
}
},
"led_matrix": {
"type": "object",
"properties": {
Expand Down
1 change: 1 addition & 0 deletions docs/_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
* [LED Indicators](feature_led_indicators.md)
* [MIDI](feature_midi.md)
* [Pointing Device](feature_pointing_device.md)
* [Potentiometer](feature_potentiometers.md)
* [PS/2 Mouse](feature_ps2_mouse.md)
* [Split Keyboard](feature_split_keyboard.md)
* [Stenography](feature_stenography.md)
Expand Down
35 changes: 35 additions & 0 deletions docs/feature_potentiometers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Potentiometers

Add this to your `rules.mk`:

```make
POTENTIOMETER_ENABLE = yes
```

and this to your `config.h`:

```c
#define POTENTIOMETER_PINS { B0 }
```
## Callbacks
The callback functions can be inserted into your `<keyboard>.c`:
```c
bool potentiometer_update_kb(uint8_t index, uint16_t value) {
if (!potentiometer_update_user(index, value)) {
midi_send_cc(&midi_device, 2, 0x3E, 0x7F + (value >> 3));
}
return true;
}
```

or `keymap.c`:

```c
bool potentiometer_update_user(uint8_t index, uint16_t value) {
midi_send_cc(&midi_device, 2, 0x3E, 0x7F + (value >> 3));
return false;
}
```
11 changes: 11 additions & 0 deletions docs/reference_info_json.md
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,17 @@ Configures [One Shot keys](one_shot_keys.md).
* `timeout`
* The amount of time before the key is released in milliseconds.

## Potentiometer :id=potentiometer

Configures the [Potentiometer](feature_Potentiometers.md) feature.

* `potentiometer`
* `enabled`
* Enable the Potentiometer feature.
* Default: `false`
* `pins` (Required)
* The GPIO pin(s) connected to the Potentiometer(s).

## PS/2 :id=ps2

Configures the [PS/2](feature_ps2_mouse.md) feature.
Expand Down
2 changes: 1 addition & 1 deletion keyboards/gmmk/numpad/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

#pragma once

#define SLIDER_PIN B0
#define POTENTIOMETER_PINS { B0 }
#define MIDI_ADVANCED

#define LOCKING_SUPPORT_ENABLE
Expand Down
17 changes: 0 additions & 17 deletions keyboards/gmmk/numpad/keymaps/default/keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include QMK_KEYBOARD_H
#include "analog.h"
#include "qmk_midi.h"

// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
Expand All @@ -43,18 +41,3 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
RGB_TOG, QK_BOOT
)
};

// Potentiometer Slider, MIDI Control

uint8_t divisor = 0;

void slider(void) {
if (divisor++) { /* only run the slider function 1/256 times it's called */
return;
}
midi_send_cc(&midi_device, 2, 0x3E, 0x7F + (analogReadPin(SLIDER_PIN) >> 3));
}

void housekeeping_task_user(void) {
slider();
}
17 changes: 0 additions & 17 deletions keyboards/gmmk/numpad/keymaps/via/keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include QMK_KEYBOARD_H
#include "analog.h"
#include "qmk_midi.h"

// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
Expand Down Expand Up @@ -62,18 +60,3 @@ const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = {
[2] = { ENCODER_CCW_CW(KC_VOLD, KC_VOLU) },
[3] = { ENCODER_CCW_CW(KC_VOLD, KC_VOLU) }
};

// Potentiometer Slider, MIDI Control

uint8_t divisor = 0;

void slider(void) {
if (divisor++) { /* only run the slider function 1/256 times it's called */
return;
}
midi_send_cc(&midi_device, 2, 0x3E, 0x7F + (analogReadPin(SLIDER_PIN) >> 3));
}

void housekeeping_task_user(void) {
slider();
}
8 changes: 8 additions & 0 deletions keyboards/gmmk/numpad/numpad.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

#include "quantum.h"
#include "qmk_midi.h"

#ifdef RGB_MATRIX_ENABLE

Expand Down Expand Up @@ -117,3 +118,10 @@ void keyboard_pre_init_user(void) {
# endif

#endif

bool potentiometer_update_kb(uint8_t index, uint16_t value) {
if (!potentiometer_update_user(index, value)) {
midi_send_cc(&midi_device, 2, 0x3E, 0x7F + value);
}
return true;
}
3 changes: 1 addition & 2 deletions keyboards/gmmk/numpad/rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ AUDIO_ENABLE = no # Audio output
ENCODER_ENABLE = yes
KEYBOARD_SHARED_EP = yes
MIDI_ENABLE = yes
POTENTIOMETER_ENABLE = yes

RGB_MATRIX_ENABLE = yes

LTO_ENABLE = yes

ANALOG_DRIVER_REQUIRED = yes

SRC += matrix.c
9 changes: 9 additions & 0 deletions quantum/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef ENCODER_ENABLE
# include "encoder.h"
#endif
#ifdef POTENTIOMETER_ENABLE
# include "potentiometer.h"
#endif
#ifdef HAPTIC_ENABLE
# include "haptic.h"
#endif
Expand Down Expand Up @@ -669,6 +672,12 @@ void keyboard_task(void) {
}
#endif

#ifdef POTENTIOMETER_ENABLE
if (potentiometer_task()) {
activity_has_occurred = true;
}
#endif

#ifdef POINTING_DEVICE_ENABLE
if (pointing_device_task()) {
last_pointing_device_activity_trigger();
Expand Down
90 changes: 90 additions & 0 deletions quantum/potentiometer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#include "potentiometer.h"
#include "gpio.h"
#include "util.h"
#include "timer.h"
#include "analog.h"

#ifndef POTENTIOMETER_THROTTLE_MS
# define POTENTIOMETER_THROTTLE_MS 1
#endif

#ifndef POTENTIOMETER_OUTPUT_MIN_VALUE
# define POTENTIOMETER_OUTPUT_MIN_VALUE 0
#endif
#ifndef POTENTIOMETER_OUTPUT_MAX_VALUE
# define POTENTIOMETER_OUTPUT_MAX_VALUE 127
#endif
#ifndef POTENTIOMETER_ADC_MIN_VALUE
# define POTENTIOMETER_ADC_MIN_VALUE 0
#endif
#ifndef POTENTIOMETER_ADC_MAX_VALUE
# define POTENTIOMETER_ADC_MAX_VALUE (1 << 10)
#endif

static pin_t potentiometer_pads[] = POTENTIOMETER_PINS;
#define NUM_POTENTIOMETERS (ARRAY_SIZE(potentiometer_pads))

__attribute__((weak)) bool potentiometer_update_user(uint8_t index, uint16_t value) {
return true;
}

__attribute__((weak)) bool potentiometer_update_kb(uint8_t index, uint16_t value) {
return potentiometer_update_user(index, value);
}

__attribute__((weak)) bool potentiometer_throttle_task(void) {
#if (POTENTIOMETER_THROTTLE_MS > 0)
static uint32_t last_exec = 0;
if (timer_elapsed32(last_exec) < POTENTIOMETER_THROTTLE_MS) {
return false;
}
last_exec = timer_read32();
#endif
return true;
}

__attribute__((weak)) uint16_t potentiometer_map(uint8_t index, uint16_t value) {
(void)index;

uint32_t a = POTENTIOMETER_OUTPUT_MIN_VALUE;
uint32_t b = POTENTIOMETER_OUTPUT_MAX_VALUE;
uint32_t min = POTENTIOMETER_ADC_MIN_VALUE;
uint32_t max = POTENTIOMETER_ADC_MAX_VALUE;

// Scale value to min/max using the adc range
return ((b - a) * (value - min) / (max - min)) + a;
}

__attribute__((weak)) bool potentiometer_filter(uint8_t index, uint16_t value) {
// ADC currently limited to max of 12 bits - init to max 16 ensures
// we can correctly capture even a raw first sample at max adc bounds
static bool potentiometer_state[NUM_POTENTIOMETERS] = {UINT16_MAX};

if (value == potentiometer_state[index]) {
return false;
}

potentiometer_state[index] = value;
return true;
}

bool potentiometer_task(void) {
if (!potentiometer_throttle_task()) {
return false;
}

bool changed = false;
for (uint8_t index = 0; index < NUM_POTENTIOMETERS; index++) {
uint16_t raw = analogReadPin(potentiometer_pads[index]);
uint16_t value = potentiometer_map(index, raw);
if (potentiometer_filter(index, value)) {
changed |= true;

potentiometer_update_kb(index, value);
}
}

return changed;
}
18 changes: 18 additions & 0 deletions quantum/potentiometer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include <stdbool.h>
#include <stdint.h>

/** \brief user hook called when sampled value has changed
*/
bool potentiometer_update_user(uint8_t index, uint16_t value);

/** \brief keyboard hook called when sampled value has changed
*/
bool potentiometer_update_kb(uint8_t index, uint16_t value);

/** \brief Handle various subsystem background tasks
*/
bool potentiometer_task(void);
4 changes: 4 additions & 0 deletions quantum/quantum.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ extern layer_state_t layer_state;
# include "encoder.h"
#endif

#ifdef POTENTIOMETER_ENABLE
# include "potentiometer.h"
#endif

#ifdef POINTING_DEVICE_ENABLE
# include "pointing_device.h"
#endif
Expand Down

0 comments on commit d8e97ab

Please sign in to comment.