Drawing geometric figures on a PAL TV using ATmega32 (128x64 resolution)


Photos of my TV screen:





Introduction:
    I am interested to draw lines, square, rectangle, circle etc on my TV screen. At first I was confused where to start. While thinking about it, a pencil and an eraser came to my mind. If we have a good pencil, eraser and a paper, then we can draw on it according to our own logic. If we use the pencil with compass and scale, we can draw circle, line, box etc on the paper. This is the basic idea I implemented in this small hobby project.
    
I divided my tv screen into 128 x 64 pixels. I am using atmega32 microcontroller. It got 2kb RAM. So, if each pixel takes 1 bit, then 128 x 64 pixels takes (128*64/8) = 1024bits of RAM(screen buffer). Still I can increase the resolution but any way at present I am satisfied with this because it is my first attempt and I can improve it later. Here a horizontal line in TV(two lines) takes 16byte, ie horizontal resolution now is 16*8 = 128 pixel. Similar 64 bytes are stacked up and thus the vertical resolution now is 64 lines. I am using fake interlacing so total vertical lines are 312 and here again I combine 2 lines to a single line which takes 1 16byte row of the screen buffer.
          I told about a pencil at the starting of the post, that is nothing but a function which could set a pixel. The eraser is a function which clear a pixel. Now the paper is the a digital display of 128x64 pixels, the smallest dot is 1 pixel.
 
PENCIL =  setpixel(x,y);
ERASER = clrpixel(x,y);
PAPER = 128x64 screen
building block = one pixel

Now my first aim is to create the pencil and eraser so that I can draw any thing on my TV screen using simple mathematics....

I am setting a character array disp_buffer[64][16] ie 1024 bits (128x64) which stores each pixel information and we will be playing on this buffer to draw pictures.
A brief explanation about the working of TV is provided at my previous post.. Ok, now before going to the pencil, we need to set the paper. Here we need to set the display buffer to show it on TV. May be this will be one of the most important part of the code. A raster scanning of a TV takes 64uS. So we need to generate timer interrupts on each 64us and we need to use more precise and peak value clock, the external crystal of 16MHz. But unfortunately, I don't have a 16MHz but have a 16.450MHz. So I stick with it and all of my code will be related to that. I will update it soon when I get a 16MHz.  We can easily calculate the timer compare value to be loaded into OCCR1A using below equation.
    OCR1A = (F_CPU/1000000)*64; (see timer1_init function)
So now we can directly enter to the timer interrupt routine and look at the code and see how it works...
ISR (TIMER1_COMPA_vect)
{
    ZERO;
    char b;
    unsigned char index;
    if(line == 312) {
        _delay_us(28);BLACK;_delay_us(4);ZERO;_delay_us(28);BLACK;_delay_us(4);
        TCNT1=0;
    }
    
    _delay_us(4);BLACK;_delay_us(10);
    
    if(line >= 100 && line <228) {
        index = pgm_read_byte(&(line_lookup[line-100]));
        DDRB |= 1<< PB5;
        for(b=15;b>=0;b--) {
            SPDR = disp_buffer[index][b];
            _delay_us(1.85);
            
        }
        SPDR=0;
        DDRB &= ~(1<<PB5);
    }
    if(line==312) {
        line=0;
        TIFR |= 1<<OCF1A;
    }
    line++;
}
Yes, the above code is the interrupt service routine where we enters on every 64uS. The first line makes the O/P zero which is a part of V or H synchronization. Then it checks if it reached the bottom line. If so we need to provide a vertical synchronization as mentioned in my previous post. So, the line 7 will do it. Then we need to reset the timer otherwise our future calculations may not work as expected. Then as usual, we need to give H sync (Horizontal synchronization).
Later we start to display the contents on the buffer. Now we are not interested in all of the lines because we need some time of processor for some other activities like picture drawing etc because some drawing need some more processor time for quick result. Also we have only 64 pixel in vertical. So,
if(line >= 100 && line <228) 
will select lines in between 100 and 228. ie 228-100=128 = 64*2
So one pixel line is shared by two lines on the tv.
 Now the code
index = pgm_read_byte(&(line_lookup[line-100])); 
will find the index of the y axis of the two dimensional display buffer. It is implemented as look up table to reduce the time wastage for calculations. Now the next part of the code is to push each byte(15 to 0) bit by bit to the TV as 'white voltage'. The better method for this is to use the inbuilt SPI module. The reason is it can push one by one bits in background from the time we put the 8 bit value to it, so that we can do some other work while it is automatically pushing x axis bits from the display buffer to tv.... So when 16*8 bits are pushed, we displayed 128 pixels. Then we return from the interrupt. Similarly we displays all the 64 lines and then it continues like that and thus we MAPPED THE DISPLAY BUFFER TO OUR TV.... 60% WORK IS FINISHED... ;-)

Now the next step is to make our PENCIL...
Fortunately the pencil is a very simple code:
void setpix(unsigned char x, unsigned char y)
{
    disp_buffer[63-y][pgm_read_byte(&h_pixel[2*x])] |= pgm_read_byte(&h_pixel[2*x + 1]);
}


So we made the pencil now!!!

to make the eraser , we just need to use &= ~( in the above code instead of |=
......

So the building tools are ready.
Now the remaining is pure mathematics.

See how a line is drawn on the screen:
drawline(unsigned char x1, unsigned char y1,unsigned char x2,unsigned char y2)
{
    unsigned char l,b,q;
    
    if(x1>127)x1=127;
    if(y1>63)y1=63;
    if(x2>127)x2=127;
    if(y2>63)y2=63;
    if(x1>x2){q=x1;x1=x2;x2=q;q=y1;y1=y2;y2=q;}
    
    if(y2>y1) {
        l=x2-x1;
        b=y2-y1;
        if(l>=b)
        for(q=0;q<=l;q++)
        setpix(x1 + q, (y1 + ((q*b)/l)));
        else
        for(q=0;q<=b;q++)
        setpix((x1 + ((q*l)/b)), y1 + q);
    }
    else{
        l=x2-x1;
        b=y1-y2;
        if(l>=b)
        for(q=0;q<=l;q++)
        setpix(x1 + q, (y1 - ((q*b)/l)));
        else
        for(q=0;q<=b;q++)
        setpix((x1 + ((q*l)/b)), y1 - q);
    }
}

The above code is the line drawing function. It draws line if we provide two points.

to draw a line from 0,0 to 120,60
drawline(0,0,120,60);
How simple it is!
Similarly there are many function here,
like draw circle, draw box, clear box, fill box, fill circle etc etc.. Also we can easily make new functions according to our need...

Another interesting thing is , we can do some sort of animations here, by some tricky loop codings etc..

Any way, here is the complete source code of this stuff. It draws a cube and a nearby circle as in one of the pic...
R1=6.2K, R2=2.2K, R3=470 ohms. 
10k is connected to reset(forgot to mark it). 
NOTE: Actually there should be a 75 ohms internal impedance for the composite video input of TV, that is what I learned from many reference sites., but any way I don't know why it is not there in my converter. So I am using above circuit, but some time it may not work in ur case, then assume 75 ohms internal impedence is there and design the R1,R2 accordingly (or check my reference in my previous post, there u can see Rickard's pong game circuit).
Condition is: 
PC0=1, MOSI=0   => .3v o/p
    PC0=1,MOSI=1  =>.8 ~1v o/p
