summaryrefslogtreecommitdiff
path: root/quantum/joystick.c
blob: 02818e4acd394ad24798290a3c476169bbd66209 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* Copyright 2022
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "joystick.h"

#include "analog.h"
#include "wait.h"

joystick_t joystick_state = {
    .buttons = {0},
    .axes =
        {
#if JOYSTICK_AXIS_COUNT > 0
            0
#endif
        },
    .dirty = false,
};

// array defining the reading of analog values for each axis
__attribute__((weak)) joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {};

__attribute__((weak)) void joystick_task(void) {
    joystick_read_axes();
}

void joystick_flush(void) {
    if (joystick_state.dirty) {
        host_joystick_send(&joystick_state);
        joystick_state.dirty = false;
    }
}

void register_joystick_button(uint8_t button) {
    if (button >= JOYSTICK_BUTTON_COUNT) return;
    joystick_state.buttons[button / 8] |= 1 << (button % 8);
    joystick_state.dirty = true;
    joystick_flush();
}

void unregister_joystick_button(uint8_t button) {
    if (button >= JOYSTICK_BUTTON_COUNT) return;
    joystick_state.buttons[button / 8] &= ~(1 << (button % 8));
    joystick_state.dirty = true;
    joystick_flush();
}

int16_t joystick_read_axis(uint8_t axis) {
    if (axis >= JOYSTICK_AXIS_COUNT) return 0;

    // disable pull-up resistor
    writePinLow(joystick_axes[axis].input_pin);

    // if pin was a pull-up input, we need to uncharge it by turning it low
    // before making it a low input
    setPinOutput(joystick_axes[axis].input_pin);

    wait_us(10);

    if (joystick_axes[axis].output_pin != JS_VIRTUAL_AXIS) {
        setPinOutput(joystick_axes[axis].output_pin);
        writePinHigh(joystick_axes[axis].output_pin);
    }

    if (joystick_axes[axis].ground_pin != JS_VIRTUAL_AXIS) {
        setPinOutput(joystick_axes[axis].ground_pin);
        writePinLow(joystick_axes[axis].ground_pin);
    }

    wait_us(10);

    setPinInput(joystick_axes[axis].input_pin);

    wait_us(10);

#if defined(ANALOG_JOYSTICK_ENABLE) && (defined(__AVR__) || defined(PROTOCOL_CHIBIOS))
    int16_t axis_val = analogReadPin(joystick_axes[axis].input_pin);
#else
    // default to resting position
    int16_t axis_val = joystick_axes[axis].mid_digit;
#endif

    // test the converted value against the lower range
    int32_t ref        = joystick_axes[axis].mid_digit;
    int32_t range      = joystick_axes[axis].min_digit;
    int32_t ranged_val = ((axis_val - ref) * -JOYSTICK_MAX_VALUE) / (range - ref);

    if (ranged_val > 0) {
        // the value is in the higher range
        range      = joystick_axes[axis].max_digit;
        ranged_val = ((axis_val - ref) * JOYSTICK_MAX_VALUE) / (range - ref);
    }

    // clamp the result in the valid range
    ranged_val = ranged_val < -JOYSTICK_MAX_VALUE ? -JOYSTICK_MAX_VALUE : ranged_val;
    ranged_val = ranged_val > JOYSTICK_MAX_VALUE ? JOYSTICK_MAX_VALUE : ranged_val;

    return ranged_val;
}

void joystick_read_axes(void) {
#if JOYSTICK_AXIS_COUNT > 0
    for (int i = 0; i < JOYSTICK_AXIS_COUNT; ++i) {
        if (joystick_axes[i].input_pin == JS_VIRTUAL_AXIS) {
            continue;
        }

        joystick_set_axis(i, joystick_read_axis(i));
    }

    joystick_flush();
#endif
}

void joystick_set_axis(uint8_t axis, int16_t value) {
    if (axis >= JOYSTICK_AXIS_COUNT) return;

    if (value != joystick_state.axes[axis]) {
        joystick_state.axes[axis] = value;
        joystick_state.dirty      = true;
    }
}