SEGA adapters now support 8bitdo HOME button. NeoGeo adapter added. Forced two-button mode added to CD32 adapter.

This commit is contained in:
MickGyver 2020-09-07 10:49:51 +03:00
parent 227a10814c
commit fa9e82e4b6
15 changed files with 542 additions and 130 deletions

View File

@ -21,6 +21,7 @@
* *
*/ */
#include <EEPROM.h>
#include "Gamepad.h" #include "Gamepad.h"
// ATT: 20 chars max (including NULL at the end) according to Arduino source code. // ATT: 20 chars max (including NULL at the end) according to Arduino source code.
@ -28,6 +29,8 @@
const char *gp_serial = "CD32/C= to USB"; const char *gp_serial = "CD32/C= to USB";
#define BUTTON_READ_DELAY 100 // Button read delay in µs #define BUTTON_READ_DELAY 100 // Button read delay in µs
#define MODE_CD32 0
#define MODE_3BUTTON 42
// Controller DB9 pins (looking face-on to the end of the plug): // Controller DB9 pins (looking face-on to the end of the plug):
// //
@ -72,9 +75,11 @@ uint8_t buttonsPrev = 0;
// Timing // Timing
uint32_t microsButtons = 0; uint32_t microsButtons = 0;
uint32_t millisStart = 0;
// CD32 controller detection // CD32 controller detection
uint8_t detection = 0; uint8_t detection = 0;
uint8_t mode = MODE_CD32;
void setup() void setup()
{ {
@ -87,59 +92,69 @@ void setup()
PORTD |= B10011101; // high to enable internal pull-up PORTD |= B10011101; // high to enable internal pull-up
DDRF &= ~B11000000; // input DDRF &= ~B11000000; // input
PORTF |= B11000000; // high to enable internal pull-up PORTF |= B11000000; // high to enable internal pull-up
delay(500);
startupConfig();
} }
void loop() void loop() { while(1)
{ {
// Read X and Y axes // Read X and Y axes
axes = ~(PIND & B00011101); axes = ~(PIND & B00011101);
// See if enough time has passed since last button read if(mode == MODE_CD32)
if(micros() - microsButtons > BUTTON_READ_DELAY)
{ {
// Set pin 6 (clock, PD7) and pin 5 (latch, PF7) as output low // See if enough time has passed since last button read
PORTD &= ~B10000000; // low to disable internal pull-up (will become low when set as output) if(micros() - microsButtons > BUTTON_READ_DELAY)
DDRD |= B10000000; // output {
PORTF &= ~B10000000; // low to disable internal pull-up (will become low when set as output) // Set pin 6 (clock, PD7) and pin 5 (latch, PF7) as output low
DDRF |= B10000000; // output PORTD &= ~B10000000; // low to disable internal pull-up (will become low when set as output)
delayMicroseconds(40); 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 // Clear buttons
buttons = 0; buttons = 0;
// Read buttons // Read buttons
(PINF & B01000000) ? buttons &= ~B00000010 : buttons |= B00000010; // Blue (2) (PINF & B01000000) ? buttons &= ~B00000010 : buttons |= B00000010; // Blue (2)
sendClock(); sendClock();
(PINF & B01000000) ? buttons &= ~B00000001 : buttons |= B00000001; // Red (1) (PINF & B01000000) ? buttons &= ~B00000001 : buttons |= B00000001; // Red (1)
sendClock(); sendClock();
(PINF & B01000000) ? buttons &= ~B00001000 : buttons |= B00001000; // Yellow (4) (PINF & B01000000) ? buttons &= ~B00001000 : buttons |= B00001000; // Yellow (4)
sendClock(); sendClock();
(PINF & B01000000) ? buttons &= ~B00000100 : buttons |= B00000100; // Green (3) (PINF & B01000000) ? buttons &= ~B00000100 : buttons |= B00000100; // Green (3)
sendClock(); sendClock();
(PINF & B01000000) ? buttons &= ~B00100000 : buttons |= B00100000; // RTrig (6) (PINF & B01000000) ? buttons &= ~B00100000 : buttons |= B00100000; // RTrig (6)
sendClock(); sendClock();
(PINF & B01000000) ? buttons &= ~B00010000 : buttons |= B00010000; // LTrig (5) (PINF & B01000000) ? buttons &= ~B00010000 : buttons |= B00010000; // LTrig (5)
sendClock(); sendClock();
(PINF & B01000000) ? buttons &= ~B01000000 : buttons |= B01000000; // Play (7) (PINF & B01000000) ? buttons &= ~B01000000 : buttons |= B01000000; // Play (7)
sendClock(); sendClock();
(PINF & B01000000) ? detection |= B00000001 : detection &= ~B00000001; // First detection bit (should be 1) (PINF & B01000000) ? detection |= B00000001 : detection &= ~B00000001; // First detection bit (should be 1)
sendClock(); sendClock();
(PINF & B01000000) ? detection |= B00000010 : detection &= ~B00000010; // Second detection bit (should be 0) (PINF & B01000000) ? detection |= B00000010 : detection &= ~B00000010; // Second detection bit (should be 0)
// Set pin 5 (latch, PF7) and pin 6 (clock, PD7) as input with pull-ups // Set pin 5 (latch, PF7) and pin 6 (clock, PD7) as input with pull-ups
DDRF &= ~B10000000; // input DDRF &= ~B10000000; // input
PORTF |= B10000000; // high to enable internal pull-up PORTF |= B10000000; // high to enable internal pull-up
DDRD &= ~B10000000; // input DDRD &= ~B10000000; // input
PORTD |= B10000000; // high to enable internal pull-up PORTD |= B10000000; // high to enable internal pull-up
delayMicroseconds(40); delayMicroseconds(40);
// Was a CD32 gamepad detected? If not, read button 1 and 2 "normally". // Was a CD32 gamepad detected? If not, read button 1 and 2 "normally".
if(detection != B0000001) if(detection != B0000001)
buttons = ~( ((PIND & B10000000) >> 7) | ((PINF & B01000000) >> 5) | B11111100 ); buttons = ~( ((PIND & B10000000) >> 7) | ((PINF & B01000000) >> 5) | B11111100 );
microsButtons = micros(); microsButtons = micros();
}
} }
else
{
buttons = ~( ((PIND & B10000000) >> 7) | ((PINF & B11000000) >> 5) | B11111000 );
}
// Has any buttons changed state? // Has any buttons changed state?
if (buttons != buttonsPrev) if (buttons != buttonsPrev)
{ {
@ -163,7 +178,7 @@ void loop()
Gamepad.send(); Gamepad.send();
usbUpdate = false; usbUpdate = false;
} }
} }}
void sendClock() void sendClock()
{ {
@ -173,3 +188,28 @@ void sendClock()
PORTD &= ~B10000000; // Disable pull-up PORTD &= ~B10000000; // Disable pull-up
delayMicroseconds(40); delayMicroseconds(40);
} }
void startupConfig()
{
// Read current mode from eeprom
mode = EEPROM.read(0);
if(mode != MODE_3BUTTON)
mode = MODE_CD32;
// Get time
millisStart = millis();
// Wait as long as button 1 is pressed
while(!(PIND & B10000000))
{
if(millis() - millisStart > 5000) // Button 1 has been pressed for more than 5 seconds
{
// Toggle mode and save to EEPROM
(mode == MODE_3BUTTON) ? mode = MODE_CD32 : mode = MODE_3BUTTON;
EEPROM.update(0,mode);
return;
}
}
return;
}

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) 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"
static const uint8_t _hidReportDescriptor[] PROGMEM = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x04, // USAGE (Joystick)
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) 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 "HID.h"
extern const char* gp_serial;
typedef struct {
uint16_t buttons : 12;
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,169 @@
/* NeoGeo Controller to USB
* 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"
#define DEBOUNCE 0 // 1=Diddly-squat-Delay-Debouncing™ activated, 0=Debounce deactivated
#define DEBOUNCE_TIME 10 // Debounce time in milliseconds
//#define DEBUG // Enables debugging (sends debug data to usb serial)
const char *gp_serial = "NeoGeo to USB";
Gamepad_ Gamepad; // Set up USB HID gamepad
bool usbUpdate = false; // Should gamepad data be sent to USB?
bool debounce = DEBOUNCE; // Debounce?
uint8_t pin; // Used in for loops
uint32_t millisNow = 0; // Used for Diddly-squat-Delay-Debouncing™
uint8_t axesDirect = 0x0f;
uint8_t axes = 0x0f;
uint8_t axesPrev = 0x0f;
uint8_t axesBits[4] = {0x10,0x20,0x40,0x80};
uint32_t axesMillis[4];
uint16_t buttonsDirect = 0;
uint16_t buttons = 0;
uint16_t buttonsPrev = 0;
uint16_t buttonsBits[12] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800};
uint32_t buttonsMillis[12];
#ifdef DEBUG
char buf[16];
uint32_t millisSent = 0;
#endif
void setup()
{
// Axes
DDRF &= ~B11110000; // Set A0-A3 as inputs
PORTF |= B11110000; // Enable internal pull-up resistors
// Buttons
DDRD &= ~B10011111; // Set PD0-PD4 and PD7 as inputs
PORTD |= B10011111; // Enable internal pull-up resistors
DDRB &= ~B01111110; // Set PB1-PB6 as inputs
PORTB |= B01111110; // Enable internal pull-up resistors
// Debounce selector switch (currently disabled)
DDRE &= ~B01000000; // Pin 7 as input
PORTE |= B01000000; // Enable internal pull-up resistor
// Initialize debouncing timestamps
for(pin=0; pin<4; pin++)
axesMillis[pin]=0;
for(pin=0; pin<12; pin++)
buttonsMillis[pin]=0;
#ifdef DEBUG
Serial.begin(115200);
#endif
}
void loop()
{
// Get current time, the millis() function should take about 2µs to complete
millisNow = millis();
for(uint8_t i=0; i<10; i++) // One iteration (when debounce is enabled) takes approximately 35µs to complete, so we don't need to check the time between every iteration
{
// Read axis and button inputs (bitwise NOT results in a 1 when button/axis pressed)
axesDirect = ~(PINF & B11110000);
buttonsDirect = ~((PIND & B00011111) | ((PIND & B10000000) << 4) | ((PINB & B01111110) << 4));
if(debounce)
{
// Debounce axes
for(pin=0; pin<4; pin++)
{
// Check if the current pin state is different to the stored state and that enough time has passed since last change
if((axesDirect & axesBits[pin]) != (axes & axesBits[pin]) && (millisNow - axesMillis[pin]) > DEBOUNCE_TIME)
{
// Toggle the pin, we can safely do this because we know the current state is different to the stored state
axes ^= axesBits[pin];
// Update the timestamp for the pin
axesMillis[pin] = millisNow;
}
}
// Debounce buttons
for(pin=0; pin<12; pin++)
{
// Check if the current pin state is different to the stored state and that enough time has passed since last change
if((buttonsDirect & buttonsBits[pin]) != (buttons & buttonsBits[pin]) && (millisNow - buttonsMillis[pin]) > DEBOUNCE_TIME)
{
// Toggle the pin, we can safely do this because we know the current state is different to the stored state
buttons ^= buttonsBits[pin];
// Update the timestamp for the pin
buttonsMillis[pin] = millisNow;
}
}
}
else
{
axes = axesDirect;
buttons = buttonsDirect;
}
// Has axis inputs changed?
if(axes != axesPrev)
{
// UP + DOWN = UP, SOCD (Simultaneous Opposite Cardinal Directions) Cleaner
if(axes & B10000000)
Gamepad._GamepadReport.Y = -1;
else if(axes & B01000000)
Gamepad._GamepadReport.Y = 1;
else
Gamepad._GamepadReport.Y = 0;
// UP + DOWN = NEUTRAL
//Gamepad._GamepadReport.Y = ((axes & B01000000)>>6) - ((axes & B10000000)>>7);
// LEFT + RIGHT = NEUTRAL
Gamepad._GamepadReport.X = ((axes & B00010000)>>4) - ((axes & B00100000)>>5);
axesPrev = axes;
usbUpdate = true;
}
// Has button inputs changed?
if(buttons != buttonsPrev)
{
Gamepad._GamepadReport.buttons = buttons;
buttonsPrev = buttons;
usbUpdate = true;
}
// Should gamepad data be sent to USB?
if(usbUpdate)
{
Gamepad.send();
usbUpdate = false;
#ifdef DEBUG
sprintf(buf, "%06lu: %d%d%d%d", millisNow-millisSent, ((axes & 0x10)>>4), ((axes & 0x20)>>5), ((axes & 0x40)>>6), ((axes & 0x80)>>7) );
Serial.print(buf);
sprintf(buf, " %d%d%d%d", (buttons & 0x01), ((buttons & 0x02)>>1), ((buttons & 0x04)>>2), ((buttons & 0x08)>>3) );
Serial.println(buf);
millisSent = millisNow;
#endif
}
}
}

View File

@ -1,9 +1,7 @@
# DaemonBite PC Engine / TurboGrafx-16 Controllers To USB Adapter # DaemonBite PC Engine / TurboGrafx-16 Controllers To USB Adapter
## Introduction ## Introduction
This is a simple to build adapter for connecting PC Engine / TurboGrafx-16 controllers to USB. This is a simple to build adapter for connecting PC Engine / TurboGrafx-16 controllers to USB with turbo functionality support.
NOTE: This adapter is in BETA and not yet properly tested.
The input lag for this adapter is minimal (should be less 1ms average connected to MiSTer). The input lag for this adapter is minimal (should be less 1ms average connected to MiSTer).

View File

@ -34,12 +34,16 @@ static const uint8_t _hidReportDescriptor[] PROGMEM = {
0x05, 0x09, // USAGE_PAGE (Button) 0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1) 0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x08, // USAGE_MAXIMUM (Button 8) 0x29, 0x09, // USAGE_MAXIMUM (Button 9)
0x15, 0x00, // LOGICAL_MINIMUM (0) 0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x08, // REPORT_COUNT (8) 0x95, 0x09, // REPORT_COUNT (9)
0x75, 0x01, // REPORT_SIZE (1) 0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs) 0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1) ; pad out the bits into a number divisible by 8
0x75, 0x07, // REPORT_SIZE (7)
0x81, 0x03, // INPUT (Const,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (pointer) 0x09, 0x01, // USAGE (pointer)

View File

@ -34,23 +34,9 @@ extern const char* gp_serial;
// The numbers after colon are bit fields, meaning how many bits the field uses. // The numbers after colon are bit fields, meaning how many bits the field uses.
// Remove those if there are problems // Remove those if there are problems
typedef struct { typedef struct {
union uint16_t buttons;
{ int8_t X;
struct { int8_t Y;
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; } GamepadReport;

View File

@ -62,7 +62,7 @@ word SegaController32U4::getStateMD()
// 3 HI C B Right Left Down Up (Read B, C and directions in this cycle) // 3 HI C B Right Left Down Up (Read B, C and directions in this cycle)
// 4 LO Start A 0 0 0 0 (Check for six button controller in this cycle) // 4 LO Start A 0 0 0 0 (Check for six button controller in this cycle)
// 5 HI C B Mode X Y Z (Read X,Y,Z and Mode in this cycle) // 5 HI C B Mode X Y Z (Read X,Y,Z and Mode in this cycle)
// 6 LO --- --- --- --- --- --- // 6 LO --- --- --- --- --- Home (Home only for 8bitdo wireless gamepads)
// 7 HI --- --- --- --- --- --- // 7 HI --- --- --- --- --- ---
// Set the select pin low/high // Set the select pin low/high
@ -139,7 +139,10 @@ word SegaController32U4::getStateMD()
} }
else else
{ {
_ignoreCycles--; if(_ignoreCycles-- == 2) // Decrease the ignore cycles counter and read 8bitdo home in first "ignored" cycle, this cycle is unused on normal 6-button controllers
{
(bitRead(_inputReg1, DB9_PIN1_BIT) == LOW) ? _currentState |= SC_BTN_HOME : _currentState &= ~SC_BTN_HOME;
}
} }
return _currentState; return _currentState;

View File

@ -42,23 +42,23 @@
enum enum
{ {
SC_CTL_ON = 1, // The controller is connected (not used) SC_BTN_UP = 1,
SC_BTN_UP = 2, SC_BTN_DOWN = 2,
SC_BTN_DOWN = 4, SC_BTN_LEFT = 4,
SC_BTN_LEFT = 8, SC_BTN_RIGHT = 8,
SC_BTN_RIGHT = 16, SC_BTN_A = 16,
SC_BTN_A = 32, SC_BTN_B = 32,
SC_BTN_B = 64, SC_BTN_C = 64,
SC_BTN_C = 128, SC_BTN_X = 128,
SC_BTN_X = 256, SC_BTN_Y = 256,
SC_BTN_Y = 512, SC_BTN_Z = 512,
SC_BTN_Z = 1024, SC_BTN_START = 1024,
SC_BTN_START = 2048, SC_BTN_MODE = 2048,
SC_BTN_MODE = 4096, SC_BTN_HOME = 4096,
SC_BIT_UP = 1, SC_BIT_SH_UP = 0,
SC_BIT_DOWN = 2, SC_BIT_SH_DOWN = 1,
SC_BIT_LEFT = 3, SC_BIT_SH_LEFT = 2,
SC_BIT_RIGHT = 4, SC_BIT_SH_RIGHT = 3,
DB9_PIN1_BIT = 7, DB9_PIN1_BIT = 7,
DB9_PIN2_BIT = 6, DB9_PIN2_BIT = 6,
DB9_PIN3_BIT = 5, DB9_PIN3_BIT = 5,

View File

@ -58,20 +58,20 @@ void setup()
{ {
} }
void loop() void loop() { while(1)
{ {
currentState = controller.getStateMD(); currentState = controller.getStateMD();
sendState(); sendState();
} }}
void sendState() void sendState()
{ {
// Only report controller state if it has changed // Only report controller state if it has changed
if (currentState != lastState) if (currentState != lastState)
{ {
Gamepad._GamepadReport.buttons = currentState >> 5; Gamepad._GamepadReport.buttons = currentState >> 4;
Gamepad._GamepadReport.Y = ((currentState & SC_BTN_DOWN) >> SC_BIT_DOWN) - ((currentState & SC_BTN_UP) >> SC_BIT_UP); Gamepad._GamepadReport.Y = ((currentState & SC_BTN_DOWN) >> SC_BIT_SH_DOWN) - ((currentState & SC_BTN_UP) >> SC_BIT_SH_UP);
Gamepad._GamepadReport.X = ((currentState & SC_BTN_RIGHT) >> SC_BIT_RIGHT) - ((currentState & SC_BTN_LEFT) >> SC_BIT_LEFT); Gamepad._GamepadReport.X = ((currentState & SC_BTN_RIGHT) >> SC_BIT_SH_RIGHT) - ((currentState & SC_BTN_LEFT) >> SC_BIT_SH_LEFT);
Gamepad.send(); Gamepad.send();
lastState = currentState; lastState = currentState;
} }

View File

@ -34,12 +34,16 @@ static const uint8_t _hidReportDescriptor[] PROGMEM = {
0x05, 0x09, // USAGE_PAGE (Button) 0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1) 0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x08, // USAGE_MAXIMUM (Button 8) 0x29, 0x09, // USAGE_MAXIMUM (Button 9)
0x15, 0x00, // LOGICAL_MINIMUM (0) 0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x08, // REPORT_COUNT (8) 0x95, 0x09, // REPORT_COUNT (9)
0x75, 0x01, // REPORT_SIZE (1) 0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs) 0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1) ; pad out the bits into a number divisible by 8
0x75, 0x07, // REPORT_SIZE (7)
0x81, 0x03, // INPUT (Const,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (pointer) 0x09, 0x01, // USAGE (pointer)

View File

@ -34,23 +34,9 @@ extern const char* gp_serial;
// The numbers after colon are bit fields, meaning how many bits the field uses. // The numbers after colon are bit fields, meaning how many bits the field uses.
// Remove those if there are problems // Remove those if there are problems
typedef struct { typedef struct {
union uint16_t buttons;
{ int8_t X;
struct { int8_t Y;
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; } GamepadReport;

View File

@ -89,7 +89,7 @@ void SegaControllers32U4::readState()
// 3 HI C B Right Left Down Up (Read B, C and directions in this cycle) // 3 HI C B Right Left Down Up (Read B, C and directions in this cycle)
// 4 LO Start A 0 0 0 0 (Check for six button controller in this cycle) // 4 LO Start A 0 0 0 0 (Check for six button controller in this cycle)
// 5 HI C B Mode X Y Z (Read X,Y,Z and Mode in this cycle) // 5 HI C B Mode X Y Z (Read X,Y,Z and Mode in this cycle)
// 6 LO --- --- --- --- --- --- // 6 LO --- --- --- --- --- Home (Home only for 8bitdo wireless gamepads)
// 7 HI --- --- --- --- --- --- // 7 HI --- --- --- --- --- ---
void SegaControllers32U4::readPort1() void SegaControllers32U4::readPort1()
@ -157,7 +157,10 @@ void SegaControllers32U4::readPort1()
} }
else else
{ {
_ignoreCycles[0]--; if(_ignoreCycles[0]-- == 2) // Decrease the ignore cycles counter and read 8bitdo home in first "ignored" cycle, this cycle is unused on normal 6-button controllers
{
(bitRead(_inputReg1, DB9_PIN1_BIT1) == LOW) ? currentState[0] |= SC_BTN_HOME : currentState[0] &= ~SC_BTN_HOME;
}
} }
} }
@ -226,7 +229,10 @@ void SegaControllers32U4::readPort2()
} }
else else
{ {
_ignoreCycles[1]--; if(_ignoreCycles[1]-- == 2) // Decrease the ignore cycles counter and read 8bitdo home in first "ignored" cycle, this cycle is unused on normal 6-button controllers
{
(bitRead(_inputReg3, DB9_PIN1_BIT2) == LOW) ? currentState[1] |= SC_BTN_HOME : currentState[1] &= ~SC_BTN_HOME;
}
} }
} }

View File

@ -30,23 +30,23 @@
enum enum
{ {
SC_CTL_ON = 1, // The controller is connected (not used) SC_BTN_UP = 1,
SC_BTN_UP = 2, SC_BTN_DOWN = 2,
SC_BTN_DOWN = 4, SC_BTN_LEFT = 4,
SC_BTN_LEFT = 8, SC_BTN_RIGHT = 8,
SC_BTN_RIGHT = 16, SC_BTN_A = 16,
SC_BTN_A = 32, SC_BTN_B = 32,
SC_BTN_B = 64, SC_BTN_C = 64,
SC_BTN_C = 128, SC_BTN_X = 128,
SC_BTN_X = 256, SC_BTN_Y = 256,
SC_BTN_Y = 512, SC_BTN_Z = 512,
SC_BTN_Z = 1024, SC_BTN_START = 1024,
SC_BTN_START = 2048, SC_BTN_MODE = 2048,
SC_BTN_MODE = 4096, SC_BTN_HOME = 4096,
SC_BIT_UP = 1, SC_BIT_SH_UP = 0,
SC_BIT_DOWN = 2, SC_BIT_SH_DOWN = 1,
SC_BIT_LEFT = 3, SC_BIT_SH_LEFT = 2,
SC_BIT_RIGHT = 4, SC_BIT_SH_RIGHT = 3,
SC_PIN1_BIT = 0, SC_PIN1_BIT = 0,
SC_PIN2_BIT = 1, SC_PIN2_BIT = 1,
SC_PIN3_BIT = 2, SC_PIN3_BIT = 2,

View File

@ -79,9 +79,9 @@ void sendState(byte gp)
// Only report controller state if it has changed // Only report controller state if it has changed
if (controllers.currentState[gp] != lastState[gp]) if (controllers.currentState[gp] != lastState[gp])
{ {
Gamepad[gp]._GamepadReport.buttons = controllers.currentState[gp] >> 5; Gamepad[gp]._GamepadReport.buttons = controllers.currentState[gp] >> 4;
Gamepad[gp]._GamepadReport.Y = ((controllers.currentState[gp] & SC_BTN_DOWN) >> SC_BIT_DOWN) - ((controllers.currentState[gp] & SC_BTN_UP) >> SC_BIT_UP); Gamepad[gp]._GamepadReport.Y = ((controllers.currentState[gp] & SC_BTN_DOWN) >> SC_BIT_SH_DOWN) - ((controllers.currentState[gp] & SC_BTN_UP) >> SC_BIT_SH_UP);
Gamepad[gp]._GamepadReport.X = ((controllers.currentState[gp] & SC_BTN_RIGHT) >> SC_BIT_RIGHT) - ((controllers.currentState[gp] & SC_BTN_LEFT) >> SC_BIT_LEFT); Gamepad[gp]._GamepadReport.X = ((controllers.currentState[gp] & SC_BTN_RIGHT) >> SC_BIT_SH_RIGHT) - ((controllers.currentState[gp] & SC_BTN_LEFT) >> SC_BIT_SH_LEFT);
Gamepad[gp].send(); Gamepad[gp].send();
lastState[gp] = controllers.currentState[gp]; lastState[gp] = controllers.currentState[gp];
} }