FINAL SOURCE CODE:
/*
Drawing geometric figures on PAL TV using Atmega32 
By Vinod S  

Date: 10/04/2012

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include<avr/io.h>
#define F_CPU 16450000
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <string.h>
#define ZERO PORTC=0
#define BLACK PORTC=1

extern const unsigned char line_lookup[] PROGMEM;
extern const unsigned char h_pixel[] PROGMEM;

volatile unsigned int line;
volatile unsigned char disp_buffer[64][16];
volatile unsigned char and1,and2,and3,and4;
volatile unsigned char ret[3];
unsigned char ball1 = 1;
unsigned char ball2 = 8;
unsigned char box1[4];

void spi_init();
void timer1_init();
void setpix(unsigned char, unsigned char);
void clrpix(unsigned char, unsigned char);
void drawbox(unsigned char, unsigned char,unsigned char,unsigned char);
void clrbox(unsigned char, unsigned char,unsigned char,unsigned char);
void fillbox(unsigned char, unsigned char,unsigned char,unsigned char);
void emptybox(unsigned char, unsigned char,unsigned char,unsigned char);
void drawline(unsigned char, unsigned char,unsigned char,unsigned char);
void clrline(unsigned char, unsigned char,unsigned char,unsigned char);
void drawcircle(unsigned char, unsigned char,unsigned char);
void clrcircle(unsigned char, unsigned char,unsigned char);
void drawchessboard(unsigned char, unsigned char,unsigned char,unsigned char);
void clrchessboard(unsigned char, unsigned char,unsigned char,unsigned char);


ISR (TIMER1_COMPA_vect)
{
    ZERO;
    char b;
    unsigned char index;
    if(line == 313) {
        _delay_us(28);BLACK;_delay_us(4);ZERO;_delay_us(28);BLACK;_delay_us(4);
        TCNT1=0;
    }
    
    _delay_us(4);BLACK;_delay_us(10);
    
    if(line >= 100 && line <228) {
        index = pgm_read_byte(&(line_lookup[line-100])); 
        DDRB |= 1<< PB5;
        for(b=15;b>=0;b--) {
            SPDR = disp_buffer[index][b];
            _delay_us(1.85);
            
        }
        SPDR=0;
        DDRB &= ~(1<<PB5);
    }
    if(line==313) {
        line=0;
        TIFR |= 1<<OCF1A;
    }
    line++;
}



void main()
{
    DDRC = 1;
    spi_init();
    timer1_init();
    
    while(1) {
        
        //cube///
        drawbox(10,10,30,30);
        drawbox(20,20,30,30);
        drawline(40,40,50,50);
        drawline(40,10,50,20);
        drawline(10,10,20,20);
        drawline(10,40,20,50);
        //circle//
        drawcircle(74,30,20);
        //filled box//
        fillbox(40,0,10,5);
        while(1);
        
    }
}

/*---------------------------FUNCTIONS----------------------------------*/

void setpix(unsigned char x, unsigned char y)
{
    disp_buffer[63-y][pgm_read_byte(&h_pixel[2*x])] |= pgm_read_byte(&h_pixel[2*x + 1]);
}


void clrpix(unsigned char x, unsigned char y)
{
    disp_buffer[63-y][pgm_read_byte(&h_pixel[2*x])] &= ~pgm_read_byte(&h_pixel[2*x + 1]);
}

void drawbox(unsigned char x1, unsigned char y1,unsigned char l,unsigned char b)
{
    if(x1>127)x1=127;
    if(y1>63)y1=63;
    unsigned char q,temp1,temp2;
    temp1=x1+l;
    if(temp1>127)temp1=127;
    temp2=y1+b;
    if(temp2>63)temp2=63;
    for(q=x1;q<=temp1;q++) {
        setpix(q,y1);
        setpix(q,temp2);
    }
    for(q=y1;q<=temp2;q++) {
        setpix(x1,q);
        setpix(temp1,q);
    }
}

void clrbox(unsigned char x1, unsigned char y1,unsigned char l,unsigned char b)
{
    if(x1>127)x1=127;
    if(y1>63)y1=63;
    unsigned char q,temp1,temp2;
    if(temp1>127)temp1=127;
    temp2=y1+b;
    if(temp2>63)temp2=63;
    for(q=x1;q<=temp1;q++) {
        clrpix(q,y1);
        clrpix(q,temp2);
    }
    for(q=y1;q<=temp2;q++) {
        clrpix(x1,q);
        clrpix(temp1,q);
    }
}

