Joystick: add support for 8-way hat switch (#24515)
This commit is contained in:
		
							parent
							
								
									69093f6de9
								
							
						
					
					
						commit
						a3cfb1dab7
					
				
					 7 changed files with 124 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
/** \} */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 =
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue