Refactoring

This commit is contained in:
MickGyver 2020-02-06 15:09:38 +02:00
parent 1f417a3285
commit c098ddc905
18 changed files with 525 additions and 40 deletions

View File

@ -0,0 +1,163 @@
/* DaemonBite CD32 to USB Adapter
* Author: Mikael Norrgård <mick@daemonbite.com>
*
* Copyright (c) 2020 Mikael Norrgård <http://daemonbite.com>
*
* 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"
enum Mode
{
TwoButton,
CD32
};
// Controller DB9 pins (looking face-on to the end of the plug):
//
// 5 4 3 2 1
// 9 8 7 6
//
// Wire it up according to the following table:
//
// DB9 Arduino Pro Micro
// --------------------------------------
// 1 TXO PD3
// 2 RXI PD2
// 3 3 PD0
// 4 4 PD4
// 5 A0 PF7
// 6 6 PD7
// 7 VCC
// 8 GND
// 9 A1 PF6
//
// Connect a slide switch to pins GND,GND and 2 (PD1)
// Set up USB HID gamepad
Gamepad_ Gamepad;
bool usbUpdate = false; // Should gamepad data be sent to USB?
// Controller
uint8_t axes = 0;
uint8_t axesPrev = 0;
uint8_t buttons = 0;
uint8_t buttonsPrev = 0;
Mode mode = CD32;
Mode modePrev = mode;
void setup()
{
// Setup switch pin (2, PD1)
DDRD &= ~B00000010; // input
PORTD |= B00000010; // high to enable internal pull-up
// Setup controller pins
DDRD &= ~B10011101; // inputs
PORTD |= B10011101; // high to enable internal pull-up
DDRF &= ~B11000000; // input
PORTF |= B11000000; // high to enable internal pull-up
}
void loop()
{
// Set mode from switch
(PIND & B00000010) ? mode = CD32 : mode = TwoButton;
// Read X and Y axes
axes = ~(PIND & B00011101);
switch(mode)
{
// Two button mode
case TwoButton:
buttons = ~( ((PIND & B10000000) >> 7) | ((PINF & B01000000) >> 5) | B11111100 );
break;
// CD32 button mode
case CD32:
// Set pin 6 (clock, PD7) and pin 5 (latch, PF7) as output low
PORTD &= ~B10000000; // low to disable internal pull-up (will become low when set as output)
DDRD |= B10000000; // output
PORTF &= ~B10000000; // low to disable internal pull-up (will become low when set as output)
DDRF |= B10000000; // output
delayMicroseconds(40);
// Clear buttons
buttons = 0;
// Read buttons
(PINF & B01000000) ? buttons &= ~B00000010 : buttons |= B00000010; // Blue (2)
sendClock();
(PINF & B01000000) ? buttons &= ~B00000001 : buttons |= B00000001; // Red (1)
sendClock();
(PINF & B01000000) ? buttons &= ~B00001000 : buttons |= B00001000; // Yellow (4)
sendClock();
(PINF & B01000000) ? buttons &= ~B00000100 : buttons |= B00000100; // Green (3)
sendClock();
(PINF & B01000000) ? buttons &= ~B00100000 : buttons |= B00100000; // RTrig (6)
sendClock();
(PINF & B01000000) ? buttons &= ~B00010000 : buttons |= B00010000; // LTrig (5)
sendClock();
(PINF & B01000000) ? buttons &= ~B01000000 : buttons |= B01000000; // Play (7)
// Set pin 5 (latch, PF7) and pin 6 (clock, PD7) as input with pull-ups
DDRF &= ~B10000000; // input
PORTF |= B10000000; // high to enable internal pull-up
DDRD &= ~B10000000; // input
PORTD |= B10000000; // high to enable internal pull-up
delayMicroseconds(40);
break;
}
// Has any buttons changed state?
if (buttons != buttonsPrev)
{
Gamepad._GamepadReport.buttons = buttons;
buttonsPrev = buttons;
usbUpdate = true;
}
// Has any axes changed state?
if (axes != axesPrev)
{
Gamepad._GamepadReport.Y = ((axes & B00000100) >> 2) - ((axes & B00001000) >> 3);
Gamepad._GamepadReport.X = ((axes & B00010000) >> 4) - (axes & B00000001);
axesPrev = axes;
usbUpdate = true;
}
if(usbUpdate)
{
Gamepad.send();
usbUpdate = false;
}
}
void sendClock()
{
// Send a clock pulse to pin 6 and wait
PORTD |= B10000000;
delayMicroseconds(10);
PORTD &= ~B10000000;
delayMicroseconds(40);
}

View File

@ -0,0 +1,145 @@
/* Gamepad.cpp
*
* Based on the advanced HID library for Arduino:
* https://github.com/NicoHood/HID
* Copyright (c) 2014-2015 NicoHood
*
* Copyright (c) 2020 Mikael Norrgård <http://daemonbite.com>
*
* 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 "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, 0x08, // USAGE_MAXIMUM (Button 8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x08, // REPORT_COUNT (8)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,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));
}

View File

@ -0,0 +1,80 @@
/* Gamepad.h
*
* Based on the advanced HID library for Arduino:
* https://github.com/NicoHood/HID
* Copyright (c) 2014-2015 NicoHood
*
* Copyright (c) 2020 Mikael Norrgård <http://daemonbite.com>
*
* 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 <Arduino.h>
#include "HID.h"
// NOTE: To make this work on the MiSTer (or possibly other Linux distros),
// you need to edit USBDesc.h like follows. Change:
// #define ISERIAL 3
// to
// #define ISERIAL 0
// The numbers after colon are bit fields, meaning how many bits the field uses.
// Remove those if there are problems
typedef struct {
union
{
struct {
bool b0: 1 ;
bool b1: 1 ;
bool b2: 1 ;
bool b3: 1 ;
bool b4: 1 ;
bool b5: 1 ;
bool b6: 1 ;
bool b7: 1 ;
};
uint8_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);
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

@ -1,31 +1,19 @@
# DaemonBite Sega Controller To USB Adapter
# DaemonBite Retro Controllers To USB Adapters
## Introduction
This is a simple to build adapter for connecting Mega Drive (Genesis), Master System (+ Atari and C= controllers) to USB. It supports 3 and 6-button Mega Drive controllers and 1 and 2-button SMS/Atari/C= controllers.
This is a collection of easy to build adapters for connecting Mega Drive/Genesis (3/6-button), Master System, Atari, Commodore, Amiga (incl. CD32) controllers to USB. Support for more controllers is on the way (NES, SNES, NeoGeo etc.).
The input lag for this adapter is minimal. Here is the result from a test with a 1ms polling rate on a MiSTer:
The input lag for these adapters is minimal. Here is the result of the Sega controller adapter from a test with a 1ms polling rate on a MiSTer:
| Controller | Samples | Average | Max | Min | Std Dev |
| ------ | ------ | ------ | ------ | ------ | ------ |
| Original 3-Button Mega Drive Controller | 2342 | 0.75ms | 1.28ms | 0.24ms | 0.29ms |
| 8bitdo M30 Wireless 2.4G | 2348 | 4.54ms | 8.05ms | 2.22ms | 1.31ms |
The Mega Drive gamepad interface is based on this repository : https://github.com/jonthysell/SegaController but almost entirely rewritten and a lot of optimisations have been made.
## Parts you need
- Arduino Pro Micro (ATMega32U4)
- Male end of Mega Drive controller extension (or DSUB 9Pin Male connector and some wires)
- Heat shrink tube (Ø ~20mm)
- Micro USB cable
## Wiring
![Assemble1](images/sega-usb-adapter-wiring.png)
## How to assemble (please ignore the switch)
![Assemble1](images/sega-usb-adapter-1.png)
![Assemble1](images/sega-usb-adapter-2.png)
(The switch goes to pins GND-GND-2 even if the picture above shows it connected to GND-2-3)
![Assemble1](images/sega-usb-adapter-3.png)
![Assemble1](images/sega-usb-adapter-4.png)
## How to build
See the README files in the subfolders for build instructions. All the adapters are build around the Arduino Pro Micro.
## License
This project is licensed under the GNU General Public License v3.0.
## Credits
The Mega Drive gamepad interface is based on this repository : https://github.com/jonthysell/SegaController but almost entirely rewritten and a lot of optimisations have been made.

View File

@ -1,4 +1,8 @@
/* Gamepad.cpp
*
* Based on the advanced HID library for Arduino:
* https://github.com/NicoHood/HID
* Copyright (c) 2014-2015 NicoHood
*
* Copyright (c) 2020 Mikael Norrgård <http://daemonbite.com>
*
@ -20,13 +24,14 @@
*
*/
#pragma once
#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)
0x85, 0x01, // REPORT_ID (1) // change to 3 if using mouse and keyboard on 1&2
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)
@ -54,22 +59,76 @@ static const uint8_t _hidReportDescriptor[] PROGMEM = {
0xc0, // END_COLLECTION
};
Gamepad_::Gamepad_(void)
{
reportId=1;
static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
HID().AppendDescriptor(&node);
Gamepad_::Gamepad_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1)
{
epType[0] = EP_TYPE_INTERRUPT_IN;
PluggableUSB().plug(this);
}
void Gamepad_::begin(uint8_t id)
int Gamepad_::getInterface(uint8_t* interfaceCount)
{
reportId=id;
*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));
}
void Gamepad_::end(void)
int Gamepad_::getDescriptor(USBSetup& setup)
{
this->reset();
// 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()
@ -82,5 +141,5 @@ void Gamepad_::reset()
void Gamepad_::send()
{
HID().SendReport(reportId,&_GamepadReport,sizeof(GamepadReport));
USB_Send(pluggedEndpoint | TRANSFER_RELEASE, &_GamepadReport, sizeof(GamepadReport));
}

View File

@ -1,4 +1,8 @@
/* Gamepad.h
*
* Based on the advanced HID library for Arduino:
* https://github.com/NicoHood/HID
* Copyright (c) 2014-2015 NicoHood
*
* Copyright (c) 2020 Mikael Norrgård <http://daemonbite.com>
*
@ -20,8 +24,17 @@
*
*/
#pragma once
#include <Arduino.h>
#include "HID.h"
// NOTE: To make this work on the MiSTer (or possibly other Linux distros),
// you need to edit USBDesc.h like follows. Change:
// #define ISERIAL 3
// to
// #define ISERIAL 0
// The numbers after colon are bit fields, meaning how many bits the field uses.
// Remove those if there are problems
typedef struct {
@ -45,17 +58,23 @@ typedef struct {
} GamepadReport;
class Gamepad_
class Gamepad_ : public PluggableUSBModule
{
private:
uint8_t reportId;
protected:
int getInterface(uint8_t* interfaceCount);
int getDescriptor(USBSetup& setup);
bool setup(USBSetup& setup);
uint8_t epType[1];
uint8_t protocol;
uint8_t idle;
public:
GamepadReport _GamepadReport;
Gamepad_(void);
void begin(uint8_t id);
void end(void);
void reset(void);
void send();
};
extern Gamepad_ Gamepad;

View File

@ -0,0 +1,32 @@
# DaemonBite Sega Controller To USB Adapter
## Introduction
This is a simple to build adapter for connecting Mega Drive (Genesis), Master System (+ Atari and C= controllers) to USB. It supports 3 and 6-button Mega Drive controllers and 1 and 2-button SMS/Atari/C= controllers.
The input lag for this adapter is minimal. Here is the result from a test with a 1ms polling rate on a MiSTer:
| Controller | Samples | Average | Max | Min | Std Dev |
| ------ | ------ | ------ | ------ | ------ | ------ |
| Original 3-Button Mega Drive Controller | 2342 | 0.75ms | 1.28ms | 0.24ms | 0.29ms |
| 8bitdo M30 Wireless 2.4G | 2348 | 4.54ms | 8.05ms | 2.22ms | 1.31ms |
## Parts you need
- Arduino Pro Micro (ATMega32U4)
- Male end of Mega Drive controller extension (or DSUB 9Pin Male connector and some wires)
- Heat shrink tube (Ø ~20mm)
- Micro USB cable
## Wiring
![Assemble1](images/sega-usb-adapter-wiring.png)
## How to assemble (please ignore the switch)
![Assemble1](images/sega-usb-adapter-1.png)
![Assemble1](images/sega-usb-adapter-2.png)
(The switch goes to pins GND-GND-2 even if the picture above shows it connected to GND-2-3)
![Assemble1](images/sega-usb-adapter-3.png)
![Assemble1](images/sega-usb-adapter-4.png)
## License
This project is licensed under the GNU General Public License v3.0.
## Credits
The Mega Drive gamepad interface is based on this repository : https://github.com/jonthysell/SegaController but almost entirely rewritten and a lot of optimisations have been made.

View File

@ -52,8 +52,7 @@ word currentState = 0;
word lastState = 1;
void setup()
{
Gamepad.begin(1);
{
}
void loop()

View File

Before

Width:  |  Height:  |  Size: 409 KiB

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

View File

Before

Width:  |  Height:  |  Size: 357 KiB

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB