encoder_quadrature.c (7326B)
1 // Copyright 2018 Jack Humbert <jack.humb@gmail.com> 2 // Copyright 2018-2023 Nick Brassel (@tzarc) 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 #include <stdint.h> 6 #include "encoder.h" 7 #include "gpio.h" 8 #include "keyboard.h" 9 #include "action.h" 10 #include "keycodes.h" 11 #include "wait.h" 12 13 #ifdef SPLIT_KEYBOARD 14 # include "split_util.h" 15 #endif 16 17 // for memcpy 18 #include <string.h> 19 20 #if !defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTION) 21 # define ENCODER_RESOLUTION 4 22 #endif 23 24 #undef ENCODER_DEFAULT_PIN_API_IMPL 25 #if defined(ENCODER_A_PINS) && defined(ENCODER_B_PINS) 26 // Inform the quadrature driver that it needs to implement pin init/read functions 27 # define ENCODER_DEFAULT_PIN_API_IMPL 28 #endif 29 30 extern volatile bool isLeftHand; 31 32 __attribute__((weak)) void encoder_quadrature_init_pin(uint8_t index, bool pad_b); 33 __attribute__((weak)) uint8_t encoder_quadrature_read_pin(uint8_t index, bool pad_b); 34 35 #ifdef ENCODER_DEFAULT_PIN_API_IMPL 36 37 static pin_t encoders_pad_a[NUM_ENCODERS_MAX_PER_SIDE] = ENCODER_A_PINS; 38 static pin_t encoders_pad_b[NUM_ENCODERS_MAX_PER_SIDE] = ENCODER_B_PINS; 39 40 __attribute__((weak)) void encoder_wait_pullup_charge(void) { 41 wait_us(100); 42 } 43 44 __attribute__((weak)) void encoder_quadrature_init_pin(uint8_t index, bool pad_b) { 45 pin_t pin = pad_b ? encoders_pad_b[index] : encoders_pad_a[index]; 46 if (pin != NO_PIN) { 47 gpio_set_pin_input_high(pin); 48 } 49 } 50 51 __attribute__((weak)) uint8_t encoder_quadrature_read_pin(uint8_t index, bool pad_b) { 52 pin_t pin = pad_b ? encoders_pad_b[index] : encoders_pad_a[index]; 53 if (pin != NO_PIN) { 54 return gpio_read_pin(pin) ? 1 : 0; 55 } 56 return 0; 57 } 58 59 #endif // ENCODER_DEFAULT_PIN_API_IMPL 60 61 #ifdef ENCODER_RESOLUTIONS 62 static uint8_t encoder_resolutions[NUM_ENCODERS] = ENCODER_RESOLUTIONS; 63 #endif 64 65 #ifndef ENCODER_DIRECTION_FLIP 66 # define ENCODER_CLOCKWISE true 67 # define ENCODER_COUNTER_CLOCKWISE false 68 #else 69 # define ENCODER_CLOCKWISE false 70 # define ENCODER_COUNTER_CLOCKWISE true 71 #endif 72 static int8_t encoder_LUT[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}; 73 74 static uint8_t encoder_state[NUM_ENCODERS] = {0}; 75 static int8_t encoder_pulses[NUM_ENCODERS] = {0}; 76 77 // encoder counts 78 static uint8_t thisCount; 79 #ifdef SPLIT_KEYBOARD 80 // encoder offsets for each hand 81 static uint8_t thisHand, thatHand; 82 // encoder counts for each hand 83 static uint8_t thatCount; 84 #endif 85 86 __attribute__((weak)) void encoder_quadrature_post_init_kb(void) { 87 extern void encoder_quadrature_handle_read(uint8_t index, uint8_t pin_a_state, uint8_t pin_b_state); 88 // Unused normally, but can be used for things like setting up pin-change interrupts in keyboard code. 89 // During the interrupt, read the pins then call `encoder_handle_read()` with the pin states and it'll queue up an encoder event if needed. 90 } 91 92 void encoder_quadrature_post_init(void) { 93 #ifdef ENCODER_DEFAULT_PIN_API_IMPL 94 for (uint8_t i = 0; i < thisCount; i++) { 95 encoder_quadrature_init_pin(i, false); 96 encoder_quadrature_init_pin(i, true); 97 } 98 encoder_wait_pullup_charge(); 99 for (uint8_t i = 0; i < thisCount; i++) { 100 encoder_state[i] = (encoder_quadrature_read_pin(i, false) << 0) | (encoder_quadrature_read_pin(i, true) << 1); 101 } 102 #else 103 memset(encoder_state, 0, sizeof(encoder_state)); 104 #endif 105 106 encoder_quadrature_post_init_kb(); 107 } 108 109 void encoder_driver_init(void) { 110 #ifdef SPLIT_KEYBOARD 111 thisHand = isLeftHand ? 0 : NUM_ENCODERS_LEFT; 112 thatHand = NUM_ENCODERS_LEFT - thisHand; 113 thisCount = isLeftHand ? NUM_ENCODERS_LEFT : NUM_ENCODERS_RIGHT; 114 thatCount = isLeftHand ? NUM_ENCODERS_RIGHT : NUM_ENCODERS_LEFT; 115 #else // SPLIT_KEYBOARD 116 thisCount = NUM_ENCODERS; 117 #endif 118 119 #ifdef ENCODER_TESTS 120 // Annoying that we have to clear out values during initialisation here, but 121 // because all the arrays are static locals, rerunning tests in the same 122 // executable doesn't reset any of these. Kinda crappy having test-only code 123 // here, but it's the simplest solution. 124 memset(encoder_state, 0, sizeof(encoder_state)); 125 memset(encoder_pulses, 0, sizeof(encoder_pulses)); 126 const pin_t encoders_pad_a_left[] = ENCODER_A_PINS; 127 const pin_t encoders_pad_b_left[] = ENCODER_B_PINS; 128 for (uint8_t i = 0; i < thisCount; i++) { 129 encoders_pad_a[i] = encoders_pad_a_left[i]; 130 encoders_pad_b[i] = encoders_pad_b_left[i]; 131 } 132 #endif 133 134 #if defined(SPLIT_KEYBOARD) && defined(ENCODER_A_PINS_RIGHT) && defined(ENCODER_B_PINS_RIGHT) 135 // Re-initialise the pads if it's the right-hand side 136 if (!isLeftHand) { 137 const pin_t encoders_pad_a_right[] = ENCODER_A_PINS_RIGHT; 138 const pin_t encoders_pad_b_right[] = ENCODER_B_PINS_RIGHT; 139 for (uint8_t i = 0; i < thisCount; i++) { 140 encoders_pad_a[i] = encoders_pad_a_right[i]; 141 encoders_pad_b[i] = encoders_pad_b_right[i]; 142 } 143 } 144 #endif // defined(SPLIT_KEYBOARD) && defined(ENCODER_A_PINS_RIGHT) && defined(ENCODER_B_PINS_RIGHT) 145 146 // Encoder resolutions is defined differently in config.h, so concatenate 147 #if defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS) 148 # if defined(ENCODER_RESOLUTIONS_RIGHT) 149 static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS_RIGHT; 150 # else // defined(ENCODER_RESOLUTIONS_RIGHT) 151 static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS; 152 # endif // defined(ENCODER_RESOLUTIONS_RIGHT) 153 for (uint8_t i = 0; i < NUM_ENCODERS_RIGHT; i++) { 154 encoder_resolutions[NUM_ENCODERS_LEFT + i] = encoder_resolutions_right[i]; 155 } 156 #endif // defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS) 157 158 encoder_quadrature_post_init(); 159 } 160 161 static void encoder_handle_state_change(uint8_t index, uint8_t state) { 162 uint8_t i = index; 163 164 #ifdef SPLIT_KEYBOARD 165 index += thisHand; 166 #endif 167 168 #ifdef ENCODER_RESOLUTIONS 169 const uint8_t resolution = encoder_resolutions[index]; 170 #else 171 const uint8_t resolution = ENCODER_RESOLUTION; 172 #endif 173 174 encoder_pulses[i] += encoder_LUT[state & 0xF]; 175 176 #ifdef ENCODER_DEFAULT_POS 177 if ((encoder_pulses[i] >= resolution) || (encoder_pulses[i] <= -resolution) || ((state & 0x3) == ENCODER_DEFAULT_POS)) { 178 if (encoder_pulses[i] >= 1) { 179 #else 180 if (encoder_pulses[i] >= resolution) { 181 #endif 182 183 encoder_queue_event(index, ENCODER_COUNTER_CLOCKWISE); 184 } 185 186 #ifdef ENCODER_DEFAULT_POS 187 if (encoder_pulses[i] <= -1) { 188 #else 189 if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise 190 #endif 191 encoder_queue_event(index, ENCODER_CLOCKWISE); 192 } 193 encoder_pulses[i] %= resolution; 194 #ifdef ENCODER_DEFAULT_POS 195 encoder_pulses[i] = 0; 196 } 197 #endif 198 } 199 200 void encoder_quadrature_handle_read(uint8_t index, uint8_t pin_a_state, uint8_t pin_b_state) { 201 uint8_t state = pin_a_state | (pin_b_state << 1); 202 if ((encoder_state[index] & 0x3) != state) { 203 encoder_state[index] <<= 2; 204 encoder_state[index] |= state; 205 encoder_handle_state_change(index, encoder_state[index]); 206 } 207 } 208 209 __attribute__((weak)) void encoder_driver_task(void) { 210 for (uint8_t i = 0; i < thisCount; i++) { 211 encoder_quadrature_handle_read(i, encoder_quadrature_read_pin(i, false), encoder_quadrature_read_pin(i, true)); 212 } 213 }