Main Logo
Main
Hardware
Software
Misc
Search

Old school Nintendo will always be part of my generation's past time. It was my first gaming system and I still remember the day that my parents brought it home. I recently became interested in acquiring a Nintendo for old time reminicing. Sure, I have an XBOX, but its just not the same. While I was looking online and at stores, I noticed that it was hard, if not impossible, to find a lot of my favorite games. This is what started my interest in NES emulators and ROM's for the PC. This seemed like the easiest and best solution. I had all of the games at my disposal and I never had to blow on any of the cartridges to get them to work.

There was always one thing that bothered me about playing on my computer though; it just doesn't have the same feel without the controllers. I looked around online and discovered companies that created adapters to connect a controller up to the computer, but they were all either ridiculously overpriced or used a serial connector (my computer does not have any serial ports.. All USB). This is why I started to seek out a way to inexpensively connect an authentic Nintendo Controller to my PC. Having recently graduated with my Bachelors of Sciencce in Computer Engineering, this seemed like a pretty trivial task.

For those interested, I will go through all of the steps to create your very own USB NES Controller. However, if you are not interested in HOW it is done and just want the HEX files and the schematic, you can skip to the end.

The first thing I did was order some old controllers off of EBay and tear them apart. I was a little surprised at the simplicity of the controllers, 5 wires and one IC. Upon further examination, I noticed that the IC was just a CD4021 - 8 Bit parallel/serial in, serial out shift register.

[CD4021 Datasheet]

Controller Schematic

From the datasheet and the schematic, you can see that all of the buttons are active low. Furthermore, the order of the data is, from the MSB: A, B, Start, Select, Up, Down, Left, Right. The pins for the NES controller are now apparent, Data, Clock, Latch, Vdd, and Vss. Before we get too far ahead of ourselves here, lets get the controller working with a simple PIC chip. A Microchip PIC is just a microcontroller, any microcontroller will work, I happen to prefer the PIC's myself, but you really could use just about ANY microcontroller. We need to generate some waveforms for the controller to send us back data. Namely, we need to pulse the latch pin and then clock the data out of the shift register. The waveform should look something like this:

Again, for prototyping I hooked the controller up to a PIC18F452. The code to read the controller and output the keys to the serial port is shown below. It is quite simple, the code just scans the controller and outputs the results to the serial port.

[Full Source Code]


		  
output_bit(NES_LATCH, 1);
delay_ms(1);
output_bit(NES_LATCH, 0);

ConState = 0;

for(n=0; n <8; n++)
{
	shift_right(&ConState, 1, !input(NES_DATA));
	delay_ms(1);
	output_bit(NES_CLK, 1);
	delay_ms(1);
	output_bit(NES_CLK, 0);
}

Before we get too much further, I want to talk just a little bit about how the actual Nintendo handles these controllers. The NES system scans the controllers 60 times per second (every 16.6 ms). The data is buffered and held in a register that holds the present state of the controller. The rate at which this register is scanned is controlled by the ROM (usually in a periodic interrupt). Having said this, lets go back to our example. How often should the controller be scanned? Does it have to be scanned at exactly the same rate as the NES would do it? No... We are free to scan it however fast or slow we want. It really doesn't matter! Ideally, we should scan it at LEAST every 16.6 ms but that is a long time in the world of microcontrollers. I have updated the code below to take advantage of a couple other features of the PIC. Instead of bit banging the waveforms in the main loop, I moved the code to an interrupt. This will be very easy to include when we start to add USB functionality. The scanning code is now implemented in a 17 state finite state machine (FSM).

[Full Source Code]


		  
#int_timer1
void NES_Read()
{
	static int8 PS = 0;
	static int8 FIFO;
	
	set_timer1(60536);                           // 1 KHz

	if(ps == 0)
	{
		output_bit(NES_LATCH, 1);
		output_bit(NES_CLK, 0);
	}
	else if(ps == 1)
	{
		output_bit(NES_LATCH, 0);
		FIFO = 0;
		shift_right(&FIFO, 1, !input(NES_DATA));
	}
	else if(ps == 15)
	{
		shift_right(&FIFO, 1, !input(NES_DATA));
		output_bit(NES_CLK, 0);
		ConState = FIFO;
		ps = 255;
	}
	else if(bit_test(PS,0))                     // Falling edge
	{
		shift_right(&FIFO, 1, !input(NES_DATA));
		output_bit(NES_CLK, 0);
	}
	else                                        // Rising edge
	{
		output_bit(NES_CLK, 1);
	}
	
	if(++ps > 15)
		ps = 0;
}

Before I hop right into the USB code, I am going to briefly try to explain the major details of the USB protocol so that the code makes a little more sense. The USB protocol is partitioned into several different layers. The root is always a device. Under a device is a configuration. A device may have different configurations, such as powered from the USB port or powered externally. Under each configuration is an interface. The interface describes how the communiction will take place between the host and the device. Lastly, there are the endpoints where all of the data transactions occur. Data transfer falls into the following categories: Control, Interrupt, Bulk, and Isochronous. Each has benefits over others and is more appropriate in different situations.