void fillbox(unsigned char x1, unsigned char y1,unsigned char l,unsigned char b)
{
    if(x1>127)x1=127;
    if(y1>63)y1=63;
    unsigned char q,temp1,temp2,r;
    temp1=x1+l;
    if(temp1>127)temp1=127;
    temp2=y1+b;
    if(temp2>63)temp2=63;
    for(q=x1;q<=temp1;q++) {
        for(r=y1;r<temp2;r++)
        setpix(q,r);
    }
    for(q=y1;q<=temp2;q++) {
        for(r=y1;r<temp1;r++)
        setpix(r,q);
    }
}

void emptybox(unsigned char x1, unsigned char y1,unsigned char l,unsigned char b)
{
    if(x1>127)x1=127;
    if(y1>63)y1=63;
    unsigned char q,temp1,temp2,r;
    temp1=x1+l;
    if(temp1>127)temp1=127;
    temp2=y1+b;
    if(temp2>63)temp2=63;
    for(q=x1;q<=temp1;q++) {
        for(r=y1;r<temp2;r++)
        clrpix(q,r);
    }
    for(q=y1;q<=temp2;q++) {
        for(r=y1;r<temp1;r++)
        clrpix(r,q);
    }
}

void drawline(unsigned char x1, unsigned char y1,unsigned char x2,unsigned char y2)
{
    unsigned char l,b,q;
    
    if(x1>127)x1=127;
    if(y1>63)y1=63;
    if(x2>127)x2=127;
    if(y2>63)y2=63;
    if(x1>x2){q=x1;x1=x2;x2=q;q=y1;y1=y2;y2=q;}
    
    if(y2>y1) {
        l=x2-x1;
        b=y2-y1;
        if(l>=b)
        for(q=0;q<=l;q++)
        setpix(x1 + q, (y1 + ((q*b)/l)));
        else
        for(q=0;q<=b;q++)
        setpix((x1 + ((q*l)/b)), y1 + q);
    }
    else{
        l=x2-x1;
        b=y1-y2;
        if(l>=b)
        for(q=0;q<=l;q++)
        setpix(x1 + q, (y1 - ((q*b)/l)));
        else
        for(q=0;q<=b;q++)
        setpix((x1 + ((q*l)/b)), y1 - q);
    }
}
void clrline(unsigned char x1, unsigned char y1,unsigned char x2,unsigned char y2)
{
    unsigned char l,b,q;
    
    if(x1>127)x1=127;
    if(y1>63)y1=63;
    if(x2>127)x2=127;
    if(y2>63)y2=63;
    if(x1>x2){q=x1;x1=x2;x2=q;q=y1;y1=y2;y2=q;}
    
    if(y2>y1) {
        l=x2-x1;
        b=y2-y1;
        if(l>=b)
        for(q=0;q<=l;q++)
        clrpix(x1 + q, (y1 + ((q*b)/l)));
        else
        for(q=0;q<=b;q++)
        clrpix((x1 + ((q*l)/b)), y1 + q);
    }
    else{
        l=x2-x1;
        b=y1-y2;
        if(l>=b)
        for(q=0;q<=l;q++)
        clrpix(x1 + q, (y1 - ((q*b)/l)));
        else
        for(q=0;q<=b;q++)
        clrpix((x1 + ((q*l)/b)), y1 - q);
    }
}

void drawcircle(unsigned char x,unsigned char y,unsigned char r)
{
    unsigned char p,q;
    unsigned int sqlow, sqhigh;
    unsigned int value;
    sqlow = ((unsigned int)(r-1) * r-1);
    sqhigh = ((unsigned int)(r+1) * r+1);
    for(p=x-r;p<=x+r;p++)
    for(q=y-r;q<=y+r;q++) {
        value = (unsigned int)(p-x)*(p-x) + (unsigned int)(q-y)*(q-y);
        if(value >sqlow && value < sqhigh) {
            setpix(p,q);
        }
    }
}


