aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build_test.mk1
-rw-r--r--docs/feature_encoders.md15
-rw-r--r--quantum/encoder.c146
-rw-r--r--quantum/encoder.h3
-rw-r--r--quantum/encoder/tests/encoder_tests.cpp144
-rw-r--r--quantum/encoder/tests/encoder_tests_split.cpp143
-rw-r--r--quantum/encoder/tests/mock.c34
-rw-r--r--quantum/encoder/tests/mock.h40
-rw-r--r--quantum/encoder/tests/mock_split.c36
-rw-r--r--quantum/encoder/tests/mock_split.h48
-rw-r--r--quantum/encoder/tests/rules.mk13
-rw-r--r--quantum/encoder/tests/testlist.mk3
-rw-r--r--quantum/split_common/transport.h6
-rw-r--r--testlist.mk1
14 files changed, 575 insertions, 58 deletions
diff --git a/build_test.mk b/build_test.mk
index 3553125a0..36cb7936e 100644
--- a/build_test.mk
+++ b/build_test.mk
@@ -57,6 +57,7 @@ include $(BUILDDEFS_PATH)/generic_features.mk
57include $(PLATFORM_PATH)/common.mk 57include $(PLATFORM_PATH)/common.mk
58include $(TMK_PATH)/protocol.mk 58include $(TMK_PATH)/protocol.mk
59include $(QUANTUM_PATH)/debounce/tests/rules.mk 59include $(QUANTUM_PATH)/debounce/tests/rules.mk
60include $(QUANTUM_PATH)/encoder/tests/rules.mk
60include $(QUANTUM_PATH)/sequencer/tests/rules.mk 61include $(QUANTUM_PATH)/sequencer/tests/rules.mk
61include $(PLATFORM_PATH)/test/rules.mk 62include $(PLATFORM_PATH)/test/rules.mk
62ifneq ($(filter $(FULL_TESTS),$(TEST)),) 63ifneq ($(filter $(FULL_TESTS),$(TEST)),)
diff --git a/docs/feature_encoders.md b/docs/feature_encoders.md
index 8e854c1e5..5b6c3f163 100644
--- a/docs/feature_encoders.md
+++ b/docs/feature_encoders.md
@@ -46,7 +46,9 @@ For 4× encoders you also can assign default position if encoder skips pulses wh
46 46
47## Split Keyboards 47## Split Keyboards
48 48
49If 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: 49The 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.
50If the halves are not symmetrical, you can define the pinout (and optionally, resolutions) of the right half separately.
51The left half will use the definitions above.
50 52
51```c 53```c
52#define ENCODERS_PAD_A_RIGHT { encoder1a, encoder2a } 54#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
54#define ENCODER_RESOLUTIONS_RIGHT { 2, 4 } 56#define ENCODER_RESOLUTIONS_RIGHT { 2, 4 }
55``` 57```
56 58
59If 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`).
60
61```c
62#define ENCODERS_PAD_A { }
63#define ENCODERS_PAD_B { }
64#define ENCODER_RESOLUTIONS { }
65#define ENCODERS_PAD_A_RIGHT { encoder1a, encoder2a }
66#define ENCODERS_PAD_B_RIGHT { encoder1b, encoder2b }
67#define ENCODER_RESOLUTIONS_RIGHT { 2, 4 }
68```
69
57## Callbacks 70## Callbacks
58 71
59The callback functions can be inserted into your `<keyboard>.c`: 72The callback functions can be inserted into your `<keyboard>.c`:
diff --git a/quantum/encoder.c b/quantum/encoder.c
index 8fb87281c..7d4e97898 100644
--- a/quantum/encoder.c
+++ b/quantum/encoder.c
@@ -16,8 +16,17 @@
16 */ 16 */
17 17
18#include "encoder.h" 18#include "encoder.h"
19#ifdef SPLIT_KEYBOARD 19
20# include "split_util.h" 20// this is for unit testing
21#if defined(ENCODER_MOCK_SINGLE)
22# include "encoder/tests/mock.h"
23#elif defined(ENCODER_MOCK_SPLIT)
24# include "encoder/tests/mock_split.h"
25#else
26# include <gpio.h>
27# ifdef SPLIT_KEYBOARD
28# include "split_util.h"
29# endif
21#endif 30#endif
22 31
23// for memcpy 32// for memcpy
@@ -27,17 +36,41 @@
27# define ENCODER_RESOLUTION 4 36# define ENCODER_RESOLUTION 4
28#endif 37#endif
29 38
30#if !defined(ENCODERS_PAD_A) || !defined(ENCODERS_PAD_B) 39#if (!defined(ENCODERS_PAD_A) || !defined(ENCODERS_PAD_B)) && (!defined(ENCODERS_PAD_A) || !defined(ENCODERS_PAD_B))
31# error "No encoder pads defined by ENCODERS_PAD_A and ENCODERS_PAD_B" 40# error "No encoder pads defined by ENCODERS_PAD_A and ENCODERS_PAD_B or ENCODERS_PAD_A_RIGHT and ENCODERS_PAD_B_RIGHT"
32#endif 41#endif
33 42
34#define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t)) 43// on split keyboards, these are the pads and resolutions for the left half
35static pin_t encoders_pad_a[] = ENCODERS_PAD_A; 44static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
36static pin_t encoders_pad_b[] = ENCODERS_PAD_B; 45static pin_t encoders_pad_b[] = ENCODERS_PAD_B;
37#ifdef ENCODER_RESOLUTIONS 46#ifdef ENCODER_RESOLUTIONS
38static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS; 47static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;
39#endif 48#endif
40 49
50#ifndef SPLIT_KEYBOARD
51# define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t))
52#else
53// if no pads for right half are defined, we assume the keyboard is symmetric (i.e. same pads)
54# ifndef ENCODERS_PAD_A_RIGHT
55# define ENCODERS_PAD_A_RIGHT ENCODERS_PAD_A
56# endif
57# ifndef ENCODERS_PAD_B_RIGHT
58# define ENCODERS_PAD_B_RIGHT ENCODERS_PAD_B
59# endif
60# if defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTIONS_RIGHT)
61# define ENCODER_RESOLUTIONS_RIGHT ENCODER_RESOLUTIONS
62# endif
63
64# define NUMBER_OF_ENCODERS ((sizeof(encoders_pad_a) + sizeof(encoders_pad_a_right)) / sizeof(pin_t))
65# define NUMBER_OF_ENCODERS_LEFT (sizeof(encoders_pad_a) / sizeof(pin_t))
66# define NUMBER_OF_ENCODERS_RIGHT (sizeof(encoders_pad_a_right) / sizeof(pin_t))
67static pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT;
68static pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT;
69# ifdef ENCODER_RESOLUTIONS_RIGHT
70static uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT;
71# endif
72#endif
73
41#ifndef ENCODER_DIRECTION_FLIP 74#ifndef ENCODER_DIRECTION_FLIP
42# define ENCODER_CLOCKWISE true 75# define ENCODER_CLOCKWISE true
43# define ENCODER_COUNTER_CLOCKWISE false 76# 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,
50static uint8_t encoder_state[NUMBER_OF_ENCODERS] = {0}; 83static uint8_t encoder_state[NUMBER_OF_ENCODERS] = {0};
51static int8_t encoder_pulses[NUMBER_OF_ENCODERS] = {0}; 84static int8_t encoder_pulses[NUMBER_OF_ENCODERS] = {0};
52 85
53#ifdef SPLIT_KEYBOARD
54// right half encoders come over as second set of encoders
55static uint8_t encoder_value[NUMBER_OF_ENCODERS * 2] = {0};
56// row offsets for each hand
57static uint8_t thisHand, thatHand;
58#else
59static uint8_t encoder_value[NUMBER_OF_ENCODERS] = {0}; 86static uint8_t encoder_value[NUMBER_OF_ENCODERS] = {0};
60#endif
61 87
62__attribute__((weak)) bool encoder_update_user(uint8_t index, bool clockwise) { return true; } 88__attribute__((weak)) bool encoder_update_user(uint8_t index, bool clockwise) { return true; }
63 89
64__attribute__((weak)) bool encoder_update_kb(uint8_t index, bool clockwise) { return encoder_update_user(index, clockwise); } 90__attribute__((weak)) bool encoder_update_kb(uint8_t index, bool clockwise) { return encoder_update_user(index, clockwise); }
65 91
92// number of encoders connected to this controller
93static uint8_t numEncodersHere;
94// index of the first encoder connected to this controller (only for right halves, this will be nonzero)
95static uint8_t firstEncoderHere;
96#ifdef SPLIT_KEYBOARD
97// index of the first encoder connected to the other half
98static uint8_t firstEncoderThere;
99#endif
100// the pads for this controller
101static pin_t* pad_a;
102static pin_t* pad_b;
103
66void encoder_init(void) { 104void encoder_init(void) {
67#if defined(SPLIT_KEYBOARD) && defined(ENCODERS_PAD_A_RIGHT) && defined(ENCODERS_PAD_B_RIGHT) 105#ifndef SPLIT_KEYBOARD
68 if (!isLeftHand) { 106 numEncodersHere = NUMBER_OF_ENCODERS;
69 const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT; 107 pad_a = encoders_pad_a;
70 const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT; 108 pad_b = encoders_pad_b;
71# if defined(ENCODER_RESOLUTIONS_RIGHT) 109 firstEncoderHere = 0;
72 const uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT; 110#else
73# endif 111 if (isLeftHand) {
74 for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { 112 numEncodersHere = NUMBER_OF_ENCODERS_LEFT;
75 encoders_pad_a[i] = encoders_pad_a_right[i]; 113 pad_a = encoders_pad_a;
76 encoders_pad_b[i] = encoders_pad_b_right[i]; 114 pad_b = encoders_pad_b;
77# if defined(ENCODER_RESOLUTIONS_RIGHT) 115 firstEncoderHere = 0;
78 encoder_resolutions[i] = encoder_resolutions_right[i]; 116 firstEncoderThere = NUMBER_OF_ENCODERS_LEFT;
79# endif 117 } else {
80 } 118 numEncodersHere = NUMBER_OF_ENCODERS_RIGHT;
119 pad_a = encoders_pad_a_right;
120 pad_b = encoders_pad_b_right;
121 firstEncoderHere = NUMBER_OF_ENCODERS_LEFT;
122 firstEncoderThere = 0;
81 } 123 }
82#endif 124#endif
83 125
84 for (int i = 0; i < NUMBER_OF_ENCODERS; i++) { 126 for (int i = 0; i < numEncodersHere; i++) {
85 setPinInputHigh(encoders_pad_a[i]); 127 setPinInputHigh(pad_a[i]);
86 setPinInputHigh(encoders_pad_b[i]); 128 setPinInputHigh(pad_b[i]);
87 129
88 encoder_state[i] = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1); 130 encoder_state[firstEncoderHere + i] = (readPin(pad_a[i]) << 0) | (readPin(pad_b[i]) << 1);
89 } 131 }
90
91#ifdef SPLIT_KEYBOARD
92 thisHand = isLeftHand ? 0 : NUMBER_OF_ENCODERS;
93 thatHand = NUMBER_OF_ENCODERS - thisHand;
94#endif
95} 132}
96 133
97static bool encoder_update(uint8_t index, uint8_t state) { 134static bool encoder_update(int8_t index, uint8_t state) {
98 bool changed = false; 135 bool changed = false;
99 uint8_t i = index;
100 136
101#ifdef ENCODER_RESOLUTIONS 137#ifdef ENCODER_RESOLUTIONS
102 uint8_t resolution = encoder_resolutions[i]; 138# ifndef SPLIT_KEYBOARD
139 int8_t resolution = encoder_resolutions[index];
140# else
141 int8_t resolution = isLeftHand ? encoder_resolutions[index] : encoder_resolutions_right[index - NUMBER_OF_ENCODERS_LEFT];
142# endif
103#else 143#else
104 uint8_t resolution = ENCODER_RESOLUTION; 144 uint8_t resolution = ENCODER_RESOLUTION;
105#endif 145#endif
106 146 encoder_pulses[index] += encoder_LUT[state & 0xF];
107#ifdef SPLIT_KEYBOARD 147 if (encoder_pulses[index] >= resolution) {
108 index += thisHand;
109#endif
110 encoder_pulses[i] += encoder_LUT[state & 0xF];
111 if (encoder_pulses[i] >= resolution) {
112 encoder_value[index]++; 148 encoder_value[index]++;
113 changed = true; 149 changed = true;
114 encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); 150 encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
115 } 151 }
116 if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise 152 if (encoder_pulses[index] <= -resolution) { // direction is arbitrary here, but this clockwise
117 encoder_value[index]--; 153 encoder_value[index]--;
118 changed = true; 154 changed = true;
119 encoder_update_kb(index, ENCODER_CLOCKWISE); 155 encoder_update_kb(index, ENCODER_CLOCKWISE);
120 } 156 }
121 encoder_pulses[i] %= resolution; 157 encoder_pulses[index] %= resolution;
122#ifdef ENCODER_DEFAULT_POS 158#ifdef ENCODER_DEFAULT_POS
123 if ((state & 0x3) == ENCODER_DEFAULT_POS) { 159 if ((state & 0x3) == ENCODER_DEFAULT_POS) {
124 encoder_pulses[i] = 0; 160 encoder_pulses[index] = 0;
125 } 161 }
126#endif 162#endif
127 return changed; 163 return changed;
@@ -129,10 +165,10 @@ static bool encoder_update(uint8_t index, uint8_t state) {
129 165
130bool encoder_read(void) { 166bool encoder_read(void) {
131 bool changed = false; 167 bool changed = false;
132 for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { 168 for (uint8_t i = 0; i < numEncodersHere; i++) {
133 encoder_state[i] <<= 2; 169 encoder_state[firstEncoderHere + i] <<= 2;
134 encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1); 170 encoder_state[firstEncoderHere + i] |= (readPin(pad_a[i]) << 0) | (readPin(pad_b[i]) << 1);
135 changed |= encoder_update(i, encoder_state[i]); 171 changed |= encoder_update(firstEncoderHere + i, encoder_state[firstEncoderHere + i]);
136 } 172 }
137 return changed; 173 return changed;
138} 174}
@@ -140,12 +176,12 @@ bool encoder_read(void) {
140#ifdef SPLIT_KEYBOARD 176#ifdef SPLIT_KEYBOARD
141void last_encoder_activity_trigger(void); 177void last_encoder_activity_trigger(void);
142 178
143void encoder_state_raw(uint8_t* slave_state) { memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * NUMBER_OF_ENCODERS); } 179void encoder_state_raw(uint8_t* slave_state) { memcpy(slave_state, &encoder_value[firstEncoderHere], sizeof(uint8_t) * numEncodersHere); }
144 180
145void encoder_update_raw(uint8_t* slave_state) { 181void encoder_update_raw(uint8_t* slave_state) {
146 bool changed = false; 182 bool changed = false;
147 for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { 183 for (uint8_t i = 0; i < NUMBER_OF_ENCODERS - numEncodersHere; i++) {
148 uint8_t index = i + thatHand; 184 uint8_t index = firstEncoderThere + i;
149 int8_t delta = slave_state[i] - encoder_value[index]; 185 int8_t delta = slave_state[i] - encoder_value[index];
150 while (delta > 0) { 186 while (delta > 0) {
151 delta--; 187 delta--;
diff --git a/quantum/encoder.h b/quantum/encoder.h
index 25dc77721..67f71ec0f 100644
--- a/quantum/encoder.h
+++ b/quantum/encoder.h
@@ -17,7 +17,8 @@
17 17
18#pragma once 18#pragma once
19 19
20#include "quantum.h" 20#include <stdbool.h>
21#include <stdint.h>
21 22
22void encoder_init(void); 23void encoder_init(void);
23bool encoder_read(void); 24bool encoder_read(void);
diff --git a/quantum/encoder/tests/encoder_tests.cpp b/quantum/encoder/tests/encoder_tests.cpp
new file mode 100644
index 000000000..1888fdab8
--- /dev/null
+++ b/quantum/encoder/tests/encoder_tests.cpp
@@ -0,0 +1,144 @@
1/* Copyright 2021 Balz Guenat
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "gtest/gtest.h"
18#include "gmock/gmock.h"
19#include <vector>
20#include <algorithm>
21#include <stdio.h>
22
23extern "C" {
24#include "encoder.h"
25#include "encoder/tests/mock.h"
26}
27
28struct update {
29 int8_t index;
30 bool clockwise;
31};
32
33uint8_t uidx = 0;
34update updates[32];
35
36bool encoder_update_kb(uint8_t index, bool clockwise) {
37 updates[uidx % 32] = {index, clockwise};
38 uidx++;
39 return true;
40}
41
42bool setAndRead(pin_t pin, bool val) {
43 setPin(pin, val);
44 return encoder_read();
45}
46
47class EncoderTest : public ::testing::Test {};
48
49TEST_F(EncoderTest, TestInit) {
50 uidx = 0;
51 encoder_init();
52 EXPECT_EQ(pinIsInputHigh[0], true);
53 EXPECT_EQ(pinIsInputHigh[1], true);
54 EXPECT_EQ(uidx, 0);
55}
56
57TEST_F(EncoderTest, TestOneClockwise) {
58 uidx = 0;
59 encoder_init();
60 // send 4 pulses. with resolution 4, that's one step and we should get 1 update.
61 setAndRead(0, false);
62 setAndRead(1, false);
63 setAndRead(0, true);
64 setAndRead(1, true);
65
66 EXPECT_EQ(uidx, 1);
67 EXPECT_EQ(updates[0].index, 0);
68 EXPECT_EQ(updates[0].clockwise, true);
69}
70
71TEST_F(EncoderTest, TestOneCounterClockwise) {
72 uidx = 0;
73 encoder_init();
74 setAndRead(1, false);
75 setAndRead(0, false);
76 setAndRead(1, true);
77 setAndRead(0, true);
78
79 EXPECT_EQ(uidx, 1);
80 EXPECT_EQ(updates[0].index, 0);
81 EXPECT_EQ(updates[0].clockwise, false);
82}
83
84TEST_F(EncoderTest, TestTwoClockwiseOneCC) {
85 uidx = 0;
86 encoder_init();
87 setAndRead(0, false);
88 setAndRead(1, false);
89 setAndRead(0, true);
90 setAndRead(1, true);
91 setAndRead(0, false);
92 setAndRead(1, false);
93 setAndRead(0, true);
94 setAndRead(1, true);
95 setAndRead(1, false);
96 setAndRead(0, false);
97 setAndRead(1, true);
98 setAndRead(0, true);
99
100 EXPECT_EQ(uidx, 3);
101 EXPECT_EQ(updates[0].index, 0);
102 EXPECT_EQ(updates[0].clockwise, true);
103 EXPECT_EQ(updates[1].index, 0);
104 EXPECT_EQ(updates[1].clockwise, true);
105 EXPECT_EQ(updates[2].index, 0);
106 EXPECT_EQ(updates[2].clockwise, false);
107}
108
109TEST_F(EncoderTest, TestNoEarly) {
110 uidx = 0;
111 encoder_init();
112 // send 3 pulses. with resolution 4, that's not enough for a step.
113 setAndRead(0, false);
114 setAndRead(1, false);
115 setAndRead(0, true);
116 EXPECT_EQ(uidx, 0);
117 // now send last pulse
118 setAndRead(1, true);
119 EXPECT_EQ(uidx, 1);
120 EXPECT_EQ(updates[0].index, 0);
121 EXPECT_EQ(updates[0].clockwise, true);
122}
123
124TEST_F(EncoderTest, TestHalfway) {
125 uidx = 0;
126 encoder_init();
127 // go halfway
128 setAndRead(0, false);
129 setAndRead(1, false);
130 EXPECT_EQ(uidx, 0);
131 // back off
132 setAndRead(1, true);
133 setAndRead(0, true);
134 EXPECT_EQ(uidx, 0);
135 // go all the way
136 setAndRead(0, false);
137 setAndRead(1, false);
138 setAndRead(0, true);
139 setAndRead(1, true);
140 // should result in 1 update
141 EXPECT_EQ(uidx, 1);
142 EXPECT_EQ(updates[0].index, 0);
143 EXPECT_EQ(updates[0].clockwise, true);
144}
diff --git a/quantum/encoder/tests/encoder_tests_split.cpp b/quantum/encoder/tests/encoder_tests_split.cpp
new file mode 100644
index 000000000..25e52c83f
--- /dev/null
+++ b/quantum/encoder/tests/encoder_tests_split.cpp
@@ -0,0 +1,143 @@
1/* Copyright 2021 Balz Guenat
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "gtest/gtest.h"
18#include "gmock/gmock.h"
19#include <vector>
20#include <algorithm>
21#include <stdio.h>
22
23extern "C" {
24#include "encoder.h"
25#include "encoder/tests/mock_split.h"
26}
27
28struct update {
29 int8_t index;
30 bool clockwise;
31};
32
33uint8_t uidx = 0;
34update updates[32];
35
36bool isLeftHand;
37
38bool encoder_update_kb(uint8_t index, bool clockwise) {
39 if (!isLeftHand) {
40 // this method has no effect on slave half
41 printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");
42 return true;
43 }
44 updates[uidx % 32] = {index, clockwise};
45 uidx++;
46 return true;
47}
48
49bool setAndRead(pin_t pin, bool val) {
50 setPin(pin, val);
51 return encoder_read();
52}
53
54class EncoderTest : public ::testing::Test {
55 protected:
56 void SetUp() override {
57 uidx = 0;
58 for (int i = 0; i < 32; i++) {
59 pinIsInputHigh[i] = 0;
60 pins[i] = 0;
61 }
62 }
63};
64
65TEST_F(EncoderTest, TestInitLeft) {
66 isLeftHand = true;
67 encoder_init();
68 EXPECT_EQ(pinIsInputHigh[0], true);
69 EXPECT_EQ(pinIsInputHigh[1], true);
70 EXPECT_EQ(pinIsInputHigh[2], false);
71 EXPECT_EQ(pinIsInputHigh[3], false);
72 EXPECT_EQ(uidx, 0);
73}
74
75TEST_F(EncoderTest, TestInitRight) {
76 isLeftHand = false;
77 encoder_init();
78 EXPECT_EQ(pinIsInputHigh[0], false);
79 EXPECT_EQ(pinIsInputHigh[1], false);
80 EXPECT_EQ(pinIsInputHigh[2], true);
81 EXPECT_EQ(pinIsInputHigh[3], true);
82 EXPECT_EQ(uidx, 0);
83}
84
85TEST_F(EncoderTest, TestOneClockwiseLeft) {
86 isLeftHand = true;
87 encoder_init();
88 // send 4 pulses. with resolution 4, that's one step and we should get 1 update.
89 setAndRead(0, false);
90 setAndRead(1, false);
91 setAndRead(0, true);
92 setAndRead(1, true);
93
94 EXPECT_EQ(uidx, 1);
95 EXPECT_EQ(updates[0].index, 0);
96 EXPECT_EQ(updates[0].clockwise, true);
97}
98
99TEST_F(EncoderTest, TestOneClockwiseRightSent) {
100 isLeftHand = false;
101 encoder_init();
102 // send 4 pulses. with resolution 4, that's one step and we should get 1 update.
103 setAndRead(2, false);
104 setAndRead(3, false);
105 setAndRead(2, true);
106 setAndRead(3, true);
107
108 uint8_t slave_state[2] = {0};
109 encoder_state_raw(slave_state);
110
111 EXPECT_EQ((int8_t)slave_state[0], -1);
112}
113
114/* this test will not work after the previous test.
115 * this is due to encoder_value[1] already being set to -1 when simulating the right half.
116 * When we now receive this update acting as the left half, there is no change.
117 * This is hard to mock, as the static values inside encoder.c normally exist twice, once on each half,
118 * but here, they only exist once.
119 */
120
121// TEST_F(EncoderTest, TestOneClockwiseRightReceived) {
122// isLeftHand = true;
123// encoder_init();
124
125// uint8_t slave_state[2] = {255, 0};
126// encoder_update_raw(slave_state);
127
128// EXPECT_EQ(uidx, 1);
129// EXPECT_EQ(updates[0].index, 1);
130// EXPECT_EQ(updates[0].clockwise, true);
131// }
132
133TEST_F(EncoderTest, TestOneCounterClockwiseRightReceived) {
134 isLeftHand = true;
135 encoder_init();
136
137 uint8_t slave_state[2] = {0, 0};
138 encoder_update_raw(slave_state);
139
140 EXPECT_EQ(uidx, 1);
141 EXPECT_EQ(updates[0].index, 1);
142 EXPECT_EQ(updates[0].clockwise, false);
143}
diff --git a/quantum/encoder/tests/mock.c b/quantum/encoder/tests/mock.c
new file mode 100644
index 000000000..d0506a938
--- /dev/null
+++ b/quantum/encoder/tests/mock.c
@@ -0,0 +1,34 @@
1/* Copyright 2021 Balz Guenat
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "mock.h"
18
19bool pins[32] = {0};
20bool pinIsInputHigh[32] = {0};
21
22uint8_t mockSetPinInputHigh(pin_t pin) {
23 // dprintf("Setting pin %d input high.", pin);
24 pins[pin] = true;
25 pinIsInputHigh[pin] = true;
26 return 0;
27}
28
29bool mockReadPin(pin_t pin) { return pins[pin]; }
30
31bool setPin(pin_t pin, bool val) {
32 pins[pin] = val;
33 return val;
34}
diff --git a/quantum/encoder/tests/mock.h b/quantum/encoder/tests/mock.h
new file mode 100644
index 000000000..dbc25a084
--- /dev/null
+++ b/quantum/encoder/tests/mock.h
@@ -0,0 +1,40 @@
1/* Copyright 2021 Balz Guenat
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21
22/* Here, "pins" from 0 to 31 are allowed. */
23#define ENCODERS_PAD_A \
24 { 0 }
25#define ENCODERS_PAD_B \
26 { 1 }
27
28typedef uint8_t pin_t;
29
30extern bool pins[];
31extern bool pinIsInputHigh[];
32
33#define setPinInputHigh(pin) (mockSetPinInputHigh(pin))
34#define readPin(pin) (mockReadPin(pin))
35
36uint8_t mockSetPinInputHigh(pin_t pin);
37
38bool mockReadPin(pin_t pin);
39
40bool setPin(pin_t pin, bool val);
diff --git a/quantum/encoder/tests/mock_split.c b/quantum/encoder/tests/mock_split.c
new file mode 100644
index 000000000..68bf3af59
--- /dev/null
+++ b/quantum/encoder/tests/mock_split.c
@@ -0,0 +1,36 @@
1/* Copyright 2021 Balz Guenat
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "mock_split.h"
18
19bool pins[32] = {0};
20bool pinIsInputHigh[32] = {0};
21
22uint8_t mockSetPinInputHigh(pin_t pin) {
23 // dprintf("Setting pin %d input high.", pin);
24 pins[pin] = true;
25 pinIsInputHigh[pin] = true;
26 return 0;
27}
28
29bool mockReadPin(pin_t pin) { return pins[pin]; }
30
31bool setPin(pin_t pin, bool val) {
32 pins[pin] = val;
33 return val;
34}
35
36void last_encoder_activity_trigger(void) {}
diff --git a/quantum/encoder/tests/mock_split.h b/quantum/encoder/tests/mock_split.h
new file mode 100644
index 000000000..0ae62652f
--- /dev/null
+++ b/quantum/encoder/tests/mock_split.h
@@ -0,0 +1,48 @@
1/* Copyright 2021 Balz Guenat
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21
22#define SPLIT_KEYBOARD
23/* Here, "pins" from 0 to 31 are allowed. */
24#define ENCODERS_PAD_A \
25 { 0 }
26#define ENCODERS_PAD_B \
27 { 1 }
28#define ENCODERS_PAD_A_RIGHT \
29 { 2 }
30#define ENCODERS_PAD_B_RIGHT \
31 { 3 }
32
33typedef uint8_t pin_t;
34extern bool isLeftHand;
35void encoder_state_raw(uint8_t* slave_state);
36void encoder_update_raw(uint8_t* slave_state);
37
38extern bool pins[];
39extern bool pinIsInputHigh[];
40
41#define setPinInputHigh(pin) (mockSetPinInputHigh(pin))
42#define readPin(pin) (mockReadPin(pin))
43
44uint8_t mockSetPinInputHigh(pin_t pin);
45
46bool mockReadPin(pin_t pin);
47
48bool setPin(pin_t pin, bool val);
diff --git a/quantum/encoder/tests/rules.mk b/quantum/encoder/tests/rules.mk
new file mode 100644
index 000000000..b826ce3ae
--- /dev/null
+++ b/quantum/encoder/tests/rules.mk
@@ -0,0 +1,13 @@
1encoder_DEFS := -DENCODER_MOCK_SINGLE
2
3encoder_SRC := \
4 $(QUANTUM_PATH)/encoder/tests/mock.c \
5 $(QUANTUM_PATH)/encoder/tests/encoder_tests.cpp \
6 $(QUANTUM_PATH)/encoder.c
7
8encoder_split_DEFS := -DENCODER_MOCK_SPLIT
9
10encoder_split_SRC := \
11 $(QUANTUM_PATH)/encoder/tests/mock_split.c \
12 $(QUANTUM_PATH)/encoder/tests/encoder_tests_split.cpp \
13 $(QUANTUM_PATH)/encoder.c
diff --git a/quantum/encoder/tests/testlist.mk b/quantum/encoder/tests/testlist.mk
new file mode 100644
index 000000000..1be9f4a05
--- /dev/null
+++ b/quantum/encoder/tests/testlist.mk
@@ -0,0 +1,3 @@
1TEST_LIST += \
2 encoder \
3 encoder_split
diff --git a/quantum/split_common/transport.h b/quantum/split_common/transport.h
index 1d4f6ed0c..ab65ff56d 100644
--- a/quantum/split_common/transport.h
+++ b/quantum/split_common/transport.h
@@ -42,7 +42,11 @@ bool transport_execute_transaction(int8_t id, const void *initiator2target_buf,
42 42
43#ifdef ENCODER_ENABLE 43#ifdef ENCODER_ENABLE
44# include "encoder.h" 44# include "encoder.h"
45# define NUMBER_OF_ENCODERS (sizeof((pin_t[])ENCODERS_PAD_A) / sizeof(pin_t)) 45// if no pads for right half are defined, we assume the keyboard is symmetric (i.e. same pads)
46# ifndef ENCODERS_PAD_A_RIGHT
47# define ENCODERS_PAD_A_RIGHT ENCODERS_PAD_A
48# endif
49# define NUMBER_OF_ENCODERS ((sizeof((pin_t[])ENCODERS_PAD_A) + (sizeof((pin_t[])ENCODERS_PAD_A_RIGHT)) / sizeof(pin_t))
46#endif // ENCODER_ENABLE 50#endif // ENCODER_ENABLE
47 51
48#ifdef BACKLIGHT_ENABLE 52#ifdef BACKLIGHT_ENABLE
diff --git a/testlist.mk b/testlist.mk
index faff4d201..cff784dad 100644
--- a/testlist.mk
+++ b/testlist.mk
@@ -2,6 +2,7 @@ TEST_LIST = $(notdir $(patsubst %/rules.mk,%,$(wildcard $(ROOT_DIR)/tests/*/rule
2FULL_TESTS := $(TEST_LIST) 2FULL_TESTS := $(TEST_LIST)
3 3
4include $(QUANTUM_PATH)/debounce/tests/testlist.mk 4include $(QUANTUM_PATH)/debounce/tests/testlist.mk
5include $(QUANTUM_PATH)/encoder/tests/testlist.mk
5include $(QUANTUM_PATH)/sequencer/tests/testlist.mk 6include $(QUANTUM_PATH)/sequencer/tests/testlist.mk
6include $(PLATFORM_PATH)/test/testlist.mk 7include $(PLATFORM_PATH)/test/testlist.mk
7 8