February 13, 2011 21 Comments
A couple of months ago I explained how to access and interpret the video signal from the original grayscale Gameboy. My image reconstruction at the time was done in a graphing program with some saved data. Good for a proof of principle, but a little bit lame as far as applications go.
Let me show you where I was actually headed with this project:
(This site has moved to http://www.flashingleds.net, go there if you have comments or questions)
That’s right my friends. Gameboy on an oscilloscope. I’ll walk you through the details of what’s going on here, but if you’re feeling impatient there’s a YouTube clip at the end of the post.
This project can be pretty cleanly broken up into two parts: Getting the data from the Gameboy and then displaying it on the oscilloscope.
1) Getting data from the Gameboy
When I discussed the video signals earlier, I implied that they were slow enough to be trivial for your typical 20MHz 8-bit microcontroller. In actual fact a 4MHz datastream is fast! You get 250ns between rising clock edges, and in that time you need to:
- Wait for the clock edge (it’s not perfectly synchronous, so you can’t just use a 4MHz timer)
- Read the digital value on two pins
- Store that value in memory somewhere
At 20MHz, you’ve got 5 clock cycles to do it. Good luck with that.
I thought about this for a while, and decided what I needed was some kind of serial→ parallel conversion. That way I could read in 8 bits at a time on each channel, and only have to do it every (250ns*8)=2μs. Since I’m not yet one of the cool kids who plays with FPGAs, I started doing this with 74 logic – counting clock pulses, latching data from a serial-parallel shift register every 8 counts, so forth and so such. It took a while.
And then a month into setting up this complicated circuit, it struck me that I am a moron; that most microcontrollers already have exactly this feature and it’s called SPI. Also since SPI modules are done in hardware they run much faster than you could do in software, definitely fast enough to keep up with this video signal in any case.
With that little design hiccup aside, the rest went pretty quickly. I’m using an XMega128A1 controller on one of Justin Mattair’s XMEGA development boards. This thing runs at 32MHz, and is a great option for this project because the XMega series have peripherals coming out of their asses. Seriously, there’s 4 SPI modules, 2 DACs, something like 800 timers and probably a couple thousand UART modules, I lost count.
In this case having two SPI modules makes life very easy – one for each data channel. I gave each module one of the data channels and the clock signal from the Gameboy. The HSYNC and VSYNC signals go to IO pins to keep everything synchronized. I’ll try to keep this description fairly high level; check out the source code if you want to get into the details.
The only other thing to attend to is level conversion – the Gameboy is giving 5V signals but the XMega wants 3.3V. Since this is a read-only arrangement, it’s sufficient to just use a voltage divider on every signal line.
The end result of this is that I have a couple of arrays of pixel data that are being updated in real time. All I have to do is find something suitably cool to do with that information.
2) Putting it on a frickin oscilloscope
Here’s a shot of the hardware, which I’m now halfway through describing. The other half consists of the digital-to-analog converters responsible for driving the oscilloscope.
One of the neat things about analog scopes is how easy it is to repurpose the display. You are typically given the option to switch from the time-sweep mode of operation to instead control the brightness and x-y deflection with analog voltages. Which means you just have to stick some DACs on your micro to produce those voltage signals and you have a video display! There a whole bunch of great projects along these lines; two very noteworthy examples are Jan’s clock and Matthew’s incredible terminal console.
The scope I have is a PM3065, a 100MHz dual channel cathode ray scope and apparently a collaboration between Fluke and Phillips. I paid AU$130 second hand last year. Importantly it has a BNC input at the rear for controlling the beam intensity (“Z input”), in this case going smoothly from full-on at about 0.8V to completely blanked at about 1.06V. The exact numbers depend on what you have the intensity knob set to, but 26mV is more or less the range.
Let’s discuss the X-Y control first. Switched to XY mode, the voltage on the first input controls the X-deflection of the spot and the second controls the Y-deflection. By applying sawtooth ramp signals to both inputs you can raster out a square.
To match the Gameboy resolution we need a resolution of 160 x 144 pixels (DAC steps), and to get a halfway decent framerate we’re going to need to sweep them pretty fast. The analog voltage range is not particularly important because we can easily adjust the gain settings on the scope.
This is where the boatload of inbuilt peripherals on the XMega comes in handy again – it has 2 inbuilt 12-bit DACs that can run at 1MHz. They have a little bit of non-linearity at low and high values, but with 4096 (12bit) resolution there’s plenty of room to just add an offset to the ramps.
We’re nearly there – all that’s left is to set the intensity at each point of the raster according to the pixel data read from the Gameboy. Sadly we’ve used up the 2 inbuilt DACs to do the rastering, but since we only need 2-bit control a simple R2R ladder will suffice. There’s a little bit of fine tuning required though – for the intensity modulation input we need something like 9mV steps with about 800mV offset. Here’s what I’m using:
Pretty straightforward I hope. I built it all on breadboard with multi-turn potentiometers so I could easily tune resistor values/swap things out. By putting a 2-bit value on DB1 and DB2 with a couple of IO pin on the XMega, we get an analog voltage out with an adjustable step size and offset. A good check that this works is to put the scope on regular (time sweep) mode and watch the R2R analog output. You’ll see the discrete values, and if at the same time you connect that output to the intensity modulation input you get something like this:
Demonstrating intensity modulation
From there it’s easy to play with the various potentiometers until you get 4 different spot intensities (as I have in the picture above).
And that’s pretty much it! The program logic is along the lines of:
Capture a frame:
Listen in on the video data until a new frame starts (VSYNC pulse)
Capture the pixel data for that frame with the SPI ports
Draw the frame:
For each pixel in Y (0…144)
Increment the Y-deflection DAC output
For each pixel in X (0…160)
Increment the X-deflection DAC output
Set the R2R DAC value for this pixel
The XMega is capable of capturing data at the full 60Hz that the Gameboy is refreshing, but in the above scheme we stop capturing when we draw a frame to the screen. While that’s going on we miss some frames; at the moment it captures one then skips three. This gives a 15Hz refresh rate on the scope. Here’s a logic analyzer capture that shows this going on:
The signals of interest here are #3 (Gameboy rereshing screen), #5 (XMega capturing data) and #6 (XMega drawing). #3 is just the VSYNC signal from the Gameboy, while I’m getting #5 and #6 from flashing status LEDs on the XMega board. #5 goes high while it’s reading in a frame, #6 while it’s drawing on the scope.
15Hz is a slow enough frame rate to be kind of annoying. The XMega has the speed to push this up to at least 20Hz, however at that speed the image starts to look terrible. I’m using a 1μs delay after each pixel to give the DACs time to settle. We can check this out with the scope by looking at DAC sweeps:
Unsurprisingly the culprit is the R2R DAC output, which is being passed back and forth all over a breadboard. It’s taking about 1μs to settle after small changes and nearly 2μs for large changes. Hence the 1μs delay in the raster loop – it sharpens up the picture at the expense of refresh rate.
It would be very easy to improve on this by simply relocating the R2R ladder and amplifier to some stripboard or a proper PCB. Alternatively you could think about scrapping the R2R DAC for a decent IC version, or shopping around for a micro with 3 DACs.
And now I’ll leave you with a video of it in action. Sound is coming from the Gameboy speaker, and the weird horizontal brightness bands are an artifact of trying to film it. Enjoy, and be sure to let me know if you try something similar!