void clrcircle(unsigned char x,unsigned char y,unsigned char r)
{
    unsigned char p,q;
    unsigned int sqlow, sqhigh;
    unsigned int value;
    sqlow = ((unsigned int)(r-1) * r-1);
    sqhigh = ((unsigned int)(r+1) * r+1);
    for(p=x-r;p<=x+r;p++)
    for(q=y-r;q<=y+r;q++) {
        value = (unsigned int)(p-x)*(p-x) + (unsigned int)(q-y)*(q-y);
        if(value >sqlow && value < sqhigh)
        clrpix(p,q);
    }
}

void drawchessboard(unsigned char x1, unsigned char y1,unsigned char l,unsigned char b)
{
    if(x1>127)x1=127;
    if(y1>63)y1=63;
    unsigned char q,temp1,temp2,r;
    temp1=x1+l;
    if(temp1>127)temp1=127;
    temp2=y1+b;
    if(temp2>63)temp2=63;
    for(q=x1;q<=temp1;q++) {
        for(r=y1;r<temp2;r++)
        if(q&1) {
            if(r&1)setpix(q,r);
            else(clrpix(q,r));
        } else
        if(r&1)clrpix(q,r);
        else(setpix(q,r));
    }
}
void clrchessboard(unsigned char x1, unsigned char y1,unsigned char l,unsigned char b)
{
    if(x1>127)x1=127;
    if(y1>63)y1=63;
    unsigned char q,temp1,temp2,r;
    temp1=x1+l;
    if(temp1>127)temp1=127;
    temp2=y1+b;
    if(temp2>63)temp2=63;
    for(q=x1;q<=temp1;q++) {
        for(r=y1;r<temp2;r++)
        clrpix(q,r);
    }
}

void spi_init()
{
    DDRB |= (1<<5)|(1<<7)|(1<<4);
    SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPHA);
    
}

void timer1_init()
{
    TCCR1B |= (1 << WGM12)|(1 << CS10);
    TCNT1 = 0;
    OCR1A = (F_CPU/1000000)*64;
    TIMSK |= (1 << OCIE1A);
    sei();
}


const unsigned char line_lookup[] PROGMEM = {
    0,0,
    1,1,
    2,2,
    3,3,
    4,4,
    5,5,
    6,6,
    7,7,
    8,8,
    9,9,
    10,10,
    11,11,
    12,12,
    13,13,
    14,14,
    15,15,
    16,16,
    17,17,
    18,18,
    19,19,
    20,20,
    21,21,
    22,22,
    23,23,
    24,24,
    25,25,
    26,26,
    27,27,
    28,28,
    29,29,
    30,30,
    31,31,
    32,32,
    33,33,
    34,34,
    35,35,
    36,36,
    37,37,
    38,38,
    39,39,
    40,40,
    41,41,
    42,42,
    43,43,
    44,44,
    45,45,
    46,46,
    47,47,
    48,48,
    49,49,
    50,50,
    51,51,
    52,52,
    53,53,
    54,54,
    55,55,
    56,56,
    57,57,
    58,58,
    59,59,
    60,60,
    61,61,
    62,62,
    63,63
};



