Rework encoders to enable asymmetric split keyboards (#12090)

Co-authored-by: Balz Guenat <balz.guenat@siemens.com>
Co-authored-by: Nick Brassel <nick@tzarc.org>
This commit is contained in:
Balz Guenat 2021-11-20 18:06:08 +01:00 committed by GitHub
parent d11d2459ce
commit 32215d5bff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 579 additions and 62 deletions

View File

@ -57,6 +57,7 @@ include $(BUILDDEFS_PATH)/generic_features.mk
include $(PLATFORM_PATH)/common.mk
include $(TMK_PATH)/protocol.mk
include $(QUANTUM_PATH)/debounce/tests/rules.mk
include $(QUANTUM_PATH)/encoder/tests/rules.mk
include $(QUANTUM_PATH)/sequencer/tests/rules.mk
include $(PLATFORM_PATH)/test/rules.mk
ifneq ($(filter $(FULL_TESTS),$(TEST)),)

View File

@ -46,7 +46,9 @@ For 4× encoders you also can assign default position if encoder skips pulses wh
## Split Keyboards
If you are using different pinouts for the encoders on each half of a split keyboard, you can define the pinout (and optionally, resolutions) for the right half like this:
The above is enough for split keyboards that are symmetrical, i.e. the halves have the same number of encoders and they are on the same pins.
If the halves are not symmetrical, you can define the pinout (and optionally, resolutions) of the right half separately.
The left half will use the definitions above.
```c
#define ENCODERS_PAD_A_RIGHT { encoder1a, encoder2a }
@ -54,6 +56,17 @@ If you are using different pinouts for the encoders on each half of a split keyb
#define ENCODER_RESOLUTIONS_RIGHT { 2, 4 }
```
If only the right half has encoders, you must still define an empty array for the left pads (and resolutions, if you define `ENCODER_RESOLUTIONS_RIGHT`).
```c
#define ENCODERS_PAD_A { }
#define ENCODERS_PAD_B { }
#define ENCODER_RESOLUTIONS { }
#define ENCODERS_PAD_A_RIGHT { encoder1a, encoder2a }
#define ENCODERS_PAD_B_RIGHT { encoder1b, encoder2b }
#define ENCODER_RESOLUTIONS_RIGHT { 2, 4 }
```
## Callbacks
The callback functions can be inserted into your `<keyboard>.c`:

View File

@ -16,9 +16,18 @@
*/
#include "encoder.h"
// this is for unit testing
#if defined(ENCODER_MOCK_SINGLE)
# include "encoder/tests/mock.h"
#elif defined(ENCODER_MOCK_SPLIT)
# include "encoder/tests/mock_split.h"
#else
# include <gpio.h>
# ifdef SPLIT_KEYBOARD
# include "split_util.h"
# endif
#endif
// for memcpy
#include <string.h>
@ -27,17 +36,41 @@
# 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"
#if (!defined(ENCODERS_PAD_A) || !defined(ENCODERS_PAD_B)) && (!defined(ENCODERS_PAD_A) || !defined(ENCODERS_PAD_B))
# error "No encoder pads defined by ENCODERS_PAD_A and ENCODERS_PAD_B or ENCODERS_PAD_A_RIGHT and ENCODERS_PAD_B_RIGHT"
#endif
#define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t))
// on split keyboards, these are the pads and resolutions for the left half
static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
static pin_t encoders_pad_b[] = ENCODERS_PAD_B;
#ifdef ENCODER_RESOLUTIONS
static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;
#endif
#ifndef SPLIT_KEYBOARD
# define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t))
#else
// if no pads for right half are defined, we assume the keyboard is symmetric (i.e. same pads)
# ifndef ENCODERS_PAD_A_RIGHT
# define ENCODERS_PAD_A_RIGHT ENCODERS_PAD_A
# endif
# ifndef ENCODERS_PAD_B_RIGHT
# define ENCODERS_PAD_B_RIGHT ENCODERS_PAD_B
# endif
# if defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTIONS_RIGHT)
# define ENCODER_RESOLUTIONS_RIGHT ENCODER_RESOLUTIONS
# endif
# define NUMBER_OF_ENCODERS ((sizeof(encoders_pad_a) + sizeof(encoders_pad_a_right)) / sizeof(pin_t))
# define NUMBER_OF_ENCODERS_LEFT (sizeof(encoders_pad_a) / sizeof(pin_t))
# define NUMBER_OF_ENCODERS_RIGHT (sizeof(encoders_pad_a_right) / sizeof(pin_t))
static pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT;
static pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT;
# ifdef ENCODER_RESOLUTIONS_RIGHT
static uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT;
# endif
#endif
#ifndef ENCODER_DIRECTION_FLIP
# define ENCODER_CLOCKWISE true
# define ENCODER_COUNTER_CLOCKWISE false
@ -50,78 +83,81 @@ static int8_t encoder_LUT[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1,
static uint8_t encoder_state[NUMBER_OF_ENCODERS] = {0};
static int8_t encoder_pulses[NUMBER_OF_ENCODERS] = {0};
#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
static uint8_t thisHand, thatHand;
#else
static uint8_t encoder_value[NUMBER_OF_ENCODERS] = {0};
#endif
__attribute__((weak)) bool encoder_update_user(uint8_t index, bool clockwise) { return true; }
__attribute__((weak)) bool encoder_update_kb(uint8_t index, bool clockwise) { return encoder_update_user(index, clockwise); }
void encoder_init(void) {
#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;
# 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++) {
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
for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
setPinInputHigh(encoders_pad_a[i]);
setPinInputHigh(encoders_pad_b[i]);
encoder_state[i] = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
}
// number of encoders connected to this controller
static uint8_t numEncodersHere;
// index of the first encoder connected to this controller (only for right halves, this will be nonzero)
static uint8_t firstEncoderHere;
#ifdef SPLIT_KEYBOARD
thisHand = isLeftHand ? 0 : NUMBER_OF_ENCODERS;
thatHand = NUMBER_OF_ENCODERS - thisHand;
// index of the first encoder connected to the other half
static uint8_t firstEncoderThere;
#endif
// the pads for this controller
static pin_t* pad_a;
static pin_t* pad_b;
void encoder_init(void) {
#ifndef SPLIT_KEYBOARD
numEncodersHere = NUMBER_OF_ENCODERS;
pad_a = encoders_pad_a;
pad_b = encoders_pad_b;
firstEncoderHere = 0;
#else
if (isLeftHand) {
numEncodersHere = NUMBER_OF_ENCODERS_LEFT;
pad_a = encoders_pad_a;
pad_b = encoders_pad_b;
firstEncoderHere = 0;
firstEncoderThere = NUMBER_OF_ENCODERS_LEFT;
} else {
numEncodersHere = NUMBER_OF_ENCODERS_RIGHT;
pad_a = encoders_pad_a_right;
pad_b = encoders_pad_b_right;
firstEncoderHere = NUMBER_OF_ENCODERS_LEFT;
firstEncoderThere = 0;
}
#endif
for (int i = 0; i < numEncodersHere; i++) {
setPinInputHigh(pad_a[i]);
setPinInputHigh(pad_b[i]);
encoder_state[firstEncoderHere + i] = (readPin(pad_a[i]) << 0) | (readPin(pad_b[i]) << 1);
}
}
static bool encoder_update(uint8_t index, uint8_t state) {
static bool encoder_update(int8_t index, uint8_t state) {
bool changed = false;
uint8_t i = index;
#ifdef ENCODER_RESOLUTIONS
uint8_t resolution = encoder_resolutions[i];
# ifndef SPLIT_KEYBOARD
int8_t resolution = encoder_resolutions[index];
# else
int8_t resolution = isLeftHand ? encoder_resolutions[index] : encoder_resolutions_right[index - NUMBER_OF_ENCODERS_LEFT];
# endif
#else
uint8_t resolution = ENCODER_RESOLUTION;
#endif
#ifdef SPLIT_KEYBOARD
index += thisHand;
#endif
encoder_pulses[i] += encoder_LUT[state & 0xF];
if (encoder_pulses[i] >= resolution) {
encoder_pulses[index] += encoder_LUT[state & 0xF];
if (encoder_pulses[index] >= resolution) {
encoder_value[index]++;
changed = true;
encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
}
if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise
if (encoder_pulses[index] <= -resolution) { // direction is arbitrary here, but this clockwise
encoder_value[index]--;
changed = true;
encoder_update_kb(index, ENCODER_CLOCKWISE);
}
encoder_pulses[i] %= resolution;
encoder_pulses[index] %= resolution;
#ifdef ENCODER_DEFAULT_POS
if ((state & 0x3) == ENCODER_DEFAULT_POS) {
encoder_pulses[i] = 0;
encoder_pulses[index] = 0;
}
#endif
return changed;
@ -129,10 +165,10 @@ 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 < numEncodersHere; i++) {
encoder_state[firstEncoderHere + i] <<= 2;
encoder_state[firstEncoderHere + i] |= (readPin(pad_a[i]) << 0) | (readPin(pad_b[i]) << 1);
changed |= encoder_update(firstEncoderHere + i, encoder_state[firstEncoderHere + i]);
}
return changed;
}
@ -140,12 +176,12 @@ bool encoder_read(void) {
#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[firstEncoderHere], sizeof(uint8_t) * numEncodersHere); }
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;
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS - numEncodersHere; i++) {
uint8_t index = firstEncoderThere + i;
int8_t delta = slave_state[i] - encoder_value[index];
while (delta > 0) {
delta--;

View File

@ -17,7 +17,8 @@
#pragma once
#include "quantum.h"
#include <stdbool.h>
#include <stdint.h>
void encoder_init(void);
bool encoder_read(void);

View File

@ -0,0 +1,144 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <vector>
#include <algorithm>
#include <stdio.h>
extern "C" {
#include "encoder.h"
#include "encoder/tests/mock.h"
}
struct update {
int8_t index;
bool clockwise;
};
uint8_t uidx = 0;
update updates[32];
bool encoder_update_kb(uint8_t index, bool clockwise) {
updates[uidx % 32] = {index, clockwise};
uidx++;
return true;
}
bool setAndRead(pin_t pin, bool val) {
setPin(pin, val);
return encoder_read();
}
class EncoderTest : public ::testing::Test {};
TEST_F(EncoderTest, TestInit) {
uidx = 0;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(uidx, 0);
}
TEST_F(EncoderTest, TestOneClockwise) {
uidx = 0;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderTest, TestOneCounterClockwise) {
uidx = 0;
encoder_init();
setAndRead(1, false);
setAndRead(0, false);
setAndRead(1, true);
setAndRead(0, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, false);
}
TEST_F(EncoderTest, TestTwoClockwiseOneCC) {
uidx = 0;
encoder_init();
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
setAndRead(1, false);
setAndRead(0, false);
setAndRead(1, true);
setAndRead(0, true);
EXPECT_EQ(uidx, 3);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
EXPECT_EQ(updates[1].index, 0);
EXPECT_EQ(updates[1].clockwise, true);
EXPECT_EQ(updates[2].index, 0);
EXPECT_EQ(updates[2].clockwise, false);
}
TEST_F(EncoderTest, TestNoEarly) {
uidx = 0;
encoder_init();
// send 3 pulses. with resolution 4, that's not enough for a step.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
EXPECT_EQ(uidx, 0);
// now send last pulse
setAndRead(1, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderTest, TestHalfway) {
uidx = 0;
encoder_init();
// go halfway
setAndRead(0, false);
setAndRead(1, false);
EXPECT_EQ(uidx, 0);
// back off
setAndRead(1, true);
setAndRead(0, true);
EXPECT_EQ(uidx, 0);
// go all the way
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
// should result in 1 update
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}

View File

@ -0,0 +1,143 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <vector>
#include <algorithm>
#include <stdio.h>
extern "C" {
#include "encoder.h"
#include "encoder/tests/mock_split.h"
}
struct update {
int8_t index;
bool clockwise;
};
uint8_t uidx = 0;
update updates[32];
bool isLeftHand;
bool encoder_update_kb(uint8_t index, bool clockwise) {
if (!isLeftHand) {
// this method has no effect on slave half
printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");
return true;
}
updates[uidx % 32] = {index, clockwise};
uidx++;
return true;
}
bool setAndRead(pin_t pin, bool val) {
setPin(pin, val);
return encoder_read();
}
class EncoderTest : public ::testing::Test {
protected:
void SetUp() override {
uidx = 0;
for (int i = 0; i < 32; i++) {
pinIsInputHigh[i] = 0;
pins[i] = 0;
}
}
};
TEST_F(EncoderTest, TestInitLeft) {
isLeftHand = true;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(pinIsInputHigh[2], false);
EXPECT_EQ(pinIsInputHigh[3], false);
EXPECT_EQ(uidx, 0);
}
TEST_F(EncoderTest, TestInitRight) {
isLeftHand = false;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], false);
EXPECT_EQ(pinIsInputHigh[1], false);
EXPECT_EQ(pinIsInputHigh[2], true);
EXPECT_EQ(pinIsInputHigh[3], true);
EXPECT_EQ(uidx, 0);
}
TEST_F(EncoderTest, TestOneClockwiseLeft) {
isLeftHand = true;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderTest, TestOneClockwiseRightSent) {
isLeftHand = false;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(2, false);
setAndRead(3, false);
setAndRead(2, true);
setAndRead(3, true);
uint8_t slave_state[2] = {0};
encoder_state_raw(slave_state);
EXPECT_EQ((int8_t)slave_state[0], -1);
}
/* this test will not work after the previous test.
* this is due to encoder_value[1] already being set to -1 when simulating the right half.
* When we now receive this update acting as the left half, there is no change.
* This is hard to mock, as the static values inside encoder.c normally exist twice, once on each half,
* but here, they only exist once.
*/
// TEST_F(EncoderTest, TestOneClockwiseRightReceived) {
// isLeftHand = true;
// encoder_init();
// uint8_t slave_state[2] = {255, 0};
// encoder_update_raw(slave_state);
// EXPECT_EQ(uidx, 1);
// EXPECT_EQ(updates[0].index, 1);
// EXPECT_EQ(updates[0].clockwise, true);
// }
TEST_F(EncoderTest, TestOneCounterClockwiseRightReceived) {
isLeftHand = true;
encoder_init();
uint8_t slave_state[2] = {0, 0};
encoder_update_raw(slave_state);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 1);
EXPECT_EQ(updates[0].clockwise, false);
}

View File

@ -0,0 +1,34 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mock.h"
bool pins[32] = {0};
bool pinIsInputHigh[32] = {0};
uint8_t mockSetPinInputHigh(pin_t pin) {
// dprintf("Setting pin %d input high.", pin);
pins[pin] = true;
pinIsInputHigh[pin] = true;
return 0;
}
bool mockReadPin(pin_t pin) { return pins[pin]; }
bool setPin(pin_t pin, bool val) {
pins[pin] = val;
return val;
}

View File

@ -0,0 +1,40 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0 }
#define ENCODERS_PAD_B \
{ 1 }
typedef uint8_t pin_t;
extern bool pins[];
extern bool pinIsInputHigh[];
#define setPinInputHigh(pin) (mockSetPinInputHigh(pin))
#define readPin(pin) (mockReadPin(pin))
uint8_t mockSetPinInputHigh(pin_t pin);
bool mockReadPin(pin_t pin);
bool setPin(pin_t pin, bool val);

View File

@ -0,0 +1,36 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mock_split.h"
bool pins[32] = {0};
bool pinIsInputHigh[32] = {0};
uint8_t mockSetPinInputHigh(pin_t pin) {
// dprintf("Setting pin %d input high.", pin);
pins[pin] = true;
pinIsInputHigh[pin] = true;
return 0;
}
bool mockReadPin(pin_t pin) { return pins[pin]; }
bool setPin(pin_t pin, bool val) {
pins[pin] = val;
return val;
}
void last_encoder_activity_trigger(void) {}

View File

@ -0,0 +1,48 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define SPLIT_KEYBOARD
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0 }
#define ENCODERS_PAD_B \
{ 1 }
#define ENCODERS_PAD_A_RIGHT \
{ 2 }
#define ENCODERS_PAD_B_RIGHT \
{ 3 }
typedef uint8_t pin_t;
extern bool isLeftHand;
void encoder_state_raw(uint8_t* slave_state);
void encoder_update_raw(uint8_t* slave_state);
extern bool pins[];
extern bool pinIsInputHigh[];
#define setPinInputHigh(pin) (mockSetPinInputHigh(pin))
#define readPin(pin) (mockReadPin(pin))
uint8_t mockSetPinInputHigh(pin_t pin);
bool mockReadPin(pin_t pin);
bool setPin(pin_t pin, bool val);

View File

@ -0,0 +1,13 @@
encoder_DEFS := -DENCODER_MOCK_SINGLE
encoder_SRC := \
$(QUANTUM_PATH)/encoder/tests/mock.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests.cpp \
$(QUANTUM_PATH)/encoder.c
encoder_split_DEFS := -DENCODER_MOCK_SPLIT
encoder_split_SRC := \
$(QUANTUM_PATH)/encoder/tests/mock_split.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests_split.cpp \
$(QUANTUM_PATH)/encoder.c

View File

@ -0,0 +1,3 @@
TEST_LIST += \
encoder \
encoder_split

View File

@ -42,7 +42,11 @@ bool transport_execute_transaction(int8_t id, const void *initiator2target_buf,
#ifdef ENCODER_ENABLE
# include "encoder.h"
# define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t))
// if no pads for right half are defined, we assume the keyboard is symmetric (i.e. same pads)
# ifndef ENCODERS_PAD_A_RIGHT
# define ENCODERS_PAD_A_RIGHT ENCODERS_PAD_A
# endif
# define NUMBER_OF_ENCODERS ((sizeof((pin_t[])ENCODERS_PAD_A) + (sizeof((pin_t[])ENCODERS_PAD_A_RIGHT)) / sizeof(pin_t))
#endif // ENCODER_ENABLE
#ifdef BACKLIGHT_ENABLE

View File

@ -2,6 +2,7 @@ TEST_LIST = $(notdir $(patsubst %/rules.mk,%,$(wildcard $(ROOT_DIR)/tests/*/rule
FULL_TESTS := $(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