June 20th, 2013 @ 01:10 PM
Teensy 2.0 - NES/SNES Gamepad to USB

I got my Teensy 2.0 board shipped earlier this week, and after an hour or so of playing around with it, I felt comfortable enough to start my first "Hello World!" project, which I determined should be a NES/SNES Gamepad to USB adapter.

NES to USB

The first step in the process was figuring out how to read signal from a NES gamepad, and relay that over the Serial.println() function for debugging. Nintendo was actually quite innovative in designing their early controllers. Unlike other systems made by Atari and Sega which were basically just parallel ports (with some special logic for the Genesis to allow more buttons than lines), the NES and SNES used cheap, off-the-shelf, 4021 parallel to serial shift registers. Fortunately, data sheets for the 4021 are freely available, so we can easily figure out how to drive it:

  1. Raise CLOCK and LATCH.
  2. Lower LATCH. This causes the 4021 to sample inputs.
  3. Lower CLOCK. Data is now being sent out on the DATA pin.
  4. Raise CLOCK. This shifts the register to the next bit.
  5. Repeat steps 3 and 4 until remaining bits are read.

So thats the background information, but how do we wire this thing up? Well, pinouts for both NES and SNES are pretty abundant, so it doesn't actually require that much effort. I choose pins A0, A1, and A2 for CLOCK, DATA, and LATCH, and pulled power from the Vcc and Ground pins.


+5 = +5 Volts
 G = Ground
 C = Clock
 L = Latch
 D = Data

NES:

  ______
 // | G |
//+5| C |
|  x| L |
|  x| D |
---------

SNES

__________________
|+5 C L D | x x G )
------------------

Now, the reason for sending debugging information over Serial is probably pretty obvious at this point. We're reading bits out of a shift register, but we don't actually know which bit does what! The NES and SNES also have the bits inverted, so HIGH means OFF and LOW means ON. Using this information, we can send debugging information to figure out which bits map to which buttons. I did this by printing the bit number followed by the bit value:

for (i = 0; i < 8; i++) {
    Take Sample
    ...
    Serial.print(i, DEC);
    Serial.println(value, DEC);
}

Using this method, I was able to determine the button mappings for both the NES and SNES gamepads. The next thing I needed to do was translate the bits received from the gamepad, to USB events. Initially, I did this by using just the Joystick.button() and Joystick.hat() functions in the Teensy library, but later I also added keyboard mappings, for games or applications which don't natively support joysticks.

Finally, I added device type auto detection, by reading 16 bits out of the connected device, and looking at which bits are HIGH. This works since the bits are inverted by default. We can assume that if the gamepad was just powered on, that no buttons are being pressed, and that if any of the bits above the 8 bit are HIGH, then it must be an SNES gamepad (since that sends out 16 bits of data instead of 8).

In total, this project only took about 2 hours to do, and the final result is under 300 lines of C. It really got me to jump head first into embedded development, and I already have several more project ideas I want to try out. Also, the full source code is available on my GitHub, if anyone wants to play around or improve what I've done already.


Post Comment
April 2nd, 2013 @ 07:21 PM
Finding Color Palettes in Images

A side project I'm working on, in the Computer Vision field, is an algorithm which searches images to find a usable color palette for a given tone (low, mid, or high).

My initial thoughts were that it would be significantly easier if the image was converted to HSV color space, instead of working in RGB. If you're unaware, HSV stands for Hue Saturation Value. It works particularly well for this problem, because that is exactly the type of information we want to look at. Its also a much more natural color space to think in, because it's modeled after the Color Wheel.

The procedure for my first attempt was:

  • Convert image to HSV color space.
  • Sort each line by hue, find the median (for use as a color sample).
  • Filter useless color samples (the "noise").
  • For the remaining color samples, find ones that work well together (similar saturation/value).

This method worked fairly well for some images, but failed for some. The problem was that it wasn't looking at enough color samples. My second attempt was very similar, but pulled 5 samples per line, instead of 1. This worked significantly better, but still failed on some test images.

My third, and most successful attempt was:

  • Convert image to HSV color space.
    • For each color, create a "hash" at a reduced bit depth (e.g. 3-6 bits per channel).
    • If hash does not exist in "already processed" dictionary, add color to list of samples.
  • Sort samples by frequency (most to least).
  • Linear search list [of roughly 50 samples] until N colors matching the following criteria are found:
    • Saturation/value must be within user-supplied levels.
    • Colors at similar saturation/value levels must be separated by a minimum of 20º.

Here's one of the tests for the third method. I have a couple ideas to improve it further, which I'll be working on when I get a little more free time.


Post Comment
Newer Posts Older Posts
©2011 Cameron Eure. All rights reserved.