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:
achol 2018-10-17 11:23:20 +02:00 committed by a-chol
parent d8f3c28a37
commit 3cf7611139
15 changed files with 675 additions and 2 deletions

View file

@ -511,3 +511,12 @@ ifeq ($(strip $(AUTO_SHIFT_ENABLE)), yes)
OPT_DEFS += -DAUTO_SHIFT_MODIFIERS OPT_DEFS += -DAUTO_SHIFT_MODIFIERS
endif endif
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

View file

@ -97,6 +97,7 @@
* [DIP Switch](feature_dip_switch.md) * [DIP Switch](feature_dip_switch.md)
* [Encoders](feature_encoders.md) * [Encoders](feature_encoders.md)
* [Haptic Feedback](feature_haptic_feedback.md) * [Haptic Feedback](feature_haptic_feedback.md)
* [Joystick](feature_joystick.md)
* [Proton C Conversion](proton_c_conversion.md) * [Proton C Conversion](proton_c_conversion.md)
* [PS/2 Mouse](feature_ps2_mouse.md) * [PS/2 Mouse](feature_ps2_mouse.md)
* [Split Keyboard](feature_split_keyboard.md) * [Split Keyboard](feature_split_keyboard.md)

85
docs/feature_joystick.md Normal file
View 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
View 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
View 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

View 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;
}

View 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

View file

@ -63,6 +63,10 @@ float bell_song[][2] = SONG(TERMINAL_SOUND);
# endif # endif
#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)) { static void do_code16(uint16_t code, void (*f)(uint8_t)) {
switch (code) { switch (code) {
case QK_MODS ... QK_MODS_MAX: case QK_MODS ... QK_MODS_MAX:
@ -265,6 +269,9 @@ bool process_record_quantum(keyrecord_t *record) {
#endif #endif
#if defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE) #if defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)
process_rgb(keycode, record) && process_rgb(keycode, record) &&
#endif
#ifdef JOYSTICK_ENABLE
process_joystick(keycode, record) &&
#endif #endif
true)) { true)) {
return false; return false;

View file

@ -512,6 +512,41 @@ enum quantum_keycodes {
DYN_MACRO_PLAY1, DYN_MACRO_PLAY1,
DYN_MACRO_PLAY2, 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 // always leave at the end
SAFE_RANGE SAFE_RANGE
}; };

View file

