diff --git a/platformio.ini b/platformio.ini index 05d6572..587a589 100644 --- a/platformio.ini +++ b/platformio.ini @@ -329,6 +329,49 @@ src_filter = ${in-gc-micro.src_filter} ${out-switchusb.src_filter} build_flags = ${in-gc-micro.build_flags} ${out-switchusb.build_flags} lib_deps = ${in-gc-micro.lib_deps}, ${out-switchusb.lib_deps} +# saturn input + +[in-saturn] +src_filter = -<*> + +build_flags = ${common.build_flags} -DGAMEPAD_INPUT=6 + +[env:esp32-saturn-bt] +extends = esp32, in-saturn, out-bt +src_filter = ${in-saturn.src_filter} ${out-bt.src_filter} +build_flags = ${in-saturn.build_flags} ${out-bt.build_flags} + +[env:esp32-saturn-debug] +extends = esp32, in-saturn, out-debug +src_filter = ${in-saturn.src_filter} ${out-debug.src_filter} +build_flags = ${in-saturn.build_flags} ${out-debug.build_flags} + +[env:micro-saturn-usb] +extends = micro, in-saturn, out-usb +src_filter = ${in-saturn.src_filter} ${out-usb.src_filter} +build_flags = ${in-saturn.build_flags} ${out-usb.build_flags} + +[env:micro-saturn-debug] +extends = micro, in-saturn, out-debug +src_filter = ${in-saturn.src_filter} ${out-debug.src_filter} +build_flags = ${in-saturn.build_flags} ${out-debug.build_flags} + +# micro doesn't have enough pins for both radio and 2 gamepads +# so these must be built with only support for 1 gamepad + +[env:micro-saturn-radio] +extends = micro, in-saturn, out-radio +src_filter = ${in-saturn.src_filter} ${out-radio.src_filter} +build_flags = ${in-saturn.build_flags} ${out-radio.build_flags} -DGAMEPAD_COUNT=1 + +[env:micro-saturn-usbradio] +extends = micro, in-saturn, out-usbradio +src_filter = ${in-saturn.src_filter} ${out-usbradio.src_filter} +build_flags = ${in-saturn.build_flags} ${out-usbradio.build_flags} -DGAMEPAD_COUNT=1 + +[env:micro-saturn-switchusb] +extends = micro, in-saturn, out-switchusb +src_filter = ${in-saturn.src_filter} ${out-switchusb.src_filter} +build_flags = ${in-saturn.build_flags} ${out-switchusb.build_flags} # debug input diff --git a/readme.md b/readme.md index e670ce1..6a91269 100644 --- a/readme.md +++ b/readme.md @@ -14,7 +14,7 @@ Build using [PlatformIO](https://platformio.org/) using `pio run` or `pio run -e env's are laid out like `$board-$input-$output` supported values: * $board: micro, esp32 - * $input: snes, genesis, psx, n64, gc, radio, debug + * $input: snes, genesis, saturn, psx, n64, gc, radio, debug * $output: radio, usb, usbradio, switchusb, bt, debug * please note not all boards are compatible with all inputs/outputs, for example esp32 can only do bt, micro can only do radio or usb @@ -33,33 +33,33 @@ Wiring ![DB-25 Pinout](images/db25pins.jpg) -| DB-25 Pins | Arduino Pro Micro GPIO | ESP32 GPIO | Radio | SNES | PSX | N64 | Gamecube | Genesis | Dreamcast | -|---------------|------------------------|------------|----------|---------|--------|----------|----------|-------------|-----------| -| 1 TX | 1 | 19 | - | LATCH | - | - | - | P1-1 | P1-DATA1 | -| 2 SDA | 2 | 21 | - | CLOCK | DATA | P1-DATA | P1-DATA | P1-3 | P1-DATA5 | -| 3 SCL | 3 | 22 | - | P1-DATA | CMD | - | - | P1-4 | - | -| 4 Analog | 4 | 15 | - | P2-DATA | ATT | - | - | P1-6 | - | -| 5 Digital | 5 | 16 | - | P3-DATA | CLK | - | - | P1-7 | - | -| 6 Analog | 6 | 2 | - | P4-DATA | - | - | - | P1-9 | - | -| 7 Digital | 7 | 17 | CE | - | - | - | - | P2-7* | - | -| 8 Analog | 8 | 4 | CSN | - | - | - | - | - | - | -| 9 Analog | 9 > 1k Ω | 35 > 1k Ω | - | 330 Ω | 100 Ω | 220 Ω | 680 Ω | 470 Ω | 820 Ω | -| 10 Analog | 10 | 32 | - | - | - | - | - | - | - | -| 11 RX | 0 | 18 | - | - | - | - | - | P1-2 | - | -| 12 - | - | - | - | - | - | - | - | - | - | -| 13 - | - | - | - | - | - | - | - | - | - | -| 14 MISO | 14 | 12 | MISO | - | - | - | - | P2-6* | - | -| 15 SCLK | 15 | 14 | SCLK | - | - | - | - | P2-9* | - | -| 16 MOSI | 16 | 13 | MOSI | - | - | - | - | - | - | -| 17 - | - | - | - | - | - | - | - | - | - | -| 18 Analog | 18 | 27 | - | - | - | - | - | P2-1 | - | -| 19 Analog | 19 | 26 | - | - | - | - | - | P2-2 | - | -| 20 Analog | 20 | 25 | - | - | - | - | - | P2-3 | - | -| 21 Analog | 21 | 33 | - | - | - | - | - | P2-4 | - | -| 22 - | - | - | - | - | - | - | - | - | - | -| 23 3.3V VCC | - | 3.3V VCC | 3.3V VCC | - | - | 3.3V VCC | 3.3V VCC | - | - | -| 24 5V VCC | 5V VCC OUT | 5V VCC | 5V VCC | 5V VCC | 5V VCC | - | 5V VCC | PX-5 5V VCC | 5V VCC | -| 25 GND | GND | GND | GND | GND | - | GND | GND | PX-8 GND | GND | +| DB-25 Pins | Arduino Pro Micro GPIO | ESP32 GPIO | Radio | SNES | PSX | N64 | Gamecube | Genesis | Dreamcast | Saturn | +|---------------|------------------------|------------|----------|---------|--------|----------|----------|-------------|-----------|-------------| +| 1 TX | 1 | 19 | - | LATCH | - | - | - | P1-1 | P1-DATA1 | P1-7 | +| 2 SDA | 2 | 21 | - | CLOCK | DATA | P1-DATA | P1-DATA | P1-3 | P1-DATA5 | P1-2 | +| 3 SCL | 3 | 22 | - | P1-DATA | CMD | - | - | P1-4 | - | P1-3 | +| 4 Analog | 4 | 15 | - | P2-DATA | ATT | - | - | P1-6 | - | P1-6 | +| 5 Digital | 5 | 16 | - | P3-DATA | CLK | - | - | P1-7 | - | | +| 6 Analog | 6 | 2 | - | P4-DATA | - | - | - | P1-9 | - | P2-6 | +| 7 Digital | 7 | 17 | CE | - | - | - | - | P2-7* | - | | +| 8 Analog | 8 | 4 | CSN | - | - | - | - | - | - | | +| 9 Analog | 9 > 1k Ω | 35 > 1k Ω | - | 330 Ω | 100 Ω | 220 Ω | 680 Ω | 470 Ω | 820 Ω | TODO | +| 10 Analog | 10 | 32 | - | - | - | - | - | - | - | | +| 11 RX | 0 | 18 | - | - | - | - | - | P1-2 | - | P1-8 | +| 12 - | - | - | - | - | - | - | - | - | - | | +| 13 - | - | - | - | - | - | - | - | - | - | | +| 14 MISO | 14 | 12 | MISO | - | - | - | - | P2-6* | - | PX-5 | +| 15 SCLK | 15 | 14 | SCLK | - | - | - | - | P2-9* | - | PX-4 | +| 16 MOSI | 16 | 13 | MOSI | - | - | - | - | - | - | | +| 17 - | - | - | - | - | - | - | - | - | - | | +| 18 Analog | 18 | 27 | - | - | - | - | - | P2-1 | - | P2-7 | +| 19 Analog | 19 | 26 | - | - | - | - | - | P2-2 | - | P2-8 | +| 20 Analog | 20 | 25 | - | - | - | - | - | P2-3 | - | P2-2 | +| 21 Analog | 21 | 33 | - | - | - | - | - | P2-4 | - | P2-3 | +| 22 - | - | - | - | - | - | - | - | - | - | | +| 23 3.3V VCC | - | 3.3V VCC | 3.3V VCC | - | - | 3.3V VCC | 3.3V VCC | - | - | | +| 24 5V VCC | 5V VCC OUT | 5V VCC | 5V VCC | 5V VCC | 5V VCC | - | 5V VCC | PX-5 5V VCC | 5V VCC | PX-1 5V VCC | +| 25 GND | GND | GND | GND | GND | - | GND | GND | PX-8 GND | GND | PX-9 GND | * 2nd player Genesis is incompatible with Radio because it uses the same pins, 1 player Genesis is compatible Ω This is optional and only used for dongle detection. On the microcontroller side, put a 1k resistor between DB-25 pin 9 and VCC (3.3v for ESP32, 5V for Micro). On each controller dongle, put a resistor of the given value between DB-25 pin 9 and GND. diff --git a/src/SegaSaturn.cpp b/src/SegaSaturn.cpp new file mode 100644 index 0000000..676508f --- /dev/null +++ b/src/SegaSaturn.cpp @@ -0,0 +1,228 @@ +/* DaemonBite Saturn USB Adapter + * Author: Mikael Norrgård + * + * Copyright (c) 2020 Mikael Norrgård + * + * 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 . + * + */ + +#include "Arduino.h" + +#ifndef GAMEPAD_COUNT +#define GAMEPAD_COUNT 2 +#endif + +#include "gamepad/Gamepad.h" + +#include "pins.h" + +// How many microseconds to wait after setting select lines? (2µs is enough according to the Saturn developer's manual) +// 20µs is a "safe" value that seems to work for original Saturn controllers and Retrobit wired controllers +#define SELECT_PAUSE 20 + +// Uncomment to support the Retro Bit 2.4GHz wireless controller (this will increase lag a lot) +//#define RETROBIT_WL + +#define UP 0x01 +#define DOWN 0x02 +#define LEFT 0x04 +#define RIGHT 0x08 + +/* ------------------------------------------------------------------------- +Saturn controller socket (looking face-on at the front of the socket): +___________________ +/ 1 2 3 4 5 6 7 8 9 \ +|___________________| + +Saturn controller plug (looking face-on at the front of the controller plug): +___________________ +/ 9 8 7 6 5 4 3 2 1 \ +|___________________| + +Saturn (P1) Arduino Pro Micro +-------------------------------------- +1 VCC VCC - black +2 DATA1 2 PD1 - white +3 DATA0 3 PD0 - gray +4 SEL1 15 PB1 (Shared with P2) - blue +5 SEL0 14 PB3 (Shared with P2) - green +6 TL (5V) 4 PD4 - yellow +7 DATA3 TXO PD3 - orange +8 DATA2 RXI PD2 - red +9 GND GND - brown + +Saturn (P2) Arduino Pro Micro +-------------------------------------- +1 VCC VCC +2 DATA1 A2 PF5 +3 DATA0 A3 PF4 +4 SEL1 15 PB1 (Shared with P1) +5 SEL0 14 PB3 (Shared with P1) +6 TL (5V) 6 PD7 +7 DATA3 A0 PF7 +8 DATA2 A1 PF6 +9 GND GND + +NOTE: The receiver of the Retro Bit 2.4GHz controller needs to be plugged + in after the adapter has been connected to USB and the RETROBIT_WL + define needs to be uncommented. +------------------------------------------------------------------------- */ + +// Set up USB HID gamepads +GAMEPAD_CLASS gamepad; + +// Controllers +uint8_t buttons[2][2] = {{0, 0}, {0, 0}}; +uint8_t buttonsPrev[2][2] = {{0, 0}, {0, 0}}; +uint8_t gp = 0; + +// Read R, X, Y, Z +void read1() { + PORTB &= ~B00001010; // Set select outputs to 00 + delayMicroseconds(SELECT_PAUSE); + buttons[0][1] |= (PIND & 0x0f) << 4; + if (GAMEPAD_COUNT == 2) + buttons[1][1] |= (PINF & 0xf0); +} + +// Read ST, A, C, B +void read2() { + PORTB ^= B00001010; // Toggle select outputs (01->10 or 10->01) + delayMicroseconds(SELECT_PAUSE); + buttons[0][1] |= (PIND & 0x0f); + if (GAMEPAD_COUNT == 2) + buttons[1][1] |= (PINF & 0xf0) >> 4; +} + +// Read DR, DL, DD, DU +void read3() { + PORTB ^= B00000010; // Set select outputs to 10 from 11 (toggle) + delayMicroseconds(SELECT_PAUSE); + buttons[0][0] |= (PIND & 0x0f); + if (GAMEPAD_COUNT == 2) + buttons[1][0] |= (PINF & 0xf0) >> 4; +} + +// Read L, *, *, * +void read4() { + PORTB |= B00001010; // Set select outputs to 11 + delayMicroseconds(SELECT_PAUSE); + buttons[0][0] |= (PIND & 0x0f) << 4; + if (GAMEPAD_COUNT == 2) + buttons[1][0] |= (PINF & 0xf0); +} + +void setup() { + // Set D0-D3 as inputs and enable pull-up resistors (port1 data pins) + DDRD &= ~B00001111; + PORTD |= B00001111; + + // Set F4-F7 as inputs and enable pull-up resistors (port2 data pins) + DDRF &= ~B11110000; + PORTF |= B11110000; + + // Set D4 and D7 as inputs and enable pull-up resistors (port1/2 TL) + DDRD &= ~B10010000; + PORTD |= B10010000; + + // Set B1 and B3 as outputs and set them HIGH (select pins) + PORTB |= B00001010; + DDRB |= B00001010; + + // Wait for the controller(s) to settle + delay(100); +} + +void controllerChanged(const int c) { + // if start and down are held at the same time, send menu and only menu + gamepad.buttons(c, buttons[gp][1] | ((buttons[gp][0] & 0x80) << 1)); + /* todo: + if (controllers.down(c, SC_BTN_START) && ((buttons[gp][0] & DOWN) >> 1)) { + gamepad.buttons(c, 0); + gamepad.press(c, BUTTON_MENU); + gamepad.setHatSync(c, DPAD_CENTERED); + return; + } + */ + if (((buttons[gp][0] & DOWN) >> 1)) { + if (((buttons[gp][0] & RIGHT) >> 3)) { + gamepad.setHatSync(c, DPAD_DOWN_RIGHT); + } else if (((buttons[gp][0] & LEFT) >> 2)) { + gamepad.setHatSync(c, DPAD_DOWN_LEFT); + } else { + gamepad.setHatSync(c, DPAD_DOWN); + } + } else if ((buttons[gp][0] & UP)) { + if (((buttons[gp][0] & RIGHT) >> 3)) { + gamepad.setHatSync(c, DPAD_UP_RIGHT); + } else if (((buttons[gp][0] & LEFT) >> 2)) { + gamepad.setHatSync(c, DPAD_UP_LEFT); + } else { + gamepad.setHatSync(c, DPAD_UP); + } + } else if (((buttons[gp][0] & RIGHT) >> 3)) { + gamepad.setHatSync(c, DPAD_RIGHT); + } else if (((buttons[gp][0] & LEFT) >> 2)) { + gamepad.setHatSync(c, DPAD_LEFT); + } else { + gamepad.setHatSync(c, DPAD_CENTERED); + } +} + +void loop() { + while (1) { + // Clear button data + buttons[0][0] = 0; + buttons[0][1] = 0; + buttons[1][0] = 0; + buttons[1][1] = 0; + + // Read all button and axes states + read3(); + read2(); + read1(); + read4(); + + // Invert the readings so a 1 means a pressed button + buttons[0][0] = ~buttons[0][0]; + buttons[0][1] = ~buttons[0][1]; + buttons[1][0] = ~buttons[1][0]; + buttons[1][1] = ~buttons[1][1]; + + // Send data to USB if values have changed + for (gp = 0; gp < GAMEPAD_COUNT; gp++) { + // Has any buttons changed state? + if (buttons[gp][0] != buttonsPrev[gp][0] || buttons[gp][1] != buttonsPrev[gp][1]) { + /* + Gamepad[gp]._GamepadReport.buttons = buttons[gp][1] | ((buttons[gp][0] & 0x80)<<1); + Gamepad[gp]._GamepadReport.Y = ((buttons[gp][0] & DOWN) >> 1) - (buttons[gp][0] & UP); + Gamepad[gp]._GamepadReport.X = ((buttons[gp][0] & RIGHT) >> 3) - ((buttons[gp][0] & LEFT) >> 2); + Gamepad[gp].send(); + */ + controllerChanged(gp); + buttonsPrev[gp][0] = buttons[gp][0]; + buttonsPrev[gp][1] = buttons[gp][1]; + } + } + +#ifdef RETROBIT_WL + // This delay is needed for the retro bit 2.4GHz wireless controller, making it more or less useless with this adapter + delay(17); +#endif + } +}