Gerador de barras e padrões para monitor VGA

Esta aplicação serve para gerar padrões coloridos em monitores VGA (640x480), com até 8 cores (1 bit por canal de cor).

O projeto feito em VHDL é simples e serve como bom exemplo de como um circuito de hardware pode ser descrito através de um texto.

O princípio de funcionamento é o seguine:
1) Gerar um sinal de sincronismo horizontal com largura de 3.7us e demais temporizações obedecendo o diagrama.


2) Gerar um sinal de sincronismo vertical com largura de 64us e temporizações como no diagrama.

3) Para gerar estes sinais utiliza-se contadores e o valor destes contadores indicam também a posição de um determinado pixel na tela do monitor. Empregando o valor dos contadores para comparar com referências, outros contadores, etc, é a forma de determinar o momento de ligar ou não os sinais de vídeo (R,G e B).

O esquema do conector encontra-se neste link.

Trata-se de uma simples saída digital com um resistor de 300ohms em série. Como a entrada do monitor possui um resistor de 75ohms (impedância padrão do sinal de vídeo) com um nível de saída de 3V3 resulta em 0.66V na entrada de cada canal (RGB). Normalmente a saída do CPLD vai ficar um pouco abaixo da tensão nominal disponível no pino VCCO, ainda mais com uma carga de alguns mA.


Os canais RGB são especificados para 0.7V de pico na entrada. Qualquer valor abaixo deste vai apenas diminuir o brilho da cor acionada. Se todos estiverem iguais (mesmos valores de resistores) a combinação de cores será mantida uniforme.

 

A listagem VHDL é a seguinte:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
 
entity vga is
port(clk : in std_logic; -- 50MHz
red_out : out std_logic;
green_out : out std_logic;
blue_out : out std_logic;
hs_out : out std_logic;
vs_out : out std_logic);
end vga;
 
architecture Behavioral of vga is
signal hc,vc : std_logic_vector (9 downto 0);
signal clk25 : std_logic;
begin
 
process (clk)
begin
if rising_edge(clk) then
clk25<=not clk25; -- divide 50MHz para 25MHz
end if;
end process;
 
process (clk25)
begin
if rising_edge(clk25) then
red_out <= '0';
green_out <= '0';
blue_out <= '0';
-------------------------------------------------
-- esta parte do codigo testa se os contadores estao
-- passando pela parte visivel da tela e, se positivo,
-- os canais RGB sao ligados de acordo conforme desejado
if (hc >= 144 ) -- 144
and (hc < 784 ) -- 784
and (vc >= 39 ) -- 39
and (vc < 519 ) -- 519
then
if (vc>hc) then -- enquanto contador V for maior que H liga AMARELO
red_out <= '1';
green_out <='1';
else
blue_out <= '1';
end if;
end if;
--------------------------------------------------
hs_out <= '1';
if (hc > 0 ) and (hc < 97 ) then hs_out <= '0'; end if;

vs_out <= '1';
if (vc > 0 ) and (vc < 3 ) then vs_out <= '0'; end if;

hc <= hc+1;
if (hc = 800) then vc <= vc+1; hc <= (others=>'0'); end if;
if (vc = 521) then vc <= (others=>'0'); end if;
end if;
end process;
end Behavioral;

O código acima produz um triângulo amarelo dentro de um fundo azul.

Para gerar barras verticais o código poderia ser modificado para:

-------------------------------------------------
-- esta parte do codigo testa se os contadores estao
-- passando pela parte visivel da tela e, se positivo,
-- os canais RGB sao ligados de acordo conforme desejado
if (hc >= 144 ) -- 144
and (hc < 784 ) -- 784
and (vc >= 39 ) -- 39
and (vc < 519 ) -- 519
then
red_out <= hc(3); -- liga vermelho a cada 8 pixeis
green_out <= hc(5); -- liga verde a cada 32 pixeis
blue_out <= hc (7); -- liga azul a cada 128 pixeis
end if;
--------------------------------------------------

As barras geradas são de 8 cores (3 bits). Para gerar mais cores pode-se empregar um DAC mais elaborado com 2 resistores para cada canal. Se o valor destes resistores quando calculados em paralelo deve ser maior que 270 ohms para garantir uma tensão de pico menor ou igual a 0,7V. Além disso é preciso que sejam de valores diferentes para garantir 4 níveis distintos. Com dois valores iguais seriam produzidos apenas 3 níveis distintos.

Assim, com um resistor de 470ohms e outro de 680ohms obtem-se 4 níveis por canal:

 R1=470R R2=680R Vrgb para Voh=3V
 0  0  0V
 0  1  0.26V
 1  0  0.37V
 1  1  0.64V

No código bastaria incluir 3 novos sinais de saída e associá-los aos pinos do CPLD que forem convenientes e disponíveis no conector.

Com 2 resistores por canal é possível exibir 64 cores diferentes usando esta conversão direta. Contudo, cada pino de saída poderia ainda ficar em estado tri-state ('Z'). Com isso o número de combinações por canal aumentaria para 6, permitindo gerar 216 cores diferentes:

 

 R1=470R R2=680R Vrgb para Voh=3V
 0  0  0V
 0  1  0.26V
 Z  1  0.29V
 1  0  0.37V
 1  Z  0.41V
 1  1  0.64V

Claro que a manipulação com 'Z' consumiria mais macrocélulas mas pode ser um experimento interessante e muito didático.

Finalmente, uma escolha mais criteriosa do valor dos resistores permitirá gerar uma escala de tensões mais linear. Os exemplos acima são apenas ilustrativos e a tensão final depende diretamente da tensão de saída efetiva do CPLD e da tolerância dos resistores.