qmk_firmware

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

joystick.c (4306B)


      1 /* Copyright 2022
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 2 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15  */
     16 
     17 #include "joystick.h"
     18 #include "wait.h"
     19 
     20 #if defined(JOYSTICK_ANALOG)
     21 #    include "analog.h"
     22 #endif
     23 
     24 joystick_t joystick_state = {
     25     .buttons = {0},
     26     .axes =
     27         {
     28 #if JOYSTICK_AXIS_COUNT > 0
     29             0
     30 #endif
     31         },
     32 #ifdef JOYSTICK_HAS_HAT
     33     .hat = -1,
     34 #endif
     35     .dirty = false,
     36 };
     37 
     38 // array defining the reading of analog values for each axis
     39 __attribute__((weak)) joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {
     40 #if JOYSTICK_AXIS_COUNT > 0
     41     [0 ...(JOYSTICK_AXIS_COUNT - 1)] = JOYSTICK_AXIS_VIRTUAL
     42 #endif
     43 };
     44 
     45 __attribute__((weak)) void joystick_axis_init(uint8_t axis) {
     46     if (axis >= JOYSTICK_AXIS_COUNT) return;
     47 
     48 #if defined(JOYSTICK_ANALOG)
     49     gpio_set_pin_input(joystick_axes[axis].input_pin);
     50 #endif
     51 }
     52 
     53 __attribute__((weak)) uint16_t joystick_axis_sample(uint8_t axis) {
     54     if (axis >= JOYSTICK_AXIS_COUNT) return 0;
     55 
     56 #if defined(JOYSTICK_ANALOG)
     57     return analogReadPin(joystick_axes[axis].input_pin);
     58 #else
     59     // default to resting position
     60     return joystick_axes[axis].mid_digit;
     61 #endif
     62 }
     63 
     64 static inline bool is_virtual_axis(uint8_t axis) {
     65     return joystick_axes[axis].input_pin == NO_PIN;
     66 }
     67 
     68 void joystick_flush(void) {
     69     if (!joystick_state.dirty) return;
     70 
     71     // TODO: host.h?
     72     void host_joystick_send(joystick_t * joystick);
     73     host_joystick_send(&joystick_state);
     74     joystick_state.dirty = false;
     75 }
     76 
     77 void register_joystick_button(uint8_t button) {
     78     if (button >= JOYSTICK_BUTTON_COUNT) return;
     79 
     80     joystick_state.buttons[button / 8] |= 1 << (button % 8);
     81     joystick_state.dirty = true;
     82     joystick_flush();
     83 }
     84 
     85 void unregister_joystick_button(uint8_t button) {
     86     if (button >= JOYSTICK_BUTTON_COUNT) return;
     87 
     88     joystick_state.buttons[button / 8] &= ~(1 << (button % 8));
     89     joystick_state.dirty = true;
     90     joystick_flush();
     91 }
     92 
     93 int16_t joystick_read_axis(uint8_t axis) {
     94     if (axis >= JOYSTICK_AXIS_COUNT) return 0;
     95 
     96     int16_t axis_val = joystick_axis_sample(axis);
     97 
     98     // test the converted value against the lower range
     99     int32_t ref        = joystick_axes[axis].mid_digit;
    100     int32_t range      = joystick_axes[axis].min_digit;
    101     int32_t ranged_val = ((axis_val - ref) * -JOYSTICK_MAX_VALUE) / (range - ref);
    102 
    103     if (ranged_val > 0) {
    104         // the value is in the higher range
    105         range      = joystick_axes[axis].max_digit;
    106         ranged_val = ((axis_val - ref) * JOYSTICK_MAX_VALUE) / (range - ref);
    107     }
    108 
    109     // clamp the result in the valid range
    110     ranged_val = ranged_val < -JOYSTICK_MAX_VALUE ? -JOYSTICK_MAX_VALUE : ranged_val;
    111     ranged_val = ranged_val > JOYSTICK_MAX_VALUE ? JOYSTICK_MAX_VALUE : ranged_val;
    112 
    113     return ranged_val;
    114 }
    115 
    116 void joystick_init_axes(void) {
    117 #if JOYSTICK_AXIS_COUNT > 0
    118     for (int i = 0; i < JOYSTICK_AXIS_COUNT; ++i) {
    119         if (is_virtual_axis(i)) {
    120             continue;
    121         }
    122 
    123         joystick_axis_init(i);
    124     }
    125 #endif
    126 }
    127 
    128 void joystick_read_axes(void) {
    129 #if JOYSTICK_AXIS_COUNT > 0
    130     for (int i = 0; i < JOYSTICK_AXIS_COUNT; ++i) {
    131         if (is_virtual_axis(i)) {
    132             continue;
    133         }
    134 
    135         joystick_set_axis(i, joystick_read_axis(i));
    136     }
    137 
    138     joystick_flush();
    139 #endif
    140 }
    141 
    142 void joystick_set_axis(uint8_t axis, int16_t value) {
    143     if (axis >= JOYSTICK_AXIS_COUNT) return;
    144 
    145     if (value != joystick_state.axes[axis]) {
    146         joystick_state.axes[axis] = value;
    147         joystick_state.dirty      = true;
    148     }
    149 }
    150 
    151 #ifdef JOYSTICK_HAS_HAT
    152 void joystick_set_hat(int8_t value) {
    153     joystick_state.hat   = value;
    154     joystick_state.dirty = true;
    155 }
    156 #endif
    157 
    158 void joystick_init(void) {
    159     joystick_init_axes();
    160 }
    161 
    162 void joystick_task(void) {
    163     joystick_read_axes();
    164 }