Gerando sinal VGA (colorido) com Arduino - Completo!

Fala galera!

 

Pela foto acima dá para perceber que o problema com a área vazia foi resolvido, agora toda a área da tela é preenchida. Então a gente pode desenhar em qualquer canto (640 x 480 @ 60Hz). O problema era a função de interrução, os tempos de chamada e retorno eram altos demais no ATmega328 para o monitor, então substituí a função por um "if", checando o o valor no registrador do timer o tempo todo. Quando o timer chega a um determinado valor (31.3KHz), a condição do "if" é verdadeira e a linha é impressa. Assim o programa não desperdiça tempo chamando e retornando da função de interrupção.

 

 

Outro problema era que apenas monitores CRT entendiam o sinal. Esse problema foi resolvido ajustando o nível de tensão dos pinos RGB para o padrão de 0,7V, usando um resistor em série com um diodo, reduzindo de TTL (5V) para 0,7V, conforme imagem acima. Assim, todos os monitores estão entendendo o sinal (incluindo LCD e LED).

 

 

Com tudo resolvido, nós podemos gerar sinal VGA COLORIDO!!!

Agora vou tentar criar uma biblioteca inteligente o suficiente para desenhar figuras e letras, e talvez gerar mais cores.

Tudo usando apenas um Arduino UNO. :))

 

Aqui está o código que escrevi. Aproveitem! Estamos aqui para compartilhar.

 

https://github.com/LaboratorioDeGaragem/VGA-com-Arduino/blob/gh-pag...

 

Se você vai tentar usar esse código, é bom saber também:

 

 

E isso também:

 

Clique na imagem para ampliar

"VGA industry standard" 640x480 pixel mode

General characteristics

Clock frequency 25.175 MHz
Line frequency 31469 Hz
Field frequency 59.94 Hz

One line

8 pixels front porch
96 pixels horizontal sync
40 pixels back porch
8 pixels left border
640 pixels video
8 pixels right border
---
800 pixels total per line

One field

2 lines front porch
2 lines vertical sync
25 lines back porch
8 lines top border
480 lines video
8 lines bottom border
---
525 lines total per field 

 

Atente-se em desligar seu sinal RGB sempre que for pulsar um de sincronia.

E fique esperto com o que o compilador faz. Muito tempo é desperdiçado em muitas situações, e os tempos não repetitivos são os mais perigosos.

Se o seu sinal não estiver em perfeita sincronia, o monitor vai rejeitar.

Use o osciloscópio para monitorar tudo!

Boa sorte e conte comigo se precisar de ajuda!

 

Abraço!!!

 

Atualização 05 novembro de 2014:

Atendendo a pedido, segue abaixo o código para gerar a imagem acima (precisa revisar, faz tempo que fiz). A matriz logoldg[25][40] é a representação da imagem pixel a pixel. Ela é obtida através do script em Python abaixo que lê a imagem BMP e transforma cada pixel nos valores para carregamento no registrador para gerar os bits RGB. A imagem tem 40x25 pixels e pode ser feita em qualquer software gráfico.

Imagem original BMP (a ser apresentada na tela):

Ela é pequena porque é o máximo que o ATmega328 consegue armazenar na flash.

#include <avr/pgmspace.h>

#define NOP asm("nop")
#define PRETO PORTB = B00000000; //0
#define AZUL PORTB = B00000001; //1
#define VERDE PORTB = B00000010; //2
#define CIANO PORTB = B00000011; //3
#define VERMELHO PORTB = B00000100; //4
#define MAGENTA PORTB = B00000101; //5
#define AMARELO PORTB = B00000110; //6
#define BRANCO PORTB = B00000111; //7

unsigned int contalinhas = 1;

