Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asymmetric encoders, encoder tests. #16068

Merged
merged 12 commits into from
Mar 8, 2022
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