add support for hid gamepad interface
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 read
This commit is contained in:
parent
d8f3c28a37
commit
3cf7611139
15 changed files with 675 additions and 2 deletions
|
@ -511,3 +511,12 @@ ifeq ($(strip $(AUTO_SHIFT_ENABLE)), yes)
|
|||
OPT_DEFS += -DAUTO_SHIFT_MODIFIERS
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(JOYSTICK_ENABLE)), yes)
|
||||
OPT_DEFS += -DJOYSTICK_ENABLE
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_joystick.c
|
||||
SRC += $(QUANTUM_DIR)/joystick.c
|
||||
ifeq ($(PLATFORM),AVR)
|
||||
SRC += drivers/avr/analog.c
|
||||
endif
|
||||
endif
|
||||
|
|
|
@ -97,6 +97,7 @@
|
|||
* [DIP Switch](feature_dip_switch.md)
|
||||
* [Encoders](feature_encoders.md)
|
||||
* [Haptic Feedback](feature_haptic_feedback.md)
|
||||
* [Joystick](feature_joystick.md)
|
||||
* [Proton C Conversion](proton_c_conversion.md)
|
||||
* [PS/2 Mouse](feature_ps2_mouse.md)
|
||||
* [Split Keyboard](feature_split_keyboard.md)
|
||||
|
|
85
docs/feature_joystick.md
Normal file
85
docs/feature_joystick.md
Normal file
|
@ -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.
|
||||
|
||||
|
||||
|
11
quantum/joystick.c
Normal file
11
quantum/joystick.c
Normal file
|
@ -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] = {};
|
48
quantum/joystick.h
Normal file
48
quantum/joystick.h
Normal file
|
@ -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
|
79
quantum/process_keycode/process_joystick.c
Normal file
79
quantum/process_keycode/process_joystick.c
Normal file
|
@ -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;
|
||||
}
|
13
quantum/process_keycode/process_joystick.h
Normal file
13
quantum/process_keycode/process_joystick.h
Normal file
|
@ -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
|
|
@ -63,6 +63,10 @@ float bell_song[][2] = SONG(TERMINAL_SOUND);
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
#include <process_keycode/process_joystick.h>
|
||||
#endif //JOYSTICK_ENABLE
|
||||
|
||||
static void do_code16(uint16_t code, void (*f)(uint8_t)) {
|
||||
switch (code) {
|
||||
case QK_MODS ... QK_MODS_MAX:
|
||||
|
@ -265,6 +269,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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -71,6 +71,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#ifdef MIDI_ENABLE
|
||||
# include "process_midi.h"
|
||||
#endif
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
# include "process_joystick.h"
|
||||
#endif
|
||||
#ifdef HD44780_ENABLE
|
||||
# include "hd44780.h"
|
||||
#endif
|
||||
|
@ -396,6 +399,10 @@ MATRIX_LOOP_END:
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
joystick_task();
|
||||
#endif
|
||||
|
||||
// update LED
|
||||
if (led_status != host_keyboard_leds()) {
|
||||
led_status = host_keyboard_leds();
|
||||
|
|
|
@ -47,6 +47,10 @@
|
|||
extern keymap_config_t keymap_config;
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
# include <quantum/joystick.h>
|
||||
#endif
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* Global interface variables and declarations
|
||||
* ---------------------------------------------------------
|
||||
|
@ -246,6 +250,9 @@ typedef struct {
|
|||
#endif
|
||||
#ifdef VIRTSER_ENABLE
|
||||
usb_driver_config_t serial_driver;
|
||||
#endif
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
usb_driver_config_t joystick_driver;
|
||||
#endif
|
||||
};
|
||||
usb_driver_config_t array[0];
|
||||
|
@ -283,6 +290,14 @@ static usb_driver_configs_t drivers = {
|
|||
# define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK
|
||||
.serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false),
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
#define JOYSTICK_IN_CAPACITY 4
|
||||
#define JOYSTICK_OUT_CAPACITY 4
|
||||
#define JOYSTICK_IN_MODE USB_EP_MODE_TYPE_BULK
|
||||
#define JOYSTICK_OUT_MODE USB_EP_MODE_TYPE_BULK
|
||||
.joystick_driver = QMK_USB_DRIVER_CONFIG(JOYSTICK, 0, false),
|
||||
#endif
|
||||
};
|
||||
|
||||
#define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
|
||||
|
@ -870,3 +885,61 @@ void virtser_task(void) {
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
|
||||
typedef struct {
|
||||
#if JOYSTICK_AXES_COUNT>0
|
||||
int8_t axes[JOYSTICK_AXES_COUNT];
|
||||
#endif
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT>0
|
||||
uint8_t buttons[(JOYSTICK_BUTTON_COUNT-1)/8+1];
|
||||
#endif
|
||||
} __attribute__ ((packed)) joystick_report_t;
|
||||
|
||||
void send_joystick_packet(joystick_t* joystick) {
|
||||
joystick_report_t rep = {
|
||||
#if JOYSTICK_AXES_COUNT>0
|
||||
.axes = {
|
||||
joystick->axes[0]
|
||||
|
||||
#if JOYSTICK_AXES_COUNT >= 2
|
||||
,joystick->axes[1]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 3
|
||||
,joystick->axes[2]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 4
|
||||
,joystick->axes[3]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 5
|
||||
,joystick->axes[4]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 6
|
||||
,joystick->axes[5]
|
||||
#endif
|
||||
},
|
||||
#endif //JOYSTICK_AXES_COUNT>0
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT>0
|
||||
.buttons = {
|
||||
joystick->buttons[0]
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT>8
|
||||
,joystick->buttons[1]
|
||||
#endif
|
||||
#if JOYSTICK_BUTTON_COUNT>16
|
||||
,joystick->buttons[2]
|
||||
#endif
|
||||
#if JOYSTICK_BUTTON_COUNT>24
|
||||
,joystick->buttons[3]
|
||||
#endif
|
||||
}
|
||||
#endif //JOYSTICK_BUTTON_COUNT>0
|
||||
};
|
||||
|
||||
chnWrite(&drivers.joystick_driver.driver, (uint8_t*)&rep, sizeof(rep));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -85,6 +85,10 @@ extern keymap_config_t keymap_config;
|
|||
# include "raw_hid.h"
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
#include "joystick.h"
|
||||
#endif
|
||||
|
||||
uint8_t keyboard_idle = 0;
|
||||
/* 0: Boot Protocol, 1: Report Protocol(default) */
|
||||
uint8_t keyboard_protocol = 1;
|
||||
|
@ -263,6 +267,85 @@ static void Console_Task(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Joystick
|
||||
******************************************************************************/
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
|
||||
typedef struct {
|
||||
#if JOYSTICK_AXES_COUNT>0
|
||||
int8_t axes[JOYSTICK_AXES_COUNT];
|
||||
#endif
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT>0
|
||||
uint8_t buttons[(JOYSTICK_BUTTON_COUNT-1)/8+1];
|
||||
#endif
|
||||
} __attribute__ ((packed)) joystick_report_t;
|
||||
|
||||
void send_joystick_packet(joystick_t* joystick){
|
||||
|
||||
uint8_t timeout = 255;
|
||||
uint8_t where = where_to_send();
|
||||
|
||||
if (where != OUTPUT_USB && where != OUTPUT_USB_AND_BT) {
|
||||
return;
|
||||
}
|
||||
|
||||
joystick_report_t r = {
|
||||
#if JOYSTICK_AXES_COUNT>0
|
||||
.axes = {
|
||||
joystick->axes[0]
|
||||
|
||||
#if JOYSTICK_AXES_COUNT >= 2
|
||||
,joystick->axes[1]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 3
|
||||
,joystick->axes[2]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 4
|
||||
,joystick->axes[3]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 5
|
||||
,joystick->axes[4]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 6
|
||||
,joystick->axes[5]
|
||||
#endif
|
||||
},
|
||||
#endif //JOYSTICK_AXES_COUNT>0
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT>0
|
||||
.buttons = {
|
||||
joystick->buttons[0]
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT>8
|
||||
,joystick->buttons[1]
|
||||
#endif
|
||||
#if JOYSTICK_BUTTON_COUNT>16
|
||||
,joystick->buttons[2]
|
||||
#endif
|
||||
#if JOYSTICK_BUTTON_COUNT>24
|
||||
,joystick->buttons[3]
|
||||
#endif
|
||||
}
|
||||
#endif //JOYSTICK_BUTTON_COUNT>0
|
||||
};
|
||||
|
||||
/* Select the Joystick Report Endpoint */
|
||||
Endpoint_SelectEndpoint(JOYSTICK_IN_EPNUM);
|
||||
|
||||
/* Check if write ready for a polling interval around 10ms */
|
||||
while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
|
||||
if (!Endpoint_IsReadWriteAllowed()) return;
|
||||
|
||||
/* Write Joystick Report Data */
|
||||
Endpoint_Write_Stream_LE(&r, sizeof(joystick_report_t), NULL);
|
||||
|
||||
/* Finalize the stream transfer to send the last packet */
|
||||
Endpoint_ClearIN();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* USB Events
|
||||
******************************************************************************/
|
||||
|
|
|
@ -278,6 +278,53 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
const USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] =
|
||||
{
|
||||
HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */
|
||||
HID_RI_USAGE(8, 0x04), /* Joystick */
|
||||
HID_RI_COLLECTION(8, 0x01), /* Application */
|
||||
HID_RI_USAGE(8, 0x01), /* Pointer */
|
||||
HID_RI_COLLECTION(8, 0x00), /* Physical */
|
||||
|
||||
#if JOYSTICK_AXES_COUNT >= 1
|
||||
HID_RI_USAGE(8, 0x30), // USAGE (X)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 2
|
||||
HID_RI_USAGE(8, 0x31), // USAGE (Y)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 3
|
||||
HID_RI_USAGE(8, 0x32), // USAGE (Z)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 4
|
||||
HID_RI_USAGE(8, 0x33), // USAGE (RX)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 5
|
||||
HID_RI_USAGE(8, 0x34), // USAGE (RY)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 6
|
||||
HID_RI_USAGE(8, 0x35), // USAGE (RZ)
|
||||
#endif
|
||||
HID_RI_LOGICAL_MINIMUM(8, -127),
|
||||
HID_RI_LOGICAL_MAXIMUM(8, 127),
|
||||
HID_RI_REPORT_COUNT(8, JOYSTICK_AXES_COUNT),
|
||||
HID_RI_REPORT_SIZE(8, 0x08),
|
||||
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),
|
||||
|
||||
HID_RI_USAGE_PAGE(8, 0x09), /* Button */
|
||||
HID_RI_USAGE_MINIMUM(8, 0x01), /* Button 1 */
|
||||
HID_RI_USAGE_MAXIMUM(8, JOYSTICK_BUTTON_COUNT), /* Button 5 */
|
||||
HID_RI_LOGICAL_MINIMUM(8, 0x00),
|
||||
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
|
||||
HID_RI_REPORT_COUNT(8, JOYSTICK_BUTTON_COUNT),
|
||||
HID_RI_REPORT_SIZE(8, 0x01),
|
||||
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||
|
||||
HID_RI_END_COLLECTION(0),
|
||||
HID_RI_END_COLLECTION(0),
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Device descriptor
|
||||
*/
|
||||
|
@ -287,7 +334,6 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = {
|
|||
.Type = DTYPE_Device
|
||||
},
|
||||
.USBSpecification = VERSION_BCD(1, 1, 0),
|
||||
|
||||
#if VIRTSER_ENABLE
|
||||
.Class = USB_CSCP_IADDeviceClass,
|
||||
.SubClass = USB_CSCP_IADDeviceSubclass,
|
||||
|
@ -812,6 +858,49 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
|
|||
.PollingIntervalMS = 0x05
|
||||
},
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Joystick
|
||||
*/
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
.Joystick_Interface =
|
||||
{
|
||||
.Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},
|
||||
|
||||
.InterfaceNumber = JOYSTICK_INTERFACE,
|
||||
.AlternateSetting = 0x00,
|
||||
|
||||
.TotalEndpoints = 1,
|
||||
|
||||
.Class = HID_CSCP_HIDClass,
|
||||
.SubClass = HID_CSCP_NonBootSubclass,
|
||||
.Protocol = HID_CSCP_NonBootProtocol,
|
||||
|
||||
.InterfaceStrIndex = NO_DESCRIPTOR
|
||||
},
|
||||
|
||||
.Joystick_HID =
|
||||
{
|
||||
.Header = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID},
|
||||
|
||||
.HIDSpec = VERSION_BCD(1,1,1),
|
||||
.CountryCode = 0x00,
|
||||
.TotalReportDescriptors = 1,
|
||||
.HIDReportType = HID_DTYPE_Report,
|
||||
.HIDReportLength = sizeof(JoystickReport)
|
||||
},
|
||||
|
||||
.Joystick_INEndpoint =
|
||||
{
|
||||
.Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
|
||||
|
||||
.EndpointAddress = (ENDPOINT_DIR_IN | JOYSTICK_IN_EPNUM),
|
||||
.Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
|
||||
.EndpointSize = JOYSTICK_EPSIZE,
|
||||
.PollingIntervalMS = 0x0A
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -944,6 +1033,12 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
|
|||
|
||||
break;
|
||||
#endif
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
case JOYSTICK_INTERFACE:
|
||||
Address = &ConfigurationDescriptor.Joystick_HID;
|
||||
Size = sizeof(USB_HID_Descriptor_HID_t);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -988,6 +1083,12 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
|
|||
|
||||
break;
|
||||
#endif
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
case JOYSTICK_INTERFACE:
|
||||
Address = &JoystickReport;
|
||||
Size = sizeof(JoystickReport);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -123,6 +123,13 @@ typedef struct {
|
|||
USB_Descriptor_Endpoint_t CDC_DataOutEndpoint;
|
||||
USB_Descriptor_Endpoint_t CDC_DataInEndpoint;
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
// Joystick HID Interface
|
||||
USB_Descriptor_Interface_t Joystick_Interface;
|
||||
USB_HID_Descriptor_HID_t Joystick_HID;
|
||||
USB_Descriptor_Endpoint_t Joystick_INEndpoint;
|
||||
#endif
|
||||
} USB_Descriptor_Configuration_t;
|
||||
|
||||
/*
|
||||
|
@ -164,6 +171,9 @@ enum usb_interfaces {
|
|||
CDI_INTERFACE,
|
||||
#endif
|
||||
|
||||
#if defined(JOYSTICK_ENABLE)
|
||||
JOYSTICK_INTERFACE,
|
||||
#endif
|
||||
TOTAL_INTERFACES
|
||||
};
|
||||
|
||||
|
@ -224,6 +234,10 @@ enum usb_endpoints {
|
|||
# define CDC_IN_EPADDR (ENDPOINT_DIR_IN | CDC_IN_EPNUM)
|
||||
# define CDC_OUT_EPADDR (ENDPOINT_DIR_OUT | CDC_OUT_EPNUM)
|
||||
#endif
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
JOYSTICK_IN_EPNUM = NEXT_EPNUM,
|
||||
JOYSTICK_OUT_EPNUM = NEXT_EPNUM,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef PROTOCOL_LUFA
|
||||
|
@ -248,6 +262,7 @@ enum usb_endpoints {
|
|||
#define MIDI_STREAM_EPSIZE 64
|
||||
#define CDC_NOTIFICATION_EPSIZE 8
|
||||
#define CDC_EPSIZE 16
|
||||
#define JOYSTICK_EPSIZE 8
|
||||
|
||||
uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress);
|
||||
#endif
|
||||
|
|
|
@ -26,6 +26,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "debug.h"
|
||||
#include "host_driver.h"
|
||||
#include "vusb.h"
|
||||
#include "joystick.h"
|
||||
#include "joystick.h"
|
||||
#include <util/delay.h>
|
||||
|
||||
static uint8_t vusb_keyboard_leds = 0;
|
||||
|
@ -79,6 +81,7 @@ static void send_keyboard(report_keyboard_t *report);
|
|||
static void send_mouse(report_mouse_t *report);
|
||||
static void send_system(uint16_t data);
|
||||
static void send_consumer(uint16_t data);
|
||||
static void send_joystick(void);
|
||||
|
||||
static host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
|
||||
|
||||
|
@ -139,6 +142,68 @@ static void send_consumer(uint16_t data) {
|
|||
#endif
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t report_id;
|
||||
|
||||
#if JOYSTICK_AXES_COUNT>0
|
||||
int8_t axes[JOYSTICK_AXES_COUNT];
|
||||
#endif
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT>0
|
||||
uint8_t buttons[(JOYSTICK_BUTTON_COUNT-1)/8+1];
|
||||
#endif
|
||||
} __attribute__ ((packed)) vusb_joystick_report_t;
|
||||
|
||||
void send_joystick_packet(joystick_t* status)
|
||||
{
|
||||
vusb_joystick_report_t r = {
|
||||
.report_id = 0x4,
|
||||
|
||||
#if JOYSTICK_AXES_COUNT>0
|
||||
.axes = {
|
||||
status->axes[0]
|
||||
|
||||
#if JOYSTICK_AXES_COUNT >= 2
|
||||
,status->axes[1]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 3
|
||||
,status->axes[2]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 4
|
||||
,status->axes[3]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 5
|
||||
,status->axes[4]
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 6
|
||||
,status->axes[5]
|
||||
#endif
|
||||
},
|
||||
#endif //JOYSTICK_AXES_COUNT>0
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT>0
|
||||
.buttons = {
|
||||
status->buttons[0]
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT>8
|
||||
,status->buttons[1]
|
||||
#endif
|
||||
#if JOYSTICK_BUTTON_COUNT>16
|
||||
,status->buttons[2]
|
||||
#endif
|
||||
#if JOYSTICK_BUTTON_COUNT>24
|
||||
,status->buttons[3]
|
||||
#endif
|
||||
}
|
||||
#endif //JOYSTICK_BUTTON_COUNT>0
|
||||
};
|
||||
if (usbInterruptIsReady3()) {
|
||||
usbSetInterrupt3((void *)&r, sizeof(vusb_joystick_report_t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------*
|
||||
* Request from host *
|
||||
*------------------------------------------------------------------*/
|
||||
|
@ -251,7 +316,7 @@ const PROGMEM uchar keyboard_hid_report[] = {
|
|||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE)
|
||||
#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) || defined(JOYSTICK_ENABLE)
|
||||
const PROGMEM uchar mouse_extra_hid_report[] = {
|
||||
# ifdef MOUSE_ENABLE
|
||||
// Mouse report descriptor
|
||||
|
@ -332,6 +397,47 @@ const PROGMEM uchar mouse_extra_hid_report[] = {
|
|||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
# endif
|
||||
#if JOYSTICK_ENABLE
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x04, // USAGE (Joystick)
|
||||
0xa1, 0x01, // COLLECTION (Application)
|
||||
0x85, 0x4, // REPORT_ID (4)
|
||||
0x09, 0x01, // USAGE (Pointer)
|
||||
0xa1, 0x00, // COLLECTION (Physical)
|
||||
#if JOYSTICK_AXES_COUNT >= 1
|
||||
0x09, 0x30, // USAGE (X)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 2
|
||||
0x09, 0x31, // USAGE (Y)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 3
|
||||
0x09, 0x32, // USAGE (Z)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 4
|
||||
0x09, 0x33, // USAGE (RX)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 5
|
||||
0x09, 0x34, // USAGE (RY)
|
||||
#endif
|
||||
#if JOYSTICK_AXES_COUNT >= 6
|
||||
0x09, 0x35, // USAGE (RZ)
|
||||
#endif
|
||||
0x15, 0x81, // LOGICAL_MINIMUM (-127)
|
||||
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
|
||||
0x75, 0x08, // REPORT_SIZE (8)
|
||||
0x95, JOYSTICK_AXES_COUNT, // REPORT_COUNT (JOYSTICK_AXES_COUNT)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0xc0, // END_COLLECTION
|
||||
0x05, 0x09, // USAGE_PAGE (Button)
|
||||
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||
0x29, JOYSTICK_BUTTON_COUNT, // USAGE_MAXIMUM
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x95, JOYSTICK_BUTTON_COUNT, // REPORT_COUNT
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0xc0 // END_COLLECTION
|
||||
#endif //GAMEPAD_ENABLE
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in a new issue