summaryrefslogtreecommitdiff
path: root/quantum/joystick.c
blob: 32f19b2cd99d26bf7034f50a55945121ba546b8f (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* 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 "wait.h"

#if defined(JOYSTICK_ANALOG)
#    include "analog.h"
#endif

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] = {
#if JOYSTICK_AXIS_COUNT > 0
    [0 ...(JOYSTICK_AXIS_COUNT - 1)] = JOYSTICK_AXIS_VIRTUAL
#endif
};

__attribute__((weak)) void joystick_axis_init(uint8_t axis) {
    if (axis >= JOYSTICK_AXIS_COUNT) return;

#if defined(JOYSTICK_ANALOG)
    gpio_set_pin_input(joystick_axes[axis].input_pin);
#endif
}

__attribute__((weak)) uint16_t joystick_axis_sample(uint8_t axis) {
    if (axis >= JOYSTICK_AXIS_COUNT) return 0;

#if defined(JOYSTICK_ANALOG)
    return analogReadPin(joystick_axes[axis].input_pin);
#else
    // default to resting position
    return joystick_axes[axis].mid_digit;
#endif
}

static inline bool is_virtual_axis(uint8_t axis) {
    return joystick_axes[axis].input_pin == NO_PIN;
}

void joystick_flush(void) {
    if (!joystick_state.dirty) return;

    // TODO: host.h?
    void host_joystick_send(joystick_t * joystick);
    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;

    int16_t axis_val = joystick_axis_sample(axis);

    // 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_init_axes(void) {
#if JOYSTICK_AXIS_COUNT > 0
    for (int i = 0; i < JOYSTICK_AXIS_COUNT; ++i) {
        if (is_virtual_axis(i)) {
            continue;
        }

        joystick_axis_init(i);
    }
#endif
}

void joystick_read_axes(void) {
#if JOYSTICK_AXIS_COUNT > 0
    for (int i = 0; i < JOYSTICK_AXIS_COUNT; ++i) {
        if (is_virtual_axis(i)) {
            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;
    }
}

void joystick_init(void) {
    joystick_init_axes();
}

void joystick_task(void) {
    joystick_read_axes();
}