diff options
| author | Ryan <fauxpark@gmail.com> | 2024-11-10 09:10:10 +1100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-09 14:10:10 -0800 |
| commit | a3cfb1dab7679a774d8aa09f7b609f3302dad73d (patch) | |
| tree | 315e8c7a35ae390b56f84def0d37b867bfa428c7 | |
| parent | 69093f6de9b551bd84ff0a8eabcccca73026d55e (diff) | |
Joystick: add support for 8-way hat switch (#24515)
| -rw-r--r-- | docs/features/joystick.md | 51 | ||||
| -rw-r--r-- | quantum/joystick.c | 10 | ||||
| -rw-r--r-- | quantum/joystick.h | 22 | ||||
| -rw-r--r-- | tmk_core/protocol/host.c | 4 | ||||
| -rw-r--r-- | tmk_core/protocol/report.h | 5 | ||||
| -rw-r--r-- | tmk_core/protocol/usb_descriptor.c | 17 | ||||
| -rw-r--r-- | tmk_core/protocol/vusb/vusb.c | 17 |
7 files changed, 124 insertions, 2 deletions
diff --git a/docs/features/joystick.md b/docs/features/joystick.md index 69db4655d5..cbf6c6daf7 100644 --- a/docs/features/joystick.md +++ b/docs/features/joystick.md | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | # Joystick {#joystick} | 1 | # Joystick {#joystick} |
| 2 | 2 | ||
| 3 | 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](../drivers/adc), or can be virtual, so that its value is provided by your code. | 3 | This feature provides game controller input as a joystick device supporting up to 6 axes, 32 buttons and a hat switch. Axes can be read either from an [ADC-capable input pin](../drivers/adc), or can be virtual, so that its value is provided by your code. |
| 4 | 4 | ||
| 5 | 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. | 5 | 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. |
| 6 | 6 | ||
| @@ -37,6 +37,42 @@ By default, two axes and eight buttons are defined, with a reported resolution o | |||
| 37 | 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. | 37 | 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. |
| 38 | ::: | 38 | ::: |
| 39 | 39 | ||
| 40 | ### Hat Switch {#hat-switch} | ||
| 41 | |||
| 42 | To enable the 8-way hat switch, add the following to your `config.h`: | ||
| 43 | |||
| 44 | ```c | ||
| 45 | #define JOYSTICK_HAS_HAT | ||
| 46 | ```` | ||
| 47 | |||
| 48 | The position can be set by calling `joystick_set_hat(value)`. The range of values moves clockwise from the top (ie. north), with the default "center" position represented by a value of `-1`: | ||
| 49 | |||
| 50 | ``` | ||
| 51 | 0 | ||
| 52 | 7 N 1 | ||
| 53 | NW .--'--. NE | ||
| 54 | / \ | ||
| 55 | 6 W | -1 | E 2 | ||
| 56 | \ / | ||
| 57 | SW '--.--' SE | ||
| 58 | 5 S 3 | ||
| 59 | 4 | ||
| 60 | ``` | ||
| 61 | |||
| 62 | Alternatively you can use these predefined names: | ||
| 63 | |||
| 64 | |Define |Value|Angle| | ||
| 65 | |------------------------|-----|-----| | ||
| 66 | |`JOYSTICK_HAT_CENTER` |`-1` | | | ||
| 67 | |`JOYSTICK_HAT_NORTH` |`0` |0° | | ||
| 68 | |`JOYSTICK_HAT_NORTHEAST`|`1` |45° | | ||
| 69 | |`JOYSTICK_HAT_EAST` |`2` |90° | | ||
| 70 | |`JOYSTICK_HAT_SOUTHEAST`|`3` |135° | | ||
| 71 | |`JOYSTICK_HAT_SOUTH` |`4` |180° | | ||
| 72 | |`JOYSTICK_HAT_SOUTHWEST`|`5` |225° | | ||
| 73 | |`JOYSTICK_HAT_WEST` |`6` |270° | | ||
| 74 | |`JOYSTICK_HAT_NORTHWEST`|`7` |315° | | ||
| 75 | |||
| 40 | ### Axes {#axes} | 76 | ### Axes {#axes} |
| 41 | 77 | ||
| 42 | When defining axes for your joystick, you must provide a definition array typically in your `keymap.c`. | 78 | When defining axes for your joystick, you must provide a definition array typically in your `keymap.c`. |
| @@ -149,6 +185,8 @@ Contains the state of the joystick. | |||
| 149 | A bit-packed array containing the joystick button states. The size is calculated as `(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1`. | 185 | A bit-packed array containing the joystick button states. The size is calculated as `(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1`. |
| 150 | - `int16_t axes[]` | 186 | - `int16_t axes[]` |
| 151 | An array of analog values for each defined axis. | 187 | An array of analog values for each defined axis. |
| 188 | - `int8_t hat` | ||
| 189 | The hat switch position. | ||
| 152 | - `bool dirty` | 190 | - `bool dirty` |
| 153 | Whether the current state needs to be sent to the host. | 191 | Whether the current state needs to be sent to the host. |
| 154 | 192 | ||
| @@ -222,3 +260,14 @@ Set the value of the given axis. | |||
| 222 | The axis to set the value of. | 260 | The axis to set the value of. |
| 223 | - `int16_t value` | 261 | - `int16_t value` |
| 224 | The value to set. | 262 | The value to set. |
| 263 | |||
| 264 | --- | ||
| 265 | |||
| 266 | ### `void joystick_set_hat(int8_t value)` {#api-joystick-set-hat} | ||
| 267 | |||
| 268 | Set the position of the hat switch. | ||
| 269 | |||
| 270 | #### Arguments {#api-joystick-set-hat-arguments} | ||
| 271 | |||
| 272 | - `int8_t value` | ||
| 273 | The hat switch position to set. | ||
diff --git a/quantum/joystick.c b/quantum/joystick.c index 32f19b2cd9..62893fd199 100644 --- a/quantum/joystick.c +++ b/quantum/joystick.c | |||
| @@ -29,6 +29,9 @@ joystick_t joystick_state = { | |||
| 29 | 0 | 29 | 0 |
| 30 | #endif | 30 | #endif |
| 31 | }, | 31 | }, |
| 32 | #ifdef JOYSTICK_HAS_HAT | ||
| 33 | .hat = -1, | ||
| 34 | #endif | ||
| 32 | .dirty = false, | 35 | .dirty = false, |
| 33 | }; | 36 | }; |
| 34 | 37 | ||
| @@ -145,6 +148,13 @@ void joystick_set_axis(uint8_t axis, int16_t value) { | |||
| 145 | } | 148 | } |
| 146 | } | 149 | } |
| 147 | 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 | |||
| 148 | void joystick_init(void) { | 158 | void joystick_init(void) { |
| 149 | joystick_init_axes(); | 159 | joystick_init_axes(); |
| 150 | } | 160 | } |
diff --git a/quantum/joystick.h b/quantum/joystick.h index 5a69ceac64..24f80c1ad6 100644 --- a/quantum/joystick.h +++ b/quantum/joystick.h | |||
| @@ -52,6 +52,16 @@ | |||
| 52 | 52 | ||
| 53 | #define JOYSTICK_MAX_VALUE ((1L << (JOYSTICK_AXIS_RESOLUTION - 1)) - 1) | 53 | #define JOYSTICK_MAX_VALUE ((1L << (JOYSTICK_AXIS_RESOLUTION - 1)) - 1) |
| 54 | 54 | ||
| 55 | #define JOYSTICK_HAT_CENTER -1 | ||
| 56 | #define JOYSTICK_HAT_NORTH 0 | ||
| 57 | #define JOYSTICK_HAT_NORTHEAST 1 | ||
| 58 | #define JOYSTICK_HAT_EAST 2 | ||
| 59 | #define JOYSTICK_HAT_SOUTHEAST 3 | ||
| 60 | #define JOYSTICK_HAT_SOUTH 4 | ||
| 61 | #define JOYSTICK_HAT_SOUTHWEST 5 | ||
| 62 | #define JOYSTICK_HAT_WEST 6 | ||
| 63 | #define JOYSTICK_HAT_NORTHWEST 7 | ||
| 64 | |||
| 55 | // configure on input_pin of the joystick_axes array entry to NO_PIN | 65 | // configure on input_pin of the joystick_axes array entry to NO_PIN |
| 56 | // to prevent it from being read from the ADC. This allows outputting forged axis value. | 66 | // to prevent it from being read from the ADC. This allows outputting forged axis value. |
| 57 | #define JOYSTICK_AXIS_VIRTUAL \ | 67 | #define JOYSTICK_AXIS_VIRTUAL \ |
| @@ -73,7 +83,10 @@ extern joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT]; | |||
| 73 | typedef struct { | 83 | typedef struct { |
| 74 | uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1]; | 84 | uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1]; |
| 75 | int16_t axes[JOYSTICK_AXIS_COUNT]; | 85 | int16_t axes[JOYSTICK_AXIS_COUNT]; |
| 76 | bool dirty; | 86 | #ifdef JOYSTICK_HAS_HAT |
| 87 | int8_t hat; | ||
| 88 | #endif | ||
| 89 | bool dirty; | ||
| 77 | } joystick_t; | 90 | } joystick_t; |
| 78 | 91 | ||
| 79 | extern joystick_t joystick_state; | 92 | extern joystick_t joystick_state; |
| @@ -129,4 +142,11 @@ void joystick_read_axes(void); | |||
| 129 | */ | 142 | */ |
| 130 | void joystick_set_axis(uint8_t axis, int16_t value); | 143 | void joystick_set_axis(uint8_t axis, int16_t value); |
| 131 | 144 | ||
| 145 | /** | ||
| 146 | * \brief Set the position of the hat switch. | ||
| 147 | * | ||
| 148 | * \param value The hat switch position to set. | ||
| 149 | */ | ||
| 150 | void joystick_set_hat(int8_t value); | ||
| 151 | |||
| 132 | /** \} */ | 152 | /** \} */ |
diff --git a/tmk_core/protocol/host.c b/tmk_core/protocol/host.c index 732fbdc37d..df805c827c 100644 --- a/tmk_core/protocol/host.c +++ b/tmk_core/protocol/host.c | |||
| @@ -193,6 +193,10 @@ void host_joystick_send(joystick_t *joystick) { | |||
| 193 | }, | 193 | }, |
| 194 | # endif | 194 | # endif |
| 195 | 195 | ||
| 196 | # ifdef JOYSTICK_HAS_HAT | ||
| 197 | .hat = joystick->hat, | ||
| 198 | # endif | ||
| 199 | |||
| 196 | # if JOYSTICK_BUTTON_COUNT > 0 | 200 | # if JOYSTICK_BUTTON_COUNT > 0 |
| 197 | .buttons = | 201 | .buttons = |
| 198 | { | 202 | { |
diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h index 37c8ea48f1..d854f51d5c 100644 --- a/tmk_core/protocol/report.h +++ b/tmk_core/protocol/report.h | |||
| @@ -246,6 +246,11 @@ typedef struct { | |||
| 246 | joystick_axis_t axes[JOYSTICK_AXIS_COUNT]; | 246 | joystick_axis_t axes[JOYSTICK_AXIS_COUNT]; |
| 247 | #endif | 247 | #endif |
| 248 | 248 | ||
| 249 | #ifdef JOYSTICK_HAS_HAT | ||
| 250 | int8_t hat : 4; | ||
| 251 | uint8_t reserved : 4; | ||
| 252 | #endif | ||
| 253 | |||
| 249 | #if JOYSTICK_BUTTON_COUNT > 0 | 254 | #if JOYSTICK_BUTTON_COUNT > 0 |
| 250 | uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1]; | 255 | uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1]; |
| 251 | #endif | 256 | #endif |
diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index e7d12a07d9..c7fb660b65 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c | |||
| @@ -247,6 +247,23 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { | |||
| 247 | HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), | 247 | HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), |
| 248 | # endif | 248 | # endif |
| 249 | 249 | ||
| 250 | # ifdef JOYSTICK_HAS_HAT | ||
| 251 | // Hat Switch (4 bits) | ||
| 252 | HID_RI_USAGE(8, 0x39), // Hat Switch | ||
| 253 | HID_RI_LOGICAL_MINIMUM(8, 0x00), | ||
| 254 | HID_RI_LOGICAL_MAXIMUM(8, 0x07), | ||
| 255 | HID_RI_PHYSICAL_MINIMUM(8, 0), | ||
| 256 | HID_RI_PHYSICAL_MAXIMUM(16, 315), | ||
| 257 | HID_RI_UNIT(8, 0x14), // Degree, English Rotation | ||
| 258 | HID_RI_REPORT_COUNT(8, 1), | ||
| 259 | HID_RI_REPORT_SIZE(8, 4), | ||
| 260 | HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NULLSTATE), | ||
| 261 | // Padding (4 bits) | ||
| 262 | HID_RI_REPORT_COUNT(8, 0x04), | ||
| 263 | HID_RI_REPORT_SIZE(8, 0x01), | ||
| 264 | HID_RI_INPUT(8, HID_IOF_CONSTANT), | ||
| 265 | # endif | ||
| 266 | |||
| 250 | # if JOYSTICK_BUTTON_COUNT > 0 | 267 | # if JOYSTICK_BUTTON_COUNT > 0 |
| 251 | HID_RI_USAGE_PAGE(8, 0x09), // Button | 268 | HID_RI_USAGE_PAGE(8, 0x09), // Button |
| 252 | HID_RI_USAGE_MINIMUM(8, 0x01), | 269 | HID_RI_USAGE_MINIMUM(8, 0x01), |
diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index 2a29fe65d9..fdbfcc17dc 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c | |||
| @@ -621,6 +621,23 @@ const PROGMEM uchar shared_hid_report[] = { | |||
| 621 | 0x81, 0x02, // Input (Data, Variable, Absolute) | 621 | 0x81, 0x02, // Input (Data, Variable, Absolute) |
| 622 | # endif | 622 | # endif |
| 623 | 623 | ||
| 624 | # ifdef JOYSTICK_HAS_HAT | ||
| 625 | // Hat Switch (4 bits) | ||
| 626 | 0x09, 0x39, // Usage (Hat Switch) | ||
| 627 | 0x15, 0x00, // Logical Minimum (0) | ||
| 628 | 0x25, 0x07, // Logical Maximum (7) | ||
| 629 | 0x35, 0x00, // Physical Minimum (0) | ||
| 630 | 0x46, 0x3B, 0x01, // Physical Maximum (315) | ||
| 631 | 0x65, 0x14, // Unit (Degree, English Rotation) | ||
| 632 | 0x95, 0x01, // Report Count (1) | ||
| 633 | 0x75, 0x04, // Report Size (4) | ||
| 634 | 0x81, 0x42, // Input (Data, Variable, Absolute, Null State) | ||
| 635 | // Padding (4 bits) | ||
| 636 | 0x95, 0x04, // Report Count (4) | ||
| 637 | 0x75, 0x01, // Report Size (1) | ||
| 638 | 0x81, 0x01, // Input (Constant) | ||
| 639 | # endif | ||
| 640 | |||
| 624 | # if JOYSTICK_BUTTON_COUNT > 0 | 641 | # if JOYSTICK_BUTTON_COUNT > 0 |
| 625 | 0x05, 0x09, // Usage Page (Button) | 642 | 0x05, 0x09, // Usage Page (Button) |
| 626 | 0x19, 0x01, // Usage Minimum (Button 1) | 643 | 0x19, 0x01, // Usage Minimum (Button 1) |
