Rewrite with PsxNewLib

This commit is contained in:
CrazyRedMachine
2022-09-19 12:37:14 +02:00
parent 25134a9821
commit 5f53f826f7
10 changed files with 2463 additions and 2 deletions
+295
View File
@@ -0,0 +1,295 @@
#include "IIDXHID.h"
#include "POPNHID.h"
#include "PsxControllerHwSpi.h"
#include <inttypes.h>
//use doublepress hotkeys (select is E1, start is E2, double press select E3, triple press select is E1+E3, double press start is E4, triple press start is E2+E4)
#define WITH_DOUBLECLICK 1
//delay in µs between HID reports (note: 1000 for jkoc/popn, will be *15 for uskoc)
#define REPORT_DELAY 1000
#define COOLDOWN 4
IIDXHID_ IIDXHID;
POPNHID_ POPNHID;
const byte PIN_PS2_ATT = A2;
PsxControllerHwSpi<PIN_PS2_ATT> psx;
bool haveController = false;
int32_t tt_pos = 0;
typedef enum
{
TT_NEUTRAL = 0,
TT_UP,
TT_DOWN,
} tt_direction_t;
void update_encoder(tt_direction_t dir, uint8_t quantity) {
static tt_direction_t last_state;
static int cooldown = 0;
if (cooldown > 0)
cooldown--;
if (dir != last_state || cooldown == 0)
{
if (dir == TT_UP) {
tt_pos += quantity;
} else if (dir == TT_DOWN) {
tt_pos -= quantity;
}
cooldown = COOLDOWN;
}
last_state = dir;
}
bool g_popn = false;
void setup() {
haveController = psx.begin();
delay(300);
psx.read();
PsxButtons psxButtons = psx.getButtonWord();
if ((psxButtons & PSB_PAD_LEFT) && (psxButtons & PSB_PAD_DOWN) && (psxButtons & PSB_PAD_RIGHT))
{
g_popn = true;
PluggableUSB().plug(&POPNHID);
} else {
PluggableUSB().plug(&IIDXHID);
}
}
void buttonRead(uint32_t *button_state, tt_direction_t *tt_dir)
{
PsxButtons psxButtons = 0;
if (!haveController) {
if (psx.begin ()) {
delay (300);
haveController = true;
}
} else {
if (!psx.read ()) {
haveController = false;
} else {
psxButtons = psx.getButtonWord();
}
}
uint32_t buttonsState = 0;
if (g_popn)
{
buttonsState |= !!(psxButtons & PSB_TRIANGLE) << 0;
buttonsState |= !!(psxButtons & PSB_CIRCLE) << 1;
buttonsState |= !!(psxButtons & PSB_R1) << 2;
buttonsState |= !!(psxButtons & PSB_CROSS) << 3;
buttonsState |= !!(psxButtons & PSB_L1) << 4;
buttonsState |= !!(psxButtons & PSB_SQUARE) << 5;
buttonsState |= !!(psxButtons & PSB_R2) << 6;
buttonsState |= !!(psxButtons & PSB_PAD_UP) << 7;
buttonsState |= !!(psxButtons & PSB_L2) << 8;
buttonsState |= !!(psxButtons & PSB_SELECT) << 9;
buttonsState |= !!(psxButtons & PSB_START) << 10;
*button_state = buttonsState;
return;
}
/*
1 square
2 L1
3 cross
4 R1
5 circle
6 L2
7 left
sel sel
stt start
tt cw up
tt ccw down
*/
buttonsState |= !!(psxButtons & PSB_SQUARE) << 0;
buttonsState |= !!(psxButtons & PSB_L1) << 1;
buttonsState |= !!(psxButtons & PSB_CROSS) << 2;
buttonsState |= !!(psxButtons & PSB_R1) << 3;
buttonsState |= !!(psxButtons & PSB_CIRCLE) << 4;
buttonsState |= !!(psxButtons & PSB_L2) << 5;
buttonsState |= !!(psxButtons & PSB_PAD_LEFT) << 6;
buttonsState |= !!(psxButtons & PSB_START) << 8; /* E1 */
buttonsState |= !!(psxButtons & PSB_SELECT) << 9; /* E2 */
if (psxButtons & PSB_PAD_UP)
{
*tt_dir = TT_DOWN;
buttonsState |= ((uint32_t)DPAD_UP) << 16;
}
else if (psxButtons & PSB_PAD_DOWN)
{
*tt_dir = TT_UP;
buttonsState |= ((uint32_t)DPAD_DOWN) << 16;
}
else
{
*tt_dir = TT_NEUTRAL;
buttonsState |= ((uint32_t)DPAD_NEUTRAL) << 16;
}
#if WITH_DOUBLECLICK == 1
static uint16_t previous_select = 0;
static bool select_double = false;
static bool select_triple = false;
if (buttonsState >> 9 & 1)
{
if (previous_select)
{
if (previous_select < 18)
{
if (select_double) select_triple = true;
else select_double = true;
}
}
previous_select = 18;
}
else
{
if (previous_select)
{
previous_select--;
if (previous_select == 0)
{
select_double = false;
}
}
if (select_triple)
{
select_double = false;
select_triple = false;
}
}
//change output based on double/triple click
if (select_triple)
{
buttonsState |= (uint32_t)1 << 10; /* E3 */
}
else if (select_double)
{
buttonsState |= (uint32_t)1 << 10;
buttonsState &= ~((uint32_t)1 << 9);
}
static uint16_t previous_start = 0;
static bool start_double = false;
static bool start_triple = false;
if (buttonsState >> 8 & 1)
{
if (previous_start)
{
if (previous_start < 18)
{
if (start_double) start_triple = true;
else start_double = true;
}
}
previous_start = 18;
}
else
{
if (previous_start)
{
previous_start--;
if (previous_start == 0)
{
start_double = false;
}
}
if (start_triple)
{
start_double = false;
start_triple = false;
}
}
//change output based on double/triple click
if (start_triple)
{
buttonsState |= (uint32_t)1 << 11; /* E4 */
}
else if (start_double)
{
buttonsState |= (uint32_t)1 << 11;
buttonsState &= ~((uint32_t)1 << 8);
}
#endif
*button_state = buttonsState;
}
void loop() {
static unsigned long lastReport = 0;
static unsigned long lastPool = 0;
static unsigned long currTime = 0;
static int32_t saved_tt_stop = tt_pos;
static uint32_t curr_button_state;
static tt_direction_t curr_tt_dir;
static int curr_tt_pos;
currTime = micros();
if (g_popn)
{
if ( (currTime - lastReport) >= REPORT_DELAY )
{
uint32_t button_state = 0;
buttonRead(&button_state, NULL);
POPNHID.sendState(button_state);
lastReport = currTime;
}
return;
}
/* IIDX MODE */
// Limit the encoder from 0 to ENCODER_PPR
if (tt_pos >= ENCODER_PPR) {
tt_pos = 1;
} else if (tt_pos <= 0) {
tt_pos = ENCODER_PPR - 1;
}
if ( (currTime - lastPool) >= 15*REPORT_DELAY ) /* 15ms for USKOC compatibility */
{
buttonRead(&curr_button_state, &curr_tt_dir);
lastPool = currTime;
}
if ( (currTime - lastReport) >= REPORT_DELAY )
{
if ((curr_button_state >> 9) & 1 || (curr_button_state >> 10) & 1)
{
static uint8_t upd_sel_cooldown = 0;
if (upd_sel_cooldown) upd_sel_cooldown--;
if (upd_sel_cooldown == 0)
{
update_encoder(curr_tt_dir, 1);
upd_sel_cooldown = 8;
}
} else {
static uint8_t upd_cooldown = 2;
if (upd_cooldown) upd_cooldown--;
if (upd_cooldown == 0)
{
update_encoder(curr_tt_dir, 1);
upd_cooldown = 2;
}
}
IIDXHID.send_state(curr_button_state, tt_pos);
lastReport = currTime;
}
}
+249
View File
@@ -0,0 +1,249 @@
#include "IIDXHID.h"
#define BUTTON_PADDING (8 - (NUMBER_OF_BUTTONS % 8))
#define LED_PADDING (8 - (NUMBER_OF_LEDS % 8))
uint8_t usb_data[128];
uint16_t lamp_hid_state = 0;
uint8_t extern led_pins[];
bool extern hid_reactive_autoswitch;
/* HID string and device descriptor */
const DeviceDescriptor PROGMEM USB_DeviceDescriptorIAD =
D_DEVICE(0xEF, 0x02, 0x01, 64, 0x1ccf, 0x8048, 0x100, IMANUFACTURER, IPRODUCT, ISERIAL, 1);
const char* const PROGMEM String_Manufacturer = "Konami Amusement";
const char* const PROGMEM String_Product = "beatmania IIDX controller premium model";
const char* const PROGMEM String_Serial = "IIDX";
const char* const PROGMEM LEDString_00 = "Button 1";
const char* const PROGMEM LEDString_01 = "Button 2";
const char* const PROGMEM LEDString_02 = "Button 3";
const char* const PROGMEM LEDString_03 = "Button 4";
const char* const PROGMEM LEDString_04 = "Button 5";
const char* const PROGMEM LEDString_05 = "Button 6";
const char* const PROGMEM LEDString_06 = "Button 7";
const char* const PROGMEM LEDString_07 = "Misc button 1";
const char* const PROGMEM LEDString_08 = "Misc button 2";
const char* const PROGMEM LEDString_09 = "Misc button 3";
const char* const PROGMEM LEDString_10 = "Misc button 4";
const char* const PROGMEM TTString = "TT Sensitivity";
const char* String_indiv[] = { LEDString_00, LEDString_01, LEDString_02, LEDString_03, LEDString_04, LEDString_05, LEDString_06, LEDString_07, LEDString_08, LEDString_09, LEDString_10, TTString };
uint8_t STRING_ID_Count = 12;
static const uint8_t PROGMEM hid_report[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x05, // REPORT_ID (5)
// HAT Switch (1 nibble)
0x05, 0x01, // USAGE_PAGE (HAT)
0x25, 0x07, // LOGICAL_MAXIMUM (7)
0x46, 0x3B, 0x01, //
0x75, 0x04, // REPORT_SIZE (4)
0x95, 0x01, // REPORT_COUNT (1)
0x65, 0x14, // UNIT (None)
0x09, 0x39, // USAGE (57)
0x81, 0x42, // INPUT (Data,Var,Abs)
0x65, 0x00, // UNIT (None)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x01, // INPUT (Data,Var,Abs)
/* Buttons */
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, NUMBER_OF_BUTTONS, // USAGE_MAXIMUM (Button NUMBER_OF_BUTTONS)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, NUMBER_OF_BUTTONS, // REPORT_COUNT (NUMBER_OF_BUTTONS)
0x55, 0x00, // UNIT_EXPONENT (0)
0x65, 0x00, // UNIT (None)
0x81, 0x02, // INPUT (Data,Var,Abs)
/* Buttons END */
/* Buttons padding */
0x75, 0x01, // REPORT_SIZE (1)
0x95, BUTTON_PADDING, // REPORT_COUNT (BUTTON_PADDING)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
/* Buttons padding END */
/* Encoder */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (Pointer)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xFF, 0, // LOGICAL_MAXIMUM (512r)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x01, // REPORT_COUNT (1)
0xa1, 0x00, // COLLECTION (Physical)
0x09, 0x30, // USAGE (X)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
/* Encoder END*/
0x85, 0x04, // REPORT_ID (4)
/* LEDs begin */
0x05, 0x0a, // USAGE_PAGE (Ordinals)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, NUMBER_OF_LEDS, // REPORT_COUNT (NUMBER_OF_LEDS)
0xa1, 0x02, // COLLECTION (Logical)
0x89, 0x04, // STRING_MINIMUM (4)
0x99, 0x0e, // STRING_MAXIMUM (14)
0x09, 0x01, // USAGE (Instance 1)
0x09, 0x02, // USAGE (Instance 2)
0x09, 0x03, // USAGE (Instance 3)
0x09, 0x04, // USAGE (Instance 4)
0x09, 0x05, // USAGE (Instance 5)
0x09, 0x06, // USAGE (Instance 6)
0x09, 0x07, // USAGE (Instance 7)
0x09, 0x08, // USAGE (Instance 8)
0x09, 0x09, // USAGE (Instance 9)
0x09, 0x0a, // USAGE (Instance 10)
0x09, 0x0b, // USAGE (Instance 11)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
/* LEDs END */
/* LEDs padding */
0x75, 0x01, // REPORT_SIZE (1)
0x95, LED_PADDING, // REPORT_COUNT (BUTTON_PADDING)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
/* LEDs padding END */
0xc0 // END_COLLECTION
};
static bool SendControl(uint8_t d) {
return USB_SendControl(0, &d, 1) == 1;
}
static bool USB_SendStringDescriptor(const char *string_P, uint8_t string_len, uint8_t flags) {
SendControl(2 + string_len * 2);
SendControl(3);
bool pgm = flags & TRANSFER_PGM;
for (uint8_t i = 0; i < string_len; i++) {
bool r = SendControl(pgm ? pgm_read_byte(&string_P[i]) : string_P[i]);
r &= SendControl(0); // high byte
if (!r) {
return false;
}
}
return true;
}
IIDXHID_::IIDXHID_() : PluggableUSBModule(1, 1, epType) {
epType[0] = EP_TYPE_INTERRUPT_IN;
//PluggableUSB().plug(this);
}
int IIDXHID_::getInterface(byte* interface_count) {
*interface_count += 1;
HIDDescriptor hid_interface = {
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
D_HIDREPORT(sizeof(hid_report)),
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)
};
return USB_SendControl(0, &hid_interface, sizeof(hid_interface));
}
int IIDXHID_::getDescriptor(USBSetup& setup) {
if (setup.wValueH == USB_DEVICE_DESCRIPTOR_TYPE) {
return USB_SendControl(TRANSFER_PGM, (const uint8_t*)&USB_DeviceDescriptorIAD, sizeof(USB_DeviceDescriptorIAD));
}
if (setup.wValueH == USB_STRING_DESCRIPTOR_TYPE) {
if (setup.wValueL == IPRODUCT) {
return USB_SendStringDescriptor(String_Product, strlen(String_Product), 0);
}
else if (setup.wValueL == IMANUFACTURER) {
return USB_SendStringDescriptor(String_Manufacturer, strlen(String_Manufacturer), 0);
}
else if (setup.wValueL == ISERIAL) {
return USB_SendStringDescriptor(String_Serial, strlen(String_Serial), 0);
}
else if (setup.wValueL >= STRING_ID_Base && setup.wValueL < (STRING_ID_Base + STRING_ID_Count)) {
return USB_SendStringDescriptor(String_indiv[setup.wValueL - STRING_ID_Base], strlen(String_indiv[setup.wValueL - STRING_ID_Base]), 0);
}
}
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) {
return 0;
}
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) {
return 0;
}
if (setup.wIndex != pluggedInterface) {
return 0;
}
return USB_SendControl(TRANSFER_PGM, hid_report, sizeof(hid_report));
}
bool IIDXHID_::setup(USBSetup& setup) {
if (pluggedInterface != setup.wIndex) {
return false;
}
uint8_t request = setup.bRequest;
uint8_t request_type = setup.bmRequestType;
if (request_type == REQUEST_DEVICETOHOST_CLASS_INTERFACE) {
return true;
}
if (request_type == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) {
if (request == HID_SET_REPORT) {
if (setup.wValueH == HID_REPORT_TYPE_OUTPUT) {
USB_RecvControl(usb_data, setup.wLength);
if (usb_data[0] == 4) {
lamp_hid_state = usb_data[2] << 8 | usb_data[1];
lastHidUpdate = millis();
// No need to write lights or update lightmode here (for autoswitch), main loop() will take care of it
}
return true;
}
}
}
return false;
}
unsigned long IIDXHID_::getLastHidUpdate() {
return lastHidUpdate;
}
void IIDXHID_::write_lights(uint32_t button_state, bool hid, bool reactive) {
if (!reactive) {
button_state = 0;
}
if (hid) {
button_state |= lamp_hid_state;
}
for (int i = 0; i < NUMBER_OF_LEDS; i++) {
digitalWrite(led_pins[i], ((button_state >> i) & 1));
}
}
int IIDXHID_::send_state(uint32_t button_state, int32_t turntable_state) {
uint8_t data[6];
data[0] = (uint8_t) 5;
data[1] = (uint8_t) (button_state >> 16) & 0xFF; /* HAT SWITCH */
data[2] = (uint8_t) (button_state & 0xFF);
data[3] = (uint8_t) (button_state >> 8) & 0xFF;
data[4] = (uint8_t) (turntable_state & 0xFF);
data[5] = (uint8_t) (turntable_state >> 8) & 0xFF;
return USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, 6);
}
+30
View File
@@ -0,0 +1,30 @@
#include "HID.h"
#define NUMBER_OF_LEDS 11
#define NUMBER_OF_BUTTONS 12
#define ENCODER_PPR 255
#define EPTYPE_DESCRIPTOR_SIZE uint8_t
#define STRING_ID_Base 4
#define DPAD_DOWN 0x04
#define DPAD_UP 0x00
#define DPAD_NEUTRAL 0x08
class IIDXHID_ : public PluggableUSBModule {
public:
IIDXHID_();
void write_lights(uint32_t button_state, bool hid, bool reactive);
int send_state(uint32_t button_state, int32_t turntable_state);
unsigned long getLastHidUpdate();
protected:
EPTYPE_DESCRIPTOR_SIZE epType[1];
unsigned long lastHidUpdate = 0;
int getInterface(uint8_t* interface_count);
int getDescriptor(USBSetup& setup);
bool setup(USBSetup& setup);
};
extern IIDXHID_ IIDXHID;
+350
View File
@@ -0,0 +1,350 @@
/* Arduino-HID-Lighting-Library
This Arduino-HID-Lighting-Library is derived from Arduino-HID-Lighting, whose copyriht owner is mon.
More information about Arduino-HID-Lighting you can find under:
mon's Arduino-HID-Lighting
https://github.com/mon/Arduino-HID-Lighting
2018 (C) Arduino-HID-Lighting-Library, Knuckleslee
*/
#include "POPNHID.h"
byte extern LightPins[];
byte extern DipPins[];
/* HID DESCRIPTOR */
static const byte PROGMEM _hidReportPOPN[] = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x05, /* USAGE (Game Pad) */
0xa1, 0x01, /* COLLECTION (Application) */
/*Buttons */
0x85, 0x04, /* REPORT_ID 4 */
0x05, 0x09, /* USAGE_PAGE (Button) */
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
0x29, 0x0d, /* USAGE_MAXIMUM (Button 13)*/
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x95, 0x0b, /* REPORT_COUNT (11) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
/* Reserved bits */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x05, /* REPORT_SIZE (5) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
/*Lights */
0x85, 0x05, /* REPORT_ID 5*/
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
/*Led 1 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x01, /* USAGE (Instance 1) */
0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x79, 0x04, /* STRING INDEX (4) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
/*Led 2 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x02, /* USAGE (Instance 2) */
0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x79, 0x05,
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
/*Led 3 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x03, /* USAGE (Instance 3) */
0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x79, 0x06,
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
/*Led 4 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x04, /* USAGE (Instance 4) */
0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x79, 0x07,
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
/*Led 5 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x05, /* USAGE (Instance 5) */
0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x79, 0x08,
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
/*Led 6 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x06, /* USAGE (Instance 6) */
0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x79, 0x09,
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
/*Led 7 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x07, /* USAGE (Instance 7) */
0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x79, 0x0a,
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
/*Led 8 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x08, /* USAGE (Instance 8) */
0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x79, 0x0b,
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
/*Led 9 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x09, 0x09, /* USAGE (Instance 9) */
0xa1, 0x02, /* COLLECTION (Logical) */
0x05, 0x08, /* USAGE_PAGE (LEDs) */
0x09, 0x4b, /* USAGE (Generic Indicator 1) */
0x79, 0x0c,
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x02, /* OUTPUT (Data,Var,Abs) */
0xc0, /* END_COLLECTION */
/* Reserved 23 bits */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x17, /* REPORT_SIZE (23) */
0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */
/*Light mode switch*/
0x85, 0x07, /* REPORT_ID 7 */
0x05, 0x0a, /* USAGE_PAGE (Ordinals) */
0x19, 0x00, /* USAGE_MINIMUM (Button 1) */
0x29, 0x04, /* USAGE_MAXIMUM (Button 4)*/
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x04, /* LOGICAL_MAXIMUM (4) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x04, /* REPORT_SIZE (4) */
0xb1, 0x02, /* FEATURE (Data,Var,Abs) */
/* Reserved bits */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x04, /* REPORT_SIZE (4) */
0xb1, 0x03, /* FEATURE (Cnst,Var,Abs) */
/*Footer */
0xc0 /* END_COLLECTION */
};
static bool SendControl(uint8_t d)
{
return USB_SendControl(0, &d, 1) == 1;
}
static bool USB_SendStringDescriptor(const char *string_P, uint8_t string_len, uint8_t flags) {
SendControl(2 + string_len * 2);
SendControl(3);
bool pgm = flags & TRANSFER_PGM;
for (uint8_t i = 0; i < string_len; i++) {
bool r = SendControl(pgm ? pgm_read_byte(&string_P[i]) : string_P[i]);
r &= SendControl(0); // high byte
if (!r) {
return false;
}
}
return true;
}
/* HID Light descriptor strings */
const char* const PROGMEM String_Manufacturer = "CrazyRedMachine";
const char* const PROGMEM String_Product = "Pop'n controller";
const char* const PROGMEM String_Serial = "POPNHID";
const char* const PROGMEM LEDString_00 = "Button 1";
const char* const PROGMEM LEDString_01 = "Button 2";
const char* const PROGMEM LEDString_02 = "Button 3";
const char* const PROGMEM LEDString_03 = "Button 4";
const char* const PROGMEM LEDString_04 = "Button 5";
const char* const PROGMEM LEDString_05 = "Button 6";
const char* const PROGMEM LEDString_06 = "Button 7";
const char* const PROGMEM LEDString_07 = "Button 8";
const char* const PROGMEM LEDString_08 = "Button 9";
const char* LEDString_indiv[] = {LEDString_00, LEDString_01, LEDString_02, LEDString_03, LEDString_04, LEDString_05, LEDString_06, LEDString_07, LEDString_08};
uint8_t STRING_ID_LED_Count = 9;
const DeviceDescriptor PROGMEM USB_DeviceDescriptorIAD =
D_DEVICE(0xEF, 0x02, 0x01, 64, 0x1ccf, 0x1000, 0x100, IMANUFACTURER, IPRODUCT, ISERIAL, 1);
/* PluggableUSBModule IMPLEMENTATION */
POPNHID_::POPNHID_(void) : PluggableUSBModule(1, 1, epType) {
epType[0] = EP_TYPE_INTERRUPT_IN;
//PluggableUSB().plug(this);
}
int POPNHID_::getInterface(byte* interfaceCount) {
*interfaceCount += 1; // uses 1
HIDDescriptor hidInterface = {
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
D_HIDREPORT(sizeof(_hidReportPOPN)),
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, USB_EP_BINTERVAL)
};
return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
}
int POPNHID_::getDescriptor(USBSetup& setup)
{
if (setup.wValueH == USB_DEVICE_DESCRIPTOR_TYPE) {
return USB_SendControl(TRANSFER_PGM, (const uint8_t*)&USB_DeviceDescriptorIAD, sizeof(USB_DeviceDescriptorIAD));
}
if (setup.wValueH == USB_STRING_DESCRIPTOR_TYPE) {
if (setup.wValueL == IPRODUCT) {
return USB_SendStringDescriptor(String_Product, strlen(String_Product), 0);
}
else if (setup.wValueL == IMANUFACTURER) {
return USB_SendStringDescriptor(String_Manufacturer, strlen(String_Manufacturer), 0);
}
else if (setup.wValueL == ISERIAL) {
return USB_SendStringDescriptor(String_Serial, strlen(String_Serial), 0);
}
else if (setup.wValueL >= STRING_ID_LED_Base && setup.wValueL < (STRING_ID_LED_Base + STRING_ID_LED_Count)) {
return USB_SendStringDescriptor(LEDString_indiv[setup.wValueL - STRING_ID_LED_Base], strlen(LEDString_indiv[setup.wValueL - STRING_ID_LED_Base]), 0);
}
}
// Check if this is a HID Class Descriptor request
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) {
return 0;
}
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) {
return 0;
}
// In a HID Class Descriptor wIndex contains the interface number
if (setup.wIndex != pluggedInterface) {
return 0;
}
return USB_SendControl(TRANSFER_PGM, _hidReportPOPN, sizeof(_hidReportPOPN));
}
bool POPNHID_::setup(USBSetup& setup)
{
if (pluggedInterface != setup.wIndex) {
return false;
}
byte request = setup.bRequest;
byte requestType = setup.bmRequestType;
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
{
/* lightmode info */
if (setup.wValueH == HID_REPORT_TYPE_FEATURE && setup.wValueL == 7) {
uint8_t lm_data[2] = {7, 0x00};
lm_data[1] = lightMode;
USB_SendControl(0, &lm_data, 2);
return true;
}
return true;
}
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) {
if (request == HID_SET_REPORT) {
if (setup.wValueH == HID_REPORT_TYPE_OUTPUT && setup.wLength == 5) {
lastHidUpdate = millis();
USB_RecvControl(led_data, 5);
return true;
}
else if (setup.wValueH == HID_REPORT_TYPE_FEATURE ) {
/* lightmode switch */
byte usb_data[5];
USB_RecvControl(usb_data, setup.wLength);
if (usb_data[0] == 7)
{
mode_data = usb_data[1];
return true;
}
}
}
}
return false;
}
/* CUSTOM POPN FUNCTIONS */
uint8_t POPNHID_::getLightMode() {
return lightMode;
}
unsigned long POPNHID_::getLastHidUpdate() {
return lastHidUpdate;
}
uint8_t POPNHID_::setLightMode(uint8_t mode) {
uint8_t effective_mode = mode;
if ((effective_mode > 4)) {
effective_mode = 2;
}
lightMode = effective_mode;
mode_data = lightMode;
return lightMode;
}
void POPNHID_::updateLightMode() {
uint8_t mode = mode_data;
if (mode != lightMode) {
mode_data = setLightMode(mode);
}
}
void POPNHID_::updateLeds(uint32_t buttonsState, bool invert) {
uint32_t* bitfield = (uint32_t*) & (led_data[1]);
uint32_t leds = (*bitfield | buttonsState);
if (invert)
leds = ~leds;
for (int i = 0; i < 9; i++) {
if (leds >> i & 1)
digitalWrite(LightPins[i], HIGH);
else
digitalWrite(LightPins[i], LOW);
}
}
int POPNHID_::sendState(uint32_t buttonsState) {
uint8_t data[3];
data[0] = (uint8_t) 4; //report id
data[1] = (uint8_t) (buttonsState & 0xFF);
data[2] = (uint8_t) (buttonsState >> 8) & 0xFF;
return USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, 3);
}
+61
View File
@@ -0,0 +1,61 @@
#include "HID.h"
#define USB_EP_BINTERVAL 1
#define EPTYPE_DESCRIPTOR_SIZE uint8_t
#define STRING_ID_LED_Base 4
class POPNHID_ : public PluggableUSBModule {
public:
POPNHID_(void);
/**
Updates the led status based on led_data (HID report received) and button states
param[in] buttonState bitfield with currently pressed buttons (used to force additional lights for mixed mode)
param[in] invert set to true to invert on/off status (used for invert lightmode)
*/
void updateLeds(uint32_t buttonsState, bool invert);
/**
Sends the gamepad button states to the PC as an HID report
param[in] buttonsState bitfield with currently pressed buttons
return USB_Send() return value
*/
int sendState(uint32_t buttonsState);
/**
Changes the lightMode if a received HID report asks for it
*/
void updateLightMode();
/**
getter and setter for lightMode protected field.
*/
uint8_t getLightMode();
uint8_t setLightMode(uint8_t mode);
/**
getter for lastHidUpdate protected field.
*/
unsigned long getLastHidUpdate();
protected:
/* current lightMode (0 = reactive, 1 = HID only, 2 = mixed (HID+reactive auto-switch), 3 = combined (HID+button presses), 4 = combined invert) */
uint8_t lightMode = 2;
/* timestamp of last received HID report for lightMode 3 */
unsigned long lastHidUpdate = 0;
/* byte array to receive HID reports from the PC */
byte led_data[5];
byte mode_data;
/* Implementation of the PUSBListNode */
EPTYPE_DESCRIPTOR_SIZE epType[1];
uint8_t protocol;
uint8_t idle;
int getInterface(uint8_t* interfaceCount);
int getDescriptor(USBSetup& setup);
bool setup(USBSetup& setup);
};
extern POPNHID_ POPNHID;
+89
View File
@@ -0,0 +1,89 @@
#include "PsxNewLib.h"
#include <DigitalIO.h>
/** \brief Attention Delay
*
* Time between attention being issued to the controller and the first clock
* edge (us).
*/
const byte ATTN_DELAY = 50;
/** \brief Clock Period
*
* Inverse of clock frequency, i.e.: time for a *full* clock cycle, from falling
* edge to the next falling edge.
*/
const byte CLK_PERIOD = 40;
// Must be < CLK_PERIOD / 2
const byte HOLD_TIME = 2;
template <uint8_t PIN_ATT, uint8_t PIN_CMD, uint8_t PIN_DAT, uint8_t PIN_CLK>
class PsxControllerBitBang: public PsxController {
private:
DigitalPin<PIN_ATT> att;
DigitalPin<PIN_CLK> clk;
DigitalPin<PIN_CMD> cmd;
DigitalPin<PIN_DAT> dat;
protected:
virtual void attention () override {
att.low ();
delayMicroseconds (ATTN_DELAY);
}
virtual void noAttention () override {
//~ delayMicroseconds (5);
cmd.high ();
clk.high ();
att.high ();
delayMicroseconds (ATTN_DELAY);
}
virtual byte shiftInOut (const byte out) override {
byte in = 0;
// 1. The clock is held high until a byte is to be sent.
for (byte i = 0; i < 8; ++i) {
// 2. When the clock edge drops low, the values on the line start to
// change
clk.low ();
delayMicroseconds (HOLD_TIME);
if (bitRead (out, i)) {
cmd.high ();
} else {
cmd.low ();
}
delayMicroseconds (CLK_PERIOD / 2 - HOLD_TIME);
// 3. When the clock goes from low to high, value are actually read
clk.high ();
delayMicroseconds (HOLD_TIME);
if (dat) {
bitSet (in, i);
}
delayMicroseconds (CLK_PERIOD / 2 - HOLD_TIME);
}
return in;
}
public:
virtual boolean begin () override {
att.config (OUTPUT, HIGH); // HIGH -> Controller not selected
cmd.config (OUTPUT, HIGH);
clk.config (OUTPUT, HIGH);
dat.config (INPUT, HIGH); // Enable pull-up
return PsxController::begin ();
}
};
+64
View File
@@ -0,0 +1,64 @@
#include "PsxNewLib.h"
#include <SPI.h>
#include <DigitalIO.h>
/** \brief Attention Delay
*
* Time between attention being issued to the controller and the first clock
* edge (us).
*/
const byte ATTN_DELAY = 50;
// Set up the speed, data order and data mode
static SPISettings spiSettings (250000, LSBFIRST, SPI_MODE3);
template <uint8_t PIN_ATT>
class PsxControllerHwSpi: public PsxController {
private:
DigitalPin<PIN_ATT> att;
DigitalPin<MOSI> cmd;
DigitalPin<MISO> dat;
DigitalPin<SCK> clk;
protected:
virtual void attention () override {
att.low ();
SPI.beginTransaction (spiSettings);
delayMicroseconds (ATTN_DELAY);
}
virtual void noAttention () override {
//~ delayMicroseconds (5);
SPI.endTransaction ();
// Make sure CMD and CLK sit high
cmd.high ();
clk.high ();
att.high ();
delayMicroseconds (ATTN_DELAY);
}
virtual byte shiftInOut (const byte out) override {
return SPI.transfer (out);
}
public:
virtual boolean begin () override {
att.config (OUTPUT, HIGH); // HIGH -> Controller not selected
/* We need to force these at startup, that's why we need to know which
* pins are used for HW SPI. It's a sort of "start condition" the
* controller needs.
*/
cmd.config (OUTPUT, HIGH);
clk.config (OUTPUT, HIGH);
dat.config (INPUT, HIGH); // Enable pull-up
SPI.begin ();
return PsxController::begin ();
}
};
+1
View File
@@ -0,0 +1 @@
// Congratulations! You've found the end of the rainbow!
File diff suppressed because it is too large Load Diff
+88 -2
View File
@@ -1,2 +1,88 @@
# IIDXPS2toUSB
IIDX PS2 (JKOC/USKOC/KASC) Controller USB Adapter with Infinitas/Ultimate Mobile compatibility
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?hosted_button_id=WT735CX4UMZ9U)
# BEMANI PSX Adapter
USB Adapter for Playstation IIDX and Pop'n Music Controllers
Generic Playstation to USB adapters are not working correctly with Bemani controllers :
- IIDX controllers have a digital TT instead of an analog axis, and lack the correct descriptors for Infinitas or Ultimate Mobile compatibility
- Pop'n Music controllers hold left+down+right constantly and the right yellow button is mapped to up, which confuses most adapters into making the right yellow button a normally closed "down" input.
This code solves these issues and auto-detects whether a Pop'n Music controller is plugged.
You can buy a cheap "Dualshock extension cable" or a dualshock breakout board to build an external adapter box, or you can buy a pre-made adapter from me to support my work :)
## Demo
https://www.instagram.com/p/CHVLleylqFl/
## Acknowledgments
The PSX pad read code is using [PsxNewLib by SukkoPera](https://github.com/SukkoPera/PsxNewLib/).
## Features
Pop'n Music and IIDX controller modes are auto detected (you must plug the PSX controller to the adapter before plugging the USB)
### Pop'n Music mode
- Compatible with Pop'n Music Lively natively (wired, **not bluetooth**)
- All buttons are recognized as simple buttons from 1 to 9 then select and start.
- It should be compatible with Lively without any remapping, but also with UltimatePopnController's ezusb.dll for other purposes
### IIDX mode
- Compatible with Infinitas and Ultimate Mobile natively (wired, **not bluetooth**)
- Buttons 1 to 7 are mapped to the first 7 buttons, then E1-E4 are mapped to buttons 9-12.
- The TT is mapped to the X analog axis.
- Still includes a HAT switch to retrieve the digital TT signals (useful for MAME)
- Select acts as E1, Start as E2.
- Double (resp. triple) press Select acts as E3 (resp. E1+E3), double (resp. triple) press Start acts as E4 (resp. E2+E4)
- **Note**: digital TT signal is lingering a bit too much, which causes inertia on the analog axis. This is a **hardware limitation** from the controller itself. In order to make song wheel selection less painful, holding SELECT allows for very slow increments. This also means that it's important to keep alternating direction when playing a rapid succession of scratch notes to avoid dropped inputs.
## Building Instructions
- Download Arduino IDE,
- Build and Flash for your ATMEGA32U4 Board
- Have Fun
## Pinout
When looking at the plug of the *controller cable* :
```
1 2 3 4 5 6 7 8 9
-------------------------------
| o o o | o o o | o o o | (at the Controller)
\_____________________________/
1 : DAT -> to arduino MISO (on ICSP header) (with 1k ohm pullup resistor*)
2 : CMD -> to arduino MOSI (on ICSP header)
4 : GND -> to arduino GND
5 : 3.3V -> to arduino 3.3V or 5V (tested on Pop'n KASC, minicon, and IIDX JKOC)
6 : Attention -> to arduino digital pin 2
7 : SCK -> to arduino SCK (on ICSP header)
* Note: 1k ohm pullup resistor to 5V means you also have to put a 1k ohm resistor between 5V and MISO.
See the demo pic.
```
## Donation
If this project helps you and you want to give back, you can help me with my future projects.
While not necessary, donations are much appreciated and will only go towards funding future github projects (arcade hardware ain't cheap :( ).
Of course you'll also receive my gratitude and I'll remember you if you post a feature request ;)
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?hosted_button_id=WT735CX4UMZ9U)