logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

encoder_quadrature.c (7341B)


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