logo

qmk_firmware

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

opt_encoder_simple.c (5039B)


  1. /* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
  2. * Copyright 2020 Ploopy Corporation
  3. * Copyright 2022 Leorize <leorize+oss@disroot.org>
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "opt_encoder.h"
  19. #include "util.h"
  20. #include <stdbool.h>
  21. #include <stdint.h>
  22. /* An alternative implementation for interpreting the encoder status:
  23. *
  24. * From graphing the phototransistor voltages, the peak and baseline appears to
  25. * be rather stable. Therefore there is no need to average them out, and instead
  26. * just simply store the min and max voltages of each phototransistor.
  27. *
  28. * This algorithm then distinguish between high and low states by employing an
  29. * approach similar to a Schmitt trigger: a low and high threshold is defined
  30. * for each phototransistor based on their min and max voltages.
  31. *
  32. * Currently, the thresholds are:
  33. *
  34. * * High threshold: The upper quarter of the voltage range.
  35. * * Low threshold: The lower quarter of the voltage range.
  36. *
  37. * these thresholds are defined for each phototransistor.
  38. *
  39. * For a state to cross from high -> low, it must fall below the low threshold.
  40. * Similarly, to cross from low -> high, the voltage must be higher than the
  41. * high threshold.
  42. *
  43. * Having two distinct thresholds filters out the bulk of noise from the
  44. * phototransistors.
  45. *
  46. * For converting the resulting high and low signals into rotation, a simple
  47. * quadrature decoder is used.
  48. */
  49. /* The minimum value returned by the ADC */
  50. #define ENCODER_MIN 0
  51. /* The maximum value returned by the ADC */
  52. #define ENCODER_MAX 1023
  53. /* Utilities for composing the encoder state */
  54. #define MAKE_STATE(HI_A, HI_B) (((uint8_t)((HI_A) & 0x1) << 1) | ((uint8_t)((HI_B) & 0x1)))
  55. #define STATE_A(st) ((st & 0x2) >> 1)
  56. #define STATE_B(st) (st & 0x1)
  57. #define LOLO MAKE_STATE(0, 0)
  58. #define HILO MAKE_STATE(1, 0)
  59. #define LOHI MAKE_STATE(0, 1)
  60. typedef enum {
  61. CALIBRATION, /* Recalibrate encoder state by waiting for a 01 -> 00 or 10 -> 00 transistion */
  62. DECODE /* Translate changes in the encoder state into movement */
  63. } encoder_state_t;
  64. static encoder_state_t mode;
  65. static uint8_t lastState;
  66. static uint16_t lowA;
  67. static uint16_t highA;
  68. static uint16_t lowB;
  69. static uint16_t highB;
  70. #define MOVE_UP 1
  71. #define MOVE_DOWN -1
  72. #define MOVE_NONE 0
  73. #define MOVE_ERR 0x7F
  74. static const uint8_t movement[] = {
  75. // 00 -> 00, 01, 10, 11
  76. MOVE_NONE, MOVE_DOWN, MOVE_UP, MOVE_ERR,
  77. // 01 -> 00, 01, 10, 11
  78. MOVE_UP, MOVE_NONE, MOVE_ERR, MOVE_DOWN,
  79. // 10 -> 00, 01, 10, 11
  80. MOVE_DOWN, MOVE_ERR, MOVE_NONE, MOVE_UP,
  81. // 11 -> 00, 01, 10, 11
  82. MOVE_ERR, MOVE_UP, MOVE_DOWN, MOVE_NONE};
  83. void opt_encoder_init(void) {
  84. mode = CALIBRATION;
  85. lastState = 0;
  86. lowA = ENCODER_MAX;
  87. lowB = ENCODER_MAX;
  88. highA = ENCODER_MIN;
  89. highB = ENCODER_MIN;
  90. }
  91. int8_t opt_encoder_handler(uint16_t encA, uint16_t encB) {
  92. int8_t result = 0;
  93. highA = MAX(encA, highA);
  94. lowA = MIN(encA, lowA);
  95. highB = MAX(encB, highB);
  96. lowB = MIN(encB, lowB);
  97. /* Only compute the thresholds after a large enough range is established */
  98. if (highA - lowA > SCROLL_THRESH_RANGE_LIM && highB - lowB > SCROLL_THRESH_RANGE_LIM) {
  99. const int16_t lowThresholdA = (highA + lowA) / 4;
  100. const int16_t highThresholdA = (highA + lowA) - lowThresholdA;
  101. const int16_t lowThresholdB = (highB + lowB) / 4;
  102. const int16_t highThresholdB = (highB + lowB) - lowThresholdB;
  103. uint8_t state = MAKE_STATE(STATE_A(lastState) ? encA > lowThresholdA : encA > highThresholdA, STATE_B(lastState) ? encB > lowThresholdB : encB > highThresholdB);
  104. switch (mode) {
  105. case CALIBRATION:
  106. if ((lastState == HILO && state == LOLO) || (lastState == LOHI && state == LOLO))
  107. mode = DECODE;
  108. else
  109. mode = CALIBRATION;
  110. break;
  111. case DECODE:
  112. result = movement[lastState * 4 + state];
  113. /* If we detect a state change that should not be possible,
  114. * then the wheel might have moved too fast and we need to
  115. * recalibrate the encoder position. */
  116. mode = result == MOVE_ERR ? CALIBRATION : mode;
  117. result = result == MOVE_ERR ? MOVE_NONE : result;
  118. break;
  119. }
  120. lastState = state;
  121. }
  122. return result;
  123. }