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 23 /* An alternative implementation for interpreting the encoder status: 24 * 25 * From graphing the phototransistor voltages, the peak and baseline appears to 26 * be rather stable. Therefore there is no need to average them out, and instead 27 * just simply store the min and max voltages of each phototransistor. 28 * 29 * This algorithm then distinguish between high and low states by employing an 30 * approach similar to a Schmitt trigger: a low and high threshold is defined 31 * for each phototransistor based on their min and max voltages. 32 * 33 * Currently, the thresholds are: 34 * 35 * * High threshold: The upper quarter of the voltage range. 36 * * Low threshold: The lower quarter of the voltage range. 37 * 38 * these thresholds are defined for each phototransistor. 39 * 40 * For a state to cross from high -> low, it must fall below the low threshold. 41 * Similarly, to cross from low -> high, the voltage must be higher than the 42 * high threshold. 43 * 44 * Having two distinct thresholds filters out the bulk of noise from the 45 * phototransistors. 46 * 47 * For converting the resulting high and low signals into rotation, a simple 48 * quadrature decoder is used. 49 */ 50 51 /* The minimum value returned by the ADC */ 52 #define ENCODER_MIN 0 53 /* The maximum value returned by the ADC */ 54 #define ENCODER_MAX 1023 55 56 /* Utilities for composing the encoder state */ 57 #define MAKE_STATE(HI_A, HI_B) (((uint8_t)((HI_A) & 0x1) << 1) | ((uint8_t)((HI_B) & 0x1))) 58 #define STATE_A(st) ((st & 0x2) >> 1) 59 #define STATE_B(st) (st & 0x1) 60 #define LOLO MAKE_STATE(0, 0) 61 #define HILO MAKE_STATE(1, 0) 62 #define LOHI MAKE_STATE(0, 1) 63 64 typedef enum { 65 CALIBRATION, /* Recalibrate encoder state by waiting for a 01 -> 00 or 10 -> 00 transistion */ 66 DECODE /* Translate changes in the encoder state into movement */ 67 } encoder_state_t; 68 69 static encoder_state_t mode; 70 71 static uint8_t lastState; 72 73 static uint16_t lowA; 74 static uint16_t highA; 75 static uint16_t lowB; 76 static uint16_t highB; 77 78 #define MOVE_UP 1 79 #define MOVE_DOWN -1 80 #define MOVE_NONE 0 81 #define MOVE_ERR 0x7F 82 static const uint8_t movement[] = { 83 // 00 -> 00, 01, 10, 11 84 MOVE_NONE, MOVE_DOWN, MOVE_UP, MOVE_ERR, 85 // 01 -> 00, 01, 10, 11 86 MOVE_UP, MOVE_NONE, MOVE_ERR, MOVE_DOWN, 87 // 10 -> 00, 01, 10, 11 88 MOVE_DOWN, MOVE_ERR, MOVE_NONE, MOVE_UP, 89 // 11 -> 00, 01, 10, 11 90 MOVE_ERR, MOVE_UP, MOVE_DOWN, MOVE_NONE}; 91 92 void opt_encoder_init(void) { 93 mode = CALIBRATION; 94 lastState = 0; 95 96 lowA = ENCODER_MAX; 97 lowB = ENCODER_MAX; 98 highA = ENCODER_MIN; 99 highB = ENCODER_MIN; 100 } 101 102 int8_t opt_encoder_handler(uint16_t encA, uint16_t encB) { 103 int8_t result = 0; 104 105 highA = MAX(encA, highA); 106 lowA = MIN(encA, lowA); 107 highB = MAX(encB, highB); 108 lowB = MIN(encB, lowB); 109 110 /* Only compute the thresholds after a large enough range is established */ 111 if (highA - lowA > SCROLL_THRESH_RANGE_LIM && highB - lowB > SCROLL_THRESH_RANGE_LIM) { 112 const int16_t lowThresholdA = (highA + lowA) / 4; 113 const int16_t highThresholdA = (highA + lowA) - lowThresholdA; 114 const int16_t lowThresholdB = (highB + lowB) / 4; 115 const int16_t highThresholdB = (highB + lowB) - lowThresholdB; 116 117 uint8_t state = MAKE_STATE(STATE_A(lastState) ? encA > lowThresholdA : encA > highThresholdA, STATE_B(lastState) ? encB > lowThresholdB : encB > highThresholdB); 118 119 switch (mode) { 120 case CALIBRATION: 121 if ((lastState == HILO && state == LOLO) || (lastState == LOHI && state == LOLO)) 122 mode = DECODE; 123 else 124 mode = CALIBRATION; 125 break; 126 127 case DECODE: 128 result = movement[lastState * 4 + state]; 129 /* If we detect a state change that should not be possible, 130 * then the wheel might have moved too fast and we need to 131 * recalibrate the encoder position. */ 132 mode = result == MOVE_ERR ? CALIBRATION : mode; 133 result = result == MOVE_ERR ? MOVE_NONE : result; 134 135 break; 136 } 137 138 lastState = state; 139 } 140 141 return result; 142 }