# Turn your Uno into a VGA output device!

**URL:** <https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969>\
**Category:** Displays\
**Created:** [April 20, 2012, 7:12am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969 "2012-04-20T07:12:21Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 20, 2012, 7:12am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/1 "2012-04-20T07:12:21Z")

</div>

After playing with the MicroVGA card, I was wondering if it was possible to simply output VGA video directly from the Uno?

And it turns out, yes it is ... see photo:

![](https://europe1.discourse-cdn.com/arduino/original/4X/8/3/e/83eb76f66fb941dd266be08b97322d2b247438b6.jpeg)

This is the result of quite a bit of research. It seems that a few other people have done something similar, which helps, because their research and examples were a big help.

However this is the first example, that I know of, that simply runs on an ordinary Uno, using the standard Arduino IDE (no assembler code).

The only additional hardware was:

- 5 resistors
- A DB15 socket to plug the VGA cable into
- Wires to connect

There are some unavoidable limitations in trying to squeeze this sort of performance out of a small chip like this, such as:

- Monochrome output
- 20 characters wide by 30 characters deep (160 x 480 pixels)

But, hey, this is more data than you can show on a 2-line LCD screen, or even a 4-line one.

The chip is kept quite busy refreshing the screen ... there is a horizontal sync pulse every 32 uS, which means there isn't a heap of spare processing time. But I have set it up to receive "print" commands via I2C from another Uno. That way one can be doing the calculations, and the other the displays. You could have the one do the lot, but the screen might have to go blank, or flicker, if it was doing a lengthy computation. But still, for the cost of a $5 Atmega328 chip, this is still quite a cheap output device (assuming you have an old LCD monitor lying around).

I'm going to spend the next couple of days documenting how it works, posting code, and so on.

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 20, 2012, 7:42am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/2 "2012-04-20T07:42:27Z")

</div>

For anyone keen to see the sketch, here it is:

[http://gammon.com.au/Arduino/VGA\_output.zip](http://gammon.com.au/Arduino/VGA_output.zip)

You need the "timer helpers" library, available from here:

[http://gammon.com.au/Arduino/TimerHelpers.zip](http://gammon.com.au/Arduino/TimerHelpers.zip)

---

<div class="post-metadata">

**Author:** ![JO3RI](https://avatars.discourse-cdn.com/v4/letter/j/439d5e/32.png) [@JO3RI](https://forum.arduino.cc/u/JO3RI)\
**Post date:** [April 20, 2012, 8:01am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/3 "2012-04-20T08:01:31Z")

</div>

Nice work Nick.

I'm very interested in this. I also found this: [Google Code Archive - Long-term storage for Google Code Project Hosting.](http://code.google.com/p/arduino-vgaout/) which has currently : 64x64 - 64colors  
Maybe it can help you improve your code?

keep us informed.

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 20, 2012, 8:13am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/4 "2012-04-20T08:13:47Z")

</div>

~~I didn't see any code in that link,~~ but thanks for that!

(Edit) I spotted the code. 🙂

His example of 64 x 64 pixels is a total of 4096. My screen shows 160 x 480 pixels which is a total of 76,800 pixels.

So there is a trade-off. For much fewer pixels you probably have time to output colour information.

I'll be documenting the design decisions, and how it all works. I found that time was extremely critical in trying to output even 20 characters (160 pixels) horizontally. For example, I had to restructure the font table in such a way as to save a single divide by 8 (or shift right 3 bits).

It's not even as if assembler would help. It takes 16 clock cycles to output 8 pixels, so you have a limited amount of time to work out what the next 8 pixels are going to be.

---

<div class="post-metadata">

**Author:** ![JO3RI](https://avatars.discourse-cdn.com/v4/letter/j/439d5e/32.png) [@JO3RI](https://forum.arduino.cc/u/JO3RI)\
**Post date:** [April 20, 2012, 8:41am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/5 "2012-04-20T08:41:01Z")

</div>

> His example of 64 x 64 pixels is a total of 4096. My screen shows 160 x 480 pixels which is a total of 76,800 pixels.

Yep I know, something in between would be nice 😉

Would it be possible to blow up fonts, without the need of adding a bigger font? Let's say you have a 7x5 font could you make it 14x10? For my project I would need large fonts, but for now I had to change resolution to achieve this:

[![]( " - YouTube") ](https://www.youtube.com/watch?v=EnherW4hM-g)

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 20, 2012, 8:56am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/6 "2012-04-20T08:56:48Z")

</div>

Certainly you can make them larger. In my project I simply doubled the font vertically to make it look more reasonable because the horizontal drawing was a bit slow.

---

<div class="post-metadata">

**Author:** ![JO3RI](https://avatars.discourse-cdn.com/v4/letter/j/439d5e/32.png) [@JO3RI](https://forum.arduino.cc/u/JO3RI)\
**Post date:** [April 20, 2012, 9:08am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/7 "2012-04-20T09:08:24Z")

</div>

Are you going to make a library? I would certainly use is. Right now I'm using SURE3208 dotmatrix panels and also a second ATMEGA just for rendering the displays. The first Arduino does all the other stuff and sends text buffers it to the second one over I2C, just as you are doing.

My CastDuino Shield (the second ATMEGA, use the same way you do, well sort of) has the code for connecting LCD, DOTMATRIX LED, VIDEO-out and needs VGA (microvga shield, doesn't play nice)

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 21, 2012, 4:14am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/8 "2012-04-21T04:14:37Z")

</div>

Full description of this project with code, wiring, explanations, now here:

> **[Gammon Forum : Electronics : Microprocessors : Arduino Uno output to VGA monitor](http://www.gammon.com.au/forum/?id=11608)**
>
> Gammon Forum : Electronics : Microprocessors : Arduino Uno output to VGA monitor

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 22, 2012, 1:13am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/9 "2012-04-22T01:13:36Z")

</div>

I managed to get colour output:

![](https://europe1.discourse-cdn.com/arduino/original/4X/8/c/1/8c1be61bae89d6a54d559951acb1f7d6d6430545.jpeg)

However memory is tight.

Sketch:

```cpp
/*
 VGA colour video generation
 
 Author: Nick Gammon
 Date: 22nd April 2012
 Version: 1.0
 
 Version 1.0: initial release

 Connections:
 
 D3 : Horizontal Sync (68 ohms in series) --> Pin 13 on DB15 socket
 D4 : Red pixel output (470 ohms in series) --> Pin 1 on DB15 socket
 D5 : Green pixel output (470 ohms in series) --> Pin 2 on DB15 socket
 D6 : Blue pixel output (470 ohms in series) --> Pin 3 on DB15 socket
 D10 : Vertical Sync (68 ohms in series) --> Pin 14 on DB15 socket
 
 Gnd : --> Pins 5, 6, 7, 8, 10 on DB15 socket

 Note: As written, this sketch has 34 bytes of free SRAM memory.
 
 PERMISSION TO DISTRIBUTE
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 and associated documentation files (the "Software"), to deal in the Software without restriction, 
 including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
 subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in 
 all copies or substantial portions of the Software.
 
 
 LIMITATION OF LIABILITY
 
 The software is provided "as is", without warranty of any kind, express or implied, 
 including but not limited to the warranties of merchantability, fitness for a particular 
 purpose and noninfringement. In no event shall the authors or copyright holders be liable 
 for any claim, damages or other liability, whether in an action of contract, 
 tort or otherwise, arising from, out of or in connection with the software 
 or the use or other dealings in the software. 

*/

#include <TimerHelpers.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>

const byte hSyncPin = 3; // <------- HSYNC

const byte redPin = 4; // <------- Red pixel data
const byte greenPin = 5; // <------- Green pixel data
const byte bluePin = 6; // <------- Blue pixel data

const byte vSyncPin = 10; // <------- VSYNC

const int horizontalBytes = 60; // 480 pixels wide
const int verticalPixels = 480; // 480 pixels high

// Timer 1 - Vertical sync

// output OC1B pin 16 (D10) <------- VSYNC

// Period: 16.64 mS (60 Hz)
// 1/60 * 1e6 = 16666.66 uS
// Pulse for 64 uS (2 x HSync width of 32 uS)
// Sync pulse: 2 lines
// Back porch: 33 lines
// Active video: 480 lines
// Front porch: 10 lines
// Total: 525 lines

// Timer 2 - Horizontal sync

// output OC2B pin 5 (D3) <------- HSYNC

// Period: 32 uS (31.25 kHz)
// (1/60) / 525 * 1e6 = 31.74 uS
// Pulse for 4 uS (96 times 39.68 nS)
// Sync pulse: 96 pixels
// Back porch: 48 pixels
// Active video: 640 pixels
// Front porch: 16 pixels
// Total: 800 pixels

// Pixel time = ((1/60) / 525 * 1e9) / 800 = 39.68 nS
// frequency = 1 / (((1/60) / 525 * 1e6) / 800) = 25.2 MHz

// However in practice, it we can only pump out pixels at 375 nS each because it
// takes 6 clock cycles to read one in from RAM and send it out the port.

const int verticalLines = verticalPixels / 16;  
const int horizontalPixels = horizontalBytes * 8;

const byte verticalBackPorchLines = 35; // includes sync pulse?
const byte verticalFrontPorchLines = 525 - verticalBackPorchLines;

volatile int vLine;
volatile int messageLine;
volatile byte backPorchLinesToGo;

#define nop asm volatile ("nop\n\t")

// bitmap - gets sent to PORTD
// For D4/D5/D6 bits need to be shifted left 4 bits
// ie. 00BGR0000

char message [verticalLines] [horizontalBytes];

// ISR: Vsync pulse
ISR (TIMER1_OVF_vect)
  {
  vLine = 0; 
  messageLine = 0;
  backPorchLinesToGo = verticalBackPorchLines;
  } // end of TIMER1_OVF_vect
  
// ISR: Hsync pulse ... this interrupt merely wakes us up
ISR (TIMER2_OVF_vect)
  {
  } // end of TIMER2_OVF_vect

void setup()
  {
  
  // initial bitmap ... change to suit
  for (int y = 0; y < verticalLines; y++)
    for (int x = 0; x < horizontalBytes; x++)
      message [y] [x] = (x + y) << 4;
   
  // disable Timer 0
  TIMSK0 = 0; // no interrupts on Timer 0
  OCR0A = 0; // and turn it off
  OCR0B = 0;
  
  // Timer 1 - vertical sync pulses
  pinMode (vSyncPin, OUTPUT); 
  Timer1::setMode (15, Timer1::PRESCALE_1024, Timer1::CLEAR_B_ON_COMPARE);
  OCR1A = 259; // 16666 / 64 uS = 260 (less one)
  OCR1B = 0; // 64 / 64 uS = 1 (less one)
  TIFR1 = _BV (TOV1); // clear overflow flag
  TIMSK1 = _BV (TOIE1); // interrupt on overflow on timer 1

  // Timer 2 - horizontal sync pulses
  pinMode (hSyncPin, OUTPUT); 
  Timer2::setMode (7, Timer2::PRESCALE_8, Timer2::CLEAR_B_ON_COMPARE);
  OCR2A = 63; // 32 / 0.5 uS = 64 (less one)
  OCR2B = 7; // 4 / 0.5 uS = 8 (less one)
  TIFR2 = _BV (TOV2); // clear overflow flag
  TIMSK2 = _BV (TOIE2); // interrupt on overflow on timer 2
 
  // prepare to sleep between horizontal sync pulses  
  set_sleep_mode (SLEEP_MODE_IDLE);  
  
  // pins for outputting the colour information
  pinMode (redPin, OUTPUT);
  pinMode (greenPin, OUTPUT);
  pinMode (bluePin, OUTPUT);
  
} // end of setup

// draw a single scan line
void doOneScanLine ()
  {
    
  // after vsync we do the back porch
  if (backPorchLinesToGo)
    {
    backPorchLinesToGo--;
    return;   
    } // end still doing back porch
    
  // if all lines done, do the front porch
  if (vLine >= verticalPixels)
    return;
    
  // pre-load pointer for speed
  register char * messagePtr = & (message [messageLine] [0] );

  delayMicroseconds (1);
  
  // how many pixels to send
  register byte i = horizontalBytes;

  // blit pixel data to screen    
  while (i--)
    PORTD = * messagePtr++;

  // stretch final pixel
  nop; nop; nop;
  
  PORTD = 0; // back to black
  // finished this line 
  vLine++;

  // every 16 pixels it is time to move to a new line in our text
  if ((vLine & 0xF) == 0)
    messageLine++;
    
  } // end of doOneScanLine

void loop() 
  {
  // sleep to ensure we start up in a predictable way
  sleep_mode ();
  doOneScanLine ();
 } // end of loop

```

Circuit:

 ![](https://europe1.discourse-cdn.com/arduino/original/4X/4/d/8/4d8ab3947bf0264f54b22a7c4d258eff1d724215.png)

(edit) Amended sketch slightly to add three "nop" instructions (no operation) after drawing the last pixel on the line. Without them, the final pixel is a bit narrow, as you can see in the photo.

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 22, 2012, 2:23am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/10 "2012-04-22T02:23:21Z")

</div>

> JO3RI:  
> Are you going to make a library?

You don't really need a library. Just load the "display VGA" text onto one processor, and do all the other work on the other one. A have a sample sketch on my web page that shows how you might send text from one to the other.

---

<div class="post-metadata">

**Author:** ![raron](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/raron/32/213667_2.png) [@raron](https://forum.arduino.cc/u/raron)\
**Post date:** [April 22, 2012, 2:51am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/11 "2012-04-22T02:51:34Z")

</div>

Wow!

Well done, and very well explained! Although I don't quite yet understand it, but that's not your fault!

I have one question / bug report (maybe). I just had a look at the color VGA example. I haven't connected anything to an Arduino yet, just looked at it in the IDE. It compiles just fine (and at 1116 bytes!)

Of course from you screen photo and all it seems to work, but did you do some trick with this part (line 96-97)?

```cpp
const byte verticalBackPorchLines = 35;  // includes sync pulse?
const byte verticalFrontPorchLines = 525 - verticalBackPorchLines;

```

It would seem that the byte "verticalFrontPorchLines" is truncated or wrapped around?

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 22, 2012, 7:39am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/12 "2012-04-22T07:39:08Z")

</div>

Well spotted! You are right, that was an error on my part. In my defence however, that variable is never used ... it was just there for documentation (and being const, it doesn't use any memory). That's why it worked. 🙂

---

<div class="post-metadata">

**Author:** ![iyahdub](https://avatars.discourse-cdn.com/v4/letter/i/d2c977/32.png) [@iyahdub](https://forum.arduino.cc/u/iyahdub)\
**Post date:** [April 22, 2012, 10:37am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/13 "2012-04-22T10:37:43Z")

</div>

Do keep us updated on this...Wicked stuff, no doubt !!

---

<div class="post-metadata">

**Author:** ![Nabe\_RMC](https://avatars.discourse-cdn.com/v4/letter/n/71c47a/32.png) [@Nabe\_RMC](https://forum.arduino.cc/u/Nabe_RMC)\
**Post date:** [April 23, 2012, 10:53am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/14 "2012-04-23T10:53:27Z")

</div>

> I'm very interested in this. I also found this: [Google Code Archive - Long-term storage for Google Code Project Hosting.](http://code.google.com/p/arduino-vgaout/) which has currently : 64x64 - 64colors

I'm Nabe.  
I'm glad that you were interested in my work.

That work started from here.  
[http://arduino.cc/forum/index.php/topic,76020.15.html](http://arduino.cc/forum/index.php/topic,76020.15.html)  
Sorry, I have been very busy so that project has been stopped.  
I have not to do programing for several month. =(  
[http://arduino.cc/forum/index.php/topic,76020.45.html](http://arduino.cc/forum/index.php/topic,76020.45.html)

one clock resolition soft timer - it was able to achieve.  
So, I drwew "sine wave".  
I had upped last version video before.  
Sorry, the sound is too loud.

[![](https://europe1.discourse-cdn.com/arduino/original/4X/c/a/3/ca393fe630c2644efbd9ab788dc07655e0856335.jpeg "Connect Arduino to VGA input on TV (sorry too loud)") ](https://www.youtube.com/watch?v=eGhsN9GWh48)

I have made some video signals with Arduino and registers.

> **[MeowJapan](https://www.youtube.com/user/MeowJapan/videos?sort=da&view=0)**
>
> Share your videos with friends, family, and the world

> **[NabeRMC](https://www.youtube.com/user/NabeRMC/videos?sort=da&view=0)**
>
> Share your videos with friends, family, and the world

To realize that it has a variety of techniques.  
Many pepole generate many ways.  
So It's intersting!  
Limit the ability of Arduino seems to be beyond more than we think.

Let's enjoy with Arduino!

Thanks!  
sorry I'm poor in English.

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 23, 2012, 11:30am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/15 "2012-04-23T11:30:51Z")

</div>

Thanks for that! (I had to turn the music off, sorry).

I haven't done "moving" graphics yet but I thought a "pong" game would be interesting.

> one clock resolition soft timer - it was able to achieve.

How did you do that?

---

<div class="post-metadata">

**Author:** ![Nabe\_RMC](https://avatars.discourse-cdn.com/v4/letter/n/71c47a/32.png) [@Nabe\_RMC](https://forum.arduino.cc/u/Nabe_RMC)\
**Post date:** [April 23, 2012, 12:49pm UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/16 "2012-04-23T12:49:14Z")

</div>

very very sorry too loud music 😰

> > one clock resolition soft timer - it was able to achieve.
> 
> How did you do that?

ATmega328 - NOP -\> 1clock  
I did a simulation for operation check.  
Arduino IDE generate .elf file. So I opened the ".elf " file with AVRstudio(free soft) and simulated.  
[https://picasaweb.google.com/117534855759200010314/Twitter\_Recently#5680380556146248114](https://picasaweb.google.com/117534855759200010314/Twitter_Recently#5680380556146248114)

I got a advice in this forum.  
[http://arduino.cc/forum/index.php/topic,80320.0.html](http://arduino.cc/forum/index.php/topic,80320.0.html)

I had upped the code to Google Code site.  
can download from here.  
[http://code.google.com/p/arduino-vgaout/downloads/list](http://code.google.com/p/arduino-vgaout/downloads/list)  
INT\_ICF1\_RGB\_DRAW.cpp - interrupt routine for line draw  
INT\_OutDraw.cpp - one clock resolution soft delay

sorry poor code.  
I have studied C++.

Thanks!

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 24, 2012, 10:10am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/17 "2012-04-24T10:10:17Z")

</div>

I modified my sketch to output a sine wave:

https://player.vimeo.com/video/80330369?h=fb88b53a36&app_id=122963

It could be faster if I pre-calculated the sine figures and stored them in flash memory.

```cpp
/*
 VGA colour video generation - Sine wave generation
 
 Author:   Nick Gammon
 Date:     22nd April 2012
 Version:  1.0
 
 Version 1.0: initial release

 Connections:
 
 D3 : Horizontal Sync (68 ohms in series) --> Pin 13 on DB15 socket
 D4 : Red pixel output (470 ohms in series) --> Pin 1 on DB15 socket
 D5 : Green pixel output (470 ohms in series) --> Pin 2 on DB15 socket
 D6 : Blue pixel output (470 ohms in series) --> Pin 3 on DB15 socket
 D10 : Vertical Sync (68 ohms in series) --> Pin 14 on DB15 socket
 
 Gnd : --> Pins 5, 6, 7, 8, 10 on DB15 socket

 Note: As written, this sketch has 34 bytes of free SRAM memory.
 
 PERMISSION TO DISTRIBUTE
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 and associated documentation files (the "Software"), to deal in the Software without restriction, 
 including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
 subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in 
 all copies or substantial portions of the Software.
 
 
 LIMITATION OF LIABILITY
 
 The software is provided "as is", without warranty of any kind, express or implied, 
 including but not limited to the warranties of merchantability, fitness for a particular 
 purpose and noninfringement. In no event shall the authors or copyright holders be liable 
 for any claim, damages or other liability, whether in an action of contract, 
 tort or otherwise, arising from, out of or in connection with the software 
 or the use or other dealings in the software. 

*/

#include <TimerHelpers.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>

const byte hSyncPin = 3;     // <------- HSYNC

const byte redPin = 4;       // <------- Red pixel data
const byte greenPin = 5;     // <------- Green pixel data
const byte bluePin = 6;      // <------- Blue pixel data

const byte vSyncPin = 10;    // <------- VSYNC

const int horizontalBytes = 50;  // 480 pixels wide
const int verticalPixels = 480;  // 480 pixels high

// Timer 1 - Vertical sync

// output    OC1B   pin 16  (D10) <------- VSYNC

//   Period: 16.64 mS (60 Hz)
//      1/60 * 1e6 = 16666.66 uS
//   Pulse for 64 uS  (2 x HSync width of 32 uS)
//    Sync pulse: 2 lines
//    Back porch: 33 lines
//    Active video: 480 lines
//    Front porch: 10 lines
//       Total: 525 lines

// Timer 2 - Horizontal sync

// output    OC2B   pin 5  (D3)   <------- HSYNC

//   Period: 32 uS (31.25 kHz)
//      (1/60) / 525 * 1e6 = 31.74 uS
//   Pulse for 4 uS (96 times 39.68 nS)
//    Sync pulse: 96 pixels
//    Back porch: 48 pixels
//    Active video: 640 pixels
//    Front porch: 16 pixels
//       Total: 800 pixels

// Pixel time =  ((1/60) / 525 * 1e9) / 800 = 39.68  nS
//  frequency =  1 / (((1/60) / 525 * 1e6) / 800) = 25.2 MHz

// However in practice, it we can only pump out pixels at 375 nS each because it
//  takes 6 clock cycles to read one in from RAM and send it out the port.

const int verticalLines = verticalPixels / 16;  
const int horizontalPixels = horizontalBytes * 8;

const byte verticalBackPorchLines = 35;  // includes sync pulse?
const int verticalFrontPorchLines = 525 - verticalBackPorchLines;

volatile int vLine;
volatile int messageLine;
volatile int backPorchLinesToGo;
volatile byte newFrame;

#define nop asm volatile ("nop\n\t")

// bitmap - gets sent to PORTD
// For D4/D5/D6 bits need to be shifted left 4 bits
//  ie. 00BGR0000

char message [verticalLines]  [horizontalBytes];

// ISR: Vsync pulse
ISR (TIMER1_OVF_vect)
  {
  vLine = 0; 
  messageLine = 0;
  backPorchLinesToGo = verticalBackPorchLines;
  newFrame = true;
  } // end of TIMER1_OVF_vect
  
// ISR: Hsync pulse ... this interrupt merely wakes us up
ISR (TIMER2_OVF_vect)
  {
  backPorchLinesToGo--;    
  } // end of TIMER2_OVF_vect

void setup()
  {
  
  // initial bitmap ... change to suit
  for (int y = 0; y < verticalLines; y++)
    for (int x = 0; x < horizontalBytes; x++)
      message [y] [x] = (7) << 4;
   
  // disable Timer 0
  TIMSK0 = 0;  // no interrupts on Timer 0
  OCR0A = 0;   // and turn it off
  OCR0B = 0;
  
  // Timer 1 - vertical sync pulses
  pinMode (vSyncPin, OUTPUT); 
  Timer1::setMode (15, Timer1::PRESCALE_1024, Timer1::CLEAR_B_ON_COMPARE);
  OCR1A = 259;  // 16666 / 64 uS = 260 (less one)
  OCR1B = 0;    // 64 / 64 uS = 1 (less one)
  TIFR1 = _BV (TOV1);   // clear overflow flag
  TIMSK1 = _BV (TOIE1);  // interrupt on overflow on timer 1

  // Timer 2 - horizontal sync pulses
  pinMode (hSyncPin, OUTPUT); 
  Timer2::setMode (7, Timer2::PRESCALE_8, Timer2::CLEAR_B_ON_COMPARE);
  OCR2A = 63;   // 32 / 0.5 uS = 64 (less one)
  OCR2B = 7;    // 4 / 0.5 uS = 8 (less one)
  TIFR2 = _BV (TOV2);   // clear overflow flag
  TIMSK2 = _BV (TOIE2);  // interrupt on overflow on timer 2
 
  // prepare to sleep between horizontal sync pulses  
  set_sleep_mode (SLEEP_MODE_IDLE);  
  
  // pins for outputting the colour information
  pinMode (redPin, OUTPUT);
  pinMode (greenPin, OUTPUT);
  pinMode (bluePin, OUTPUT);
  
}  // end of setup

// draw a single scan line
boolean doOneScanLine ()
  {
    
  // after vsync we do the back porch
  if (backPorchLinesToGo > 0)
    {
    backPorchLinesToGo--;
    return false;   
    }  // end still doing back porch
    
  // if all lines done, do the front porch
  if (vLine == verticalPixels)
    return newFrame;
    
  // pre-load pointer for speed
  register char * messagePtr =  & (message [messageLine] [0] );

  delayMicroseconds (1);
  
  // how many pixels to send
  register byte i = horizontalBytes;

  // blit pixel data to screen    
  while (i--)
    PORTD = * messagePtr++;

  // stretch final pixel
  nop; nop; nop;
  
  PORTD = 0;  // back to black
  // finished this line 
  vLine++;

  // every 16 pixels it is time to move to a new line in our text
  if ((vLine & 0xF) == 0)
    messageLine++;
    
  return false;
  }  // end of doOneScanLine

float radians = 0;
const float pi = 3.1415926;
const float radiansIncrement = (pi / 2.0) / (horizontalBytes / 2);
byte x;
boolean Up = true;
byte colour = 0;
boolean Calc = true;

void advanceLine ()
  {
  if (Calc)
    {
    x = sin (radians) * horizontalBytes;
    if (Up)
      {
      radians += radiansIncrement;
      if (radians >= pi / 2)
        Up = false;
      }
    else
      {
      radians -= radiansIncrement;
      if (radians <= 0)
        {
        Up = true;
        radians = 0;
        colour++;
        }
      }
    Calc = false;
    }
  else
    {
    memmove (& message [0] [0], & message [1] [0], sizeof message - horizontalBytes);  
    memset (&message [verticalLines - 1] [0], (colour + 1) << 4, horizontalBytes);
    memset (&message [verticalLines - 1] [0], colour << 4, x);
    Calc = true;
    }

  newFrame = false;
  }
  
void loop() 
  {
  // loop to avoid overhead of function call
  while (true)
    {
    // sleep to ensure we start up in a predictable way
    sleep_mode ();
    if (doOneScanLine ())
      advanceLine ();
    }  // end of while
 }  // end of loop

```

---

<div class="post-metadata">

**Author:** ![iyahdub](https://avatars.discourse-cdn.com/v4/letter/i/d2c977/32.png) [@iyahdub](https://forum.arduino.cc/u/iyahdub)\
**Post date:** [April 24, 2012, 2:46pm UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/18 "2012-04-24T14:46:36Z")

</div>

I know this is just a proof of concept more than anything, but wouldnt there be a way of centring the display, despite the limitations u already mentioned ?!? Sorry for my _"doh!!!"ness_ on the subject !

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 24, 2012, 8:43pm UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/19 "2012-04-24T20:43:52Z")

</div>

Sure. But my particular monitor is "smart" enough to left-justify on the start of the visible area. Initially when I tested it was more centered. I strongly suspect that the monitor firmware tries to detect where the visible signal starts (because different VGA cards might be out by a few nanonseconds one way or another) and then remembers something like "oh this card has a back porch of 2.1 uS, so we'll start drawing after that each time".

Judging by the logic analyzer output the signal is fairly correctly centered.

The monitor has a "horizontal position" menu setting. If I play with that I can center it. Ditto for vertical.

---

<div class="post-metadata">

**Author:** ![nickgammon](https://avatars.discourse-cdn.com/v4/letter/n/45deac/32.png) [@nickgammon](https://forum.arduino.cc/u/nickgammon)\
**Post date:** [April 25, 2012, 12:29am UTC](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969/20 "2012-04-25T00:29:53Z")

</div>

This video shows the text-based output in action. You can see how quickly the text draws, and it zooms in to show the clarity of the letters. You can also see commands from the second Uno to clear the screen, clear a line, do cursor addressing, and scrolling:

https://player.vimeo.com/video/80330209?h=14ab39bc20&app_id=122963

[Next page](https://forum.arduino.cc/t/turn-your-uno-into-a-vga-output-device/99969.md?page=2)
