qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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 }