Coder Social home page Coder Social logo

orangefox86 / dreamcastcontrollerusbpico Goto Github PK

View Code? Open in Web Editor NEW
46.0 5.0 7.0 67.88 MB

Dreamcast to USB Gamepad Converter for Raspberry Pi Pico

License: MIT License

CMake 1.88% C++ 92.39% C 5.27% Shell 0.46%
controller dreamcast maple-bus usb

dreamcastcontrollerusbpico's Introduction

DreamcastControllerUsbPico

Maple Bus emulation platform for interfacing to a Dreamcast controller port (client mode) or from a Dreamcast peripheral (host mode)

Update 2024-03-16: I let this project go a bit stale after running into a wall with HID gamepad support and not knowing where to take it from there. My personal goals have changed since I modified one of my Dreamcasts. I no longer care so much about interfacing a Dreamcast controller to USB. My focus has shifted towards creating peripherals to connect to a real Dreamcast - this library can go both ways :P Feel free to reach out if you encounter any issues or want to help out.

New goals for this project:

  • Client mode
  • Enable lightgun support (currently in progress)
  • Interface with multiple USB devices (currently only Dualshock 4 controller interface works)
  • Harden the USB host interface (hot-swap doesn't work at the moment)

Original goals for this project:

  • Host mode
  • Detect and interact with the following:
    • Controller
    • VMU
    • Jump pack
  • Setup USB HID Gamepad that supports vibration
  • Setup some other USB device for VMU access
  • Create whatever Linux/Windows drivers are necessary to communicate with all devices (probably will just use libusb)
  • Interface with emulator such as Redream for controller, jump pack, and VMU

Refer to the releases page for current progress. Refer to the issues tab for things left to be implemented and known bugs.

Why the RP2040 is a Game Changer (and what makes this project different from others)

To emulate a bespoke bus such as the Maple Bus on an MCU, one would usually either need to add extra hardware or bit bang the interface. This is not true with the RP2040 and its PIO. Think of it as several extra small processors on the side using a special machine language purpose-built for handling I/O. This means communication can be offloaded to the PIO and only check on them after an interrupt is activated or a timeout has elapsed. Check out maple_in.pio and maple_out.pio to see the PIO code.

Luckily, the RP2040 comes with 2 PIO blocks each with 4 separate state machines. This means that the RP2040 can easily emulate 4 separate controller interfaces, each at full speed!


Quick Installation Guide

Connecting the Hardware for Host Mode

Host mode allows you to connect up to 4 Dreamcast controllers to a PC over USB.

This should be implemented at your own risk! There is risk of damage to your PC USB ports, Pico, Dreamcast peripherals, or Dreamcast if circuitry is improperly handled, and I am not liable for any damage that may occur due to use of the following schematic or firmware (see LICENSE.md).

Schematic

Isolation Circuitry

Select the appropriate isolation circuitry for your needs.

Isolation Circuitry

Dreamcast Controller Pinout

For reference, the following is the pinout for the Dreamcast controller port. Take note that many other sources found online refer to one of the ground pins as a connection sense, but the Dreamcast controller port module has both of these ground pins hard wired together. As such, this project doesn't rely on any such hardware sense line. Instead, the detection of a connected device is performed by polling the bus until a response is received, just as a real Dreamcast would.

Dreamcast Port

Connecting the Hardware for Client Mode

Client mode emulates a single controller for use with a Dreamcast. This was added in as an extra feature for this project mainly to demonstrate that the MapleBus library may be used in either direction.

This should be implemented at your own risk! There is risk of damage to your PC USB ports, Pico, Dreamcast peripherals, or Dreamcast if circuitry is improperly handled, and I am not liable for any damage that may occur due to use of the following schematic or firmware (see LICENSE.md).

Client-Schematic

Build Instructions (for Linux and Windows)

If running under Windows, install WSL and your desired flavor of Linux. I recommend using Ubuntu 20.04 as that is what I have used for development. Then the steps below may be run within your WSL instance.

  1. Install git, cmake, and gcc-arm-none-eabi compiler by running the following commands
sudo apt update
sudo apt -y install git cmake gcc-arm-none-eabi
  1. (optional) In order to run and debug tests, install standard gcc compilers and gdb by running the following
sudo apt -y install build-essential gdb
  1. Clone this repo then cd into the created directory
git clone https://github.com/Tails86/DreamcastControllerUsbPico.git
cd DreamcastControllerUsbPico
  1. Pull down the pico SDK (this project now uses a fork of the SDK - it will no longer compile under the standard SDK)
git submodule update --recursive --init

Hint: if you have issues building, the easiest way to correct any submodule synchronization issue is to delete the ext/pico-sdk directory (ex: rm -rf ext/pico-sdk), and then re-run the above submodule update command.

  1. (optional) Build and run tests - this runs core lib unit tests locally
./run_tests.sh
  1. Execute the build script
./build.sh

After build completes, binaries should be located under dist/. Pre-built release binaries may be found here.

This project may be opened in vscode. In vscode, the default shortcut ctrl+shift+b will build the project. The default shortcut F5 will run tests with gdb for local debugging. Open the terminal tab after executing tests with debugging to see the results.

Loading the UF2 Binary

Hold the BOOTSEL button on the Pico while plugging the USB connection into your PC. A drive with a FAT partition labeled RPI-RP2 should pop up on your system. Open this drive, and then copy the desired uf2 file for either host or client operation here. The Pico should then automatically load the binary into flash and run it. For more information, refer to the official Raspberry Pi Pico documentation.

Helpful Tips

Host Mode Tips

  • The LED on the Pico board may be used for quick status - when connected to USB, it should remain on when no button is pressed on any controller and turn off once a button is pressed.
  • The included file formatted_storage.bin may be used to delete and format a VMU attached to a controller when this project is used in host mode. For example, rename this file vmu0.bin and copy to DC-Memory drive when a VMU is inserted into the upper slot of Player 1's controller.
  • A serial device shows up on the PC once attached - open serial terminal (BAUD and other settings don't matter), type h, and then press enter to see available instructions.

Maple Bus Implementation

Refer to documentation here for general information about the Maple Bus.

Interfacing with the PIO State Machines

The MapleBus class operates as the interface between the microcontroller's code and the PIO state machines, maple_in.pio and maple_out.pio.

Using 2 separate PIO blocks for reading and writing is necessary because each PIO block can only hold up to 32 instructions, and this interface is too complex to fit both read and write into a single block. Therefore, the write state machine is completely stopped before starting the read state machine for the targeted bus. Switching state machines is fast enough that there shouldn't be a problem. Testing showed the handoff always occurs within 1 microsecond after bringing the bus back to neutral. A device on the Maple Bus starts responding some time after 50 microseconds from the point of the bus going neutral after an end sequence. This ensures that a response is always captured.

The following lays out the phases of the state machine handled within the MapleBus class.

MapleBus Class State Machine

PIO Data Handoff

When the write method is called, data is loaded into the Direct Memory Access (DMA) channel designated for use with the maple_out state machine in the MapleBus instance. The DMA will automatically load data onto the TX FIFO of the output PIO state machine so it won't stall waiting for more data.

The first 32-bit word loaded onto the output DMA is how many transmission bits will follow. In order for the state machine to process things properly, (x - 8) % 32 == 0 && x >= 40 must be true where x is the value of that first 32-bit word i.e. every word is 32 bits long and at least a frame word (32 bits) plus a CRC byte (8 bits) are in the packet. This value needs to be loaded with byte order flipped because byte swap is enabled in the DMA so that all other words are written in the correct byte order. The rest of the data loaded into DMA is the entirety of a single packet as a uint32 array. The last uint32 value holds the 8-bit CRC.

A blocking IRQ is triggered once the maple_out state machine completes the transfer. This then allows MapleBus to stop the maple_out state machine and start the maple_in state machine.

A Direct Memory Access (DMA) channel is setup to automatically pop items off of the RX FIFO of the maple_in state machine so that the maple_in state machine doesn't stall while reading. Once the IRQ is triggered by the maple_in state machine, MapleBus stops the state machine and reads from data in the DMA.

Generating Maple Bus Output

The maple_out PIO state machine handles Maple Bus output. Data is generated by following the signal definition here.

Sampling Maple Bus Input

The maple_in PIO state machine handles Maple Bus input. Some concessions had to be made in order to handle all input operations within the 32 instruction set limit of the input PIO block. The following are the most notable limitations.

  • Only a standard data packet may be sampled
    • The Maple Bus protocol has different types of packets depending on how many times B pulses in the start sequence, but those packets are ignored in this implementation
  • The full end sequence is not sampled
    • The packet length in the frame word plus the CRC are relied upon during post-processing in order to verify that the received packet is valid

Sampling the Start Sequence

The input PIO state machine will wait until A transitions LOW and then count how many times B toggles LOW then HIGH while making sure A doesn't transition HIGH until after B transitions HIGH. If the toggle count isn't 4, then the state machine keeps waiting. Otherwise, the state machine signals the application with a non-blocking IRQ and continues to the next phase where data bits are sampled.

Sampling Data Bits

For each bit, the state machine first waits for the designated clock to be HIGH before proceeding. Then once this line transitions to LOW, the state of the designated data line is sampled. State transitions of the designated data line are ignored except for the case when sensing the end sequence is required as described in the next section.

Sampling the End Sequence

Whenever A is designated as the clock, the input PIO state machine will detect when B toggles HIGH then LOW while A remains HIGH. It is assumed that this is the beginning of the end sequence since this is not a normal behavior during data transmission. The state machine will then block on an IRQ so that the application can handle the received data.


Appendix A: Abbreviations and Definitions

  • 0x Prefix: The following value is hex format
  • Byte: Data consisting of 8 consecutive bits
  • DMA: Direct Memory Access
  • LSB: Least Significant Byte
  • LSb: Least Significant bit
  • MSB: Most Significant Byte
  • MSb: Most Significant bit
  • Nibble: Data consisting of 4 consecutive bits
  • PIO: Programmable Input/Output
  • SDCK: Serial Data and Clock I/O
  • Word: Data consisting of 32 consecutive bits

External Resources

Maple Bus Resources

https://archive.org/details/MaplePatent/page/n7/mode/1up

http://mc.pp.se/dc/maplebus.html and http://mc.pp.se/dc/controller.html

https://tech-en.netlify.app/articles/en540236/index.html

https://www.raphnet.net/programmation/dreamcast_usb/index_en.php

https://web.archive.org/web/20100425041817/http://www.maushammer.com/vmu.html

https://hackaday.io/project/170365-blueretro/log/180790-evolution-of-segas-io-interface-from-sg-1000-to-saturn

https://segaretro.org/History_of_the_Sega_Dreamcast/Development

dreamcastcontrollerusbpico's People

Contributors

tails86 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

dreamcastcontrollerusbpico's Issues

Clean up MaplePacket

MaplePacket was originally made to have read-only data. More use-cases were shimmed in over time and caused a mess of a structure.

Look further into Retro Fighters controller

Issue #31 addressed an issue by allowing 2 consecutive communication failures. Only on 3rd failure would the controller disconnect. I'm not entirely convinced this is an issue with the controller but instead with the way bits are read (I seem to recall my logic analyzer properly picking up the data when things failed). This issue just serves as a reminder for me to go back and look at any other funny business with bit sampling.

Fix some CDC interface issues

  1. Handle \r

Testing this stuff on PuTTY on windows is problematic. It seems that PuTTY sends '\r' on enter (not \r\n as expected). Commands are never parsed in that case. It probably makes sense to start parsing a command on either \r or \n.

  1. It may make sense to output \r\n instead of just \n for universal compatibility

  2. Multiple commands in same line may not process

mCommandReady is cleared even if mulptiple \n may exist - only one \n is parsed

  1. tud_task() gets called from the maple bus core on printf() - I don't think this is right, and I'm a little surprised this still works

  2. Handling backspace isn't working properly

Host: Windows doesn't parse players 1 through 4 labels properly

It seems to be a long-known bug with Windows DirectX/drivers from forum posts I have gone through. It still happens in Windows 11 now that I've actually looked. What's worse is that Windows doesn't even seem to sort them in the proper order. I'm assuming that the web interface is grabbing raw data instead of going through DirectX which is why things show up properly at gamepad-tester.com.

I think my only options here would be to either use a different USB descriptor for the device like xinput or spin up my own driver. Using xinput seems to be what most controller manufacturers are doing these days in order to avoid writing drivers.

This project will likely be very helpful.

Reorganize README

The README needs to be reorganized to provide a better flow, especially for those who just want quick install instructions and not my whole life story.

Client: Allow for DreamcastStorage component to flip bytes before writing and after reading

The arrays of uint32 values being written are stored on the RP2040 in little-endian format. When VMU data is stored in file form, words are normally stored in big-endian format. This conversion wasn't originally done because it allowed for no conversion when spitting out the data, and data was only ever used internally. Proposed use-cases will allow for external injection of data which will be in big-endian format. This makes storing data in the correct format more crucial.

This change will likely cause the Dreamcast to not understand files generated with past versions of this library.

Rename coreLib to hostLib

It occurs to me that coreLib has all the code necessary to communicate on MapleBus as a host. There has been external interest to using MapleBus in device mode. In order to reduce confusion, renaming coreLib to hostLib makes sense.

It would be nice to break this up into separate projects in the future.

Client: Make a storage variant with non-volatile memory

The DreamcastStorage class in client currently reads from and writes to RAM which was done to simplify things in order to test capabilities. In order to instead use flash memory, both cores need to be executing from RAM or be otherwise disabled while flash is being written to. The flag -DPICO_NO_FLASH=1 may be used to load and run all code from RAM on startup. This limits the program size to the size of RAM (256 KB), but it will relieve that constraint completely.

Secondly, it takes too long to write 512 bytes in line with normal Maple Bus execution. The best option would be to use the second core to write flash, as needed. Some care would then need to be taken with the read operation to make sure:

  • read from flash doesn't happen at the same time flash is being written
  • read operation isn't reading stale data from flash while some data is waiting to be written to the target address range

It may be helpful to see if it is possible to send the start sequence out first as a way to hold the line in a busy state while read or write operations are being carried out.

It may be beneficial to make 2 variants of client: one with volatile memory and one with non-volatile memory. There are use-cases where volatility of memory doesn't matter, such as one where this system is attached to a PC host, and that host is feeding memory to the pico. Using onboard flash adds too much complexity in that case.

DreamcastStorage::read() improperly uses scheduler

The scheduler was not meant to be safe for accessing across multiple cores, but it is being used by the "other", USB core in DreamcastStorage::read(). The easiest way to fix this is to pass block number to read unto the task() function.

Implement function definition into peripherals

Knowing the function definition is necessary to activate some functionality properly - these are the 3 words at the beginning of device info. Pass FD word to the proper peripheral.

Also, it seems that 3 is the upper limit on number of peripheral types on a single device. That fact could be used to simplify things.

Fault while using Retro Fighters controller

To reproduce:

  1. Plug in Retro Fighters controller and verify controls are working
  2. Wait 15 minutes
  3. Check gamepad controls again

Expected response:
Controls no longer working

I haven't experienced this with an OEM controller yet.

Potential for segmentation fault in storage read operation

Multi-byte read/write operations are done between cores in DreamcastStorage. This could cause segmentation fault if the mReadPacket pointer is partially written while being accessed. There is less of an issue with the use of mReadingTxId between cores, but it's still sloppy. Use std::atomic to synchronize values.

Add support for all different types of controllers

I obtained some documentation on button layout for different controllers - support all different types in the controller peripheral. Apparently, fishing rod DOES need to be handled differently. doh!

Host & Client: Implement using a bus transceiver in order to protect Pico and attached devices

The Pico is not able to source or sink any more than 50 mA on the I/O. This isn't an issue unless a fault occurs which would kill the Pico. Using a 2-bit bus transceiver which could source/sink higher current value per pin would be beneficial. Selecting one with good transient voltage suppression is desired. The transceiver will need to switch from output to input and back within 1 microsecond, and it must be able to handle pulse widths of 150 ns.

This involves setting up 4 new outputs in order to select the external transceiver direction. The documentation must then be updated to provide this as an option.

Host: Weird things can happen if VMU added or removed after host reads and caches mass storage FAT

When the file system adds or removes a file, it sends UNIT ATTENTION to the host so it knows something changed. Windows, however, does not reread the FAT, even when F5 is pressed. I've found some help in resending UNIT ATTENTION until read of FAT is detected, but it only works once. I haven't confirmed if the host is actually receiving UNIT ATTENTION.

Figure out what needs to be done in order to force Windows to recheck the FAT.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.