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 }