Initial potentiometer support

This commit is contained in:
zvecr 2023-11-11 16:05:55 +00:00
parent e2472ee8f1
commit 929da00c2f
21 changed files with 251 additions and 37 deletions

View File

@ -914,6 +914,10 @@ ifeq ($(strip $(DIP_SWITCH_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

View File

@ -40,6 +40,7 @@ GENERIC_FEATURES = \
MOUSEKEY \
MUSIC \
OS_DETECTION \
POTENTIOMETER \
PROGRAMMABLE_BUTTON \
REPEAT_KEY \
SECURE \

View File

@ -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"},

View File

@ -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"},

View File

@ -418,6 +418,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": {

View File

@ -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)

View File

@ -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);
}
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);
return false;
}
```

View File

@ -480,6 +480,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.

View File

@ -17,7 +17,6 @@
#pragma once
#define SLIDER_PIN B0
#define MIDI_ADVANCED
#define LOCKING_SUPPORT_ENABLE

View File

@ -20,6 +20,9 @@
{"pin_a": "A2", "pin_b": "A1"}
]
},
"potentiometer": {
"pins": ["B0"]
},
"rgb_matrix": {
"driver": "aw20216s"
},

View File

@ -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] = {
@ -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();
}

View File

@ -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] = {
@ -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();
}

View File

@ -16,6 +16,7 @@
*/
#include "quantum.h"
#include "qmk_midi.h"
#ifdef RGB_MATRIX_ENABLE
@ -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;
}

View File

@ -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

View File

@ -0,0 +1,6 @@
// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// TODO: Remove reuse of pin
#define POTENTIOMETER_PINS { ADC_PIN }

View File

@ -0,0 +1,16 @@
// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
LAYOUT_ortho_1x1(KC_A)
};
bool potentiometer_update_user(uint8_t index, uint16_t value) {
uprintf("ADC:%u\n", value);
return false;
}
void keyboard_post_init_user(void) {
debug_enable=true;
}

View File

@ -0,0 +1,3 @@
CONSOLE_ENABLE = yes
POTENTIOMETER_ENABLE = yes

View File

@ -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
@ -143,6 +146,9 @@ uint32_t last_input_activity_time(void) {
uint32_t last_input_activity_elapsed(void) {
return sync_timer_elapsed32(last_input_modification_time);
}
void last_input_activity_trigger(void) {
last_input_modification_time = sync_timer_read32();
}
static uint32_t last_matrix_modification_time = 0;
uint32_t last_matrix_activity_time(void) {
@ -440,6 +446,9 @@ void keyboard_init(void) {
#ifdef DIP_SWITCH_ENABLE
dip_switch_init();
#endif
#ifdef POTENTIOMETER_ENABLE
potentiometer_init();
#endif
#ifdef SLEEP_LED_ENABLE
sleep_led_init();
#endif
@ -669,6 +678,13 @@ void keyboard_task(void) {
}
#endif
#ifdef POTENTIOMETER_ENABLE
if (potentiometer_task()) {
last_input_activity_trigger();
activity_has_occurred = true;
}
#endif
#ifdef POINTING_DEVICE_ENABLE
if (pointing_device_task()) {
last_pointing_device_activity_trigger();

113
quantum/potentiometer.c Normal file
View File

@ -0,0 +1,113 @@
// 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
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ADC Driver
// Matches default adc range
#ifndef POTENTIOMETER_RAW_MIN_VALUE
# define POTENTIOMETER_RAW_MIN_VALUE 0
#endif
#ifndef POTENTIOMETER_RAW_MAX_VALUE
# define POTENTIOMETER_RAW_MAX_VALUE (1 << 10)
#endif
static pin_t potentiometer_pads[] = POTENTIOMETER_PINS;
#define NUM_POTENTIOMETERS (ARRAY_SIZE(potentiometer_pads))
__attribute__((weak)) void potentiometer_driver_init(void) {
// do nothing
}
__attribute__((weak)) uint16_t potentiometer_driver_sample(uint8_t index) {
return analogReadPin(potentiometer_pads[index]);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Callbacks
__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);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Default scanning routine
__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_RAW_MIN_VALUE;
uint32_t max = POTENTIOMETER_RAW_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 uint16_t potentiometer_state[NUM_POTENTIOMETERS] = {UINT16_MAX};
if (value == potentiometer_state[index]) {
return false;
}
potentiometer_state[index] = value;
return true;
}
__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;
}
void potentiometer_init(void) {
potentiometer_driver_init();
}
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 = potentiometer_driver_sample(index);
uint16_t value = potentiometer_map(index, raw);
if (potentiometer_filter(index, value)) {
changed |= true;
potentiometer_update_kb(index, value);
}
}
return changed;
}

18
quantum/potentiometer.h Normal file
View File

@ -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);

View File

@ -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