PROGMEM const unsigned char h_pixel[] = {
    
    15,1<<7,
    15,1<<6,
    15,1<<5,
    15,1<<4,
    15,1<<3,
    15,1<<2,
    15,1<<1,
    15,1<<0,
    
    14,1<<7,
    14,1<<6,
    14,1<<5,
    14,1<<4,
    14,1<<3,
    14,1<<2,
    14,1<<1,
    14,1<<0,
    
    13,1<<7,
    13,1<<6,
    13,1<<5,
    13,1<<4,
    13,1<<3,
    13,1<<2,
    13,1<<1,
    13,1<<0,
    
    12,1<<7,
    12,1<<6,
    12,1<<5,
    12,1<<4,
    12,1<<3,
    12,1<<2,
    12,1<<1,
    12,1<<0,
    
    11,1<<7,
    11,1<<6,
    11,1<<5,
    11,1<<4,
    11,1<<3,
    11,1<<2,
    11,1<<1,
    11,1<<0,
    
    10,1<<7,
    10,1<<6,
    10,1<<5,
    10,1<<4,
    10,1<<3,
    10,1<<2,
    10,1<<1,
    10,1<<0,
    
    9,1<<7,
    9,1<<6,
    9,1<<5,
    9,1<<4,
    9,1<<3,
    9,1<<2,
    9,1<<1,
    9,1<<0,
    
    8,1<<7,
    8,1<<6,
    8,1<<5,
    8,1<<4,
    8,1<<3,
    8,1<<2,
    8,1<<1,
    8,1<<0,
    
    7,1<<7,
    7,1<<6,
    7,1<<5,
    7,1<<4,
    7,1<<3,
    7,1<<2,
    7,1<<1,
    7,1<<0,
    
    
    
    6,1<<7,
    6,1<<6,
    6,1<<5,
    6,1<<4,
    6,1<<3,
    6,1<<2,
    6,1<<1,
    6,1<<0,
    
    5,1<<7,
    5,1<<6,
    5,1<<5,
    5,1<<4,
    5,1<<3,
    5,1<<2,
    5,1<<1,
    5,1<<0,
    
    4,1<<7,
    4,1<<6,
    4,1<<5,
    4,1<<4,
    4,1<<3,
    4,1<<2,
    4,1<<1,
    4,1<<0,
    
    3,1<<7,
    3,1<<6,
    3,1<<5,
    3,1<<4,
    3,1<<3,
    3,1<<2,
    3,1<<1,
    3,1<<0,
    
    2,1<<7,
    2,1<<6,
    2,1<<5,
    2,1<<4,
    2,1<<3,
    2,1<<2,
    2,1<<1,
    2,1<<0,
    
    1,1<<7,
    1,1<<6,
    1,1<<5,
    1,1<<4,
    1,1<<3,
    1,1<<2,
    1,1<<1,
    1,1<<0,
    
    0,1<<7,
    0,1<<6,
    0,1<<5,
    0,1<<4,
    0,1<<3,
    0,1<<2,
    0,1<<1,
    0,1<<0
};

