From c5536971f64508bf845fcd3f2240f59d8f1982c1 Mon Sep 17 00:00:00 2001 From: Leah Post Date: Wed, 13 Apr 2022 14:37:28 +0200 Subject: [PATCH] Add custom encoders --- builddefs/common_features.mk | 9 +++++ docs/feature_encoders.md | 52 +++++++++++++++++++++++++ quantum/encoder.c | 73 +++++++++++++++++++++++------------- quantum/encoder.h | 50 ++++++++++++++---------- 4 files changed, 138 insertions(+), 46 deletions(-) diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index c976b8296d5..30e8a642ec7 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -589,6 +589,15 @@ ifneq ($(strip $(CUSTOM_MATRIX)), yes) endif endif +VALID_CUSTOM_ENCODER_TYPES:= yes no +CUSTOM_ENCODER ?= no +ifneq ($(strip $(CUSTOM_ENCODER)), no) + ifeq ($(filter $(CUSTOM_ENCODER),$(VALID_CUSTOM_MATRIX_TYPES)),) + $(call CATASTROPHIC_ERROR,Invalid CUSTOM_ENCODER,CUSTOM_ENCODER="$(CUSTOM_ENCODER)" is not a valid custom encoder type) + endif + OPT_DEFS += -DCUSTOM_ENCODER +endif + # Debounce Modules. Set DEBOUNCE_TYPE=custom if including one manually. DEBOUNCE_TYPE ?= sym_defer_g ifneq ($(strip $(DEBOUNCE_TYPE)), custom) diff --git a/docs/feature_encoders.md b/docs/feature_encoders.md index a3d56fd5eff..3873dc19405 100644 --- a/docs/feature_encoders.md +++ b/docs/feature_encoders.md @@ -183,3 +183,55 @@ You could even support three encoders using only three pins (one per encoder) ho #define ENCODERS_PAD_B { B2, B3, B3 } ``` Here rotating Encoder 0 `B1 B2` and Encoder 1 `B1 B3` could be interpreted as rotating Encoder 2 `B2 B3` or `B3 B2` depending on the timing. This may still be a useful configuration depending on your use case + +## Custom encoders + +Not everyone has encoders wired directly to the MCU. If yours are wired to external hardware (such as an I/O multiplexer), you can supplement the default encoder logic with your own. + +Add a new file to your keyboard directory: +``` +keyboards//encoder.c +``` + +And to configure compilation for the new file, add this to your `rules.mk`: +```make +CUSTOM_ENCODER = yes +SRC += encoder.c +``` + +Implement the following functions in the `encoder.c` file in your keyboard folder: + +```c +void encoder_init_pads(uint8_t count, bool pads[]) { + // TODO: initialize hardware here + // EXAMPLE: + // expanderSetInputHigh(ENCODER_PAD_1A); + // expanderSetInputHigh(ENCODER_PAD_1B); + // expanderSetInputHigh(ENCODER_PAD_2A); + // expanderSetInputHigh(ENCODER_PAD_2B); + // // make sure we start with a known value + // encoder_read_pads(pads); +} + +void encoder_read_pads(uint8_t count, bool pads[]) { + // TODO: add encoder reading routine here + // EXAMPLE: + // pads[0] = expanderReadPin(ENCODER_PAD_1A); + // pads[1] = expanderReadPin(ENCODER_PAD_1B); + // pads[2] = expanderReadPin(ENCODER_PAD_2A); + // pads[3] = expanderReadPin(ENCODER_PAD_2B); +} +``` + +Finally, let QMK know how many encoders you want to use by adding this to your `config.h`: + +```c +#define NUM_ENCODERS 2 // or any other number +``` + +Or for a split keyboard: + +```c +#define NUM_ENCODERS_LEFT 2 +#define NUM_ENCODERS_RIGHT 2 +``` diff --git a/quantum/encoder.c b/quantum/encoder.c index 105bed0147b..ecc73bf5ba9 100644 --- a/quantum/encoder.c +++ b/quantum/encoder.c @@ -31,14 +31,15 @@ # define ENCODER_RESOLUTION 4 #endif -#if !defined(ENCODERS_PAD_A) || !defined(ENCODERS_PAD_B) -# error "No encoder pads defined by ENCODERS_PAD_A and ENCODERS_PAD_B" -#endif - -extern volatile bool isLeftHand; - +#ifndef CUSTOM_ENCODER +# if !defined(ENCODERS_PAD_A) || !defined(ENCODERS_PAD_B) +# error "No encoder pads defined by ENCODERS_PAD_A and ENCODERS_PAD_B" +# endif static pin_t encoders_pad_a[NUM_ENCODERS_MAX_PER_SIDE] = ENCODERS_PAD_A; static pin_t encoders_pad_b[NUM_ENCODERS_MAX_PER_SIDE] = ENCODERS_PAD_B; +#endif // CUSTOM_ENCODER + +extern volatile bool isLeftHand; #ifdef ENCODER_RESOLUTIONS static uint8_t encoder_resolutions[NUM_ENCODERS] = ENCODER_RESOLUTIONS; @@ -79,6 +80,39 @@ __attribute__((weak)) bool encoder_update_kb(uint8_t index, bool clockwise) { return encoder_update_user(index, clockwise); } +void encoder_init_pads(uint8_t count, bool pads[]); +void encoder_read_pads(uint8_t count, bool pads[]); + +#ifndef CUSTOM_ENCODER +void encoder_init_pads(uint8_t count, bool pads[]) { +# if defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT) + if (!isLeftHand) { + const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT; + const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT; + for (uint8_t i = 0; i < count; i++) { + encoders_pad_a[i] = encoders_pad_a_right[i]; + encoders_pad_b[i] = encoders_pad_b_right[i]; + } + } +# endif // defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT) + + for (int i = 0; i < count; i++) { + setPinInputHigh(encoders_pad_a[i]); + setPinInputHigh(encoders_pad_b[i]); + } + encoder_wait_pullup_charge(); + + encoder_read_pads(count, pads); +} + +void encoder_read_pads(uint8_t count, bool pads[]) { + for (int i = 0; i < count; i++) { + pads[2 * i + 0] = readPin(encoders_pad_a[i]); + pads[2 * i + 1] = readPin(encoders_pad_b[i]); + } +} +#endif // CUSTOM_ENCODER + void encoder_init(void) { #ifdef SPLIT_KEYBOARD thisHand = isLeftHand ? 0 : NUM_ENCODERS_LEFT; @@ -105,18 +139,6 @@ void encoder_init(void) { } #endif -#if defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT) - // Re-initialise the pads if it's the right-hand side - if (!isLeftHand) { - static const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT; - static const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT; - for (uint8_t i = 0; i < thisCount; i++) { - encoders_pad_a[i] = encoders_pad_a_right[i]; - encoders_pad_b[i] = encoders_pad_b_right[i]; - } - } -#endif // defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT) - // Encoder resolutions is handled purely master-side, so concatenate the two arrays #if defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS) # if defined(ENCODER_RESOLUTIONS_RIGHT) @@ -129,13 +151,10 @@ void encoder_init(void) { } #endif // defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS) - for (uint8_t i = 0; i < thisCount; i++) { - setPinInputHigh(encoders_pad_a[i]); - setPinInputHigh(encoders_pad_b[i]); - } - encoder_wait_pullup_charge(); - for (uint8_t i = 0; i < thisCount; i++) { - encoder_state[i] = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1); + bool pads[NUM_ENCODERS_MAX_PER_SIDE * 2]; + encoder_init_pads(thisCount, pads); + for (int i = 0; i < thisCount; i++) { + encoder_state[i] = (pads[2 * i + 0] << 0) | (pads[2 * i + 1] << 1); } } @@ -192,8 +211,10 @@ static bool encoder_update(uint8_t index, uint8_t state) { bool encoder_read(void) { bool changed = false; + bool pads[NUM_ENCODERS_MAX_PER_SIDE * 2]; + encoder_read_pads(thisCount, pads); for (uint8_t i = 0; i < thisCount; i++) { - uint8_t new_status = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1); + uint8_t new_status = (pads[2 * i + 0] << 0) | (pads[2 * i + 1] << 1); if ((encoder_state[i] & 0x3) != new_status) { encoder_state[i] <<= 2; encoder_state[i] |= new_status; diff --git a/quantum/encoder.h b/quantum/encoder.h index 82f95b4931c..b923d222ba3 100644 --- a/quantum/encoder.h +++ b/quantum/encoder.h @@ -31,28 +31,38 @@ bool encoder_update_user(uint8_t index, bool clockwise); void encoder_state_raw(uint8_t* slave_state); void encoder_update_raw(uint8_t* slave_state); -# if defined(ENCODERS_PAD_A_RIGHT) -# define NUM_ENCODERS_LEFT (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t)) -# define NUM_ENCODERS_RIGHT (sizeof(((pin_t[])ENCODERS_PAD_A_RIGHT)) / sizeof(pin_t)) -# else -# define NUM_ENCODERS_LEFT (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t)) -# define NUM_ENCODERS_RIGHT NUM_ENCODERS_LEFT -# endif -# define NUM_ENCODERS (NUM_ENCODERS_LEFT + NUM_ENCODERS_RIGHT) - -#else // SPLIT_KEYBOARD - -# define NUM_ENCODERS (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t)) -# define NUM_ENCODERS_LEFT NUM_ENCODERS -# define NUM_ENCODERS_RIGHT 0 - #endif // SPLIT_KEYBOARD -#ifndef NUM_ENCODERS -# define NUM_ENCODERS 0 -# define NUM_ENCODERS_LEFT 0 -# define NUM_ENCODERS_RIGHT 0 -#endif // NUM_ENCODERS +#ifndef CUSTOM_ENCODER +# define NUM_ENCODERS_LEFT (sizeof(((pin_t[])ENCODERS_PAD_A)) / sizeof(pin_t)) +# ifdef SPLIT_KEYBOARD +# ifdef ENCODERS_PAD_A_RIGHT +# define NUM_ENCODERS_RIGHT (sizeof(((pin_t[])ENCODERS_PAD_A_RIGHT)) / sizeof(pin_t)) +# else +# define NUM_ENCODERS_RIGHT NUM_ENCODERS_LEFT +# endif +# else // SPLIT_KEYBOARD +# define NUM_ENCODERS_RIGHT 0 +# endif // SPLIT_KEYBOARD +# define NUM_ENCODERS (NUM_ENCODERS_LEFT + NUM_ENCODERS_RIGHT) +#else // CUSTOM_ENCODER +# ifdef SPLIT_KEYBOARD +# ifndef NUM_ENCODERS_LEFT +# error NUM_ENCODERS_LEFT must be defined +# endif +# ifndef NUM_ENCODERS_RIGHT +# error NUM_ENCODERS_RIGHT must be defined +# endif +# define NUM_ENCODERS (NUM_ENCODERS_LEFT + NUM_ENCODERS_RIGHT) +# else // SPLIT_KEYBOARD +# ifndef NUM_ENCODERS +# error NUM_ENCODERS must be defined +# else +# define NUM_ENCODERS_LEFT NUM_ENCODERS +# define NUM_ENCODERS_RIGHT 0 +# endif +# endif // SPLIT_KEYBOARD +#endif // CUSTOM_ENCODER #define NUM_ENCODERS_MAX_PER_SIDE MAX(NUM_ENCODERS_LEFT, NUM_ENCODERS_RIGHT)