Go Down

Topic: Turn your Uno into a VGA output device! (Read 30747 times) previous topic - next topic

Nabe_RMC

very very sorry too loud music :smiley-roll-sweat:

Quote

Quote
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

I got a advice in this forum.
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
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!

Nick Gammon

#16
Apr 24, 2012, 12:10 pm Last Edit: Nov 26, 2013, 02:30 am by Nick Gammon Reason: 1
I modified my sketch to output a sine wave:

http://vimeo.com/80330369

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

Code: [Select]
/*
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
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

iyahdub

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 !
10 LET Loop=Infinite
20 GO TO 10

Nick Gammon

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.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

#19
Apr 25, 2012, 02:29 am Last Edit: Nov 26, 2013, 02:29 am by Nick Gammon Reason: 1
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:

http://vimeo.com/80330209
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Graynomad

I saw a method of doing this the other day that used the SPI shift register to spit the bits out, that took a large load of the uC as you just have to handle V and H-sync. Only mono of course.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Nick Gammon

Was that my method? I am using SPI and the output is mono.

But the processor still has to work pretty hard (well it would get bored otherwise wouldn't it?).

You have 32.746 uS to draw a line. The line consists of 20 characters (160 pixels). To spit them out using SPI means sending 20 bytes. The highest clock rate you can output with SPI is the processor clock rate / 2, which is once every 125 nS. So you must take:

Code: [Select]
125 * 20 * 8 = 20000 nS (20 uS).

So that leaves 12 uS over. It will take about 3 to enter and leave the ISR, which is required for synchronization. A couple more to load up the register with the correct line information. So really, not much spare time.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Graynomad

May have been, I guess you know about it then :)

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Nick Gammon

From another thread:


Your video project looks really nice. Do you have an easy suggestion if I just want larger letters or numbers on the screen?

Or maybe just a single large number?


For something large I would probably go with the "colour" version which basically draws a large bitmap: 60 x 30 pixels.

Then store the patterns for whatever you want to display in progmem. The original text version has the bitmaps for every letter in it (one pit per pixel). You could expand that out to one byte per pixel. Then a letter or number would be 8 x 8 pixels, so you could fit 7 across and about 3 down. That also lets you choose the colour of each letter.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

JO3RI

Quote
You can also see commands from the second Uno to clear the screen, clear a line, do cursor addressing, and scrolling:


Can you do text scrolling? just sending a text to the output atmega and make it scroll over the screen (left to right)? Could you show a video on that, like in the video I showed you before?

Thanks.
http://www.JO3RI.be

Nederlandstalige sectie - http://arduino.cc/forum/index.php/board,77.0.html -

Nick Gammon

I haven't built in a "scroll sideways" interface option at this stage. Personally I wouldn't because of the artifacts you get when updating the screen buffer, so "smoothly scrolling sideways text" probably isn't going to work ... unless you can solve the timing issues.

You could do it to an extent by doing something like:


  • Carriage-return (take you to column 1)

  • Clear to end of line

  • Cursor position to desired column

  • Draw desired text



I suggest you make one up and try it out - the parts are only about $5 (depending on how much the connector costs).

I think it would be perfect for something like a home security system / weather monitoring where you want to have more text than you can fit onto an LCD, and don't mind a bit of flicker when it updates.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

JO3RI

Quote
You could do it to an extent by doing something like:

    Carriage-return (take you to column 1)
    Clear to end of line
    Cursor position to desired column
    Draw desired text


wouldn't that make it scroll per character? I was thinking a per pixel scroll.
But I guess you're system is only able to position in rows and columns and not in pixels. So actually yours is a great system for text output aka LCD character displays

Quote
I suggest you make one up and try it out - the parts are only about $5


you are right, I should. Let's see if I can clip some things.
http://www.JO3RI.be

Nederlandstalige sectie - http://arduino.cc/forum/index.php/board,77.0.html -

Nick Gammon


wouldn't that make it scroll per character? I was thinking a per pixel scroll.


That's right. But to store individual pixels is going to take a lot of memory. As it is, each character is looked-up from a font array on the fly.

This isn't supposed to be as fancy as a sideways scrolling shooter. Not with a $5 chip.

The intention was to show that you could display 20 x 30 characters just using a Uno, and not having to spend $30 on a graphics board. And quite possibly more if you wanted bitmapped graphics.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

JO3RI

Quote
The intention was to show that you could display 20 x 30 characters just using a Uno, and not having to spend $30 on a graphics board. And quite possibly more if you wanted bitmapped graphics.


yeah, sorry about that. But the thing is, I have been with the video-out library in my head the whole time. So I was thinking, you where making the same thing but for vga-out (and maybe it wouldn't be a bad idea, to do so)

any way good work. You could say, if you want something like the video-out library, why don't you try to make it yourself? Well it's over my head.  :smiley-roll: I know how to use it, but not how to make it.
http://www.JO3RI.be

Nederlandstalige sectie - http://arduino.cc/forum/index.php/board,77.0.html -

Nick Gammon

Try this:

http://electronics.stackexchange.com/questions/4172/how-can-i-control-a-cga-screen-with-an-arduino

and:

http://zone.ni.com/devzone/cda/tut/p/id/3020


It looks like the vertical is the same (60 Hz) so Timer 1 would be unchanged.
It also looks like the vertical sync pulses are the same (it still uses 525 lines).

So I would be inclined to try and see.

If what I read is correct the colour information is sent over 4 consecutive lines, I'm not too sure about that and don't have a CGA monitor to test it on. So you might get something to appear if you tweak delays a bit (eg. the delay at the start of each line).
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Go Up