qmk_firmware

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

opt_encoder_tiny.c (4001B)


      1 /* Copyright 2023 Leorize <leorize+oss@disroot.org>
      2  * Copyright 2011 Ben Buxton <bb@cactii.net>
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  */
     17 #include "opt_encoder.h"
     18 #include <stdbool.h>
     19 #include <stdint.h>
     20 
     21 /* An extremely simple implementation of the encoder:
     22  *
     23  * For read out, the mechanism mimics that of a Schmitt trigger, with
     24  * statically defined high/low thresholds used instead of computing
     25  * one at runtime.
     26  *
     27  * The advantage of this approach is computing less in the decoder
     28  * implementation and allow the state to be measured before the wheel
     29  * moved.
     30  *
     31  * Compared to opt_encoder_simple.c, the use of an intermediary state
     32  * reduces sensitivity and de-sensitize against tiny movements caused
     33  * when lifting finger off the wheel.
     34  *
     35  * For turning decoded values into rotation, an algorithm inspired by
     36  * http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
     37  * is employed.
     38  */
     39 
     40 #if !defined(ENCODER_LOW_THRES_A) || !defined(ENCODER_HIGH_THRES_A)
     41 #    error "The thresholds for phototransistors A is not defined in config.h"
     42 #endif
     43 #if !defined(ENCODER_LOW_THRES_B) || !defined(ENCODER_HIGH_THRES_B)
     44 #    error "The thresholds for phototransistors B is not defined in config.h"
     45 #endif
     46 /*
     47  * Sample values, captured for a Ploopy Mini Trackball
     48  *
     49  * The following min-max values was captured by aggregating data recorded
     50  * when debug_encoder is enabled:
     51  *
     52  *   A: min: 0, max: 214
     53  *   B: min: 0, max: 204
     54  *
     55  * The threshold specified is then defined at the 1/4 and the 3/4 points.
     56  *
     57  * As these values might vary between units, you're encouraged to
     58  * measure your own.
     59  */
     60 #if 0
     61 #    define ENCODER_LOW_THRES_A 53
     62 #    define ENCODER_HIGH_THRES_A 161
     63 #    define ENCODER_LOW_THRES_B 52
     64 #    define ENCODER_HIGH_THRES_B 153
     65 #endif
     66 
     67 /* Utilities for composing the encoder state */
     68 #define MAKE_STATE(HI_A, HI_B) (((uint8_t)((HI_A) & 0x1) << 1) | ((uint8_t)((HI_B) & 0x1)))
     69 #define STATE_A(st) ((st & 0x2) >> 1)
     70 #define STATE_B(st) (st & 0x1)
     71 
     72 typedef enum {
     73     START,
     74     DOWN_BEGIN,
     75     UP_BEGIN,
     76     START_MID,
     77     DOWN_BEGIN_MID,
     78     UP_BEGIN_MID,
     79     STATE_MASK    = 0xf, /* 0b1111 */
     80     EMIT_UP       = 0x10,
     81     EMIT_UP_MID   = EMIT_UP & START_MID,
     82     EMIT_DOWN     = 0x80,
     83     EMIT_DOWN_MID = EMIT_DOWN & START_MID,
     84     EMIT_MASK     = 0xf0
     85 } encoder_state_t;
     86 
     87 static encoder_state_t state;
     88 static uint8_t         encState;
     89 
     90 static const uint8_t transitions[] = {
     91     // clang-format off
     92     // START -> 00, 01, 10, 11
     93     START, DOWN_BEGIN, UP_BEGIN, START_MID,
     94     // DOWN_BEGIN -> 00, 01, 10, 11
     95     START, DOWN_BEGIN, START, EMIT_DOWN_MID,
     96     // UP_BEGIN -> 00, 01, 10, 11
     97     START, START, UP_BEGIN, EMIT_UP_MID,
     98     // START_MID -> 00, 01, 10, 11
     99     START, UP_BEGIN_MID, DOWN_BEGIN_MID, START_MID,
    100     // DOWN_BEGIN_MID -> 00, 01, 10, 11
    101     EMIT_DOWN, START_MID, DOWN_BEGIN_MID, START_MID,
    102     // UP_BEGIN_MID -> 00, 01, 10, 11
    103     EMIT_UP, UP_BEGIN_MID, START_MID, START_MID,
    104     // clang-format on
    105 };
    106 
    107 void opt_encoder_init(void) {
    108     state    = START;
    109     encState = 0;
    110 }
    111 
    112 int8_t opt_encoder_handler(uint16_t encA, uint16_t encB) {
    113     encState = MAKE_STATE((STATE_A(encState) & (encA > ENCODER_LOW_THRES_A)) | (encA > ENCODER_HIGH_THRES_A), (STATE_B(encState) & (encB > ENCODER_LOW_THRES_B)) | (encB > ENCODER_HIGH_THRES_B));
    114     state    = transitions[((state & STATE_MASK) << 2) + encState];
    115     return state & EMIT_MASK;
    116 }