In order to establish communication with the USB, the host needs to know information about the device. The device sends descriptors for each of the layers listed above that describe how to communicate with a particular device. Lastly, the issue of power; USB specifies that a device is garunteed a 100mA (5V) block but can request more blocks. The request for more blocks of power is solely up to the host controller to grant or deny.

With that out of the way, lets talk about how I plan to communicate with the actual PC. Most USB items that you have ever purchased required you to install drivers on the computer before you plugged the device in. However, I am going to take advantage of a little secret that Microsoft Windows already has a lot of drivers that we can easily communicate with, namely the Human Interface Driver (HID). This is a generic input driver that will work perfectly for what we are trying to accomplish.

Now with that mini-USB introduction, lets get back to the hardware. In order to implement USB, we need a USB transciever. There are two options, use any microcontroller we want with and external USB chip, or use a microcontroller that already has USB support built-in. Since I am trying to keep the component count to a minimum as well as minimize the cost, the microcontroller with USB support is a much better choice. Thankfully for us, Microchip has a PIC that is perfect for this application, the 18F2455. It is a flash programmable device with built-in USB 2.0 support.

Without further ado, here is the schematic:

And now, the source code!


		  

//////////////////////////
//     ENTRY POINT      //
//////////////////////////
void main() 
{
    signed int8 out_data[3];
        
    set_tris_a(0x01);                           // PortA outputs except for NES_DATA (RA0)
    setup_timer_1(T1_INTERNAL);                 // Scan the NES controller
    usb_init();                                 // Initialize USB subsystem
    setup_wdt(WDT_ON);
    
    enable_interrupts(INT_TIMER1);              // Timer1 interrupts
    enable_interrupts(GLOBAL);                  // Get the clock pulse rocking
    
    printf("\r\n\r\nUSB NES Controller\r\n");
    printf("by Drew Hall (dhall@zero-soft.com\r\n\r\n");
    printf("Waiting for enumeration...");

    while (TRUE) 
    {
        output_bit(LED0, 0);                    // Both LED's off
        output_bit(LED1, 0);
        
        while(!usb_enumerated()) 
        {
            restart_wdt();                      // Reset the watchdog
            putc('.');
            output_toggle(LED0);
            delay_ms(250);
        }
        
        printf("\r\n\r\n***Enumerated***\r\n"); // We are connected to the PC now
        output_bit(LED0, 1);
        output_bit(LED1, 1);
        
        while(usb_enumerated()) 
        {
            restart_wdt();                      // Reset the watchdog
            out_data[0] = 0;                    // Transform the data to match the HID
            out_data[1] = 0;                    // This is done this way to match many of
            out_data[2] = 0;                    // the older games that require the data to
                                                // come in a specified order
            if(ConState & A)
                out_data[2] |= 0x01;
            
            if(ConState & B)
                out_data[2] |= 0x02;
                
            if(ConState & SELECT)
                out_data[2] |= 0x04;
            
            if(ConState & START)
                out_data[2] |= 0x08;
            
            if(ConState & UP)
            {
                out_data[2] |= 0x10;
                out_data[1] = -127;
            }
            
            if(ConState & DOWN)
            {
                out_data[2] |= 0x20;
                out_data[1] = 127;
            }
        
            if(ConState & LEFT)
            {
                out_data[0] = -127;
                out_data[2] |= 0x40;
            }
            
            if(ConState & RIGHT)
            {
                out_data[0] = 127;
                out_data[2] |= 0x80;
            }
            
            usb_put_packet(1, &out_data[0], 3, TOGGLE); // Send the data off
            
            delay_ms(10);                               // Wait for the next polling event
         }
         
         printf("\r\n\r\nDevice Un-configured.\r\n");
   }
}

I will leave the reader with an idea, I have also created a set of wireless USB remotes by simply including a transmitter in the controller. The main problem with this is that the controller needs batteries because it can no longer be powered from the USB port. It is a non-trivial task to fit batteries in the controller because lets face it, there just isn't that much empty space in there. This also requires two microcontrollers instead of just one, although the one inside the controller can be a very simple one now (12F or 16F series PIC).

While it is easy enough to use the DIP package of the PIC and just solder all of the wires directly onto the PIC, I decided to make a circuit board that uses all surface mount parts. The board nicely fits in the controller and is much more reliable than just soldering the wires onto the chip.

[Board Files]

 

My intention was to inform you, the reader, as an amatuer hobbyist, could create your own USB Nintendo controller. However, I have had several requests from people to purchase different parts of the project because they do not have the hardware or tools required. If you are interested in purchseing any of the components, send me an email. It is not my intent to become rich from this project. If I were in it for the money, I never would have given away all of the source code and the schematics (**cough** RetroZone! **cough**).

[Final Source Code] [Compiled Code (HEX)] [Final Board Layout]