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: 29954

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 Gustavo Suim em 17 julho 2013 às 10:53
Marcelo, inicialmente, parabéns pelo projeto. Muito bacana!

Fiz aqui e funcionou corretamente, porém não funcionou no Arduino Mega ADK por ter pinagem diferente. Apenas um detalhe..

É importante deixar claro aos que pretendem fazer o projeto em outras plataformas (ex: PIC) ou até mesmo alterar o código proposto, que é impossível fazer sem a utilizacao de um osciloscopio, visto que esta sincronizações da Hsync e Vsync são MUITO criticas!

Abraços.
Comentário de Marcelo Rodrigues em 11 abril 2012 às 20:33

Galera,

Grandes progessos! Agora já dá para gerar imagens mesmo. Meio toscas é verdade.

Não deu para alcançar o Atari ainda. Hahaha...

Depois posto todos os detalhes, esse va dar mais trabalho.

Abraços!!

Comentário de Marcelo Rodrigues em 4 abril 2012 às 19:58

Luís e galera,

Não deu para embedar a janela do Gist nem do Pastebin, o sistema aqui recusa e apaga a janela.

Mas de qualquer forma, troquei o código pelo link do Github. Assim, é só clicar para ver o código, agora sim, completinho.

Abraços!!

Comentário de Luís Bulek em 29 março 2012 às 11:49

Marcelo, sugestão: disponibiliza aqui http://pastebin.com/ . Fica no formato, tudo bonitinho.

Comentário de Marcelo Rodrigues em 29 março 2012 às 10:16

Luís,

Ainda bem que você postou esse erro. A rede social cortou os símbolos maior que/menor que do código que eu colei acima, provavelmente por entender que são tags html. Do jeito que está acima, não vai compilar mesmo!

Não sei como, mas vou corrigir isso e aviso aqui novamente. Se eu colar o texto como imagem não vai dar para a galera dar ctrl-c. Vou ver se a plataforma tem algum recurso para se postar códigos, ou coloco lá no Github, ou coisa do tipo.

Abraço!!

Comentário de Luís Bulek em 29 março 2012 às 3:00

Belo trabalho, mais alguém não conseguiu compilar ? 

Obtive os seguintes erros, acho que na hora de copiar pro site, alguma coisa foi removida:

sketch_mar29a.cpp:31:14: error: invalid suffix "TOIE2" on integer constant
sketch_mar29a.cpp: In function 'void setup()':
sketch_mar29a:22: error: expected `)' before numeric constant
sketch_mar29a:23: error: expected `)' before numeric constant
sketch_mar29a:24: error: expected `)' before numeric constant
sketch_mar29a.cpp: In function 'void loop()':
sketch_mar29a:47: error: expected `)' before numeric constant
sketch_mar29a:51: error: expected `)' before numeric constant
sketch_mar29a:54: error: expected `)' before numeric constant
sketch_mar29a:57: error: expected `)' before numeric constant

Comentário de Marcelo Rodrigues em 26 março 2012 às 9:54

Valeu Luís!

Hoje vou começar a brincar com desenhos. Minha ideia é tentar definir uma matriz com o menor "pixel" que o Arduino consegue desenhar, o que depende da sua velocidade. Mas eu queria fazer isso lendo cada linha ou frame de alguma memória, para que o firmware possa desenhar qualquer coisa que seja enviada para ele, não apenas coisas pré-programadas.

Abraço!!

Comentário de Marcelo Rodrigues em 25 março 2012 às 9:56

Edson,

Pode deixar, vou fazer uma placa para esse projeto e posto o esquemático aqui para a galera.

Quanto ao fluxograma, basta acompanhar o código acima, ele é bem simples. Basicamente gere um pulso de sincronia horizontal na frequência EXATA de 31,3KHz, de uma pausa e escreva a linha antes do próximo pulso acontecer. Conte as linhas e deixe o pulso vertical em baixa durante 1 ou duas linhas. O truque que nenhum site ensina é desligar o RGB quando der qualquer pulso de sincronia.

Depois posto o esquemático (vou fazer ainda).

Abraço!!

Comentário de Edson Roberto da Silva em 24 março 2012 às 18:24

Olá Marcelo,

Parabéns pelo projeto !

Gostaria de lhe fazer um pedido, se possível fazer um fluxograma do programa, desta forma poderá ser portado para outras linguagens e outras plataformas, e também o esquema de ligação do microcontrolador ao monitor.

Abraço...

Edson.

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço