mirror of
https://github.com/qmk/qmk_firmware.git
synced 2024-12-04 00:45:13 +00:00
Initial potentiometer support
This commit is contained in:
parent
e2472ee8f1
commit
929da00c2f
@ -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
|
||||
|
@ -40,6 +40,7 @@ GENERIC_FEATURES = \
|
||||
MOUSEKEY \
|
||||
MUSIC \
|
||||
OS_DETECTION \
|
||||
POTENTIOMETER \
|
||||
PROGRAMMABLE_BUTTON \
|
||||
REPEAT_KEY \
|
||||
SECURE \
|
||||
|
@ -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"},
|
||||
|
@ -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"},
|
||||
|
@ -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": {
|
||||
|
@ -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)
|
||||
|
35
docs/feature_potentiometers.md
Normal file
35
docs/feature_potentiometers.md
Normal 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;
|
||||
}
|
||||
```
|
@ -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.
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define SLIDER_PIN B0
|
||||
#define MIDI_ADVANCED
|
||||
|
||||
#define LOCKING_SUPPORT_ENABLE
|
||||
|
@ -20,6 +20,9 @@
|
||||
{"pin_a": "A2", "pin_b": "A1"}
|
||||
]
|
||||
},
|
||||
"potentiometer": {
|
||||
"pins": ["B0"]
|
||||
},
|
||||
"rgb_matrix": {
|
||||
"driver": "aw20216s"
|
||||
},
|
||||
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 }
|
16
keyboards/handwired/onekey/keymaps/potentiometer/keymap.c
Normal file
16
keyboards/handwired/onekey/keymaps/potentiometer/keymap.c
Normal 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;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
CONSOLE_ENABLE = yes
|
||||
|
||||
POTENTIOMETER_ENABLE = yes
|
@ -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
113
quantum/potentiometer.c
Normal 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
18
quantum/potentiometer.h
Normal 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);
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user