Joystick
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, or can be virtual, so that its value is provided by your code.
An analog device such as a 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.
Usage
Add the following to your rules.mk
:
JOYSTICK_ENABLE = yes
By default the joystick driver is analog
, but you can change this with:
JOYSTICK_DRIVER = digital
When using analog
with ARM, you must use 3.3v with your Joystick. Although ARM boards such as the Helios have 5v pin output, the ADC driver does not support it.
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
:
// Min 0, max 32
#define JOYSTICK_BUTTON_COUNT 16
// Min 0, max 6: X, Y, Z, Rx, Ry, Rz
#define JOYSTICK_AXIS_COUNT 3
// Min 8, max 16
#define JOYSTICK_AXIS_RESOLUTION 10
TIP
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
To enable the 8-way hat switch, add the following to your config.h
:
#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
When defining axes for your joystick, you must provide a definition array typically in your keymap.c
.
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.
joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {
JOYSTICK_AXIS_IN(A4, 900, 575, 285),
JOYSTICK_AXIS_VIRTUAL
};
Axes can be configured using one of the following macros:
JOYSTICK_AXIS_IN(input_pin, low, rest, high)
The ADC samples the provided pin.low
,high
andrest
correspond to the minimum, maximum, and resting (or centered) analog values of the axis, respectively.JOYSTICK_AXIS_VIRTUAL
No ADC reading is performed. The value should be provided by user code.
The low
and high
values can be swapped to effectively invert the axis.
Virtual Axes
The following example adjusts two virtual axes (X and Y) based on keypad presses, with KC_P0
as a precision modifier:
joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {
JOYSTICK_AXIS_VIRTUAL, // x
JOYSTICK_AXIS_VIRTUAL // y
};
static bool precision = false;
static uint16_t precision_mod = 64;
static uint16_t axis_val = 127;
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
int16_t precision_val = axis_val;
if (precision) {
precision_val -= precision_mod;
}
switch (keycode) {
case KC_P8:
joystick_set_axis(1, record->event.pressed ? -precision_val : 0);
return false;
case KC_P2:
joystick_set_axis(1, record->event.pressed ? precision_val : 0);
return false;
case KC_P4:
joystick_set_axis(0, record->event.pressed ? -precision_val : 0);
return false;
case KC_P6:
joystick_set_axis(0, record->event.pressed ? precision_val : 0);
return false;
case KC_P0:
precision = record->event.pressed;
return false;
}
return true;
}
Keycodes
Key | Aliases | Description |
---|---|---|
QK_JOYSTICK_BUTTON_0 | JS_0 | Button 0 |
QK_JOYSTICK_BUTTON_1 | JS_1 | Button 1 |
QK_JOYSTICK_BUTTON_2 | JS_2 | Button 2 |
QK_JOYSTICK_BUTTON_3 | JS_3 | Button 3 |
QK_JOYSTICK_BUTTON_4 | JS_4 | Button 4 |
QK_JOYSTICK_BUTTON_5 | JS_5 | Button 5 |
QK_JOYSTICK_BUTTON_6 | JS_6 | Button 6 |
QK_JOYSTICK_BUTTON_7 | JS_7 | Button 7 |
QK_JOYSTICK_BUTTON_8 | JS_8 | Button 8 |
QK_JOYSTICK_BUTTON_9 | JS_9 | Button 9 |
QK_JOYSTICK_BUTTON_10 | JS_10 | Button 10 |
QK_JOYSTICK_BUTTON_11 | JS_11 | Button 11 |
QK_JOYSTICK_BUTTON_12 | JS_12 | Button 12 |
QK_JOYSTICK_BUTTON_13 | JS_13 | Button 13 |
QK_JOYSTICK_BUTTON_14 | JS_14 | Button 14 |
QK_JOYSTICK_BUTTON_15 | JS_15 | Button 15 |
QK_JOYSTICK_BUTTON_16 | JS_16 | Button 16 |
QK_JOYSTICK_BUTTON_17 | JS_17 | Button 17 |
QK_JOYSTICK_BUTTON_18 | JS_18 | Button 18 |
QK_JOYSTICK_BUTTON_19 | JS_19 | Button 19 |
QK_JOYSTICK_BUTTON_20 | JS_20 | Button 20 |
QK_JOYSTICK_BUTTON_21 | JS_21 | Button 21 |
QK_JOYSTICK_BUTTON_22 | JS_22 | Button 22 |
QK_JOYSTICK_BUTTON_23 | JS_23 | Button 23 |
QK_JOYSTICK_BUTTON_24 | JS_24 | Button 24 |
QK_JOYSTICK_BUTTON_25 | JS_25 | Button 25 |
QK_JOYSTICK_BUTTON_26 | JS_26 | Button 26 |
QK_JOYSTICK_BUTTON_27 | JS_27 | Button 27 |
QK_JOYSTICK_BUTTON_28 | JS_28 | Button 28 |
QK_JOYSTICK_BUTTON_29 | JS_29 | Button 29 |
QK_JOYSTICK_BUTTON_30 | JS_30 | Button 30 |
QK_JOYSTICK_BUTTON_31 | JS_31 | Button 31 |
API
struct joystick_t
Contains the state of the joystick.
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.int8_t hat
The hat switch position.bool dirty
Whether the current state needs to be sent to the host.
struct joystick_config_t
Describes a single axis.
Members
pin_t input_pin
The pin to read the analog value from, orJS_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)
Send the joystick report to the host, if it has been marked as dirty.
void register_joystick_button(uint8_t button)
Set the state of a button, and flush the report.
Arguments
uint8_t button
The index of the button to press, from 0 to 31.
void unregister_joystick_button(uint8_t button)
Reset the state of a button, and flush the report.
Arguments
uint8_t button
The index of the button to release, from 0 to 31.
int16_t joystick_read_axis(uint8_t axis)
Sample and process the analog value of the given axis.
Arguments
uint8_t axis
The axis to read.
Return Value
A signed 16-bit integer, where 0 is the resting or mid point.
void joystick_set_axis(uint8_t axis, int16_t value)
Set the value of the given axis.
Arguments
uint8_t axis
The axis to set the value of.int16_t value
The value to set.
void joystick_set_hat(int8_t value)
Set the position of the hat switch.
Arguments
int8_t value
The hat switch position to set.