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 15 novembro 2013 às 17:45

Felipe,

Fiz uns testes com TFT e é bem mais fácil do que VGA. O princípio é o mesmo, mas como você é quem dá o clock, fica mais flexível. Como você está aproveitando o TFT de um note, precisará procurar o datasheet para ver a pinagem do flat cable, mas acho que não será nenhum terror. :)

Dá uma olhada nos testes que fiz por aqui com TFT:

Essa plaquinha de baixo é a primeira versão do Garagino, que fiz na Fresadora CNC. Hehehe... A placa de cima não deu para fresar, o conector do flat que vem no TFT tem contatos minúsculos, então tive que encomendar essa breakout board. Esse monte de jumpers são bits de cores RGB (usei só o mais significativo e curto-circuitei os demais), depois tem Clock, Hsync, Vsync, Enable, GND, Vcc, etc. As baterias são para o backlight, são 2 em série (18V).

Consegui colocar imagens na tela lendo de um cartão SD, mas o ATmega328 não é rápido o bastante e a imagem fica dando refresh. O melhor que consegui foi colocando a imagem na flash, mas a memória é baixa então não dá para aproveitar todo o potencial do TFT. Fiz um overclock do ATmega328 (clique aqui) mas ainda não testei com o TFT (falta de tempo).

Como curiosidade é legal brincar com Arduino/Garagino e TFT, mas para gerar uma imagem decente precisa de um outro uC mais rápido ou chip dedicado para essa função.

Boa sorte aí! Depois posta o andamento. Abc!

Comentário de Jéssica Moura Ribeiro em 14 novembro 2013 às 12:15

Olá Marcelo, sou novato na área do arduino, faz pouco tempo que comprei um para experimentar o potêncial, mecho muito com programação (C,C++,C#...) e um pouco com eletrônica. Achei bem interessante sua ideia, vejo que evolui até mesmo na utilização. Será que saberia como posso utilizar um monitor lcd TFT de notebook antigo no lugar de um monitor lcd de desktop?

Comentário de Marcelo Rodrigues em 18 setembro 2013 às 16:57

Gustavo,

Ainda não tentei, então não sei. Mas acho que o próprio monitor vai detectar a resolução e redimensionar a imagem se você soltar o sinal no formato certo.

Abraço!

Comentário de Gustavo Suim em 18 setembro 2013 às 16:38
Marcelo, boa tarde.

Você tem um modelo para alterar a frequencia de operação e/ou outras resoluções de vídeo? Estou tentando aqui, mas estes registradores de tempo são muito doidos.

Abracos.
Comentário de Marcelo Rodrigues em 23 agosto 2013 às 14:22

Murilo,

Dá uma olhada na foto acima. São resistores de 1K e diodos em série para limitar até 0,7V nos sinais de RGB, nos sinais de sincronia são apenas resistores de 1K para limitar a corrente.

Abraço!

Comentário de Murilo Lima Nogueira em 22 agosto 2013 às 20:05

Marcelo, quais foram as ligações, quais os valores dos resistores?

Comentário de Murilo Lima Nogueira em 22 agosto 2013 às 16:36

Obrigado, Marcelo!

Abraço!

Comentário de Marcelo Rodrigues em 22 agosto 2013 às 15:58

Murilo,

PORTB - Registrador com os pinos de I/O (entrada/saida) - Port B (pinos PB0, PB1, PB2...)

DDRB - define se os pinos do PORTB serão entradas ou saídas.

Abraço!

Comentário de Murilo Lima Nogueira em 21 agosto 2013 às 21:00

Para que serve as funcoes no arduino:

portb

ddrd

Comentário de Marcelo Rodrigues em 17 julho 2013 às 12:19

Que bacana Gustavo! Parabéns.

Concordo. Osciloscópio é fundamental para desenvolver em outras plataformas.

Abraço!

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço