How to Produce 640×480 Color VGA Video From an 8-Bit Arduino!

Let me set this up for you: most 8-bit AVRs in the wild (I happened to use an Arduino Nano for this project) are running at 16 MHz.  That’s 16,000,000 calculations per second… a very respectable number for most embedded applications.

The VGA industry standard, which is pretty much the default case “we-can-always-fallback-to-this” video standard (640 pixels wide by 480 pixels tall by 60 frames per second), requires pixels to be clocked out at 25.175 MHz:

25,175,000 > 16,000,000. 

And that was just one of the barriers to pulling off this silly project.  And, yes, with the hack I told you about last time (Please see my notes below), more is possible without overclocking the Arduino – roughly 800 or so pixels wide in 4 bit color should be doable with a 16MHz part, and, probably 1024 pixels in 4 bit color are in reach for 20 MHz clocked parts.  (If you’re willing to drop to 2 or 1 bit color and spend a ton on ICs that can handle even faster clocks, you can hit HD resolutions – but I think you’ll run into financial constraints before you max out on the technical side)

Let’s begin.

Or, if you want to skip the details:

What Was the Goal?

I’m not sure what the current resolution record on a stock clocked Arduino is, but with tight coding you can achieve around 512 pixels wide on a 20 Mhz or ~400 pixels wide on a 16 MHz part.  That math is simple: (20/25.175 * 640) or (16/25.175) * 640, if you can clock out your pixels at one pixel per clock.

My goal was to produce ‘full screen’ video from an Arduino using one of the early-gen display technologies (in America – so NTSC or VGA, sorry SCART and PAL).  Color too.

Even though people have hacked color NTSC video out of Arduinos, they generally have only done so by changing the clock to (usually) 14.31818 MHz crystals.  Television compatibility is great, but laziness won out and I targeted VGA and its simple, discrete outputs for red, green, and blue… and keeping my stock 16 MHz clock.

As a challenge to myself, I wanted to keep the clock under the maximum figures quoted by AVR (20 MHz at 5.5 volts).  Overclocking is fine for the consumer on a cheap part, but it wouldn’t be worth it to assume all risks in a commercial product by exceeding manufacturer specifications… if that’s your final destination.

So, my requirements were set:

  • Full width video, (>= 640 pixels wide) from an Arduino
  • Color
  • Use only parts I have on hand
  • No reprogrammable logic (FPGA/CPLD/GAL/PAL/PLA/Whatever else is out there)
  • No overclocking!

What Was Your Parts List?  Can I Have a Schematic?

Of course!

I did it with 6 ICs, 10 resistors, one VGA port and a bunch of jumper wire.  Substitutions are fine, just keep the propagation delay of the parts under 32 ns so you can latch in time (if you do my clock hack, probably 20ns due to the weird duty cycle) or so.  The first four are for the clock circuit I discussed last week.

  • HD74LS04P – Not Gate.  Used as Delay
  • HD74LS08P – And Gate.  XOR half
  • HD74LS32P – Or Gate.  XOR other half
  • SN74F10N – Fast 3 Input Nand Gates.  Clean up the ugly clock from the hacked XOR, also used as Inverter
  • DM74LS244N – 3 State Buffer/Line Driver/Line Receiver.  Control which pixel of the two is on the output bus.
  • SN74LS373N – 8 D Type Latches. Our digital sampler/output to resistors and sync.
  • VGA Port – See Nick Gammon’s site for how to wire this.
  • Resistors ~68 Ohm.  Sync signals.
  • Resistors ~470 Ohm. R/G/B signals.
A Note/Disclaimer:  I make no warranty that this is absolutely correct, and that I even transposed it correctly from my working copy.  I may have transposed this incorrectly, so don’t even trust the picture 100%.  I won’t be held responsible if you break any equipment using this circuit!  Before you turn this on, check my connections are accurate, your connections are correct and you understand what’s happening. 
Click to Zoom

Click to Zoom

(Note: try not to use my clock multiplier circuit if you build this.  It’s better to start with a 32 MHz oscillator and divide the clock by 2 to drive the Arduino than to use my hack.  Plus, that would cut your IC count down by 2-3.)

How Did You Achieve 640×480?

Click to Zoom

Click to Zoom

At this point I’d like to thank three folks in particular for inspiring this project.  Nick Gammon is the first – his VGA library was the start point for this successful project, and this demo requires his Timing library.  His timers greatly simplified the interrupt writing necessary for this project.  His wiring diagrams and initial code-base were also invaluable to sanity checking my early prototypes.

