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