keychron_qmk_firmware/docs/feature_joystick.md

7.7 KiB

Joystick :id=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, 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 :id=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

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:

// 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

?> 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.

Axes :id=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 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.

The low and high values can be swapped to effectively invert the axis.

Virtual Axes :id=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 :id=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 :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 unregister_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.