Second, thanks are owed to Henning Karlsen, who had great 16×16 fonts available for easy use in Microcontroller projects.  Please checkout his projects, especially his UTFT related projects where he has a ton of resources for serial displays.  My message generator uses one of his fonts.

Third, to Linus Akesson (a.k.a. lft) who bitbanged vga color video out of a similar part back in 2008.  (His site, here).  Inspirational!

As for my project?  At the most basic level, I treated the 8-bit microcontroller hobbyists love to love as a 4 bit microcontroller by dividing its output into two logical parts.  For this demo, I had two sets of Reds, Greens and Blues, laid out like you see on the right somewhere (the Microcontroller is an Atmel ATMega 328p on an Arduino Nano board).

Pin D7: Red 1
Pin D6: Green 1
Pin D5: Blue 1
Pin D4: Red 2
Pin D3: HSync
Pin D2: Green 2
Pin D1 (Tx): N/C
Pin D0 (Rx): Blue 2

… that’s all Port D.

Once I had the 32 MHz clock I discussed in my last post, the project came together quickly: the 32 MHz clock goes to the Latch Enable Pin (11) of the LS373.  Enable gets tied to ground (always on), in effect latching to the 373 outputs to whatever value happens to be presented every 32 ns.

The 244N uses the stock 16 MHz clock, plus the clock 180 degrees out of phase with itself connected to the two enables.  When one clock is high, the other is low, so only one pixel is allowed through to the 373N… and thus presenting ~810 samples to the monitor for every 640 pixel horizontal (and viewable) cycle.  Yes, that’s 640+ samples, completing my goal so I can sleep well at night.

Could You Simplify the Circuit?

If you give up color fidelity/the latch, you can use the clock and an inverted clock into the enables on the DM74LS244N – 2 chips.

As for real answers, some folks will be bothered by the mild amount of chip inflation on the board.  Setting aside the fact that you could achieve a 32MHz clock with an external crystal, and divide it to a 16MHz clock for the Arduino (thus eliminating 3 ICs) I’ve thought about it a bit, and I can’t come up with a way to clock out more than one pixel per clock without some active component assistance.

The “simplest way to simplify” would be to get rid of the latch and the buffer and go to a transistor only setup.  I tried to do this with common NPNs and PNPs – 2222s and 3904s  and 3906s -  but they just didn’t have the switching speed necessary.  I don’t have any higher speed transistors, but I’d love if someone tried to do this with something faster.  I’m thinking:

2 transistors per bus for R/G/B, one active high and one active low, then the 470 ohm resistors into the VGA port.

If you pull that off you can kill the clock circuit completely since we’ll be clocking at 32 MHz a second – low clock AND high clock assuming your transistors are fast enough.

As for doing it with all passive components?  Perhaps by introducing some well timed delays with some fancy math and carefully selected components?  I doubt it; my guess is you’ll attenuate the signal too much for it to be useful to the monitor/TV; I believe some silicon/silicon equivalent will need to be in there.  So, my guess is the minimum circuit is 6-8 transistors (PNP/NPNs) and some resistors.

Please someone try to prove that wrong, though!  It would make my year to see someone improve this design!

What Problems Did You/Do You Face?

Simple – samples, samples everywhere.  We’re talking errors on top of errors.  First off, you’ve got the ugly clock with the fluctuating duty cycle which is a consequence of the hack from last article.  That’s not the main problem though.

The biggest issue is the fact that we’re now clocking at 32 MHz into a standard calling for 25.175 MHz.  While that wasn’t a problem when folks were producing less pixels than 640 – you were guaranteed to have at least one of your pixels sampled – at 32 MHz you actually lose data.  It’s also far enough off that you get weird artifacts like you can see in this demo picture with my Nano displaying of the name of an incredible blog you should subscribe to (and showing off 640 pixels per line and its beautiful 8 color palette!):

The error is huge: 32 / 25.175 =~ 27% error rate!  It’s safe to say there’s an issue every 3-4 pixels: the monitor will sample while the latch is changing, or catch it during propagation delay (low to high or high to low isn’t instantly 5v or 0v) and render a color darker than we intended.  Take note: there shouldn’t be vertical lines in the solid color horizontal bars in my video.

The third factor is technological – the monitor samples our jittery 32MHz hacked clock at 25.175 MHz to try to get 640×480.  The monitor itself?  1920×1080.  For this particular monitor, that’s not an issue because I can get it to display double pixels at 1280×960 and letterbox the sides (and leave a sliver at the bottom – see the demo) of the screen.  But still, of course, blowing up pixels means you’re also zooming in on the problems we just discussed while the monitor is trying to smooth out the area between pixels.  So, yes, errors on top of errors.

How Could You Improve It?

First, and simply, the clock.  Best would be to clock the Arduino at 12.5875 MHz using a divide by 2 (use a counter) after a 25.175MHz oscillator.  Second best, is to target a higher clock rate.  That could be either with VESA ‘non-standard standards’ or with other accepted timings (which aren’t quite 640×480@60 – that’s the gold standard).  640×480@73 fps, for example, needs a 31.5 MHz clock – that’s only a 1.59% error.  Quite the cleanup from 27%.  You can also use VESA timings targeted at 32 MHz – YMMV, and you’ll have to read the specs on your target monitor.

Second, a fourth bit of color for each pixel (remember, we divided our 8 bit processor into 2 – we’re wasting a bit).  It’s a nonstandard palette, but RGBI is doable.  You just need to move the one sync pin.

Third, external memory.  The only practical use of the code as is?  A digital signage display, or a silly hack for a personal web site (wait a second…).  There isn’t enough on-board RAM to store discrete pixel information for 640×480, you only get one clock per two pixels already, and self-modifying code on a Harvard Architecture uC which can only run from flash is… not smart.

How Can I Use It?

Sorry I buried this – I really wanted everyone to understand the challenges first.  Usage is simple:

  1. Burn the CKOUT fuse on your AVR microcontroller – you need to Google how to do this; I did it on a Mac by modifying “boards.txt” and choosing “Burn Bootloader” in the Arduino IDE.  This puts the 16 MHz clock on pin D8 for the Nano.
  2. Next, grab Nick Gammon’s Timer libraries.
  3. Third, download our code which runs the demo for the below video from here:Full640x480Video (zip)
  4. Wire up the schematic – make sure to find a way to get a doubled clock.  We suggest a PLL or using a higher clock to begin with then dividing it for the Arduino to use.  You can always do something like my clock doubler circuit, but please, please do it a better way.

Want to edit the text to something meaningful to you?

We made a script to make it easy to change the message displayed.  Click here to try it out – you just need to copy/paste the output into the fontLines16.h and fontLines16_2.h file and overwrite whatever is there to see the message transition.  (On a side note, that’s Javascript compiling C which you’ll copy & paste into the Arduino IDE running on Java which compiles to AVR assembler.  Hard to wrap my head around it!)

Oh, of course, you do this at your own risk.  I won’t be held responsible for any trouble you get into trying to reproduce this project, especially with whatever expensive (or cheap) equipment you expose this too.  This is on you; please understand what is wired to what and why; I can’t guarantee my schematics are 100% accurate.  If there is a conflict, please refer to the picture of the circuit board, if it helps (that one works – well, at least on all the monitors in my house), but even then you should work through it on your own before trusting a random guy on the internet.

Since you’ve been waiting patiently, finally, let’s show the demo.  We show off all 8 colors – each in rows 10px high,  a ’14 color’ video rainbow made by mixing pixels, then (ugly, see: 27% timing error) some writing, then 10 lines of clocking out 640 pixels of red/white mixed followed by blue/white mixed.

The Demo!

And if video isn’t your thing, you don’t want to build it and you don’t believe me that it is doing at least 640 pixels (don’t feel bad, my wife didn’t believe me at first, either), here’s a full screen capture off my SLR.

Each band is 8 colors – black, white, red, green, blue, yellow, cyan and purple.  Remember, each pixel by definition can only hold one color – I explicitly draw a new color in each pixel.  So pick a color (say, red – that one is easy to see), count it, then multiply by 8:

Full crop of color bars from an Arduino

Full crop of color bars from an Arduino (Click for full width)

If you still don’t believe me (you must be fun at parties!), pop up the AVR 8-bit instruction listing.  Search for the ‘OUT’ instruction.  Note that it is a one clock instruction (16 MHz).  Note that we treat each ‘OUT’ as 2 pixels using our external hardware (2x16MHz = 32 MHz, which is more than 25.175), so if you look at the assembly generated by our code it looks like a whole bunch of this:

  •   // PORTD = B01010100; <— 010 Green on one pixel, 110 Yellow on the one next to it
    4c2:    9b b9           out    0x0b, r25    ; 11

Your Turn!

So, now, go have fun with the code and the schematic!  It’s released with the same license as Nick Gammon’s VGA code, so go out and make something more interesting than a poorly sampled digital signage display which shows a rainbow, then come back and share!

Leave a Reply

Your email address will not be published. Required fields are marked *