@ -71,6 +71,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef MIDI_ENABLE #ifdef MIDI_ENABLE
# include "process_midi.h" # include "process_midi.h"
#endif #endif
#ifdef JOYSTICK_ENABLE
# include "process_joystick.h"
#endif
#ifdef HD44780_ENABLE #ifdef HD44780_ENABLE
# include "hd44780.h" # include "hd44780.h"
#endif #endif
@ -396,6 +399,10 @@ MATRIX_LOOP_END:
} }
#endif #endif
#ifdef JOYSTICK_ENABLE
joystick_task();
#endif
// update LED // update LED
if (led_status != host_keyboard_leds()) { if (led_status != host_keyboard_leds()) {
led_status = host_keyboard_leds(); led_status = host_keyboard_leds();

View file

@ -47,6 +47,10 @@
extern keymap_config_t keymap_config; extern keymap_config_t keymap_config;
#endif #endif
#ifdef JOYSTICK_ENABLE
# include <quantum/joystick.h>
#endif
/* --------------------------------------------------------- /* ---------------------------------------------------------
* Global interface variables and declarations * Global interface variables and declarations
* --------------------------------------------------------- * ---------------------------------------------------------
@ -246,6 +250,9 @@ typedef struct {
#endif #endif
#ifdef VIRTSER_ENABLE #ifdef VIRTSER_ENABLE
usb_driver_config_t serial_driver; usb_driver_config_t serial_driver;
#endif
#ifdef JOYSTICK_ENABLE
usb_driver_config_t joystick_driver;
#endif #endif
}; };
usb_driver_config_t array[0]; 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 # define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK
.serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false), .serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, false),
#endif #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)) #define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t))
@ -870,3 +885,61 @@ void virtser_task(void) {
} }
#endif #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

View file

@ -85,6 +85,10 @@ extern keymap_config_t keymap_config;
# include "raw_hid.h" # include "raw_hid.h"
#endif #endif
#ifdef JOYSTICK_ENABLE
#include "joystick.h"
#endif
uint8_t keyboard_idle = 0; uint8_t keyboard_idle = 0;
/* 0: Boot Protocol, 1: Report Protocol(default) */ /* 0: Boot Protocol, 1: Report Protocol(default) */
uint8_t keyboard_protocol = 1; uint8_t keyboard_protocol = 1;
@ -263,6 +267,85 @@ static void Console_Task(void) {
} }
#endif #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 * USB Events
******************************************************************************/ ******************************************************************************/

View file

@ -278,6 +278,53 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = {
}; };
#endif #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 * Device descriptor
*/ */
@ -287,7 +334,6 @@ const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = {
.Type = DTYPE_Device .Type = DTYPE_Device
}, },
.USBSpecification = VERSION_BCD(1, 1, 0), .USBSpecification = VERSION_BCD(1, 1, 0),
#if VIRTSER_ENABLE #if VIRTSER_ENABLE
.Class = USB_CSCP_IADDeviceClass, .Class = USB_CSCP_IADDeviceClass,
.SubClass = USB_CSCP_IADDeviceSubclass, .SubClass = USB_CSCP_IADDeviceSubclass,
@ -812,6 +858,49 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
.PollingIntervalMS = 0x05 .PollingIntervalMS = 0x05
}, },
#endif #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
}; };
/* /*
@ -943,6 +1032,12 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
Size = sizeof(USB_HID_Descriptor_HID_t); Size = sizeof(USB_HID_Descriptor_HID_t);
break; break;
#endif
#ifdef JOYSTICK_ENABLE
case JOYSTICK_INTERFACE:
Address = &ConfigurationDescriptor.Joystick_HID;
Size = sizeof(USB_HID_Descriptor_HID_t);
break;
#endif #endif
} }
@ -987,6 +1082,12 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
Size = sizeof(ConsoleReport); Size = sizeof(ConsoleReport);
break; break;
#endif
#ifdef JOYSTICK_ENABLE
case JOYSTICK_INTERFACE:
Address = &JoystickReport;
Size = sizeof(JoystickReport);
break;
#endif #endif
} }

View file

@ -123,6 +123,13 @@ typedef struct {
USB_Descriptor_Endpoint_t CDC_DataOutEndpoint; USB_Descriptor_Endpoint_t CDC_DataOutEndpoint;
USB_Descriptor_Endpoint_t CDC_DataInEndpoint; USB_Descriptor_Endpoint_t CDC_DataInEndpoint;
#endif #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; } USB_Descriptor_Configuration_t;
/* /*
@ -164,6 +171,9 @@ enum usb_interfaces {
CDI_INTERFACE, CDI_INTERFACE,
#endif #endif
#if defined(JOYSTICK_ENABLE)
JOYSTICK_INTERFACE,
#endif
TOTAL_INTERFACES TOTAL_INTERFACES
}; };
@ -224,6 +234,10 @@ enum usb_endpoints {
# define CDC_IN_EPADDR (ENDPOINT_DIR_IN | CDC_IN_EPNUM) # define CDC_IN_EPADDR (ENDPOINT_DIR_IN | CDC_IN_EPNUM)
# define CDC_OUT_EPADDR (ENDPOINT_DIR_OUT | CDC_OUT_EPNUM) # define CDC_OUT_EPADDR (ENDPOINT_DIR_OUT | CDC_OUT_EPNUM)
#endif #endif
#ifdef JOYSTICK_ENABLE
JOYSTICK_IN_EPNUM = NEXT_EPNUM,
JOYSTICK_OUT_EPNUM = NEXT_EPNUM,
#endif
}; };
#ifdef PROTOCOL_LUFA #ifdef PROTOCOL_LUFA
@ -248,6 +262,7 @@ enum usb_endpoints {
#define MIDI_STREAM_EPSIZE 64 #define MIDI_STREAM_EPSIZE 64
#define CDC_NOTIFICATION_EPSIZE 8 #define CDC_NOTIFICATION_EPSIZE 8
#define CDC_EPSIZE 16 #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); uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress);
#endif #endif

View file

@ -26,6 +26,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "debug.h" #include "debug.h"
#include "host_driver.h" #include "host_driver.h"
#include "vusb.h" #include "vusb.h"
#include "joystick.h"
#include "joystick.h"
#include <util/delay.h> #include <util/delay.h>
static uint8_t vusb_keyboard_leds = 0; 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_mouse(report_mouse_t *report);
static void send_system(uint16_t data); static void send_system(uint16_t data);
static void send_consumer(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}; 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 #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 * * Request from host *
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
@ -251,7 +316,7 @@ const PROGMEM uchar keyboard_hid_report[] = {
0xC0 // End Collection 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[] = { const PROGMEM uchar mouse_extra_hid_report[] = {
# ifdef MOUSE_ENABLE # ifdef MOUSE_ENABLE
// Mouse report descriptor // Mouse report descriptor
@ -332,6 +397,47 @@ const PROGMEM uchar mouse_extra_hid_report[] = {
0x81, 0x00, // Input (Data, Array, Absolute) 0x81, 0x00, // Input (Data, Array, Absolute)
0xC0 // End Collection 0xC0 // End Collection
# endif # 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 #endif