88 comments :

  1. Not working with 16 Mhz crystal or any other higher crystal and MCU options.
    Is there anything else that should be changed apart from F_CPU if using 16 Mhzoc.

    ReplyDelete
    Replies
    1. Hi, Your site does not exist. So, no spam in this good man blog.

      Delete
  2. hey doesnt atmega work max at 16Mhz? u have used a 16.45 Mhz?/

    ReplyDelete
  3. Hi excellent article, you can also consider taking embedded training in Chennai to secure career in IT industry. As embedded systems are heart of every larger machine, it offers huge career prospects for talented professionals. embedded systems training in Chennai

    ReplyDelete
    Replies
    1. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging. If anyone wants to become a Java developer learn from Java Training in Chennai. or learn thru Java Online Training India . Nowadays Java has tons of job opportunities on various vertical industry.

      Delete
  4. This is excellent information. It is amazing and wonderful to visit your site.Thanks for sharing this information&its very useful to me...
    Android training in chennai
    Ios training in chennai

    ReplyDelete
  5. Well Written and Structured Blog. Totally a fan of your writing, I found this blog unique and i have been going through the posts in your blog. Independence Day Speech for Students

    ReplyDelete
  6. thumbsup for the author well explained. very usefull thanks for sharing.

    Data Science Training in Chennai

    ReplyDelete

  7. Thankyou for sharing this good information,nice blog.Python Training in Chennai

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. I would like to thank you for your nicely written post, its informative and your writing style encouraged me to read it till end. Thanks

    angularjs-Training in annanagar

    angularjs Training in chennai

    angularjs Training in chennai

    angularjs Training in bangalore

    ReplyDelete
  10. Awesome article. It is so detailed and well formatted that i enjoyed reading it as well as get some new information too.
    python training Course in chennai | python training in Bangalore | Python training institute in kalyan nagar

    ReplyDelete
  11. Really great post, Thank you for sharing This knowledge.Excellently written article, if only all bloggers offered the same level of content as you, the internet would be a much better place. Please keep it up!

    advanced excel training in bangalore

    ReplyDelete
  12. Thanks for the good words! Really appreciated. Great post. I’ve been commenting a lot on a few blogs recently, but I hadn’t thought about my approach until you brought it up. 

    Java training in Annanagar | Java training in Chennai

    Java training in Chennai | Java training in Electronic city

    ReplyDelete
  13. This comment has been removed by the author.

    ReplyDelete
  14. Your post is really awesome. Your blog is really helpful for me to develop my skills in a right way. Thanks for sharing this unique information with us.
    - Learn Digital Academy

    ReplyDelete
  15. Hello! This is my first visit to your blog! We are a team of volunteers and starting a new initiative in a community in the same niche. Your blog provided us useful information to work on. You have done an outstanding job.

    Best AWS Training in Chennai | Amazon Web Services Training in Chennai
    AWS Training in Bangalore | Amazon Web Services Training in Bangalore
    Amazon Web Services Training in OMR , Chennai | Best AWS Training in OMR,Chennai

    ReplyDelete
  16. This is really awesome, am so glad to read this informative article. I must confess this one of the exceptional blog that I have ever come across. Keep sharing this kind of information to users.

    I'm a freelancer by profession, I can help you write a blog post on any nich, incase you may need a professional writing service, here are some of my articles:
    Quick tips on Email marketing tips,
    Best free music download sites get high quality mp3 audios,
    Download AnonyTun Pro Apk best free vpn for Android,
    Best places to Watch Hindi movies online free,
    Best 17 GameCube ROMS for Dolphin Emulator,
    A professional tips on How to write a cover letter,
    Ultimate guide to Human resource management,
    Best websites to download free movie,
    Abokifx today's Black market Naira to Dollar exchange rate,
    Best working iptv m3u playlist url to watch live iPTV channels free on Kodi and Roku
    .
    Do you need my service?

    ReplyDelete
  17. It’s hard to come by experienced people about this subject, but you seem like you know what you’re talking about! Thanksmovie box

    ReplyDelete
  18. This comment has been removed by the author.

    ReplyDelete
  19. I simply wanted to write down a quick word to say thanks to you for those wonderful tips and hints you are showing on this site.
    iosh safety course in chennai

    ReplyDelete
  20. I still remember my childhood when i used to play videos games, now a days i play clash royale mod hack version apk with full joy.

    ReplyDelete
  21. Very amazing post. It would be really handy to use. Health Insurance Quotes

    ReplyDelete
  22. Nice Post. Thanks for sharing information about it. List of drug rehabs

    ReplyDelete
  23. Amazing blog. Thanks for posting about this article. Rehab programs near me

    ReplyDelete
  24. I really like this blog. Thanks for sharing. DHT conditioner

    ReplyDelete
  25. Great Information sharing. Thank you very much for this great post. ArticleBase

    ReplyDelete
  26. Amazing your content. Thank you for sharing. How to create www.gmail.com account? You want to create gmail account? Visit this website https://teachnow.xyz/gmail-login/

    ReplyDelete
  27. Amazing your article. Thank you for sharing. buy clones

    ReplyDelete
  28. All are saying the same thing repeatedly, but in your blog I had a chance to get some useful and unique information, I love your writing style very much, I would like to suggest your blog in my dude circle, so keep on updates.
    aws online training

    data science with python online training

    data science online training

    rpa online training

    ReplyDelete
  29. I Really Like your blog. Thanks for sharing. mobil telefonlar

    ReplyDelete
  30. Its a good post and keep posting good article.its very interesting to read.
    R Programming Training in chennai

    ReplyDelete
  31. 8 ball pool is a standout amongst the most famous diversions around the globe. This well known billiard 8 ball pool is straightforward and an arcade diversion played by each sex and distinctive ages. You will locate this amusement in each edge of the world. There are some abnormal state rivalries of this amusement done online for trophies and cash.

    https://miniclipgamesguide.jimdofree.com/2019/03/25/hoggart-twins-set-for-world-pool-champs/

    ReplyDelete
  32. Thank you for your post, I look for such article along time, today i find it finally. this post give me lots of advise it is very useful for me. Health blogger

    ReplyDelete
  33. Good post. I learn something totally new and challenging on sites I stumbleupon on a daily basis. It will always be interesting to read through content from other writers and practice a little something from other websites.
    Azar Mod APK Brief Info

    ReplyDelete
  34. Nice post. Thanks for sharing! I want people to know just how good this information is in your article. It’s interesting content and Great work.
    Thanks & Regards,
    VRIT Professionals,
    No.1 Leading Web Designing Training Institute In Chennai.

    And also those who are looking for
    Web Designing Training Institute in Chennai
    SEO Training Institute in Chennai
    Photoshop Training Institute in Chennai
    PHP & Mysql Training Institute in Chennai
    Android Training Institute in Chennai

    ReplyDelete
  35. Avast customer service is available around the clock . you can call our avast customer support team for solution of all kind of avast antivirus related issues like avast login problem, avast account issues, avast billing problems etc. Just need to dial our avast customer service number 1-855-499-1999.
    Avast Customer Service

    ReplyDelete
  36. Epson Printer Support Phone Number
    has turned into the need of each individual utilizing innovation related howdy tech items. PC and printer clients additionally experience numerous issues at the season of utilization which propel them to secure the client support number(+1-888-326-0222) and get their help to determine the issues. As the innovation of a printer and comparable gadgets are not easy to use for some clients, subsequently, Epson Printer Support Phone Number is accessible online to help end-clients to tackle tech related issues.

    ReplyDelete
  37. Thanks for sharing an informative blog keep rocking bring more details.I like the helpful info you provide in your articles. I’ll bookmark your weblog and check again here regularly. I am quite sure I will learn much new stuff right here! Good luck for the next!
    mobile application development training online
    mobile app development course
    mobile application development course
    learn mobile application development
    mobile app development training
    app development training
    mobile application development training
    mobile app development course online
    online mobile application development

    ReplyDelete
  38. thanks for your information really good and very nice web design company in velachery

    ReplyDelete
  39. Dell Printer Support is a team of well experienced experts who takes care of your dell device problems. If face any problem with your device like your device is crashed, device is not working properly, paper jamming or others then contact at Dell Printer Support Phone Number and get instant help.

    ReplyDelete
  40. Acer Support is a team of well experienced experts who takes care of your Acer devices. If you are facing any problem with your Acer device like your device is crashed, device is not working properly or any other problem then contact toll free number of Acer Tech Support and get instant help.

    ReplyDelete
  41. Epson Printer Support is available whole day and night to resolve your printer problems. If you face any problem with your epson printer device like device is not working properly, cable problem, cartage problem, blank screen and others then contact at Epson Printer Support Phone Number and resolve your issue in no time.

    ReplyDelete
  42. Canon Printer Support is a team of experts who takes care of your canon device issue. If face any problem with your device like your device is crashed , device is not working properly or others then contact Canon Printer Support Phone Number and get instant help.

    ReplyDelete
  43. AOL Tech Support Phone Number is a customer care helpline that is offered by one of world's best mail service provider. The experts at this toll-free helpline renders all your forget password, password recovery and other related issues. Reach them anytime of day or night at AOL Tech Support whenever you need their help.

    ReplyDelete
  44. This comment has been removed by the author.

    ReplyDelete
  45. You are doing a great job. I would like to appreciate your work for good accuracy
    web design company in velachery

    ReplyDelete