void setup()
{
//Seta pinos 5, 6 e 7 como saidas
// 7 - HSYNC
// 6 - VSYNC
// 5 - RGB
DDRD |= B11100000;
DDRB |= B11100111;
PORTD |= B11000000;

//set timer
TCCR2A = 0x02; // WGM22=0 + WGM21=1 + WGM20=0 = Mode2 (CTC)
TCCR2B |= (1 CS20); //
TCCR2B |= (1 CS21); // Seta prescaler
TCCR2B &= ~(1 CS22); //

TCNT2 = 0; // limpa contador
//OCR2A = 0x03; // seta contador para comaracao

TIMSK2 &= ~(1OCIE2A); // seta interrupcao por comparacao
TIMSK2 |= (1TOIE2); // seta interrupcao por overflow
}

void loop()
{

PROGMEM byte logoldg[25][40] = { //A matriz da imagem contendo elementos hexadecimais no formato correto para o LCD
6,6,6,6,6,6,6,6,6,0,0,0,0,0,0,0,6,6,6,6,6,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,0,4,4,4,4,4,0,0,6,6,6,0,0,4,4,4,4,4,0,0,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,0,4,4,4,4,4,4,0,0,0,0,0,4,4,4,4,4,4,4,0,0,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,7,7,4,4,4,4,4,4,4,4,4,4,7,4,4,4,4,4,0,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,7,7,4,4,4,4,4,4,4,4,4,4,7,4,4,4,4,4,4,0,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,7,7,4,4,4,4,4,4,4,4,4,4,7,4,4,4,4,4,4,0,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,7,7,4,4,4,7,7,7,7,7,4,4,7,7,7,7,4,4,4,4,0,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,7,7,4,4,4,4,4,4,4,7,7,4,7,4,4,7,7,4,4,4,0,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,7,7,4,4,4,4,7,7,7,7,7,4,7,4,4,7,7,4,4,4,0,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,7,7,4,4,4,7,7,4,4,7,7,4,7,4,4,7,7,4,4,4,0,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,7,7,7,7,4,7,7,4,4,7,7,4,7,7,7,7,7,4,4,4,0,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,7,7,7,7,4,4,7,7,7,7,4,4,7,7,7,7,4,4,4,0,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,0,4,4,4,4,4,0,0,4,4,4,4,4,4,4,0,0,0,0,0,4,4,4,0,6,6,6,6,6,6,6,
1,1,1,1,1,1,1,1,0,4,4,4,4,4,4,0,0,4,4,4,4,4,4,4,0,0,0,0,0,4,4,4,0,1,1,1,1,1,1,1,
1,1,1,1,1,2,2,2,0,4,4,4,0,0,0,0,0,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,0,2,2,1,1,1,1,1,
1,1,1,1,2,2,2,2,0,4,4,4,0,0,0,0,0,4,0,0,0,0,4,4,0,4,4,4,4,4,4,4,0,2,2,2,2,1,1,1,
1,1,2,2,2,2,2,2,0,4,4,4,0,4,4,0,0,4,0,4,4,0,0,4,0,4,0,0,0,4,4,4,0,2,2,2,2,1,1,1,
1,1,2,2,2,2,2,2,0,4,4,4,0,4,4,0,0,4,0,0,0,0,0,4,0,4,4,4,0,4,4,4,0,2,2,2,2,2,1,1,
1,1,2,2,2,2,2,2,0,4,4,4,0,4,4,0,0,4,0,4,4,4,4,4,0,0,0,0,0,4,4,4,0,2,2,2,2,2,1,1,
1,1,2,2,2,2,2,2,0,4,4,4,0,0,0,0,0,4,0,0,0,0,0,4,0,0,0,0,0,4,4,4,0,2,2,2,2,2,1,1,
1,1,1,2,2,2,2,2,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,2,2,2,2,1,1,1,
1,1,1,1,1,2,2,2,2,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,2,2,2,2,1,1,1,1,
1,1,1,1,1,1,1,2,2,2,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,2,2,2,2,1,1,1,1,1,
1,1,1,1,1,1,1,1,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,1,1,1,1,1,1,1,
//1,1,1,1,1,1,1,1,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,1,1,1,1,1,1,1,

};


noInterrupts();
do{
PRETO;
if (TCNT2 > 0x0f){

delayMicroseconds(1);
NOP;NOP;NOP;NOP;

TCNT2 = 0x00;



//pequeno ajuste de tempo, pode ser necessario ou nao dependendo da placa
// #### HSYNC ###
PORTD &= ~(1 7);
if (++contalinhas >= 525){ //525 linhas
contalinhas = 1;
}
PORTD |= (1 7);

// ### VSYNC ###
if ((contalinhas == 1)||(contalinhas == 2)){ //492 e 493
PORTD &= ~(1 6);
} else {
PORTD |= (1 6);

//NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;
//NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;
//NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;
//NOP;NOP;NOP;NOP;NOP;

int linhamatriz = 0;
linhamatriz = contalinhas / 16 - 1;
int j=0;

if ((contalinhas >= 16) && (contalinhas <= 415)){ // 2 e 482

PRETO;
//delayMicroseconds(1);NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP; //delay ate 25
//unsigned int i=contalinhas/9;
int j=0;
while(j<40){
PORTB = logoldg[linhamatriz][j];
j++;
}
NOP;NOP;
PRETO;
NOP;NOP;NOP;NOP;


}

}


}


}while(1);

}

Script em Python para gerar a matriz:

print "labdegaragem.com"
import Image
import ImageColor
image='paisagem.bmp' #Coloque aqui o nome do arquivo que deseja converter
texto='paisagem.txt' #Crie um arquivo texto em branco em um diretorio a sua escolha e coloque aqui o nome do arquivo texto para guardar a matriz da imagem em hexadecimal
f=open(texto,'w') #Ira abrir o arquivo texto
imopen=Image.open(image) #Abrira a imagem
x=y=0
h=0
while y<25: #Coloque aqui a altura da imagem. A imagem maxima tem 40x25 pixels
#print y
x=0
while x<40: # Coloque aqui o comprimento da imagem.
#print x
z=imopen.getpixel((x,y))
h=0
if z[0] > 200:
h = h+4
if z[1] > 200:
h = h+2
if z[2] > 200:
h = h+1
#print z[0],"-",z[1],"-",z[2],"=",h
f.write(str(h))
f.write(',')
x=x+1
y=y+1
f.write('\n')
f.close()

Exibições: 29975

Comentar

Você precisa ser um membro de Laboratorio de Garagem (arduino, eletrônica, robotica, hacking) para adicionar comentários!

Entrar em Laboratorio de Garagem (arduino, eletrônica, robotica, hacking)

Comentário de Marcelo Rodrigues em 21 janeiro 2015 às 15:37

José Paulo,

Esse código você tirou de algum outro lugar? Não usei nenhuma biblioteca VGA.h neste post como aparece no erro de compilação que você colocou. 

Abraço!

Comentário de jose paulo da silva em 20 janeiro 2015 às 14:41

erro al copilar, qualquer exemplo

o hellod 

esse e o erro (IDE 1.0.6 e 1.5.8 )
In file included from graphics.cpp:1:
/VGA.h: In function 'void _v_digitalWriteDirect(int, boolean)':
VGA.h:49: error: 'g_APinDescription' was not declared in this scope
VGA.h:50: error: 'g_APinDescription' was not declared in this scope

Comentário de Marcelo Rodrigues em 5 novembro 2014 às 11:52

Anderson,

Coloquei todos os detalhes no post.

Abraço!

Comentário de Anderson Bordin em 4 novembro 2014 às 7:48

Marcelo, como fizestes a imagem na tela da última foto do post?

Abraço

Comentário de Marcelo Rodrigues em 28 novembro 2013 às 19:08

Felipe,

Pode incomodar. Estou às ordens! :)

Placa/dongle Wi-Fi acho que não rola. No caso do dongle, talvez com algum USB Host. :(

Murilo,

Provavelmente não. Essas placas possuem hardware para geração da imagem ajustado para o tft que foi utilizado. A biblioteca possui funções de comunicação com esse hardware, não diretamente com o tft.

Boa sorte!!

Comentário de Murilo Lima Nogueira em 28 novembro 2013 às 12:18

Marcelo, tem como adaptar a biblioteca daqueles shield lcd tft para usar nesses tft de notebook po exemplo?

Comentário de Jéssica Moura Ribeiro em 16 novembro 2013 às 18:41

Valeu Marcelo, já foi muito legal ver que conseguiu e saber que é ainda mais fácil.

Com certeza vou tentar fazer, tenho um notebook antigo, vou tentar aproveitar o máximo de peças em experiências.

Procurarei o datasheet e tentarei brincar com algo, adaptando as dicas que deu. Qualquer dúvida te incomoda pedir ajuda nessa adaptação ao TFT de meu note?

P.S.: Manja como utilizar o wireless card do notebook? Esta peça seria ainda mais interessante de aproveitar :) que a própria tela. Procurei e não achei nada no google, se conseguisse montaria um tutorial interessante.

Comentário de Murilo Lima Nogueira em 16 novembro 2013 às 17:35

Obrigado Marcelo

Comentário de Marcelo Rodrigues em 16 novembro 2013 às 12:56

Murilo,

Segue código. Bom proveito! :)
Esse desenha um retângulo na tela. É bom para pegar os princípios do TFT.
A princípio, você pode usar qualquer LCD TFT, mas vai precisar conhecer a pinagem do flat cable, ter o datasheet é fundamental.
Outro problema é o conector e a placa onde irá soldá-lo que sempre é SMD com contatos minúsculos.

Abraço!


unsigned int contalinhas = 1;
unsigned int contapixels = 1;

void setup()
{
  // PD2 - CLOCK
  // PD3 - HSYNC
  // PD4 - VSYNC
  DDRD |=  B00011100; //Seta pinos como saida
  PORTD |= B00011000;

  // PC5,PC4 - B
  // PC3,PC2 - G
  // PC1,PC0 - R
  DDRC |=  B00111111; //Seta pinos como saida
  PORTC |= B00000000;

}

void loop()
{

//noInterrupts(); //Manter mesmo se nao usar timer
  do{

         // PIXEL PRETO POR PADRAO
         PORTC = B00000000;

//DESENHA QUADRO
          if ((contalinhas >= 14)&&(contalinhas<=253)&&(contapixels>=69)&&(contapixels<=388)){
                  PORTC = B00000011;
          }


          //DESENHA FRAME DE 1 PIXEL (320x240)
          if ((contalinhas == 14)||(contalinhas==253)||(contapixels==69)||(contapixels==388)){
                  PORTC = B00111111;
          }



          //PULSOS DE SINCRONIA
          //Desce bit - LOW (active low)
          if (contapixels > 408){ //ERA 408 -------------------
              //pulso de sincronia horizontal
              PORTD &= ~(1  3);
              if (contalinhas > 263){
                  //pulso de sincronia vertical
                  PORTD &= ~(1  4);
              }
          }
          //Sobe bit - HIGH (active low)
          if (contapixels > 409){ //ERA 409 --------------------
              //zera contador de pixels
              contapixels = 0;
              PORTD |= (1  3);
              if (contalinhas > 263){ //ERA 263 -----------------
                  PORTD |= (1  4);
                  contalinhas = 0;
              }
              //incrmenta contador de linhas
              contalinhas++;
           }



         //CLOCK
          //pulso de clock
          PORTD |= (1  2);
          contapixels++;
          PORTD &= ~(1  2);
  }while(1);

}

Comentário de Murilo Lima Nogueira em 16 novembro 2013 às 8:33

Olá Marcelo, gostei muito deste comentário sobre TFT, é muito mais prático que os LCD´s normais, hehehe

Gostaria de saber como seria o código pro arduino, dá pra fazer isso com LCD de dvd portátil?

Atenciosamente, Murilo Lima Nogueira

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço