add documentation for HID joystick Add joystick_task to read analog axes values even when no key is pressed or release. update doc Update docs/feature_joystick.md Manage pin setup and read to maintain matrix scan after analog readgc_switch
parent
d8f3c28a37
commit
3cf7611139
@ -0,0 +1,85 @@ |
||||
## Joystick HID Device |
||||
|
||||
The keyboard can be made to be recognized as a joystick HID device by the Operating System. |
||||
|
||||
This is enabled by adding the following to `rules.mk` |
||||
|
||||
``` |
||||
JOYSTICK_ENABLE = yes |
||||
``` |
||||
|
||||
### Configuring the joystick |
||||
|
||||
The default joystick has 2 axes and and 8 buttons. This can be changed from the config.h file : |
||||
|
||||
``` |
||||
//max 32 for JOYSTICK_BUTTON_COUNT |
||||
#define JOYSTICK_BUTTON_COUNT 16 |
||||
//max 6 for JOYSTICK_AXES_COUNT |
||||
#define JOYSTICK_AXES_COUNT 3 |
||||
``` |
||||
|
||||
When defining axes for your joystick, you have to provide a definition array for it. You can do this from your keymap.c file. |
||||
A joystick will either be read from an input pin that allows the use of an ADC, or can be virtual, so that its value is provided by your code. |
||||
You have to define an array of type ''joystick_config_t'' and of proper size, in the following way : |
||||
|
||||
``` |
||||
//joystick config |
||||
joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { |
||||
[0] = {A1, D7, 65280, 65472}, |
||||
[1] = {JS_VIRTUAL_AXIS, JS_VIRTUAL_AXIS, 65280, 65472} |
||||
}; |
||||
``` |
||||
|
||||
In this example, the first axis will be read from the D7 pin while A1 is set high, using an analogRead, whereas the second axis will not be read. |
||||
If you connected the your analog component to Vcc instead of an output pin, you can set the output setting to JS_VIRTUAL_AXIS. |
||||
In order to give a value to the second axis, you can do so in any customizable entry point of quantum : as an action, in process_record_user or in matrix_scan_user, or even in joystick_task(void) which is called even when no key has been pressed. |
||||
You assign a value by writing to joystick_status.axes[axis_index] a signed 8bit value (-127 to 127). Then it is necessary to assign the flag JS_UPDATED to joystick_status.status in order for the change to be taken into account. |
||||
|
||||
The following example writes two axes based on keypad presses, with KP_5 as a precision modifier : |
||||
|
||||
``` |
||||
#ifdef JOYSTICK_ENABLE |
||||
static bool precision_on; |
||||
#endif |
||||
|
||||
bool process_record_user(uint16_t keycode, keyrecord_t *record) { |
||||
switch(keycode){ |
||||
#ifdef JOYSTICK_ENABLE |
||||
// virtual joystick |
||||
case KC_P8: |
||||
joystick_status.axes[1] -= (record->event.pressed ? 1 : -1) * (precision_on ?70 : 127); |
||||
joystick_status.status |= JS_UPDATED; |
||||
break; |
||||
case KC_P2: |
||||
joystick_status.axes[1] += (record->event.pressed ? 1 : -1) * (precision_on ?70 : 127); |
||||
joystick_status.status |= JS_UPDATED; |
||||
break; |
||||
case KC_P4: |
||||
joystick_status.axes[0] -= (record->event.pressed ? 1 : -1) * (precision_on ?70 : 127); |
||||
joystick_status.status |= JS_UPDATED; |
||||
break; |
||||
case KC_P6: |
||||
joystick_status.axes[0] += (record->event.pressed ? 1 : -1) * (precision_on ?70 : 127); |
||||
joystick_status.status |= JS_UPDATED; |
||||
break; |
||||
case KC_P5: |
||||
precision_on = record->event.pressed; |
||||
joystick_status.axes[0] *= record->event.pressed ? 70/127.f : 127/70.f; |
||||
joystick_status.axes[1] *= record->event.pressed ? 70/127.f : 127/70.f; |
||||
joystick_status.status |= JS_UPDATED; |
||||
break; |
||||
#endif |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
``` |
||||
|
||||
### Triggering joystick buttons |
||||
|
||||
Joystick buttons are normal quantum keycode, defined as JS_BUTTON0 to JS_BUTTON_MAX, which depends on the number of buttons you have configured. |
||||
To trigger a joystick button, just add the corresponding keycode to your keymap. |
||||
|
||||
|
||||
|
@ -0,0 +1,11 @@ |
||||
#include "joystick.h" |
||||
|
||||
joystick_t joystick_status = {
|
||||
.buttons = {0}, |
||||
.axes = {0}, |
||||
.status = 0 |
||||
}; |
||||
|
||||
//array defining the reading of analog values for each axis
|
||||
__attribute__ ((weak)) |
||||
joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {}; |
@ -0,0 +1,48 @@ |
||||
#ifndef JOYSTICK_H |
||||
#define JOYSTICK_H |
||||
|
||||
#ifndef JOYSTICK_BUTTON_COUNT |
||||
#define JOYSTICK_BUTTON_COUNT 8 |
||||
#endif |
||||
|
||||
#ifndef JOYSTICK_AXES_COUNT |
||||
#define JOYSTICK_AXES_COUNT 4 |
||||
#endif |
||||
|
||||
#include <stdint.h> |
||||
|
||||
//configure on input_pin of the joystick_axes array entry to JS_VIRTUAL_AXIS
|
||||
// to prevent it from being read from the ADC. This allows outputing forged axis value.
|
||||
//
|
||||
#define JS_VIRTUAL_AXIS 0xFF |
||||
|
||||
typedef struct { |
||||
uint8_t output_pin; |
||||
uint8_t input_pin; |
||||
|
||||
//the AVR ADC offers 10 bit precision, with significant bits on the higher part
|
||||
uint16_t min_digit; |
||||
uint16_t max_digit; |
||||
} joystick_config_t; |
||||
|
||||
extern joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT]; |
||||
|
||||
enum joystick_status{ |
||||
JS_INITIALIZED = 1, |
||||
JS_UPDATED = 2 |
||||
}; |
||||
|
||||
typedef struct { |
||||
|
||||
uint8_t buttons[JOYSTICK_BUTTON_COUNT/8+1]; |
||||
|
||||
int8_t axes[JOYSTICK_AXES_COUNT]; |
||||
uint8_t status:2; |
||||
} joystick_t; |
||||
|
||||
extern joystick_t joystick_status; |
||||
|
||||
//to be implemented in the hid protocol library
|
||||
void send_joystick_packet(joystick_t* joystick); |
||||
|
||||
#endif //JOYSTICK_H
|
@ -0,0 +1,79 @@ |
||||
#include "process_joystick.h" |
||||
|
||||
#include <quantum/joystick.h> |
||||
#include <quantum/quantum_keycodes.h> |
||||
|
||||
#ifdef __AVR__ |
||||
# include <drivers/avr/analog.h> |
||||
#endif |
||||
|
||||
#include <string.h> |
||||
|
||||
bool process_joystick_buttons(uint16_t keycode, keyrecord_t *record); |
||||
|
||||
bool process_joystick(uint16_t keycode, keyrecord_t *record){ |
||||
|
||||
if (process_joystick_buttons(keycode, record)
|
||||
&& (joystick_status.status & JS_UPDATED)>0){ |
||||
send_joystick_packet(&joystick_status); |
||||
joystick_status.status &= ~JS_UPDATED; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
__attribute__ ((weak)) |
||||
void joystick_task(void){ |
||||
if (process_joystick_analog() && (joystick_status.status & JS_UPDATED)){ |
||||
send_joystick_packet(&joystick_status); |
||||
joystick_status.status &= ~JS_UPDATED; |
||||
} |
||||
} |
||||
|
||||
bool process_joystick_buttons(uint16_t keycode, keyrecord_t *record){ |
||||
|
||||
if (keycode < JS_BUTTON0 || keycode > JS_BUTTON_MAX){ |
||||
return true; |
||||
} else { |
||||
if (record->event.pressed){ |
||||
joystick_status.buttons[(keycode-JS_BUTTON0)/8] |= 1<<(keycode%8); |
||||
} else { |
||||
joystick_status.buttons[(keycode-JS_BUTTON0)/8] &= ~(1<<(keycode%8)); |
||||
} |
||||
|
||||
joystick_status.status |= JS_UPDATED; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
__attribute__ ((weak)) |
||||
bool process_joystick_analog(){ |
||||
#if JOYSTICK_AXES_COUNT > 0 |
||||
for (int axis_index=0 ; axis_index<JOYSTICK_AXES_COUNT ; ++axis_index){ |
||||
if (joystick_axes[axis_index].output_pin==JS_VIRTUAL_AXIS || joystick_axes[axis_index].input_pin==JS_VIRTUAL_AXIS){ |
||||
continue; |
||||
} |
||||
|
||||
setPinOutput(joystick_axes[axis_index].output_pin); |
||||
writePinHigh(joystick_axes[axis_index].output_pin); |
||||
|
||||
//disable pull-up resistance
|
||||
setPinInput(joystick_axes[axis_index].input_pin); |
||||
writePinLow(joystick_axes[axis_index].input_pin); |
||||
|
||||
#ifdef __AVR__ |
||||
int16_t axis_val = analogReadPin(joystick_axes[axis_index].input_pin); |
||||
#else |
||||
int16_t axis_val = 0; |
||||
#endif |
||||
if (axis_val!=joystick_status.axes[axis_index]){ |
||||
joystick_status.axes[axis_index] = axis_val; |
||||
joystick_status.status |= JS_UPDATED; |
||||
} |
||||
|
||||
writePinLow(joystick_axes[axis_index].output_pin); |
||||
} |
||||
#endif |
||||
return true; |
||||
} |
@ -0,0 +1,13 @@ |
||||
#ifndef PROCESS_JOYSTICK_H |
||||
#define PROCESS_JOYSTICK_H |
||||
|
||||
#include <stdint.h> |
||||
#include "quantum.h" |
||||
|
||||
bool process_joystick(uint16_t keycode, keyrecord_t *record); |
||||
|
||||
void joystick_task(void); |
||||
|
||||
bool process_joystick_analog(void); |
||||
|
||||
#endif //PROCESS_JOYSTICK_H
|
Loading…
Reference in new issue