aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan <[email protected]>2024-11-10 09:10:10 +1100
committerGitHub <[email protected]>2024-11-09 14:10:10 -0800
commita3cfb1dab7679a774d8aa09f7b609f3302dad73d (patch)
tree315e8c7a35ae390b56f84def0d37b867bfa428c7
parent69093f6de9b551bd84ff0a8eabcccca73026d55e (diff)
downloadqmk_firmware-a3cfb1dab7679a774d8aa09f7b609f3302dad73d.tar.gz
qmk_firmware-a3cfb1dab7679a774d8aa09f7b609f3302dad73d.zip
Joystick: add support for 8-way hat switch (#24515)
-rw-r--r--docs/features/joystick.md51
-rw-r--r--quantum/joystick.c10
-rw-r--r--quantum/joystick.h22
-rw-r--r--tmk_core/protocol/host.c4
-rw-r--r--tmk_core/protocol/report.h5
-rw-r--r--tmk_core/protocol/usb_descriptor.c17
-rw-r--r--tmk_core/protocol/vusb/vusb.c17
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 @@
# Joystick {#joystick}
-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.
+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.
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.
@@ -37,6 +37,42 @@ By default, two axes and eight buttons are defined, with a reported resolution o
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.
:::
+### Hat Switch {#hat-switch}
+
+To enable the 8-way hat switch, add the following to your `config.h`:
+
+```c
+#define JOYSTICK_HAS_HAT
+````
+
+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`:
+
+```
+ 0
+ 7 N 1
+ NW .--'--. NE
+ / \
+6 W | -1 | E 2
+ \ /
+ SW '--.--' SE
+ 5 S 3
+ 4
+```
+
+Alternatively you can use these predefined names:
+
+|Define |Value|Angle|
+|------------------------|-----|-----|
+|`JOYSTICK_HAT_CENTER` |`-1` | |
+|`JOYSTICK_HAT_NORTH` |`0` |0° |
+|`JOYSTICK_HAT_NORTHEAST`|`1` |45° |
+|`JOYSTICK_HAT_EAST` |`2` |90° |
+|`JOYSTICK_HAT_SOUTHEAST`|`3` |135° |
+|`JOYSTICK_HAT_SOUTH` |`4` |180° |
+|`JOYSTICK_HAT_SOUTHWEST`|`5` |225° |
+|`JOYSTICK_HAT_WEST` |`6` |270° |
+|`JOYSTICK_HAT_NORTHWEST`|`7` |315° |
+
### Axes {#axes}
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.
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.
+ - `int8_t hat`
+ The hat switch position.
- `bool dirty`
Whether the current state needs to be sent to the host.
@@ -222,3 +260,14 @@ Set the value of the given axis.
The axis to set the value of.
- `int16_t value`
The value to set.
+
+---
+
+### `void joystick_set_hat(int8_t value)` {#api-joystick-set-hat}
+
+Set the position of the hat switch.
+
+#### Arguments {#api-joystick-set-hat-arguments}
+
+ - `int8_t value`
+ 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 = {
0
#endif
},
+#ifdef JOYSTICK_HAS_HAT
+ .hat = -1,
+#endif
.dirty = false,
};
@@ -145,6 +148,13 @@ void joystick_set_axis(uint8_t axis, int16_t value) {
}
}
+#ifdef JOYSTICK_HAS_HAT
+void joystick_set_hat(int8_t value) {
+ joystick_state.hat = value;
+ joystick_state.dirty = true;
+}
+#endif
+
void joystick_init(void) {
joystick_init_axes();
}
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 @@
#define JOYSTICK_MAX_VALUE ((1L << (JOYSTICK_AXIS_RESOLUTION - 1)) - 1)
+#define JOYSTICK_HAT_CENTER -1
+#define JOYSTICK_HAT_NORTH 0
+#define JOYSTICK_HAT_NORTHEAST 1
+#define JOYSTICK_HAT_EAST 2
+#define JOYSTICK_HAT_SOUTHEAST 3
+#define JOYSTICK_HAT_SOUTH 4
+#define JOYSTICK_HAT_SOUTHWEST 5
+#define JOYSTICK_HAT_WEST 6
+#define JOYSTICK_HAT_NORTHWEST 7
+
// configure on input_pin of the joystick_axes array entry to NO_PIN
// to prevent it from being read from the ADC. This allows outputting forged axis value.
#define JOYSTICK_AXIS_VIRTUAL \
@@ -73,7 +83,10 @@ extern joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT];
typedef struct {
uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1];
int16_t axes[JOYSTICK_AXIS_COUNT];
- bool dirty;
+#ifdef JOYSTICK_HAS_HAT
+ int8_t hat;
+#endif
+ bool dirty;
} joystick_t;
extern joystick_t joystick_state;
@@ -129,4 +142,11 @@ void joystick_read_axes(void);
*/
void joystick_set_axis(uint8_t axis, int16_t value);
+/**
+ * \brief Set the position of the hat switch.
+ *
+ * \param value The hat switch position to set.
+ */
+void joystick_set_hat(int8_t value);
+
/** \} */
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) {
},
# endif
+# ifdef JOYSTICK_HAS_HAT
+ .hat = joystick->hat,
+# endif
+
# if JOYSTICK_BUTTON_COUNT > 0
.buttons =
{
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 {
joystick_axis_t axes[JOYSTICK_AXIS_COUNT];
#endif
+#ifdef JOYSTICK_HAS_HAT
+ int8_t hat : 4;
+ uint8_t reserved : 4;
+#endif
+
#if JOYSTICK_BUTTON_COUNT > 0
uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1];
#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[] = {
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
# endif
+# ifdef JOYSTICK_HAS_HAT
+ // Hat Switch (4 bits)
+ HID_RI_USAGE(8, 0x39), // Hat Switch
+ HID_RI_LOGICAL_MINIMUM(8, 0x00),
+ HID_RI_LOGICAL_MAXIMUM(8, 0x07),
+ HID_RI_PHYSICAL_MINIMUM(8, 0),
+ HID_RI_PHYSICAL_MAXIMUM(16, 315),
+ HID_RI_UNIT(8, 0x14), // Degree, English Rotation
+ HID_RI_REPORT_COUNT(8, 1),
+ HID_RI_REPORT_SIZE(8, 4),
+ HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NULLSTATE),
+ // Padding (4 bits)
+ HID_RI_REPORT_COUNT(8, 0x04),
+ HID_RI_REPORT_SIZE(8, 0x01),
+ HID_RI_INPUT(8, HID_IOF_CONSTANT),
+# endif
+
# if JOYSTICK_BUTTON_COUNT > 0
HID_RI_USAGE_PAGE(8, 0x09), // Button
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[] = {
0x81, 0x02, // Input (Data, Variable, Absolute)
# endif
+# ifdef JOYSTICK_HAS_HAT
+ // Hat Switch (4 bits)
+ 0x09, 0x39, // Usage (Hat Switch)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x07, // Logical Maximum (7)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x46, 0x3B, 0x01, // Physical Maximum (315)
+ 0x65, 0x14, // Unit (Degree, English Rotation)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x04, // Report Size (4)
+ 0x81, 0x42, // Input (Data, Variable, Absolute, Null State)
+ // Padding (4 bits)
+ 0x95, 0x04, // Report Count (4)
+ 0x75, 0x01, // Report Size (1)
+ 0x81, 0x01, // Input (Constant)
+# endif
+
# if JOYSTICK_BUTTON_COUNT > 0
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (Button 1)