This commit is contained in:
Hendrik Osterholz 2021-06-29 21:05:48 -07:00 committed by GitHub
commit 1561678473
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 539 additions and 0 deletions

View File

@ -0,0 +1,156 @@
/* Gamepad.cpp
*
* Based on the advanced HID library for Arduino:
* https://github.com/NicoHood/HID
* Copyright (c) 2014-2015 NicoHood
*
* Copyright (c) 2021 Hendrik Osterholz
*
* GNU GENERAL PUBLIC LICENSE
* Version 3, 29 June 2007
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "Gamepad.h"
static const uint8_t _hidReportDescriptor[] PROGMEM = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick) (Maybe change to gamepad? I don't think so but...)
0xa1, 0x01, // COLLECTION (Application)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x0c, // USAGE_MAXIMUM (Button 12)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x0c, // REPORT_COUNT (12)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1) ; pad out the bits into a number divisible by 8
0x75, 0x04, // REPORT_SIZE (4)
0x81, 0x03, // INPUT (Const,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0xff, // LOGICAL_MINIMUM (-1)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x02, // REPORT_COUNT (2)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
};
Gamepad_::Gamepad_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1)
{
epType[0] = EP_TYPE_INTERRUPT_IN;
PluggableUSB().plug(this);
}
int Gamepad_::getInterface(uint8_t* 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(_hidReportDescriptor)),
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)
};
return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
}
int Gamepad_::getDescriptor(USBSetup& setup)
{
// 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 cointains the interface number
if (setup.wIndex != pluggedInterface) { return 0; }
// Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol
// due to the USB specs, but Windows and Linux just assumes its in report mode.
protocol = HID_REPORT_PROTOCOL;
return USB_SendControl(TRANSFER_PGM, _hidReportDescriptor, sizeof(_hidReportDescriptor));
}
bool Gamepad_::setup(USBSetup& setup)
{
if (pluggedInterface != setup.wIndex) {
return false;
}
uint8_t request = setup.bRequest;
uint8_t requestType = setup.bmRequestType;
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
{
if (request == HID_GET_REPORT) {
// TODO: HID_GetReport();
return true;
}
if (request == HID_GET_PROTOCOL) {
// TODO: Send8(protocol);
return true;
}
}
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE)
{
if (request == HID_SET_PROTOCOL) {
protocol = setup.wValueL;
return true;
}
if (request == HID_SET_IDLE) {
idle = setup.wValueL;
return true;
}
if (request == HID_SET_REPORT)
{
}
}
return false;
}
void Gamepad_::reset()
{
_GamepadReport.X = 0;
_GamepadReport.Y = 0;
_GamepadReport.buttons = 0;
this->send();
}
void Gamepad_::send()
{
USB_Send(pluggedEndpoint | TRANSFER_RELEASE, &_GamepadReport, sizeof(GamepadReport));
}
uint8_t Gamepad_::getShortName(char *name)
{
if(!next)
{
strcpy(name, gp_serial);
return strlen(name);
}
return 0;
}

View File

@ -0,0 +1,60 @@
/* Gamepad.h
*
* Based on the advanced HID library for Arduino:
* https://github.com/NicoHood/HID
* Copyright (c) 2014-2015 NicoHood
*
* Copyright (c) 2021 Hendrik Osterholz
*
* GNU GENERAL PUBLIC LICENSE
* Version 3, 29 June 2007
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "HID.h"
extern const char* gp_serial;
typedef struct {
uint16_t buttons;
int8_t X;
int8_t Y;
} GamepadReport;
class Gamepad_ : public PluggableUSBModule
{
private:
uint8_t reportId;
protected:
int getInterface(uint8_t* interfaceCount);
int getDescriptor(USBSetup& setup);
uint8_t getShortName(char *name);
bool setup(USBSetup& setup);
uint8_t epType[1];
uint8_t protocol;
uint8_t idle;
public:
GamepadReport _GamepadReport;
Gamepad_(void);
void reset(void);
void send();
};

View File

@ -0,0 +1,99 @@
/* DaemonBite PSX USB Adapter
* Author: Hendrik Osterholz
*
* Copyright (c) 2021 Hendrik Osterholz
*
* GNU GENERAL PUBLIC LICENSE
* Version 3, 29 June 2007
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "Gamepad.h"
#define GAMEPAD_COUNT 1 // Set to 1 or 2 depending if you want to make a 1 or 2 port adapter
#define GAMEPAD_COUNT_MAX 4
// ATT: 20 chars max (including NULL at the end) according to Arduino source code.
// Additionally serial number is used to differentiate arduino projects to have different button maps!
const char *gp_serial = "PSX to USB";
#include "Psx.h"
#define dataPin 18
#define cmndPin 2
#define attPin 3
#define clockPin 4
/* -------------------------------------------------------------------------
PSX controller socket (looking face-on at the front of the socket):
_________________________
| 9 8 7 | 6 5 4 | 3 2 1 |
\_______________________/
PSX controller plug (looking face-on at the front of the controller plug):
_________________________
| 1 2 3 | 4 5 6 | 7 8 9 |
\_______________________/
PSX Arduino Pro Micro
--------------------------------------
1 DATA A0 PF7
2 CMD 2 PD1
3 +7.6V
4 GND GND
5 VCC VCC
6 ATT 3 PD0
7 CLK 4 PD4
8 N/C
9 ACK
------------------------------------------------------------------------- */
Psx Psx;
// Set up USB HID gamepads
Gamepad_ Gamepad[GAMEPAD_COUNT];
// Controllers
uint16_t buttons[GAMEPAD_COUNT_MAX] = {0,0,0,0};
uint16_t buttonsPrev[GAMEPAD_COUNT_MAX] = {0,0,0,0};
uint8_t gp = 0;
void setup()
{
Psx.setupPins(dataPin, cmndPin, attPin, clockPin, 10);
}
void loop() { while(1)
{
// Send data to USB if values have changed
for(gp=0; gp<GAMEPAD_COUNT; gp++)
{
buttons[gp] = 0;
buttons[gp] = Psx.read();
// Has any buttons changed state?
if (buttons[gp] != buttonsPrev[gp] || buttons[gp] != buttonsPrev[gp] )
{
Gamepad[gp]._GamepadReport.buttons = buttons[gp] >> 4;
Gamepad[gp]._GamepadReport.Y = ((buttons[gp] & psxDown) >> 1) - ((buttons[gp] & psxUp) >> 3);
Gamepad[gp]._GamepadReport.X = ((buttons[gp] & psxRight) >> 2) - (buttons[gp] & psxLeft);
buttonsPrev[gp] = buttons[gp];
Gamepad[gp].send();
}
}
}}

97
PSXControllerUSB/Psx.cpp Normal file
View File

@ -0,0 +1,97 @@
/* PSX Controller Decoder Library (Psx.cpp)
Written by: Kevin Ahrendt June 22nd, 2008
Controller protocol implemented using Andrew J McCubbin's analysis.
http://www.gamesx.com/controldata/psxcont/psxcont.htm
Shift command is based on tutorial examples for ShiftIn and ShiftOut
functions both written by Carlyn Maw and Tom Igoe
http://www.arduino.cc/en/Tutorial/ShiftIn
http://www.arduino.cc/en/Tutorial/ShiftOut
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Psx.h"
Psx::Psx()
{
}
byte Psx::shift(byte _dataOut) // Does the actual shifting, both in and out simultaneously
{
_temp = 0;
_dataIn = 0;
for (_i = 0; _i <= 7; _i++)
{
if ( _dataOut & (1 << _i) ) digitalWrite(_cmndPin, HIGH); // Writes out the _dataOut bits
else digitalWrite(_cmndPin, LOW);
digitalWrite(_clockPin, LOW);
delayMicroseconds(_delay);
_temp = digitalRead(_dataPin); // Reads the data pin
if (_temp)
{
_dataIn = _dataIn | (B10000000 >> _i); // Shifts the read data into _dataIn
}
digitalWrite(_clockPin, HIGH);
delayMicroseconds(_delay);
}
return _dataIn;
}
void Psx::setupPins(byte dataPin, byte cmndPin, byte attPin, byte clockPin, byte delay)
{
pinMode(dataPin, INPUT);
digitalWrite(dataPin, HIGH); // Turn on internal pull-up
_dataPin = dataPin;
pinMode(cmndPin, OUTPUT);
_cmndPin = cmndPin;
pinMode(attPin, OUTPUT);
_attPin = attPin;
digitalWrite(_attPin, HIGH);
pinMode(clockPin, OUTPUT);
_clockPin = clockPin;
digitalWrite(_clockPin, HIGH);
_delay = delay;
}
unsigned int Psx::read()
{
digitalWrite(_attPin, LOW);
shift(0x01);
shift(0x42);
shift(0xFF);
_data1 = ~shift(0xFF);
_data2 = ~shift(0xFF);
digitalWrite(_attPin, HIGH);
_dataOut = (_data2 << 8) | _data1;
return _dataOut;
}

82
PSXControllerUSB/Psx.h Normal file
View File

@ -0,0 +1,82 @@
/* PSX Controller Decoder Library (Psx.h)
Written by: Kevin Ahrendt June 22nd, 2008
Controller protocol implemented using Andrew J McCubbin's analysis.
http://www.gamesx.com/controldata/psxcont/psxcont.htm
Shift command is based on tutorial examples for ShiftIn and ShiftOut
functions both written by Carlyn Maw and Tom Igoe
http://www.arduino.cc/en/Tutorial/ShiftIn
http://www.arduino.cc/en/Tutorial/ShiftOut
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Psx_h
#define Psx_h
#include "Arduino.h"
// Button Hex Representations:
#define psxLeft 0x0001
#define psxDown 0x0002
#define psxRight 0x0004
#define psxUp 0x0008
#define psxStrt 0x0010
#define psxSlct 0x0080
#define psxSqu 0x0100
#define psxX 0x0200
#define psxO 0x0400
#define psxTri 0x0800
#define psxR1 0x1000
#define psxL1 0x2000
#define psxR2 0x4000
#define psxL2 0x8000
class Psx
{
public:
Psx();
void setupPins(byte , byte , byte , byte , byte ); // (Data Pin #, CMND Pin #, ATT Pin #, CLK Pin #, Delay)
// Delay is how long the clock goes without changing state
// in Microseconds. It can be lowered to increase response,
// but if it is too low it may cause glitches and have some
// keys spill over with false-positives. A regular PSX controller
// works fine at 50 uSeconds.
unsigned int read(); // Returns the status of the button presses in an unsignd int.
// The value returned corresponds to each key as defined above.
private:
byte shift(byte _dataOut);
byte _dataPin;
byte _cmndPin;
byte _attPin;
byte _clockPin;
byte _delay;
byte _i;
boolean _temp;
byte _dataIn;
byte _data1;
byte _data2;
unsigned int _dataOut;
};
#endif

View File

@ -0,0 +1,45 @@
# DaemonBite PSX Controller To USB Adapter
## Introduction
This is a simple to build adapter for connecting PSX controllers to USB. Currently it supports normal digital PSX controllers, analog sticks are not supported.
The input lag for this adapter is minimal (about 0.75ms average connected to MiSTer).
## Parts you need
- Arduino Pro Micro (ATMega32U4)
- Male end of a PSX controller extension cable
- Heat shrink tube (Ø ~20mm)
- Micro USB cable
## Wiring
PSX controller socket (looking face-on at the front of the socket):
```
_________________________
| 9 8 7 | 6 5 4 | 3 2 1 |
\_______________________/
```
PSX controller plug (looking face-on at the front of the controller plug):
```
_________________________
| 1 2 3 | 4 5 6 | 7 8 9 |
\_______________________/
```
| PSX | Arduino Pro Micro |
| ------ | ------ |
| 1 DATA | A0 PF7 |
| 2 CMD | 2 PD1 |
| 3 +7.6V ||
| 4 GND | GND |
| 5 VCC | VCC |
| 6 ATT | 3 PD0 |
| 7 CLK | 4 PD4 |
| 8 N/C ||
| 9 ACK ||
## License
This project is licensed under the GNU General Public License v3.0.
## Credits
PSX Library from Arduino Playground: https://playground.arduino.cc/Main/PSXLibrary/