Retrofitting an old-school game controller with a USB interface
Playing retro-style, side-scrolling computer games from indie developers with a keyboard and mouse just doesn't feel right. The 16-bit sprite animations, simple player controls and synthesized audio all harken back to the days when game consoles couldn’t deliver photorealistic graphics and lifelike gaming experiences. These games recall afterschool marathon sessions spent trying to level up, and recreating that experience can’t be complete without the tactile part of those memories: the old-school game controller like the ones that came with the 8-bit Nintendo system. The steps we took to retrofit a more than 20-year-old game controller with a USB interface using cheap, off-the-shelf printed circuit boards, open-source firmware and compiler tools describe an easy, fun and inspiring “do-it-yourself” project that a nostalgia-seeking gamer (or anyone else) can complete with just a few hours of effort. While using a toolset from a microcontroller (MCU) vendor may seem intimidating to a first-time developer, spending a little extra time learning the powerful and often free toolsets offered by MCU vendors can enable a developer to create exactly the system they envisioned, limited only by imagination.
Why not use Arduino?
The reasons why a do-it-yourself developer might want to use Arduino (an open-source electronic prototyping platform) for this project are obvious: the toolset is free, and the legions of developers and hobbyists that make up Arduino’s online support community can help with any questions that might arise during development.If you’re doing some casual development, taking the Arduino route is a great choice.
However, many developers find that they hit the limits of what the Arduino platform is capable of creating if they choose to progress beyond the prototyping stage or if they drift outside of the bounds drawn by the available firmware API. If a project that begins as a hobby evolves to something that begins to feel more “productize-able,” it might make more sense to develop firmware and hardware without Arduino’s level of abstraction. Similarly, many MCUs include extensive on-chip features that are not accessible through Arduino’s standard code base.Exploring MCU options and learning about all the features offered by vendors can inspire a developer to create a system that’s truly unique and exciting.
While all MCU vendors that offer USB-enabled products provide customers with starter code and most offer some form of software tools for USB device firmware generation, it pays to learn a little about USB before embarking on development. USB provides a standard protocol that is used by countless devices of infinite variety. Nearly any electronic device that can send information to a computer or receive information from a computer often uses USB because of its reliably, compatibility across platforms and high throughput.
First, a few notes on terminology.USB uses a host-device relationship to distinguish between main processors like PCs and Macs (hosts) and systems such as keyboards, hard drives and countless other peripherals (devices). The USB specification defines many different “classes” that define a certain set of capabilities to be used by devices within that class. For instance, the “mass storage device” USB class includes external hard drives and thumb drives. This class provides the highest bandwidth data interfaces possible to facilitate speedy transfer of large amounts of data. Conversely, keyboards, mice and joysticks fall under the “human interface” (HID) class. This class of devices uses data interfaces that provide guaranteed latency to ensure that there is no perceptible lag between the time that the user presses a key and the point at which the user sees a response from the USB host, such as a typed character appearing on-screen. The game controller used in this design enumerates as an HID device.
USB devices describe their capabilities to a host during a process called “enumeration,” which happens when the device connects to the host.Packets of information called “‘descriptors” sent during enumeration describe all capabilities of a device. Many descriptors are standard to the USB protocol, and others are class-specific. Descriptors can be difficult to read and understand, much less to create and debug. Fortunately, MCU vendors provide code that includes most of the hard work of descriptor creation. All that’s left for the developer to do is to change the few descriptor values to make their devices work optimally.
One example of an MCU vendor that provides a helpful codebase and support documentation for their USB-enabled MCUs is Silicon Labs, which offers both 32-bit and 8-bit mixed MCUs.We chose Silicon Labs’8-bit C8051F327 MCU for our do-it-yourself game controller design because it provides the optimal balance of mixed-signal functional, performance, cost-effectiveness and small size. Offering high-performance peripherals and a highly optimized 8051 core, Silicon Labs’ 8-bit MCUs provide developers with opportunities to vastly enhance and grow a device’s feature set and functionality, turning what was once a simple design into something much cooler, quirkier and maybe even marketable..
What Silicon Labs provides
Silicon Laboratories offers a complimentary integrated development environment (IDE), supplementary tools such as a hardware configuration wizard, compatibility with free third-party compiler tools and many code examples. All of this software can be downloaded from the Silicon Labs website at no charge. Additionally, Silicon Labs offers a cost-effective ToolStick hardware development platform. For about $20, a developer can purchase a daughter card with a C8051F327 device that offers full pin access and some other features, as well as a second board that includes the debugging circuit. Crucially, this full-featured F327 daughter card is tiny – perfect for wedging into a retrofitted game controller.
The Compiler toolset
This design uses the free, open-source Small Device Code Compiler (SDCC) to build the firmware project. The SDCC toolset was developed to enable developers to create systems when purchasing a compiler such as Keil or Raisonance is a little out of reach. SDCC can be used for prototyping, but the toolset is robust enough to be used during a product’s entire development. The Silicon Labs IDE is compatible with the SDCC toolset, and so developers will be able to set breakpoints, add variables to the watch window and debug firmware just as they would with any other supported compiler. Did I mention that it’s free?
The HID Report Descriptor
HID devices have a crucial feature that makes this device class particularly versatile and useful for USB device developers. The Report Descriptor (as shown in Figure 1) describes the size, shape and characteristics of all the data that can be sent to and from the device. The HID specification and supplemental documents provided on USB.org show the many ways developers can package data for transfer.The report descriptor’s construction is complicated, with all values encapsulated in “pages,” which are then encapsulated into “collections.”For this design, the report descriptor looks as follows. This descriptor is included in the example code included for download at the link provided at the end of this article.
Understanding how the controller works
Let’s take a look at how these game controllers work. Most game controllers, especially the ones developed for older, 8-bit systems, usually consisted only of a set of mechanical switches connected to a rudimentary processor that communicates with the game system through a bi-directional serial interface. Retrofitting a USB interface into one of these controllers can be as simple as powering the PCB with USB power and snooping the switch inputs to determine button state.
Wiring up the hardware
This design chose to hack an old 8-bit Nintendo controller. The pictures in Figure 2 and 3 show the step-by-step build process required to wire into the mechanical switches and provide power to the controller.
Interfacing the MCU with the controller
Each port pin assigned to a mechanical switch must be configured as a digital input. Silicon Labs provides an easy-to-use graphical tool for hardware configuration called the Config Wizard. Like all Silicon Labs tools, this application can be downloaded at no cost.
After configuring the port pins to digital input, code must be written that periodically polls the mechanical switches. Since the MCU will not have many responsibilities other than reading the switches and communicating across USB, the code to read the switches can simply be placed in the “main()” routine’s “while(1)” loop so that the switches will be polled constantly when the MCU isn’t busy servicing USB interrupts.
Button state is stored into a byte of data that is then interpreted and sent to the USB host when the host periodically polls the device across the data interface. The USB host’s system drivers will know what to do with the received data because the report descriptor has already defined how each bit in each byte should be used.
Other design ideas
This design creates a retro controller that can be used to play indie developer games and games on certain emulator platforms. However, the F327 MCU can do much more! By editing the HID report descriptor, the controller could be made compatible with different control schemes and interface with different types of games. For example, a developer could add analog joysticks or include potentiometers that act as control wheels.
If the developer gets really ambitious, he or she can even look into the other USB classes defined in the USB specification and pile on more functionality, such as a streaming audio interface or a mass storage device interface. A developer could make a game controller that stores its own games inside the controller itself. A user could plug in the controller, copy a game to a PC, and be up and running with a unique gaming experience a moment after connection.
With all of the possibilities enabled by free tools and powerful, cost-effective hardware, who has time to play video games, anyway?
Senior Applications Engineer, Microcontroller Products
Parker Dorris is a senior applications engineer supporting Silicon Labs’ microcontroller product line. He joined Silicon Labs in 2003 when the company acquired Cygnal Integrated Products. Mr. Dorris specializes in the areas of human interface and USB embedded system design. He holds a BSEE from the University of Texas at Austin.
Applications Engineer, Microcontroller Products, Silicon Labs
Gregory Posey is an applications engineer supporting Silicon Labs’ microcontroller product line in the Embedded Systems Group. He joined Silicon Labs as an intern in 2008. Mr. Posey specializes in software design and scripting, and he tries to incorporate engineering into every aspect of his life, going so far as to build a photo booth for his wedding instead of renting one and converting NES controllers to USB when off-the-shelf controllers weren’t good enough. He holds a BSEE from the University of Texas at Austin.