Skip to content

Commit

Permalink
Asymmetric encoders, encoder tests. (qmk#16068)
Browse files Browse the repository at this point in the history
  • Loading branch information
tzarc authored and waffle87 committed Mar 23, 2022
1 parent 80c9ce5 commit 75c9789
Show file tree
Hide file tree
Showing 27 changed files with 921 additions and 138 deletions.
2 changes: 2 additions & 0 deletions builddefs/build_test.mk
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ endif

.DEFAULT_GOAL := all

OPT = g

include paths.mk
include $(BUILDDEFS_PATH)/message.mk

Expand Down
1 change: 1 addition & 0 deletions builddefs/testlist.mk
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ TEST_LIST = $(sort $(patsubst %/test.mk,%, $(shell find $(ROOT_DIR)tests -type f
FULL_TESTS := $(notdir $(TEST_LIST))

include $(QUANTUM_PATH)/debounce/tests/testlist.mk
include $(QUANTUM_PATH)/encoder/tests/testlist.mk
include $(QUANTUM_PATH)/sequencer/tests/testlist.mk
include $(PLATFORM_PATH)/test/testlist.mk

Expand Down
13 changes: 13 additions & 0 deletions docs/feature_encoders.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ If you are using different pinouts for the encoders on each half of a split keyb
#define ENCODER_RESOLUTIONS_RIGHT { 2, 4 }
```
If the `_RIGHT` definitions aren't specified in your `config.h`, then the non-`_RIGHT` versions will be applied to both sides of the split.

Additionally, if one side does not have an encoder, you can specify `{}` for the pins/resolution -- for example, a split keyboard with only a right-side encoder:

```c
#define ENCODERS_PAD_A { }
#define ENCODERS_PAD_B { }
#define ENCODER_RESOLUTIONS { }
#define ENCODERS_PAD_A_RIGHT { B12 }
#define ENCODERS_PAD_B_RIGHT { B13 }
#define ENCODER_RESOLUTIONS_RIGHT { 4 }
```
## Callbacks
The callback functions can be inserted into your `<keyboard>.c`:
Expand Down
3 changes: 2 additions & 1 deletion keyboards/draculad/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define ENCODERS_PAD_A {B2 , B4}
#define ENCODERS_PAD_B {B6 , B5}

#define ENCODER_RESOLUTIONS { 4, 4, 4, 1}
#define ENCODER_RESOLUTIONS { 4, 4 }
#define ENCODER_RESOLUTIONS_RIGHT { 4, 1 }
#define UNUSED_PINS

#define EE_HANDS
Expand Down
11 changes: 6 additions & 5 deletions keyboards/sofle/keyhive/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@
#define DEBOUNCE 5

// Encoder support
#define ENCODERS_PAD_A { F5 }
#define ENCODERS_PAD_B { F4 }
#define ENCODERS_PAD_A_RIGHT { F4 }
#define ENCODERS_PAD_B_RIGHT { F5 }
#define ENCODER_RESOLUTIONS { 4, 2 } // Left encoder seems to have double-output issue but right does not.
#define ENCODERS_PAD_A { F5 }
#define ENCODERS_PAD_B { F4 }
#define ENCODERS_PAD_A_RIGHT { F4 }
#define ENCODERS_PAD_B_RIGHT { F5 }
#define ENCODER_RESOLUTIONS { 4 }
#define ENCODER_RESOLUTIONS_RIGHT { 2 } // Left encoder seems to have double-output issue but right does not.

#define TAP_CODE_DELAY 10

Expand Down
4 changes: 2 additions & 2 deletions keyboards/viktus/sp_mini/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.

// wiring of each half
#define MATRIX_ROW_PINS { F0, B5, B4, D7, D6 }
#define MATRIX_COL_PINS { B6, C6, C7, D4, D2, D3, D5 } // no B7 on left hand
#define MATRIX_COL_PINS { B6, C6, C7, D4, D2, D3, D5, NO_PIN } // no B7 on left hand
#define MATRIX_ROW_PINS_RIGHT { F0, B5, B4, D7, D6 }
#define MATRIX_COL_PINS_RIGHT { B6, C6, C7, D4, D2, D3, D5, B7 }

Expand Down Expand Up @@ -78,7 +78,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//#define ENCODERS_PAD_A_RIGHT {F4}
//#define ENCODERS_PAD_B_RIGHT {F1}

#define ENCODER_RESOLUTIONS { 8, 8 }
#define ENCODER_RESOLUTIONS { 8 }

/*
* Feature disable options
Expand Down
114 changes: 74 additions & 40 deletions quantum/encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
# error "No encoder pads defined by ENCODERS_PAD_A and ENCODERS_PAD_B"
#endif

#define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t))
static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
static pin_t encoders_pad_b[] = ENCODERS_PAD_B;
extern volatile bool isLeftHand;

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;

#ifdef ENCODER_RESOLUTIONS
static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;
static uint8_t encoder_resolutions[NUM_ENCODERS] = ENCODER_RESOLUTIONS;
#endif

#ifndef ENCODER_DIRECTION_FLIP
Expand All @@ -47,18 +49,20 @@ static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;
#endif
static int8_t encoder_LUT[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};

static uint8_t encoder_state[NUMBER_OF_ENCODERS] = {0};
static int8_t encoder_pulses[NUMBER_OF_ENCODERS] = {0};
static uint8_t encoder_state[NUM_ENCODERS] = {0};
static int8_t encoder_pulses[NUM_ENCODERS] = {0};

// encoder counts
static uint8_t thisCount;
#ifdef SPLIT_KEYBOARD
// right half encoders come over as second set of encoders
static uint8_t encoder_value[NUMBER_OF_ENCODERS * 2] = {0};
// row offsets for each hand
// encoder offsets for each hand
static uint8_t thisHand, thatHand;
#else
static uint8_t encoder_value[NUMBER_OF_ENCODERS] = {0};
// encoder counts for each hand
static uint8_t thatCount;
#endif

static uint8_t encoder_value[NUM_ENCODERS] = {0};

__attribute__((weak)) void encoder_wait_pullup_charge(void) {
wait_us(100);
}
Expand All @@ -72,46 +76,73 @@ __attribute__((weak)) bool encoder_update_kb(uint8_t index, bool clockwise) {
}

void encoder_init(void) {
#ifdef SPLIT_KEYBOARD
thisHand = isLeftHand ? 0 : NUM_ENCODERS_LEFT;
thatHand = NUM_ENCODERS_LEFT - thisHand;
thisCount = isLeftHand ? NUM_ENCODERS_LEFT : NUM_ENCODERS_RIGHT;
thatCount = isLeftHand ? NUM_ENCODERS_RIGHT : NUM_ENCODERS_LEFT;
#else // SPLIT_KEYBOARD
thisCount = NUM_ENCODERS;
#endif

#ifdef ENCODER_TESTS
// Annoying that we have to clear out values during initialisation here, but
// because all the arrays are static locals, rerunning tests in the same
// executable doesn't reset any of these. Kinda crappy having test-only code
// here, but it's the simplest solution.
memset(encoder_value, 0, sizeof(encoder_value));
memset(encoder_state, 0, sizeof(encoder_state));
memset(encoder_pulses, 0, sizeof(encoder_pulses));
static const pin_t encoders_pad_a_left[] = ENCODERS_PAD_A;
static const pin_t encoders_pad_b_left[] = ENCODERS_PAD_B;
for (uint8_t i = 0; i < thisCount; i++) {
encoders_pad_a[i] = encoders_pad_a_left[i];
encoders_pad_b[i] = encoders_pad_b_left[i];
}
#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) {
const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT;
const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT;
# if defined(ENCODER_RESOLUTIONS_RIGHT)
const uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT;
# endif
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
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];
# if defined(ENCODER_RESOLUTIONS_RIGHT)
encoder_resolutions[i] = encoder_resolutions_right[i];
# endif
}
}
#endif
#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)
static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS_RIGHT;
# else // defined(ENCODER_RESOLUTIONS_RIGHT)
static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS;
# endif // defined(ENCODER_RESOLUTIONS_RIGHT)
for (uint8_t i = 0; i < NUM_ENCODERS_RIGHT; i++) {
encoder_resolutions[NUM_ENCODERS_LEFT + i] = encoder_resolutions_right[i];
}
#endif // defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS)

for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
for (uint8_t i = 0; i < thisCount; i++) {
setPinInputHigh(encoders_pad_a[i]);
setPinInputHigh(encoders_pad_b[i]);
}
encoder_wait_pullup_charge();
for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
for (uint8_t i = 0; i < thisCount; i++) {
encoder_state[i] = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
}

#ifdef SPLIT_KEYBOARD
thisHand = isLeftHand ? 0 : NUMBER_OF_ENCODERS;
thatHand = NUMBER_OF_ENCODERS - thisHand;
#endif
}

static bool encoder_update(uint8_t index, uint8_t state) {
bool changed = false;
uint8_t i = index;

#ifdef ENCODER_RESOLUTIONS
uint8_t resolution = encoder_resolutions[i];
const uint8_t resolution = encoder_resolutions[i];
#else
uint8_t resolution = ENCODER_RESOLUTION;
const uint8_t resolution = ENCODER_RESOLUTION;
#endif

#ifdef SPLIT_KEYBOARD
Expand Down Expand Up @@ -139,26 +170,29 @@ static bool encoder_update(uint8_t index, uint8_t state) {

bool encoder_read(void) {
bool changed = false;
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
encoder_state[i] <<= 2;
encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
changed |= encoder_update(i, encoder_state[i]);
for (uint8_t i = 0; i < thisCount; i++) {
uint8_t new_status = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
if ((encoder_state[i] & 0x3) != new_status) {
encoder_state[i] <<= 2;
encoder_state[i] |= new_status;
changed |= encoder_update(i, encoder_state[i]);
}
}
return changed;
}

#ifdef SPLIT_KEYBOARD
void last_encoder_activity_trigger(void);

void encoder_state_raw(uint8_t* slave_state) {
memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * NUMBER_OF_ENCODERS);
void encoder_state_raw(uint8_t *slave_state) {
memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * thisCount);
}

void encoder_update_raw(uint8_t* slave_state) {
void encoder_update_raw(uint8_t *slave_state) {
bool changed = false;
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
uint8_t index = i + thatHand;
int8_t delta = slave_state[i] - encoder_value[index];
for (uint8_t i = 0; i < thatCount; i++) { // Note inverted logic -- we want the opposite side
const uint8_t index = i + thatHand;
int8_t delta = slave_state[i] - encoder_value[index];
while (delta > 0) {
delta--;
encoder_value[index]++;
Expand Down
28 changes: 27 additions & 1 deletion quantum/encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#pragma once

#include "quantum.h"
#include "util.h"

void encoder_init(void);
bool encoder_read(void);
Expand All @@ -26,6 +27,31 @@ bool encoder_update_kb(uint8_t index, bool clockwise);
bool encoder_update_user(uint8_t index, bool clockwise);

#ifdef SPLIT_KEYBOARD

void encoder_state_raw(uint8_t* slave_state);
void encoder_update_raw(uint8_t* slave_state);
#endif

# 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

#define NUM_ENCODERS_MAX_PER_SIDE MAX(NUM_ENCODERS_LEFT, NUM_ENCODERS_RIGHT)
22 changes: 22 additions & 0 deletions quantum/encoder/tests/config_mock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#define MATRIX_ROWS 1
#define MATRIX_COLS 1

/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0 }
#define ENCODERS_PAD_B \
{ 1 }

#ifdef __cplusplus
extern "C" {
#endif

#include "mock.h"

#ifdef __cplusplus
};
#endif
26 changes: 26 additions & 0 deletions quantum/encoder/tests/config_mock_split_left_eq_right.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#define MATRIX_ROWS 1
#define MATRIX_COLS 1

/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0, 2 }
#define ENCODERS_PAD_B \
{ 1, 3 }
#define ENCODERS_PAD_A_RIGHT \
{ 4, 6 }
#define ENCODERS_PAD_B_RIGHT \
{ 5, 7 }

#ifdef __cplusplus
extern "C" {
#endif

#include "mock_split.h"

#ifdef __cplusplus
};
#endif
26 changes: 26 additions & 0 deletions quantum/encoder/tests/config_mock_split_left_gt_right.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#define MATRIX_ROWS 1
#define MATRIX_COLS 1

/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0, 2, 4 }
#define ENCODERS_PAD_B \
{ 1, 3, 5 }
#define ENCODERS_PAD_A_RIGHT \
{ 6, 8 }
#define ENCODERS_PAD_B_RIGHT \
{ 7, 9 }

#ifdef __cplusplus
extern "C" {
#endif

#include "mock_split.h"

#ifdef __cplusplus
};
#endif
Loading

0 comments on commit 75c9789

Please sign in to comment.