From 1e95f7be8f214c544bf99f415916a4a5f07a1e9b Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 27 Nov 2022 03:14:45 +1100 Subject: [PATCH] Joystick feature improvements (#19052) --- docs/feature_joystick.md | 187 ++++++++++++------ .../battleship_gamepad/battleship_gamepad.c | 2 +- .../handwired/battleship_gamepad/config.h | 4 +- .../misterdeck/keymaps/default/config.h | 2 +- .../misterdeck/keymaps/default/keymap.c | 2 +- .../misterdeck/keymaps/nobuttons/config.h | 2 +- .../misterdeck/keymaps/nobuttons/keymap.c | 2 +- .../onekey/keymaps/joystick/config.h | 2 +- .../onekey/keymaps/joystick/keymap.c | 2 +- keyboards/handwired/onekey/proton_c/halconf.h | 26 +++ keyboards/handwired/onekey/proton_c/mcuconf.h | 22 +++ keyboards/lime/keymaps/default/keymap.c | 2 +- keyboards/lime/rev1/config.h | 4 +- keyboards/synthlabs/solo/config.h | 4 +- .../synthlabs/solo/keymaps/gamepad/keymap.c | 2 +- quantum/joystick.c | 55 +++--- quantum/joystick.h | 77 ++++++-- tmk_core/protocol.mk | 13 ++ tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c | 24 +-- tmk_core/protocol/chibios/usb_main.c | 4 +- tmk_core/protocol/host.c | 15 +- tmk_core/protocol/lufa/lufa.c | 2 +- tmk_core/protocol/report.h | 11 +- tmk_core/protocol/usb_descriptor.c | 134 +++++++------ tmk_core/protocol/usb_descriptor.h | 8 +- tmk_core/protocol/usb_descriptor_common.h | 5 +- tmk_core/protocol/vusb/vusb.c | 77 +++++++- 27 files changed, 475 insertions(+), 215 deletions(-) create mode 100644 keyboards/handwired/onekey/proton_c/halconf.h create mode 100644 keyboards/handwired/onekey/proton_c/mcuconf.h diff --git a/docs/feature_joystick.md b/docs/feature_joystick.md index a5e4921f9b..e60046a3c3 100644 --- a/docs/feature_joystick.md +++ b/docs/feature_joystick.md @@ -1,85 +1,72 @@ -## Joystick +# Joystick :id=joystick -The keyboard can be made to be recognized as a joystick HID device by the operating system. +This feature provides game controller input as a joystick device supporting up to 6 axes and 32 buttons. Axes can be read either from an [ADC-capable input pin](adc_driver.md), or can be virtual, so that its value is provided by your code. -!> Joystick support is not currently available on V-USB devices. +An analog device such as a [potentiometer](https://en.wikipedia.org/wiki/Potentiometer) found on an analog joystick's axes is based on a voltage divider, where adjusting the movable wiper controls the output voltage which can then be read by the microcontroller's ADC. -The joystick feature provides two services: - * reading analog input devices (eg. potentiometers) - * sending gamepad HID reports +## Usage :id=usage -Both services can be used without the other, depending on whether you just want to read a device but not send gamepad reports (for volume control for instance) -or send gamepad reports based on values computed by the keyboard. - -### Analog Input - -To use analog input you must first enable it in `rules.mk`: +Add the following to your `rules.mk`: ```make JOYSTICK_ENABLE = yes -JOYSTICK_DRIVER = analog # or 'digital' ``` -An analog device such as a potentiometer found on a gamepad's analog axes is based on a [voltage divider](https://en.wikipedia.org/wiki/Voltage_divider). -It is composed of three connectors linked to the ground, the power input and power output (usually the middle one). The power output holds the voltage that varies based on the position of the cursor, -which value will be read using your MCU's [ADC](https://en.wikipedia.org/wiki/Analog-to-digital_converter). -Depending on which pins are already used by your keyboard's matrix, the rest of the circuit can get a little bit more complicated, -feeding the power input and ground connection through pins and using diodes to avoid bad interactions with the matrix scanning procedures. +By default the joystick driver is `analog`, but you can change this with: -### Configuring the Joystick +```make +JOYSTICK_DRIVER = digital +``` -By default, two axes and eight buttons are defined. This can be changed in your `config.h`: +## Configuration :id=configuration + +By default, two axes and eight buttons are defined, with a reported resolution of 8 bits (-127 to +127). This can be changed in your `config.h`: ```c -// Max 32 +// Min 0, max 32 #define JOYSTICK_BUTTON_COUNT 16 -// Max 6: X, Y, Z, Rx, Ry, Rz -#define JOYSTICK_AXES_COUNT 3 +// Min 0, max 6: X, Y, Z, Rx, Ry, Rz +#define JOYSTICK_AXIS_COUNT 3 +// Min 8, max 16 +#define JOYSTICK_AXIS_RESOLUTION 10 ``` -When defining axes for your joystick, you have to provide a definition array. You can do this from your keymap.c file. -A joystick will either be read from an input pin that allows the use of the ADC, or can be virtual, so that its value is provided by your code. -You have to define an array of type ''joystick_config_t'' and of proper size. +?> You must define at least one button or axis. Also note that the maximum ADC resolution of the supported AVR MCUs is 10-bit, and 12-bit for most STM32 MCUs. -There are three ways for your circuit to work with the ADC, that relies on the use of 1, 2 or 3 pins of the MCU: - * 1 pin: your analog device is directly connected to your device GND and VCC. The only pin used is the ADC pin of your choice. - * 2 pins: your analog device is powered through a pin that allows toggling it on or off. The other pin is used to read the input value through the ADC. - * 3 pins: both the power input and ground are connected to pins that must be set to a proper state before reading and restored afterwards. +### Axes :id=axes -The configuration of each axis is performed using one of four macros: - * `JOYSTICK_AXIS_VIRTUAL`: no ADC reading must be performed, that value will be provided by keyboard/keymap-level code - * `JOYSTICK_AXIS_IN(INPUT_PIN, LOW, REST, HIGH)`: a voltage will be read on the provided pin, which must be an ADC-capable pin. - * `JOYSTICK_AXIS_IN_OUT(INPUT_PIN, OUTPUT_PIN, LOW, REST, HIGH)`: the provided `OUTPUT_PIN` will be set high before `INPUT_PIN` is read. - * `JOYSTICK_AXIS_IN_OUT_GROUND(INPUT_PIN, OUTPUT_PIN, GROUND_PIN, LOW, REST, HIGH)`: the `OUTPUT_PIN` will be set high and `GROUND_PIN` will be set low before reading from `INPUT_PIN`. +When defining axes for your joystick, you must provide a definition array typically in your `keymap.c`. -In any case where an ADC reading takes place (when `INPUT_PIN` is provided), additional `LOW`, `REST` and `HIGH` parameters are used. -These implement the calibration of the analog device by defining the range of read values that will be mapped to the lowest, resting position and highest possible value for the axis (-127 to 127). -In practice, you have to provide the lowest/highest raw ADC reading, and the raw reading at resting position, when no deflection is applied. You can provide inverted `LOW` and `HIGH` to invert the axis. - -For instance, an axes configuration can be defined in the following way: +For instance, the below example configures two axes. The X axis is read from the `A4` pin. With the default axis resolution of 8 bits, the range of values between 900 and 575 are scaled to -127 through 0, and values 575 to 285 are scaled to 0 through 127. The Y axis is configured as a virtual axis, and its value is not read from any pin. Instead, the user must update the axis value programmatically. ```c -//joystick config joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { - [0] = JOYSTICK_AXIS_IN_OUT_GROUND(A4, B0, A7, 900, 575, 285), - [1] = JOYSTICK_AXIS_VIRTUAL + JOYSTICK_AXIS_IN(A4, 900, 575, 285), + JOYSTICK_AXIS_VIRTUAL }; ``` -When the ADC reads 900 or higher, the returned axis value will be -127, whereas it will be 127 when the ADC reads 285 or lower. Zero is returned when 575 is read. +Axes can be configured using one of the following macros: -In this example, the first axis will be read from the `A4` pin while `B0` is set high and `A7` is set low, using `analogReadPin()`, whereas the second axis will not be read. + * `JOYSTICK_AXIS_IN(input_pin, low, rest, high)` + The ADC samples the provided pin. `low`, `high` and `rest` correspond to the minimum, maximum, and resting (or centered) analog values of the axis, respectively. + * `JOYSTICK_AXIS_IN_OUT(input_pin, output_pin, low, rest, high)` + Same as `JOYSTICK_AXIS_IN()`, but the provided `output_pin` will be pulled high before `input_pin` is read. + * `JOYSTICK_AXIS_IN_OUT_GROUND(input_pin, output_pin, ground_pin, low, rest, high)` + Same as `JOYSTICK_AXIS_IN_OUT()`, but the provided `ground_pin` will be pulled low before reading from `input_pin`. + * `JOYSTICK_AXIS_VIRTUAL` + No ADC reading is performed. The value should be provided by user code. -#### Virtual Axes +The `low` and `high` values can be swapped to effectively invert the axis. -To give a value to virtual axes, call `joystick_set_axis(axis, value)`. +#### Virtual Axes :id=virtual-axes -The following example adjusts two virtual axes (X and Y) based on keypad presses, with `KC_P5` as a precision modifier: +The following example adjusts two virtual axes (X and Y) based on keypad presses, with `KC_P0` as a precision modifier: ```c joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { - [0] = JOYSTICK_AXIS_VIRTUAL, // x - [1] = JOYSTICK_AXIS_VIRTUAL // y + JOYSTICK_AXIS_VIRTUAL, // x + JOYSTICK_AXIS_VIRTUAL // y }; static bool precision = false; @@ -105,7 +92,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { case KC_P6: joystick_set_axis(0, record->event.pressed ? precision_val : 0); return false; - case KC_P5: + case KC_P0: precision = record->event.pressed; return false; } @@ -113,13 +100,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { } ``` -### Axis Resolution - -By default, the resolution of each axis is 8 bit, giving a range of -127 to +127. If you need higher precision, you can increase it by defining eg. `JOYSTICK_AXES_RESOLUTION 12` in your `config.h`. The resolution must be between 8 and 16. - -Note that the supported AVR MCUs have a 10-bit ADC, and 12-bit for most STM32 MCUs. - -### Keycodes +## Keycodes :id=keycodes |Key |Aliases|Description| |-----------------------|-------|-----------| @@ -156,4 +137,92 @@ Note that the supported AVR MCUs have a 10-bit ADC, and 12-bit for most STM32 MC |`QK_JOYSTICK_BUTTON_30`|`JS_30`|Button 30 | |`QK_JOYSTICK_BUTTON_31`|`JS_31`|Button 31 | -You can also trigger joystick buttons in code with `register_joystick_button(button)` and `unregister_joystick_button(button)`, where `button` is the 0-based button index (0 = button 1). +## API :id=api + +### `struct joystick_t` :id=api-joystick-t + +Contains the state of the joystick. + +#### Members :id=api-joystick-t-members + + - `uint8_t buttons[]` + A bit-packed array containing the joystick button states. The size is calculated as `(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1`. + - `int16_t axes[]` + An array of analog values for each defined axis. + - `bool dirty` + Whether the current state needs to be sent to the host. + +--- + +### `struct joystick_config_t` :id=api-joystick-config-t + +Describes a single axis. + +#### Members :id=api-joystick-config-t-members + + - `pin_t output_pin` + A pin to set as output high when reading the analog value, or `JS_VIRTUAL_AXIS`. + - `pin_t input_pin` + The pin to read the analog value from, or `JS_VIRTUAL_AXIS`. + - `pin_t ground_pin` + A pin to set as output low when reading the analog value, or `JS_VIRTUAL_AXIS`. + - `uint16_t min_digit` + The minimum analog value. + - `uint16_t mid_digit` + The resting or midpoint analog value. + - `uint16_t max_digit` + The maximum analog value. + +--- + +### `void joystick_flush(void)` :id=api-joystick-flush + +Send the joystick report to the host, if it has been marked as dirty. + +--- + +### `void register_joystick_button(uint8_t button)` :id=api-register-joystick-button + +Set the state of a button, and flush the report. + +#### Arguments :id=api-register-joystick-button-arguments + + - `uint8_t button` + The index of the button to press, from 0 to 31. + +--- + +### `void register_joystick_button(uint8_t button)` :id=api-unregister-joystick-button + +Reset the state of a button, and flush the report. + +#### Arguments :id=api-unregister-joystick-button-arguments + + - `uint8_t button` + The index of the button to release, from 0 to 31. + +--- + +### `int16_t joystick_read_axis(uint8_t axis)` :id=api-joystick-read-axis + +Sample and process the analog value of the given axis. + +#### Arguments :id=api-joystick-read-axis-arguments + + - `uint8_t axis` + The axis to read. + +#### Return Value :id=api-joystick-read-axis-return + +A signed 16-bit integer, where 0 is the resting or mid point. + +### `void joystick_set_axis(uint8_t axis, int16_t value)` :id=api-joystick-set-axis + +Set the value of the given axis. + +#### Arguments :id=api-joystick-set-axis-arguments + + - `uint8_t axis` + The axis to set the value of. + - `int16_t value` + The value to set. diff --git a/keyboards/handwired/battleship_gamepad/battleship_gamepad.c b/keyboards/handwired/battleship_gamepad/battleship_gamepad.c index 2c9de15a9f..faec437f36 100644 --- a/keyboards/handwired/battleship_gamepad/battleship_gamepad.c +++ b/keyboards/handwired/battleship_gamepad/battleship_gamepad.c @@ -17,7 +17,7 @@ #include "battleship_gamepad.h" /* joystick config */ -joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { +joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { [0] = JOYSTICK_AXIS_IN(F5, 1023, 512, 0), [1] = JOYSTICK_AXIS_IN(F4, 0, 512, 1023) }; diff --git a/keyboards/handwired/battleship_gamepad/config.h b/keyboards/handwired/battleship_gamepad/config.h index 25389c0245..a1ebc585de 100644 --- a/keyboards/handwired/battleship_gamepad/config.h +++ b/keyboards/handwired/battleship_gamepad/config.h @@ -28,8 +28,8 @@ /* joystick configuration */ #define JOYSTICK_BUTTON_COUNT 25 -#define JOYSTICK_AXES_COUNT 2 -#define JOYSTICK_AXES_RESOLUTION 10 +#define JOYSTICK_AXIS_COUNT 2 +#define JOYSTICK_AXIS_RESOLUTION 10 /* COL2ROW or ROW2COL */ #define DIODE_DIRECTION COL2ROW diff --git a/keyboards/handwired/misterdeck/keymaps/default/config.h b/keyboards/handwired/misterdeck/keymaps/default/config.h index 92157a0938..f96ac1468b 100644 --- a/keyboards/handwired/misterdeck/keymaps/default/config.h +++ b/keyboards/handwired/misterdeck/keymaps/default/config.h @@ -17,5 +17,5 @@ #pragma once -#define JOYSTICK_AXES_COUNT 4 +#define JOYSTICK_AXIS_COUNT 4 #define JOYSTICK_BUTTON_COUNT 4 diff --git a/keyboards/handwired/misterdeck/keymaps/default/keymap.c b/keyboards/handwired/misterdeck/keymaps/default/keymap.c index 0992e06f87..2da6737e17 100644 --- a/keyboards/handwired/misterdeck/keymaps/default/keymap.c +++ b/keyboards/handwired/misterdeck/keymaps/default/keymap.c @@ -28,7 +28,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { ) }; -joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { +joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { [0] = JOYSTICK_AXIS_IN(F4, 0, 512, 1023), [1] = JOYSTICK_AXIS_IN(F5, 0, 512, 1023), [2] = JOYSTICK_AXIS_IN(F6, 0, 512, 1023), diff --git a/keyboards/handwired/misterdeck/keymaps/nobuttons/config.h b/keyboards/handwired/misterdeck/keymaps/nobuttons/config.h index f609bf2962..59fdfc3e7a 100644 --- a/keyboards/handwired/misterdeck/keymaps/nobuttons/config.h +++ b/keyboards/handwired/misterdeck/keymaps/nobuttons/config.h @@ -17,5 +17,5 @@ #pragma once -#define JOYSTICK_AXES_COUNT 4 +#define JOYSTICK_AXIS_COUNT 4 #define JOYSTICK_BUTTON_COUNT 0 diff --git a/keyboards/handwired/misterdeck/keymaps/nobuttons/keymap.c b/keyboards/handwired/misterdeck/keymaps/nobuttons/keymap.c index 5c69d2bc21..51a75fe204 100644 --- a/keyboards/handwired/misterdeck/keymaps/nobuttons/keymap.c +++ b/keyboards/handwired/misterdeck/keymaps/nobuttons/keymap.c @@ -28,7 +28,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { ), }; -joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { +joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { [0] = JOYSTICK_AXIS_IN(F4, 0, 512, 1023), [1] = JOYSTICK_AXIS_IN(F5, 0, 512, 1023), [2] = JOYSTICK_AXIS_IN(F6, 0, 512, 1023), diff --git a/keyboards/handwired/onekey/keymaps/joystick/config.h b/keyboards/handwired/onekey/keymaps/joystick/config.h index a3b5858ad2..8a4e461b27 100644 --- a/keyboards/handwired/onekey/keymaps/joystick/config.h +++ b/keyboards/handwired/onekey/keymaps/joystick/config.h @@ -1,4 +1,4 @@ #pragma once -#define JOYSTICK_AXES_COUNT 2 +#define JOYSTICK_AXIS_COUNT 2 #define JOYSTICK_BUTTON_COUNT 1 diff --git a/keyboards/handwired/onekey/keymaps/joystick/keymap.c b/keyboards/handwired/onekey/keymaps/joystick/keymap.c index 96115aa496..6463900b7b 100644 --- a/keyboards/handwired/onekey/keymaps/joystick/keymap.c +++ b/keyboards/handwired/onekey/keymaps/joystick/keymap.c @@ -14,7 +14,7 @@ void matrix_scan_user() { } // Joystick config -joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { +joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { [0] = JOYSTICK_AXIS_IN(ADC_PIN, 0, 512, 1023), [1] = JOYSTICK_AXIS_VIRTUAL }; diff --git a/keyboards/handwired/onekey/proton_c/halconf.h b/keyboards/handwired/onekey/proton_c/halconf.h new file mode 100644 index 0000000000..4c66e56bba --- /dev/null +++ b/keyboards/handwired/onekey/proton_c/halconf.h @@ -0,0 +1,26 @@ +/* Copyright 2020 QMK + * + * 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 . + */ + +/* + * This file was auto-generated by: + * `qmk chibios-confmigrate -i keyboards/handwired/onekey/blackpill_f401/halconf.h -r platforms/chibios/common/configs/halconf.h` + */ + +#pragma once + +#define HAL_USE_ADC TRUE + +#include_next diff --git a/keyboards/handwired/onekey/proton_c/mcuconf.h b/keyboards/handwired/onekey/proton_c/mcuconf.h new file mode 100644 index 0000000000..935056eb6a --- /dev/null +++ b/keyboards/handwired/onekey/proton_c/mcuconf.h @@ -0,0 +1,22 @@ +/* Copyright 2020 Nick Brassel (tzarc) + * + * 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 3 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 . + */ + +#pragma once + +#include_next "mcuconf.h" + +#undef STM32_ADC_USE_ADC1 +#define STM32_ADC_USE_ADC1 TRUE diff --git a/keyboards/lime/keymaps/default/keymap.c b/keyboards/lime/keymaps/default/keymap.c index 5760bd9359..9dd2894879 100644 --- a/keyboards/lime/keymaps/default/keymap.c +++ b/keyboards/lime/keymaps/default/keymap.c @@ -179,7 +179,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { } /* Joystick axes settings */ - joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { + joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { [0] = JOYSTICK_AXIS_IN(JOYSTICK_X_PIN, 268, 514, 813), [1] = JOYSTICK_AXIS_IN(JOYSTICK_Y_PIN, 865, 519, 260) }; diff --git a/keyboards/lime/rev1/config.h b/keyboards/lime/rev1/config.h index 942cb9c7b8..70f1cc419b 100644 --- a/keyboards/lime/rev1/config.h +++ b/keyboards/lime/rev1/config.h @@ -40,9 +40,9 @@ /* joystick support */ #ifdef JOYSTICK_ENABLE -# define JOYSTICK_AXES_COUNT 2 +# define JOYSTICK_AXIS_COUNT 2 # define JOYSTICK_BUTTON_COUNT 1 -# define JOYSTICK_AXES_RESOLUTION 8 +# define JOYSTICK_AXIS_RESOLUTION 8 #endif #define TAP_CODE_DELAY 10 diff --git a/keyboards/synthlabs/solo/config.h b/keyboards/synthlabs/solo/config.h index e8d9870e46..2352ba2972 100644 --- a/keyboards/synthlabs/solo/config.h +++ b/keyboards/synthlabs/solo/config.h @@ -54,5 +54,5 @@ #define BOOTMAGIC_LITE_COLUMN 1 #define JOYSTICK_BUTTON_COUNT 13 -#define JOYSTICK_AXES_COUNT 1 -#define JOYSTICK_AXES_RESOLUTION 16 +#define JOYSTICK_AXIS_COUNT 1 +#define JOYSTICK_AXIS_RESOLUTION 16 diff --git a/keyboards/synthlabs/solo/keymaps/gamepad/keymap.c b/keyboards/synthlabs/solo/keymaps/gamepad/keymap.c index e6f9678fbb..f450769b43 100644 --- a/keyboards/synthlabs/solo/keymaps/gamepad/keymap.c +++ b/keyboards/synthlabs/solo/keymaps/gamepad/keymap.c @@ -16,7 +16,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { int16_t joystick_position = 0; int16_t pulses_per_revolution = 24; // Depends on encoder model. Usually 18ppr or 24ppr for Bourns EC11s. int16_t full_joystick_value = 32767; // Equivalent to max value of int16. +full_joystick_value is +1.0 axis output. -full_joystick_value is -1.0 axis output. -joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { +joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { [0] = JOYSTICK_AXIS_VIRTUAL }; diff --git a/quantum/joystick.c b/quantum/joystick.c index d285dcdb5e..057a018dff 100644 --- a/quantum/joystick.c +++ b/quantum/joystick.c @@ -19,45 +19,48 @@ #include "analog.h" #include "wait.h" -// clang-format off -joystick_t joystick_status = { +joystick_t joystick_state = { .buttons = {0}, - .axes = { -#if JOYSTICK_AXES_COUNT > 0 - 0 + .axes = + { +#if JOYSTICK_AXIS_COUNT > 0 + 0 #endif - }, - .status = 0 + }, + .dirty = false, }; -// clang-format on // array defining the reading of analog values for each axis -__attribute__((weak)) joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {}; +__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_status.status & JS_UPDATED) > 0) { - host_joystick_send(&joystick_status); - joystick_status.status &= ~JS_UPDATED; + if (joystick_state.dirty) { + host_joystick_send(&joystick_state); + joystick_state.dirty = false; } } void register_joystick_button(uint8_t button) { - joystick_status.buttons[button / 8] |= 1 << (button % 8); - joystick_status.status |= JS_UPDATED; + 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) { - joystick_status.buttons[button / 8] &= ~(1 << (button % 8)); - joystick_status.status |= JS_UPDATED; + 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); @@ -93,24 +96,24 @@ int16_t joystick_read_axis(uint8_t 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_RESOLUTION) / (range - ref); + 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_RESOLUTION) / (range - ref); + ranged_val = ((axis_val - ref) * JOYSTICK_MAX_VALUE) / (range - ref); } // clamp the result in the valid range - ranged_val = ranged_val < -JOYSTICK_RESOLUTION ? -JOYSTICK_RESOLUTION : ranged_val; - ranged_val = ranged_val > JOYSTICK_RESOLUTION ? JOYSTICK_RESOLUTION : ranged_val; + 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() { -#if JOYSTICK_AXES_COUNT > 0 - for (int i = 0; i < JOYSTICK_AXES_COUNT; ++i) { +#if JOYSTICK_AXIS_COUNT > 0 + for (int i = 0; i < JOYSTICK_AXIS_COUNT; ++i) { if (joystick_axes[i].input_pin == JS_VIRTUAL_AXIS) { continue; } @@ -123,8 +126,10 @@ void joystick_read_axes() { } void joystick_set_axis(uint8_t axis, int16_t value) { - if (value != joystick_status.axes[axis]) { - joystick_status.axes[axis] = value; - joystick_status.status |= JS_UPDATED; + if (axis >= JOYSTICK_AXIS_COUNT) return; + + if (value != joystick_state.axes[axis]) { + joystick_state.axes[axis] = value; + joystick_state.dirty = true; } } diff --git a/quantum/joystick.h b/quantum/joystick.h index ee966fdb1a..0ac99aa590 100644 --- a/quantum/joystick.h +++ b/quantum/joystick.h @@ -16,32 +16,41 @@ #pragma once +#include #include + #include "gpio.h" +/** + * \defgroup joystick + * + * HID Joystick + * \{ + */ + #ifndef JOYSTICK_BUTTON_COUNT # define JOYSTICK_BUTTON_COUNT 8 #elif JOYSTICK_BUTTON_COUNT > 32 # error Joystick feature only supports up to 32 buttons #endif -#ifndef JOYSTICK_AXES_COUNT -# define JOYSTICK_AXES_COUNT 4 -#elif JOYSTICK_AXES_COUNT > 6 +#ifndef JOYSTICK_AXIS_COUNT +# define JOYSTICK_AXIS_COUNT 2 +#elif JOYSTICK_AXIS_COUNT > 6 # error Joystick feature only supports up to 6 axes #endif -#if JOYSTICK_AXES_COUNT == 0 && JOYSTICK_BUTTON_COUNT == 0 +#if JOYSTICK_AXIS_COUNT == 0 && JOYSTICK_BUTTON_COUNT == 0 # error Joystick feature requires at least one axis or button #endif -#ifndef JOYSTICK_AXES_RESOLUTION -# define JOYSTICK_AXES_RESOLUTION 8 -#elif JOYSTICK_AXES_RESOLUTION < 8 || JOYSTICK_AXES_RESOLUTION > 16 -# error JOYSTICK_AXES_RESOLUTION must be between 8 and 16 +#ifndef JOYSTICK_AXIS_RESOLUTION +# define JOYSTICK_AXIS_RESOLUTION 8 +#elif JOYSTICK_AXIS_RESOLUTION < 8 || JOYSTICK_AXIS_RESOLUTION > 16 +# error JOYSTICK_AXIS_RESOLUTION must be between 8 and 16 #endif -#define JOYSTICK_RESOLUTION ((1L << (JOYSTICK_AXES_RESOLUTION - 1)) - 1) +#define JOYSTICK_MAX_VALUE ((1L << (JOYSTICK_AXIS_RESOLUTION - 1)) - 1) // configure on input_pin of the joystick_axes array entry to JS_VIRTUAL_AXIS // to prevent it from being read from the ADC. This allows outputing forged axis value. @@ -68,30 +77,56 @@ typedef struct { uint16_t max_digit; } joystick_config_t; -extern joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT]; - -enum joystick_status { - JS_INITIALIZED = 1, - JS_UPDATED, -}; +extern joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT]; typedef struct { uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1]; - - int16_t axes[JOYSTICK_AXES_COUNT]; - uint8_t status : 2; + int16_t axes[JOYSTICK_AXIS_COUNT]; + bool dirty; } joystick_t; -extern joystick_t joystick_status; +extern joystick_t joystick_state; void joystick_task(void); + +/** + * \brief Send the joystick report to the host, if it has been marked as dirty. + */ void joystick_flush(void); +/** + * \brief Set the state of a button, and flush the report. + * + * \param button The index of the button to press, from 0 to 31. + */ void register_joystick_button(uint8_t button); + +/** + * \brief Reset the state of a button, and flush the report. + * + * \param button The index of the button to release, from 0 to 31. + */ void unregister_joystick_button(uint8_t button); +/** + * \brief Sample and process the analog value of the given axis. + * + * \param axis The axis to read. + * + * \return A signed 16-bit integer, where 0 is the resting or mid point. + */ int16_t joystick_read_axis(uint8_t axis); -void joystick_read_axes(void); -void joystick_set_axis(uint8_t axis, int16_t value); + +void joystick_read_axes(void); + +/** + * \brief Set the value of the given axis. + * + * \param axis The axis to set the value of. + * \param value The value to set. + */ +void joystick_set_axis(uint8_t axis, int16_t value); void host_joystick_send(joystick_t *joystick); + +/** \} */ diff --git a/tmk_core/protocol.mk b/tmk_core/protocol.mk index 19fd7d2425..d3f15c4588 100644 --- a/tmk_core/protocol.mk +++ b/tmk_core/protocol.mk @@ -68,6 +68,19 @@ ifeq ($(strip $(NO_USB_STARTUP_CHECK)), yes) TMK_COMMON_DEFS += -DNO_USB_STARTUP_CHECK endif +ifeq ($(strip $(JOYSTICK_SHARED_EP)), yes) + TMK_COMMON_DEFS += -DJOYSTICK_SHARED_EP + SHARED_EP_ENABLE = yes +endif + +ifeq ($(strip $(JOYSTICK_ENABLE)), yes) + TMK_COMMON_DEFS += -DJOYSTICK_ENABLE + ifeq ($(strip $(SHARED_EP_ENABLE)), yes) + TMK_COMMON_DEFS += -DJOYSTICK_SHARED_EP + SHARED_EP_ENABLE = yes + endif +endif + ifeq ($(strip $(DIGITIZER_SHARED_EP)), yes) TMK_COMMON_DEFS += -DDIGITIZER_SHARED_EP SHARED_EP_ENABLE = yes diff --git a/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c index 5d681a8b71..bf190b1f18 100644 --- a/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c +++ b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c @@ -637,20 +637,20 @@ static uint8_t udi_hid_raw_report_recv[UDI_HID_RAW_REPORT_SIZE]; COMPILER_WORD_ALIGNED UDC_DESC_STORAGE udi_hid_raw_report_desc_t udi_hid_raw_report_desc = {{ - 0x06, RAW_USAGE_PAGE_LO, RAW_USAGE_PAGE_HI, // Usage Page (Vendor Defined) - 0x09, RAW_USAGE_ID, // Usage (Vendor Defined) - 0xA1, 0x01, // Collection (Application) - 0x75, 0x08, // Report Size (8) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0xFF, // Logical Maximum (255) + 0x06, HID_VALUE_16(RAW_USAGE_PAGE), // Usage Page (Vendor Defined) + 0x09, RAW_USAGE_ID, // Usage (Vendor Defined) + 0xA1, 0x01, // Collection (Application) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0xFF, // Logical Maximum (255) // Data to host - 0x09, 0x62, // Usage (Vendor Defined) - 0x95, RAW_EPSIZE, // Report Count - 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x09, 0x62, // Usage (Vendor Defined) + 0x95, RAW_EPSIZE, // Report Count + 0x81, 0x02, // Input (Data, Variable, Absolute) // Data from host - 0x09, 0x63, // Usage (Vendor Defined) - 0x95, RAW_EPSIZE, // Report Count - 0x91, 0x02, // Output (Data, Variable, Absolute) + 0x09, 0x63, // Usage (Vendor Defined) + 0x95, RAW_EPSIZE, // Report Count + 0x91, 0x02, // Output (Data, Variable, Absolute) 0xC0 // End Collection }}; diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index fb3d575846..62a11faff7 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -183,7 +183,7 @@ static const USBEndpointConfig shared_ep_config = { }; #endif -#ifdef JOYSTICK_ENABLE +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) /* joystick endpoint state structure */ static USBInEndpointState joystick_ep_state; @@ -507,7 +507,7 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { #ifdef SHARED_EP_ENABLE usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config); #endif -#ifdef JOYSTICK_ENABLE +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) usbInitEndpointI(usbp, JOYSTICK_IN_EPNUM, &joystick_ep_config); #endif #if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) diff --git a/tmk_core/protocol/host.c b/tmk_core/protocol/host.c index 5fee872326..2c6654e9a6 100644 --- a/tmk_core/protocol/host.c +++ b/tmk_core/protocol/host.c @@ -164,24 +164,27 @@ void host_joystick_send(joystick_t *joystick) { if (!driver) return; report_joystick_t report = { -# if JOYSTICK_AXES_COUNT > 0 +# ifdef JOYSTICK_SHARED_EP + .report_id = REPORT_ID_JOYSTICK, +# endif +# if JOYSTICK_AXIS_COUNT > 0 .axes = { joystick->axes[0], -# if JOYSTICK_AXES_COUNT >= 2 +# if JOYSTICK_AXIS_COUNT >= 2 joystick->axes[1], # endif -# if JOYSTICK_AXES_COUNT >= 3 +# if JOYSTICK_AXIS_COUNT >= 3 joystick->axes[2], # endif -# if JOYSTICK_AXES_COUNT >= 4 +# if JOYSTICK_AXIS_COUNT >= 4 joystick->axes[3], # endif -# if JOYSTICK_AXES_COUNT >= 5 +# if JOYSTICK_AXIS_COUNT >= 5 joystick->axes[4], # endif -# if JOYSTICK_AXES_COUNT >= 6 +# if JOYSTICK_AXIS_COUNT >= 6 joystick->axes[5], # endif }, diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index af49ed5909..8f36e02b9a 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -404,7 +404,7 @@ void EVENT_USB_Device_ConfigurationChanged(void) { ConfigSuccess &= Endpoint_ConfigureEndpoint((CDC_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_BULK, CDC_EPSIZE, 1); #endif -#ifdef JOYSTICK_ENABLE +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) /* Setup joystick endpoint */ ConfigSuccess &= Endpoint_ConfigureEndpoint((JOYSTICK_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1); #endif diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h index 543c44e33c..e4526e4ee6 100644 --- a/tmk_core/protocol/report.h +++ b/tmk_core/protocol/report.h @@ -235,11 +235,14 @@ typedef struct { } __attribute__((packed)) report_digitizer_t; typedef struct { -#if JOYSTICK_AXES_COUNT > 0 -# if JOYSTICK_AXES_RESOLUTION > 8 - int16_t axes[JOYSTICK_AXES_COUNT]; +#ifdef JOYSTICK_SHARED_EP + uint8_t report_id; +#endif +#if JOYSTICK_AXIS_COUNT > 0 +# if JOYSTICK_AXIS_RESOLUTION > 8 + int16_t axes[JOYSTICK_AXIS_COUNT]; # else - int8_t axes[JOYSTICK_AXES_COUNT]; + int8_t axes[JOYSTICK_AXIS_COUNT]; # endif #endif diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index 3c170f1112..99c52952a0 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -174,6 +174,75 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { # endif #endif +#ifdef JOYSTICK_ENABLE +# ifndef JOYSTICK_SHARED_EP +const USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] = { +# elif !defined(SHARED_REPORT_STARTED) +const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { +# define SHARED_REPORT_STARTED +# endif + HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop + HID_RI_USAGE(8, 0x04), // Joystick + HID_RI_COLLECTION(8, 0x01), // Application +# ifdef JOYSTICK_SHARED_EP + HID_RI_REPORT_ID(8, REPORT_ID_JOYSTICK), +# endif + HID_RI_COLLECTION(8, 0x00), // Physical +# if JOYSTICK_AXIS_COUNT > 0 + HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop + HID_RI_USAGE(8, 0x30), // X +# if JOYSTICK_AXIS_COUNT > 1 + HID_RI_USAGE(8, 0x31), // Y +# endif +# if JOYSTICK_AXIS_COUNT > 2 + HID_RI_USAGE(8, 0x32), // Z +# endif +# if JOYSTICK_AXIS_COUNT > 3 + HID_RI_USAGE(8, 0x33), // Rx +# endif +# if JOYSTICK_AXIS_COUNT > 4 + HID_RI_USAGE(8, 0x34), // Ry +# endif +# if JOYSTICK_AXIS_COUNT > 5 + HID_RI_USAGE(8, 0x35), // Rz +# endif +# if JOYSTICK_AXIS_RESOLUTION == 8 + HID_RI_LOGICAL_MINIMUM(8, -JOYSTICK_MAX_VALUE), + HID_RI_LOGICAL_MAXIMUM(8, JOYSTICK_MAX_VALUE), + HID_RI_REPORT_COUNT(8, JOYSTICK_AXIS_COUNT), + HID_RI_REPORT_SIZE(8, 0x08), +# else + HID_RI_LOGICAL_MINIMUM(16, -JOYSTICK_MAX_VALUE), + HID_RI_LOGICAL_MAXIMUM(16, JOYSTICK_MAX_VALUE), + HID_RI_REPORT_COUNT(8, JOYSTICK_AXIS_COUNT), + HID_RI_REPORT_SIZE(8, 0x10), +# endif + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), +# endif + +# if JOYSTICK_BUTTON_COUNT > 0 + HID_RI_USAGE_PAGE(8, 0x09), // Button + HID_RI_USAGE_MINIMUM(8, 0x01), + HID_RI_USAGE_MAXIMUM(8, JOYSTICK_BUTTON_COUNT), + HID_RI_LOGICAL_MINIMUM(8, 0x00), + HID_RI_LOGICAL_MAXIMUM(8, 0x01), + HID_RI_REPORT_COUNT(8, JOYSTICK_BUTTON_COUNT), + HID_RI_REPORT_SIZE(8, 0x01), + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + +# if (JOYSTICK_BUTTON_COUNT % 8) != 0 + HID_RI_REPORT_COUNT(8, 8 - (JOYSTICK_BUTTON_COUNT % 8)), + HID_RI_REPORT_SIZE(8, 0x01), + HID_RI_INPUT(8, HID_IOF_CONSTANT), +# endif +# endif + HID_RI_END_COLLECTION(0), + HID_RI_END_COLLECTION(0), +# ifndef JOYSTICK_SHARED_EP +}; +# endif +#endif + #ifdef DIGITIZER_ENABLE # ifndef DIGITIZER_SHARED_EP const USB_Descriptor_HIDReport_Datatype_t PROGMEM DigitizerReport[] = { @@ -360,65 +429,6 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = { }; #endif -#ifdef JOYSTICK_ENABLE -const USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] = { - HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop - HID_RI_USAGE(8, 0x04), // Joystick - HID_RI_COLLECTION(8, 0x01), // Application - HID_RI_COLLECTION(8, 0x00), // Physical -# if JOYSTICK_AXES_COUNT > 0 - HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop - HID_RI_USAGE(8, 0x30), // X -# if JOYSTICK_AXES_COUNT > 1 - HID_RI_USAGE(8, 0x31), // Y -# endif -# if JOYSTICK_AXES_COUNT > 2 - HID_RI_USAGE(8, 0x32), // Z -# endif -# if JOYSTICK_AXES_COUNT > 3 - HID_RI_USAGE(8, 0x33), // Rx -# endif -# if JOYSTICK_AXES_COUNT > 4 - HID_RI_USAGE(8, 0x34), // Ry -# endif -# if JOYSTICK_AXES_COUNT > 5 - HID_RI_USAGE(8, 0x35), // Rz -# endif -# if JOYSTICK_AXES_RESOLUTION == 8 - HID_RI_LOGICAL_MINIMUM(8, -JOYSTICK_RESOLUTION), - HID_RI_LOGICAL_MAXIMUM(8, JOYSTICK_RESOLUTION), - HID_RI_REPORT_COUNT(8, JOYSTICK_AXES_COUNT), - HID_RI_REPORT_SIZE(8, 0x08), -# else - HID_RI_LOGICAL_MINIMUM(16, -JOYSTICK_RESOLUTION), - HID_RI_LOGICAL_MAXIMUM(16, JOYSTICK_RESOLUTION), - HID_RI_REPORT_COUNT(8, JOYSTICK_AXES_COUNT), - HID_RI_REPORT_SIZE(8, 0x10), -# endif - HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), -# endif - -# if JOYSTICK_BUTTON_COUNT > 0 - HID_RI_USAGE_PAGE(8, 0x09), // Button - HID_RI_USAGE_MINIMUM(8, 0x01), - HID_RI_USAGE_MAXIMUM(8, JOYSTICK_BUTTON_COUNT), - HID_RI_LOGICAL_MINIMUM(8, 0x00), - HID_RI_LOGICAL_MAXIMUM(8, 0x01), - HID_RI_REPORT_COUNT(8, JOYSTICK_BUTTON_COUNT), - HID_RI_REPORT_SIZE(8, 0x01), - HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - -# if (JOYSTICK_BUTTON_COUNT % 8) != 0 - HID_RI_REPORT_COUNT(8, 8 - (JOYSTICK_BUTTON_COUNT % 8)), - HID_RI_REPORT_SIZE(8, 0x01), - HID_RI_INPUT(8, HID_IOF_CONSTANT), -# endif -# endif - HID_RI_END_COLLECTION(0), - HID_RI_END_COLLECTION(0) -}; -#endif - /* * Device descriptor */ @@ -958,10 +968,10 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = { }, #endif +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) /* * Joystick */ -#ifdef JOYSTICK_ENABLE .Joystick_Interface = { .Header = { .Size = sizeof(USB_Descriptor_Interface_t), @@ -1169,7 +1179,7 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const break; #endif -#ifdef JOYSTICK_ENABLE +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) case JOYSTICK_INTERFACE: Address = &ConfigurationDescriptor.Joystick_HID; Size = sizeof(USB_HID_Descriptor_HID_t); @@ -1226,7 +1236,7 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const break; #endif -#ifdef JOYSTICK_ENABLE +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) case JOYSTICK_INTERFACE: Address = &JoystickReport; Size = sizeof(JoystickReport); diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 6e842f6984..bc5e84e586 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -132,7 +132,7 @@ typedef struct { USB_Descriptor_Endpoint_t CDC_DataInEndpoint; #endif -#ifdef JOYSTICK_ENABLE +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) // Joystick HID Interface USB_Descriptor_Interface_t Joystick_Interface; USB_HID_Descriptor_HID_t Joystick_HID; @@ -187,7 +187,7 @@ enum usb_interfaces { CDI_INTERFACE, #endif -#if defined(JOYSTICK_ENABLE) +#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) JOYSTICK_INTERFACE, #endif @@ -267,7 +267,11 @@ enum usb_endpoints { #endif #ifdef JOYSTICK_ENABLE +# if !defined(JOYSTICK_SHARED_EP) JOYSTICK_IN_EPNUM = NEXT_EPNUM, +# else +# define JOYSTICK_IN_EPNUM SHARED_IN_EPNUM +# endif #endif #ifdef DIGITIZER_ENABLE diff --git a/tmk_core/protocol/usb_descriptor_common.h b/tmk_core/protocol/usb_descriptor_common.h index ce0cf09763..909c230a99 100644 --- a/tmk_core/protocol/usb_descriptor_common.h +++ b/tmk_core/protocol/usb_descriptor_common.h @@ -20,6 +20,8 @@ #define USBCONCAT(a, b) a##b #define USBSTR(s) USBCONCAT(L, s) +#define HID_VALUE_16(v) ((uint8_t)(v & 0xFF)), ((uint8_t)(v >> 8)) + ///////////////////// // RAW Usage page and ID configuration @@ -30,6 +32,3 @@ #ifndef RAW_USAGE_ID # define RAW_USAGE_ID 0x61 #endif - -#define RAW_USAGE_PAGE_HI ((uint8_t)(RAW_USAGE_PAGE >> 8)) -#define RAW_USAGE_PAGE_LO ((uint8_t)(RAW_USAGE_PAGE & 0xFF)) diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index ac35043e2a..2d17761978 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -35,6 +35,10 @@ along with this program. If not, see . # include "raw_hid.h" #endif +#ifdef JOYSTICK_ENABLE +# include "joystick.h" +#endif + #if defined(CONSOLE_ENABLE) # define RBUF_SIZE 128 # include "ring_buffer.h" @@ -275,6 +279,14 @@ static void send_extra(report_extra_t *report) { #endif } +void send_joystick(report_joystick_t *report) { +#ifdef JOYSTICK_ENABLE + if (usbInterruptIsReadyShared()) { + usbSetInterruptShared((void *)report, sizeof(report_joystick_t)); + } +#endif +} + void send_digitizer(report_digitizer_t *report) { #ifdef DIGITIZER_ENABLE if (usbInterruptIsReadyShared()) { @@ -526,6 +538,65 @@ const PROGMEM uchar shared_hid_report[] = { 0xC0, // End Collection #endif +#ifdef JOYSTICK_ENABLE + // Joystick report descriptor + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + 0x85, REPORT_ID_JOYSTICK, // Report ID + 0xA1, 0x00, // Collection (Physical) +# if JOYSTICK_AXIS_COUNT > 0 + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) +# if JOYSTICK_AXIS_COUNT > 1 + 0x09, 0x31, // Usage (Y) +# endif +# if JOYSTICK_AXIS_COUNT > 2 + 0x09, 0x32, // Usage (Z) +# endif +# if JOYSTICK_AXIS_COUNT > 3 + 0x09, 0x33, // Usage (Rx) +# endif +# if JOYSTICK_AXIS_COUNT > 4 + 0x09, 0x34, // Usage (Ry) +# endif +# if JOYSTICK_AXIS_COUNT > 5 + 0x09, 0x35, // Usage (Rz) +# endif +# if JOYSTICK_AXIS_RESOLUTION == 8 + 0x15, -JOYSTICK_MAX_VALUE, // Logical Minimum + 0x25, JOYSTICK_MAX_VALUE, // Logical Maximum + 0x95, JOYSTICK_AXIS_COUNT, // Report Count + 0x75, 0x08, // Report Size (8) +# else + 0x16, HID_VALUE_16(-JOYSTICK_MAX_VALUE), // Logical Minimum + 0x26, HID_VALUE_16(JOYSTICK_MAX_VALUE), // Logical Maximum + 0x95, JOYSTICK_AXIS_COUNT, // Report Count + 0x75, 0x10, // Report Size (16) +# endif + 0x81, 0x02, // Input (Data, Variable, Absolute) +# endif + +# if JOYSTICK_BUTTON_COUNT > 0 + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (Button 1) + 0x29, JOYSTICK_BUTTON_COUNT, // Usage Maximum + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x95, JOYSTICK_BUTTON_COUNT, // Report Count + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Data, Variable, Absolute) + +# if (JOYSTICK_BUTTON_COUNT % 8) != 0 + 0x95, 8 - (JOYSTICK_BUTTON_COUNT % 8), // Report Count + 0x75, 0x01, // Report Size (1) + 0x81, 0x03, // Input (Constant) +# endif +# endif + 0xC0, // End Collection + 0xC0, // End Collection +#endif + #ifdef DIGITIZER_ENABLE // Digitizer report descriptor 0x05, 0x0D, // Usage Page (Digitizers) @@ -587,9 +658,9 @@ const PROGMEM uchar shared_hid_report[] = { #ifdef RAW_ENABLE const PROGMEM uchar raw_hid_report[] = { - 0x06, RAW_USAGE_PAGE_LO, RAW_USAGE_PAGE_HI, // Usage Page (Vendor Defined) - 0x09, RAW_USAGE_ID, // Usage (Vendor Defined) - 0xA1, 0x01, // Collection (Application) + 0x06, HID_VALUE_16(RAW_USAGE_PAGE), // Usage Page (Vendor Defined) + 0x09, RAW_USAGE_ID, // Usage (Vendor Defined) + 0xA1, 0x01, // Collection (Application) // Data to host 0x09, 0x62, // Usage (Vendor Defined) 0x15, 0x00, // Logical Minimum (0)