Merge branch 'hid_joystick' of git://github.com/a-chol/qmk_firmware into a-chol-hid_joystick
This commit is contained in:
commit
708bb4f55d
23 changed files with 1556 additions and 5 deletions
13
quantum/joystick.c
Normal file
13
quantum/joystick.c
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#include "joystick.h"
|
||||
|
||||
joystick_t joystick_status = {.buttons = {0},
|
||||
.axes =
|
||||
{
|
||||
#if JOYSTICK_AXES_COUNT > 0
|
||||
0
|
||||
#endif
|
||||
},
|
||||
.status = 0};
|
||||
|
||||
// array defining the reading of analog values for each axis
|
||||
__attribute__((weak)) joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {};
|
||||
54
quantum/joystick.h
Normal file
54
quantum/joystick.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef JOYSTICK_BUTTON_COUNT
|
||||
# define JOYSTICK_BUTTON_COUNT 8
|
||||
#endif
|
||||
|
||||
#ifndef JOYSTICK_AXES_COUNT
|
||||
# define JOYSTICK_AXES_COUNT 4
|
||||
#endif
|
||||
|
||||
#include "quantum.h"
|
||||
|
||||
#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
|
||||
|
||||
#define JOYSTICK_AXIS_VIRTUAL \
|
||||
{ JS_VIRTUAL_AXIS, JS_VIRTUAL_AXIS, JS_VIRTUAL_AXIS, 0, 1023 }
|
||||
#define JOYSTICK_AXIS_IN(INPUT_PIN, LOW, REST, HIGH) \
|
||||
{ JS_VIRTUAL_AXIS, INPUT_PIN, JS_VIRTUAL_AXIS, LOW, REST, HIGH }
|
||||
#define JOYSTICK_AXIS_IN_OUT(INPUT_PIN, OUTPUT_PIN, LOW, REST, HIGH) \
|
||||
{ OUTPUT_PIN, INPUT_PIN, JS_VIRTUAL_AXIS, LOW, REST, HIGH }
|
||||
#define JOYSTICK_AXIS_IN_OUT_GROUND(INPUT_PIN, OUTPUT_PIN, GROUND_PIN, LOW, REST, HIGH) \
|
||||
{ OUTPUT_PIN, INPUT_PIN, GROUND_PIN, LOW, REST, HIGH }
|
||||
|
||||
typedef struct {
|
||||
pin_t output_pin;
|
||||
pin_t input_pin;
|
||||
pin_t ground_pin;
|
||||
|
||||
// the AVR ADC offers 10 bit precision, with significant bits on the higher part
|
||||
uint16_t min_digit;
|
||||
uint16_t mid_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];
|
||||
|
||||
int16_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);
|
||||
168
quantum/process_keycode/process_joystick.c
Normal file
168
quantum/process_keycode/process_joystick.c
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
#include "joystick.h"
|
||||
#include "process_joystick.h"
|
||||
|
||||
#include "analog.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <math.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_analogread() && (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;
|
||||
}
|
||||
|
||||
uint16_t savePinState(pin_t pin) {
|
||||
#ifdef __AVR__
|
||||
uint8_t pinNumber = pin & 0xF;
|
||||
return ((PORTx_ADDRESS(pin) >> pinNumber) & 0x1) << 1 | ((DDRx_ADDRESS(pin) >> pinNumber) & 0x1);
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
/*
|
||||
The pin configuration is backed up in the following format :
|
||||
bit 15 9 8 7 6 5 4 3 2 1 0
|
||||
|unused|ODR|IDR|PUPDR|OSPEEDR|OTYPER|MODER|
|
||||
*/
|
||||
return (( PAL_PORT(pin)->MODER >> (2*PAL_PAD(pin))) & 0x3)
|
||||
| (((PAL_PORT(pin)->OTYPER >> (1*PAL_PAD(pin))) & 0x1) << 2)
|
||||
| (((PAL_PORT(pin)->OSPEEDR >> (2*PAL_PAD(pin))) & 0x3) << 3)
|
||||
| (((PAL_PORT(pin)->PUPDR >> (2*PAL_PAD(pin))) & 0x3) << 5)
|
||||
| (((PAL_PORT(pin)->IDR >> (1*PAL_PAD(pin))) & 0x1) << 7)
|
||||
| (((PAL_PORT(pin)->ODR >> (1*PAL_PAD(pin))) & 0x1) << 8);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void restorePinState(pin_t pin, uint16_t restoreState) {
|
||||
#if defined(PROTOCOL_LUFA)
|
||||
uint8_t pinNumber = pin & 0xF;
|
||||
PORTx_ADDRESS(pin) = (PORTx_ADDRESS(pin) & ~_BV(pinNumber)) | (((restoreState >> 1) & 0x1) << pinNumber);
|
||||
DDRx_ADDRESS(pin) = (DDRx_ADDRESS(pin) & ~_BV(pinNumber)) | ((restoreState & 0x1) << pinNumber);
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
PAL_PORT(pin)->MODER = (PAL_PORT(pin)->MODER & ~(0x3<< (2*PAL_PAD(pin)))) | (restoreState & 0x3) << (2*PAL_PAD(pin));
|
||||
PAL_PORT(pin)->OTYPER = (PAL_PORT(pin)->OTYPER & ~(0x1<< (1*PAL_PAD(pin)))) | ((restoreState>>2) & 0x1) << (1*PAL_PAD(pin));
|
||||
PAL_PORT(pin)->OSPEEDR= (PAL_PORT(pin)->OSPEEDR & ~(0x3<< (2*PAL_PAD(pin)))) | ((restoreState>>3) & 0x3) << (2*PAL_PAD(pin));
|
||||
PAL_PORT(pin)->PUPDR = (PAL_PORT(pin)->PUPDR & ~(0x3<< (2*PAL_PAD(pin)))) | ((restoreState>>5) & 0x3) << (2*PAL_PAD(pin));
|
||||
PAL_PORT(pin)->IDR = (PAL_PORT(pin)->IDR & ~(0x1<< (1*PAL_PAD(pin)))) | ((restoreState>>7) & 0x1) << (1*PAL_PAD(pin));
|
||||
PAL_PORT(pin)->ODR = (PAL_PORT(pin)->ODR & ~(0x1<< (1*PAL_PAD(pin)))) | ((restoreState>>8) & 0x1) << (1*PAL_PAD(pin));
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool process_joystick_analogread() { return process_joystick_analogread_quantum(); }
|
||||
|
||||
bool process_joystick_analogread_quantum() {
|
||||
#if JOYSTICK_AXES_COUNT > 0
|
||||
for (int axis_index = 0; axis_index < JOYSTICK_AXES_COUNT; ++axis_index) {
|
||||
if (joystick_axes[axis_index].input_pin == JS_VIRTUAL_AXIS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// save previous input pin status as well
|
||||
uint16_t inputSavedState = savePinState(joystick_axes[axis_index].input_pin);
|
||||
|
||||
// disable pull-up resistor
|
||||
writePinLow(joystick_axes[axis_index].input_pin);
|
||||
|
||||
// if pin was a pull-up input, we need to uncharge it by turning it low
|
||||
// before making it a low input
|
||||
setPinOutput(joystick_axes[axis_index].input_pin);
|
||||
|
||||
wait_us(10);
|
||||
|
||||
// save and apply output pin status
|
||||
uint16_t outputSavedState = 0;
|
||||
if (joystick_axes[axis_index].output_pin != JS_VIRTUAL_AXIS) {
|
||||
// save previous output pin status
|
||||
outputSavedState = savePinState(joystick_axes[axis_index].output_pin);
|
||||
|
||||
setPinOutput(joystick_axes[axis_index].output_pin);
|
||||
writePinHigh(joystick_axes[axis_index].output_pin);
|
||||
}
|
||||
|
||||
uint16_t groundSavedState = 0;
|
||||
if (joystick_axes[axis_index].ground_pin != JS_VIRTUAL_AXIS) {
|
||||
// save previous output pin status
|
||||
groundSavedState = savePinState(joystick_axes[axis_index].ground_pin);
|
||||
|
||||
setPinOutput(joystick_axes[axis_index].ground_pin);
|
||||
writePinLow(joystick_axes[axis_index].ground_pin);
|
||||
}
|
||||
|
||||
wait_us(10);
|
||||
|
||||
setPinInput(joystick_axes[axis_index].input_pin);
|
||||
|
||||
wait_us(10);
|
||||
|
||||
# if defined(__AVR__) || defined(PROTOCOL_CHIBIOS)
|
||||
int16_t axis_val = analogReadPin(joystick_axes[axis_index].input_pin);
|
||||
# else
|
||||
// default to resting position
|
||||
int16_t axis_val = joystick_axes[axis_index].mid_digit;
|
||||
# endif
|
||||
|
||||
//test the converted value against the lower range
|
||||
int32_t ref = joystick_axes[axis_index].mid_digit;
|
||||
int32_t range = joystick_axes[axis_index].min_digit;
|
||||
int32_t ranged_val = ((axis_val - ref) * -127) / (range - ref) ;
|
||||
|
||||
if (ranged_val > 0) {
|
||||
//the value is in the higher range
|
||||
range = joystick_axes[axis_index].max_digit;
|
||||
ranged_val = ((axis_val - ref) * 127) / (range - ref);
|
||||
}
|
||||
|
||||
//clamp the result in the valid range
|
||||
ranged_val = ranged_val < -127 ? -127 : ranged_val;
|
||||
ranged_val = ranged_val > 127 ? 127 : ranged_val;
|
||||
|
||||
if (ranged_val != joystick_status.axes[axis_index]) {
|
||||
joystick_status.axes[axis_index] = ranged_val;
|
||||
joystick_status.status |= JS_UPDATED;
|
||||
}
|
||||
|
||||
// restore output, ground and input status
|
||||
if (joystick_axes[axis_index].output_pin != JS_VIRTUAL_AXIS) {
|
||||
restorePinState(joystick_axes[axis_index].output_pin, outputSavedState);
|
||||
}
|
||||
if (joystick_axes[axis_index].ground_pin != JS_VIRTUAL_AXIS) {
|
||||
restorePinState(joystick_axes[axis_index].ground_pin, groundSavedState);
|
||||
}
|
||||
|
||||
restorePinState(joystick_axes[axis_index].input_pin, inputSavedState);
|
||||
}
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
11
quantum/process_keycode/process_joystick.h
Normal file
11
quantum/process_keycode/process_joystick.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "quantum.h"
|
||||
|
||||
bool process_joystick(uint16_t keycode, keyrecord_t *record);
|
||||
|
||||
void joystick_task(void);
|
||||
|
||||
bool process_joystick_analogread(void);
|
||||
bool process_joystick_analogread_quantum(void);
|
||||
|
|
@ -280,6 +280,9 @@ bool process_record_quantum(keyrecord_t *record) {
|
|||
#endif
|
||||
#if defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)
|
||||
process_rgb(keycode, record) &&
|
||||
#endif
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
process_joystick(keycode, record) &&
|
||||
#endif
|
||||
true)) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -142,6 +142,10 @@ extern layer_state_t layer_state;
|
|||
# include "process_magic.h"
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
# include "process_joystick.h"
|
||||
#endif
|
||||
|
||||
#ifdef GRAVE_ESC_ENABLE
|
||||
# include "process_grave_esc.h"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -512,6 +512,41 @@ enum quantum_keycodes {
|
|||
DYN_MACRO_PLAY1,
|
||||
DYN_MACRO_PLAY2,
|
||||
|
||||
JS_BUTTON0,
|
||||
JS_BUTTON_MIN = JS_BUTTON0,
|
||||
JS_BUTTON1,
|
||||
JS_BUTTON2,
|
||||
JS_BUTTON3,
|
||||
JS_BUTTON4,
|
||||
JS_BUTTON5,
|
||||
JS_BUTTON6,
|
||||
JS_BUTTON7,
|
||||
JS_BUTTON8,
|
||||
JS_BUTTON9,
|
||||
JS_BUTTON10,
|
||||
JS_BUTTON11,
|
||||
JS_BUTTON12,
|
||||
JS_BUTTON13,
|
||||
JS_BUTTON14,
|
||||
JS_BUTTON15,
|
||||
JS_BUTTON16,
|
||||
JS_BUTTON17,
|
||||
JS_BUTTON18,
|
||||
JS_BUTTON19,
|
||||
JS_BUTTON20,
|
||||
JS_BUTTON21,
|
||||
JS_BUTTON22,
|
||||
JS_BUTTON23,
|
||||
JS_BUTTON24,
|
||||
JS_BUTTON25,
|
||||
JS_BUTTON26,
|
||||
JS_BUTTON27,
|
||||
JS_BUTTON28,
|
||||
JS_BUTTON29,
|
||||
JS_BUTTON30,
|
||||
JS_BUTTON31,
|
||||
JS_BUTTON_MAX = JS_BUTTON31,
|
||||
|
||||
// always leave at the end
|
||||
SAFE_RANGE
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue