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

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 tiago merces em 23 abril 2020 às 0:44

"Putz, já tem quase 8 anos que fiz esse projeto. Difícil lembrar os detalhes... kkkk"

8 anos, 25 mil exibições.

Ainda que já tenha um tempo, o projeto ainda é moderno =)

Comentário de Marcelo Rodrigues em 12 março 2020 às 13:45

E não esqueça de aumentar e diminuir alguns NOP. Comece pelos relacionados ao tempo das linhas.

Boa sorte!

Comentário de Etienne Leite Gomide em 12 março 2020 às 10:55

Entendi, Marcelo

Bom, por enquanto não tenho osciloscópio pra conferir. Assim que puder eu volto a mexer no projeto novamente com o auxílio de um osciloscópio.

Vou dar uma conferida no "VGA industry standard" 640x480 pixel mode  e nos valores de clock que me passou.

Obrigado pelas dicas.

Comentário de Marcelo Rodrigues em 12 março 2020 às 10:09

Etienne,

"Já que a placa é a mesma do seu exemplo (Arduino Uno) acho que os tempos de NOP nem precisariam de ajustes...."

Não é verdade. Aqui eu precisava ajustar até se trocasse o ATmega. Não sei explicar a razão, mas sempre precisava ajustar. Talvez algo relacionado ao cristal... não sei. 

Meu conselho é acertar primeiro com o osciloscópio e na sequência ajustar incluindo ou tirando NOPs.

"Será que o modelo, resolução máxima do monitor e frequência influenciam?"

Sim. Essa é outra razão para ajustar. 

A minha ideia nesse projeto seria incluir uma rotina de "sintonização" desses tempos com uns pots. Assim, o usuário poderia ajustar para a imagem aparecer mais fácil. 

"Quanto ao código a única coisa que tem que corrigir são os sinais de dois "menor" que ficaram faltando em alguns lugares na exibição HTML, não é isso?"

Acho que sim. Para garantir, pega o código do Github.

Conselho: entenda o código e o circuito. Se você compreender bem, poderá ajustá-lo indo direto ao ponto, ao invés de mexer em um monte de coisas.

Outra coisa. Verifique se o seu monitor tem algum ajuste de definição. O sinal desse código gera um VGA, que por definição é:

"VGA industry standard" 640x480 pixel mode

General characteristics

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

Se você medir com o osciloscópio, os seus sinais devem seguir essas frequências.

Os seus pulsos devem cair nessa zona "Blanking Time". Se não cair, a imagem não aparece. Veja a figura abaixo:

Esse tipo de projeto precisa de entendimento para rodar certo. Sugiro estudar bem como a imagem é formada através dos sinais antes de mexer em hardware e código. Ok?

Boa sorte!

Comentário de Etienne Leite Gomide em 11 março 2020 às 10:52

Marcelo,

Conferi os resistores e os diodos... testei com os sinais VSync e HSync com e sem diodos...

Testei com Arduino Uno e Arduino Duemilanove. Mas mesmo assim nenhuma imagem aparece na tela.

Já que a placa é a mesma do seu exemplo (Arduino Uno) acho que os tempos de NOP nem precisariam de ajustes....

Será que o modelo, resolução máxima do monitor e frequência influenciam?

Quanto ao código a única coisa que tem que corrigir são os sinais de dois "menor" que ficaram faltando em alguns lugares na exibição HTML, não é isso?

Comentário de Marcelo Rodrigues em 6 março 2020 às 10:55

Etienne,

Bom dia!

Putz, já tem quase 8 anos que fiz esse projeto. Difícil lembrar os detalhes... kkkk

- Os diodos podem ser qualquer um que tenha queda de tensão na faixa de 0.7V

- Verifique se você conectou todos os terras. Os sinais precisam estar no mesmo referencial.

- O principal é acertar os tempos do HSync e do VSync, você precisar ir testando e incluir ou retirar NOP do código para os tempos baterem com seu hardware.

O ideal é usar um osciloscópio para verificar os tempos antes de aplicar ao monitor. Não se preocupe com os sinais RGB antes de acertar o frame (Hsync e Vsync).

Esse projeto é bem divertido e depois você pode aplicar o mesmo raciocínio para displays LCD.

Boa sorte!

Comentário de Etienne Leite Gomide em 6 março 2020 às 8:49

Bom dia Marcelo Rodrigues,

Estou acompanhando o projeto aqui de VGA colorido. parabéns pelos resultados, ficou muito bom. Eu queria tentar reproduzir o projeto.

Abri a foto que você postou e montei o circuito segundo o que consegui entender pela foto:

- os diodos não consegui identificar os valores,

- pinos 6, 7 conectados, cada um, a um resistor de 1K e um diodo p/ terra (para sincronização vertical e horizontal),

- pinos 8, 9, 10 conectados, cada um, a um resistor de 1K e um diodo p/ terra (para conexão RGB)

- aos pinos RED, VSync e HSync tem 2 fios ligados em cada um, mas não consegui identificar onde se conectam os fios extras nestes pontos.

- quanto ao código eu vi que o HTML retirou os símbolos de "". Pelo código que você postou no gitHub eu pude ver quais eram os símbolos corretos e fiz a substituição.

Bom, montei o circuito desta forma que descrevi, testei em 2 monitores diferentes, testei esse código com a imagem e o código de barras coloridas que está no gitHub, mas mesmo assim não funcionou. Teria como disponibilizar o diagrama de circuito?

Obrigado,

Comentário de Marcelo Rodrigues em 27 janeiro 2015 às 16:49

Jose Paulo,

Essa biblioteca é para Due. Precisaria adaptar para Uno.

Vicente,

Coloquei uma imagem no post acima com um bom resumo de tudo isso que você perguntou.

Abraços!

Comentário de jose paulo da silva em 27 janeiro 2015 às 15:07

o link pra vga arduino

https://github.com/stimmer/DueVGA

Comentário de Vicente Cesar em 26 janeiro 2015 às 17:14

Gostei muito, nuca mexi com Arduíno mas quero tentar reproduzir esse mesmo projeto em um microcontrolador PIC. Eu ainda sou inciante na parte de vídeo, só trabalhei com vídeo composto, então tem alguns temos que são novos para mim.

Poderia me esclarecer o que são: pixels front e back porch, pixels left e right border e lines top e bottom border ?

Tenho mais uma ultima duvida, qual o espaço de tempo para cada pixel ser impresso na tela ?

Agradeço desde já. Ótimo trabalho!

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço