Using AVR ATTiny2313 to generate VGA (video) signals – Part 2

For a quick reference:

  1. Part 1 – The basics
  2. Part 2 – The circuit and the algorithm

In this next part we will cover the algorithm design and the circuit. I’ll also provide my code.

Understanding the VGA timings in a little more detail…

We need to generate VSync and HSync pulses that meet the exact timing requirements as well as they overlap very well.

To understand what I meant, the sequence of steps given below might help you:

  1. First a VSync signal is generated. It starts by pulling the VSync pulse low.
  2. At this very moment, we also pull the HSync pulse low.
  3. One HSync pulse (-ve Pulse and +ve Pulse) width is 31.7 microseconds.
  4. One VSync low pulse width is 0.063 milliseconds. If you noticed, this is equal to two HSync pulses.
  5. Therefore, when VSync goes low, keep it low, until two HSync pulses have elapsed.
  6. Next, VSync remains high (the backporch) for sometime. In this time, we continue to generate HSync pulses. Again, if you look at the timings, we need to generate 33 HSync pulses which equal to the backporch time of VSync.
  7. After that, forget VSync and focus on HSync. Now, for next 480 HSync pulses, we can show video data, but remember, after the HSync pulse goes high, wait for 1.9 microseconds before sending video data. Also, video data must be less than 25 microseconds to ensure we do not send video data during HSync frontporch (of 0.63 microseconds).

The picture below outlines the relationship between VSync, HSync and video signal:

VGA timing relationships

VGA timing relationships

Developing the VGA signal generator algorithm

Now here is the big clue….

If we can create HSync waveform and use the low going pulse of HSync to control VSync and Video, we are done….this is where the 16-bit timer of Attiny2313 will help us.

We will use CTC mode. I would request that you read the datasheet and clearly understand CTC mode. I’ll reproduce only the necessary items here.

The diagram below outlines the process. We use CTC mode.

AVR Timer diagram CTC mode

AVR Timer diagram CTC mode

The counter starts to count from 0 to TOP. When the counter is counting, the output remains low. Once the counter value matches a certain value (let’s call it Va) the output goes high. Again, once the counter has reached the top value (let’s call it Vb) the output again goes low. This pulse will be used to generate an HSync.

The total pulse width should be 31.7 microseconds. The low pulse should be 3.77 microseconds. Therefore, let’s put the calculations with 20MHz crystal.

  1. Clock cycles to have an elapsed time of 31.7 microseconds (Total time period) = 634
  2. Clock cycles to have an elapsed time of 3.77 microseconds (low pulse width)= 75

Thus, Va = 75 pulses and Vb = 634 pulses.

Therefore, the 16-bit timer will be setup like this:

  1. Start with low pulse.
  2. Once clock cycle count reaches 75, toggle the output (high)
  3. Once clock cycle count reaches 634 (TOP), toggle output again (low)
  4. When output reaches TOP, fire an interrupt (Overflow interrupt)

This scheme will help us generate an accurate HSync pulse. We will manually generate the VSync through code that we will write in the overflow interrupt.

We will designate a pin as output and use that to control VSync pulses.The overflow interrupt logic is given below:

  1. If lineCount < 2, keep VSync pin low, increment lineCount and exit.
  2. If lineCount >= 2, make VSync pin high, increment lineCount
  3. If lineCount <35, exit
  4. If lineCount >515, exit
  5. If lineCount >=525, reset lineCount=0 and go back to step 1
  6. /*Control comes here when lineCount variable is between 36 and 515 (inclusive)*/ Wait for 5.66 microseconds (Hsync low pulse + backporch).
  7. Push ARGB signals to output port (we need 8 bits AARRGGBB) for exactly 25 microseconds.
  8. Push 0x00 to ARGB port (blank out) and exit

One important note on this tutorial. You can output a pixel of 8-bits in the format AARRGGBB, however, to keep things simple, I only set a pin status from low to high. When you connect this pin to any of the RGB inputs of the VGA connector (DB-15) on the monitor, you will see either a full red, a full green or a full blue screen. This should tell you that code and circuit are working…after that its upto you to play around.

This was done so that the focus is on generating the sync signals correctly. What you want to do with the video is upto you (This is something that I found wanting in other tutorials…they were focusing too much on what image or pattern to generate, that made the code very hard to understand)

The circuit

We will use minimal components…The diagram shown below only outlines the minimal required components:

  1. Attiny2313
  2. 20MHz crystal
  3. A few resistors for an R-2R ladder network. While I’ll not cover it here, you should check out http://lucidscience.com/pro-vga%20video%20generator-2.aspx to understand the VGA connection to monitor and the R-2R ladder network based DAC that is needed to connect the microcontroller ARGB output to the monitor.

In the code, just for debugging purpose (using my oscilloscope), I also output a digital high when the first video line is about to be drawn (the 35th line).

As stated before, instead of outputting AARRGGBB bits, I simply set a single bit. I call this “VIDEO_GATE” pin. This keeps the video picture frame generator logic outside of the code that generates the sync pulses. The focus of this tutorial is to show how to generate sync pulses and make your monitor lock on the signal and not on picture frame itself.

The simplified circuit is shown below (only relevant pins shown):

Basic VGA Circuit using AVR ATTiny2313

Basic VGA Circuit using AVR ATTiny2313

Again, please see http://lucidscience.com/pro-vga%20video%20generator-2.aspx to know how to build the R-2R ladder network and also the DB-15 (VGA connector) connection details.

The code

Here is the full assembly language code for the ATTINY2313. If you cannot get a signal, then check the timings and your connections. I have made this work on a monitor and a projector with many different patterns and images and it works.

————————————————————————————————————————————————

/***************************************************************************
* Description : VGA Signal Pattern generator.
This generates the HSync and VSync signals.
Additionally, it generates a pulse that acts as
gate for another chip to send video data on the
RGB line
The VGA signal adheres to 640×480 @ 60Hz
* HW-Environment : AVR MCU ATTiny2313-20MHz
*
* SW-Environment : Avrstudio 4.12 (b498), Avrasm2
* Author : Vishnu Sharma http://www.vishnusharma.com
*
****************************************************************************/

.include “tn2313def.inc”

#define acc8Bit r16
#define maxLinesL r17
#define maxLinesH r18
#define imgData r19

#define acc16Bit r24
#define acc16BitL acc16Bit
#define acc16BitH r25

#define lineCounter r28
#define lineCounterL lineCounter
#define lineCounterH r29

#define MAX_HORZ_LINES 0x020D //maximum lines are 525…
#define VSYNC_PIN 6
#define VIDEO_GATE_PIN 5
#define FIRST_VIDLINE_IND_PIN 4
rjmp RESET ; Reset Handler

.org OVF1addr
rjmp TIM1_OVF ; Timer1 Overflow Handler

.org WDTaddr
rjmp RESET

RESET:
ldi acc8Bit, low(RAMEND) ; Main program start
out SPL, acc8Bit ; Set Stack Pointer to top of RAM

/* Power consumption minimize */

/* Analog comparator off */
sbi ACSR,ACD
/*Watchdog timer off*/
ldi acc8Bit,(0<<WDE)
out WDTCSR,acc8Bit
/*sleep mode on*/
ldi acc8Bit,(1<<SM0)
out MCUCR,acc8Bit

/*Call setup*/
rcall SETUP
/*Global interrupts enabled*/
sei

rjmp PC ;//While (1)

SETUP:
//Mark DDRD as output…will use this for VSYNC And Video Gate
ldi acc8Bit,0xFF
out DDRD,acc8Bit
//Pull all output low on PORTD
ldi acc8Bit, 0x00
out PORTD,acc8Bit
//Our timer will use OC1B for HSync mark it as output
ldi acc8Bit,0xFF
out DDRB, acc8Bit
//Pull all output low on PORTB
ldi acc8Bit,0x00
out PORTB, acc8Bit

//Set the timer
ldi acc8Bit, ((1<<WGM13)|(1<<WGM12)|(1<<CS10)) // clk/1
out TCCR1B, acc8Bit
ldi acc8Bit,((1<<COM1B1)|(1<<COM1B0)|(1<<WGM11)) ;//mode 14, set on compare match clear at TOP
out TCCR1A,acc8Bit

//Set the top and compare counts
/* 20MHz / 31469Hz = 635.54 = 0x027B …the counter top value*/
ldi acc16BitL,0x7B
ldi acc16BitH,0x02
out ICR1H , acc16BitH
out ICR1L , acc16BitL

/* Hsync pulse = 3.77us, at 50ns/instr cycle = 75.4 = 0x004B …the compare value*/
ldi acc16BitL, 0x4B
ldi acc16BitH, 0x00
out OCR1BH , acc16BitH
out OCR1BL , acc16BitL

//Enable overflow interrupt
ldi acc8Bit , (1 << TOIE1)
out TIMSK , acc8Bit

//Initialize line counter
ldi lineCounterL , 0x00
ldi lineCounterH , 0x00
ldi maxLinesL , 0x0D
ldi maxLinesH , 0x02

ret
TIM1_OVF:
/*Line counter must be reset to zero if it has reached 525*/
mov acc16BitL , lineCounterL
mov acc16BitH , lineCounterH

sub acc16BitL , maxLinesL
sbc acc16BitH , maxLinesH
brne PC+3
ldi lineCounterL , 0x00
ldi lineCounterH , 0x00

adiw lineCounter, 0x0001

/*If less than 2 horizontal lines, keep VSync low*/
mov acc16BitL , lineCounterL
mov acc16BitH , lineCounterH
sbiw acc16Bit , 0x0003
brsh PC+3
//If control comes here, then number of lines is less than 2..ensure VSync is low
cbi PORTD , VSYNC_PIN
RJMP EXIT_ROUTINE

//If control comes here, then number of lines is more than 2…ensure VSync is high
sbi PORTD , VSYNC_PIN

//If number of lines painted is less than 35, do nothing
LDI acc8Bit , HIGH(35)
CPI lineCounterL , LOW(35)
CPC lineCounterH , acc8Bit
BRLO EXIT_ROUTINE
/*check if we need to inform that this is the first visible video line…35th line*/
BRNE PC + 2
SBI PORTD , FIRST_VIDLINE_IND_PIN

//If line number is more than 515, do nothing
LDI acc8Bit , HIGH(515)
CPI lineCounterL , LOW(515)
CPC lineCounterH , acc8Bit
BRSH EXIT_ROUTINE

/*By the time the control comes here, 27 cycels = 1.35 micros have elapsed…*/
/*Once HSync goes down, we must wait 5.66 micros before we can send the video signal*/
/*Wait for remaining 4.31 micros = 86 cycles*/
ldi acc8Bit , 0x1D
BC_WAIT:
dec acc8Bit
brne BC_WAIT

/*Open the Video signal gate and wait for 25 micros = 500 cycles*/
SBI PORTD , VIDEO_GATE_PIN

ldi acc8Bit, 0xA6
VIDEO_OUT:
dec acc8Bit
brne VIDEO_OUT
/*Close the video signal…ensure VSync is up*/
CBI PORTD , VIDEO_GATE_PIN
CBI PORTD , FIRST_VIDLINE_IND_PIN
EXIT_ROUTINE:

RETI

————————————————————————————————————————————————-

The picture below gives you an indication about the image quality I was able to get. On the left, is the famous penguins picture that comes with Windows OS. On the right is the image I got on my monitor that was being fed from ATTiny2313.

Image reconstruction using ATTiny 2313 over VGA

Image reconstruction using ATTiny 2313 over VGA

—————————————————————————————————————————————————————–

The code is distributed under “Creative Commons Attribution-NonCommercial 3.0” (See http://creativecommons.org/licenses/by-nc/3.0/deed.en_US) license. This is only for education and hobby. Do not use for any other purpose…or contact me for other usages.

Advertisements

10 thoughts on “Using AVR ATTiny2313 to generate VGA (video) signals – Part 2

  1. Pingback: Using ATTiny2313 to generate VGA (video) signals – Part 2 | Vga Port

  2. Thanks For that Awesome and simple Explanation 🙂
    But I ask ,
    How did you create an Overflow Interrupt without writing Interrupt Service Routine

    and RETI ?

    how did you use it without Including ISR in your Code ?!

  3. First I want to thank you for this code and simple explanation of the concept.
    Secondly, after I understood the concept I’ve written another version in the C language by program avrstudio with gcc compiler and it worked but no successfully.
    There are some blanked horizontal lines.
    I think that the error may be in the delay ,I hope to find a solution and I apologize for my along talking.
    —————————————–
    my code:
    /*
    * our_vga_c.c
    *
    * Created: 29/07/2013 01:09:22 م
    * Author: kimo000
    */

    #include
    #include
    #include

    volatile uint16_t line_count=0;
    int main(void)
    { /* Power consumption minimize */
    /* Analog comparator off */
    ACSR=(1<<ACD);
    /*Watchdog timer off*/
    WDTCSR=(0<<WDE);
    /*sleep mode on*/
    MCUCR=(1<<SM0);
    sei();
    DDRD=0xff; //Mark DDRD as output…will use this for VSYNC And Video Gate
    PORTD=0x00; //Pull all output low on PORTD
    DDRB=0xff; //Our timer will use OC1B for HSync mark it as output
    PORTB=0x00; //Pull all output low on PORTB
    TCCR1B=((1<<WGM13)|(1<<WGM12)|(1<<CS10));
    TCCR1A=((1<<COM1B1)|(1<<COM1B0)|(1<<WGM11)) ;//mode 14, set on compare match clear at TOP
    //Set the top and compare counts
    /* 20MHz / 31469Hz = 635.54 = 0x027B …the counter top value*/
    ICR1=0x027B;
    /* Hsync pulse = 3.77us, at 50ns/instr cycle = 75.4 = 0x004B …the compare value*/
    OCR1B=0X004B;
    //Enable overflow interrupt
    TIMSK=(1 << TOIE1);
    while(1);
    }

    ISR(TIMER1_OVF_vect)
    {
    line_count++;
    if (line_count35 && line_count<=515)
    {
    _delay_us(4);
    PORTD|=0b00100000;
    _delay_us(25);
    PORTD&=0b11011111;
    }
    if (line_count==525)
    {
    line_count=0;
    PORTD&=0b10111111;
    }
    }

  4. can you provide us the Edited Code for displaying such Images
    and the Concept of displaying such image on vga monitor
    Thanks In advance

    • Sorry, I’m not sure if I understand your question. The full code to generate the VGA signal is already given in the post. If you want to transfer the image instead of generating a random color value, do the following:
      1. Choose a bitmap and extract RGB values for each pixel. (You may want to scale the image to your needed size before you extract pixels , e.g. 320 x 240). You can use any programming language to do this (C, C#, java)
      2. After step 1, you now have all the RGB values to be displayed for each pixel and each line.
      3. Just put these values in the flash memory of the microcontroller (or an external memory), read from that memory and you are done.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s