Este segundo artigo tem como objetivo demonstrar na prática como um controlador VGA pode ser construído. Assim, será utilizada a placa DE0-Nano para descrever o hardware do controlador utilizando VHDL. Cabe frisar que o objetivo é demonstrar como transformar os parâmetros de controle, apresentados no primeiro artigo, em um hardware. Portanto, o FPGA e o VHDL são apenas recursos utilizados no exemplo. Para mais informações sobre a placa e a linguagem consulte as referências listadas no final do artigo. Vamos lá!
Elaborando o Controlador VGA
Como a DE0-Nano possui um oscilador de 50 MHz, será utilizado como caso de teste a configuração VGA com resolução de 800×600, frequência de atualização de 72 Hz e apenas 1 bit para cada componente do pixel.
No primeiro artigo foi apresentada a tabela com os parâmetros de controle do frame VGA.
Tabela 1 – Temporização VGA.
De acordo com a Tabela 1, os parâmetros B, C, D e E são fornecidos como pulsos de clock, já os parâmetros P, Q, R e S são fornecidos com base na contagem de linhas. Deste modo um hardware básico pode ser construído de forma a contar os pulsos de clock e com base em comparações do valor contado pode-se determinar o estado dos sinais H_SYNC, V_SYNC e das componentes RGB.
As linhas e colunas são contadores modificados conforme a entrada de Clock. Os sinais de sincronismo são alterados conforme esses contadores e devem manter estado até que outra condição de controle seja disparada.
Uma forma simples de criar esse mecanismo de controle é considerar que os contadores estão na região ativa de vídeo durante os primeiros X pulsos contados, sendo que X representa o número de linhas e/ou colunas.
Para o sincronismo horizontal devemos considerar os parâmetros B, C, D e E. Considerando que o contador é iniciado com valor zero, será necessário realizar as operações listadas abaixo:
- A linha permanece na região ativa durante os D primeiros pulsos, sendo que D é o número de pixels por linha, neste caso, 800;
- Após atingir D é necessário aguardar mais E pulsos, sendo E a região de Front Porch. Até esse ponto temos um total de 856 pulsos (D + E);
- Após a região de Front Porch o sinal H_SYNC deve ser posto em zero, permanecendo nesse estado durante B pulsos. Até esse ponto temos um total de 976 pulsos (D + E + B);
- Depois de gerar o sinal de sincronismo é necessário aguardar C pulsos da região de Back Porch, totalizando 1040 pulsos (D + E + B + C).
Para o sincronismo vertical devemos considerar os parâmetros P, Q, R e S. Considerando que o contador é iniciado com valor zero e que o seu valor é incrementado sempre que uma linha é finalizada:
- O quadro permanece na região ativa durante os R primeiros pulsos, sendo que R é o número de linhas, neste caso, 600;
- Após atingir R é necessário aguardar mais S pulsos, sendo S a região de Front Porch. Até esse ponto temos um total de 637 pulsos (R + S);
- Após a região de Front Porch o sinal V_SYNC deve ser posto em zero, permanecendo nesse estado durante P pulsos. Até esse ponto temos um total de 643 pulsos (R + S + P);
- Depois de gerar o sinal de sincronismo é necessário aguardar Q pulsos da região de Back Porch, totalizando 666 pulsos (R + S + P + Q).
Com esses requisitos é possível determinar o tamanho dos contadores. Para o contador horizontal (colunas) serão necessários 11 bits, pois o total a ser contado é 1040 e com 11 bits é possível contar até 2048. Já o contador vertical (linhas) pode ser de 10 bits, pois o maior valor que será armazenado é 666.
Descrevendo o Controlador VGA
Para descrever o controlador VGA será utilizado o mesmo modelo ilustrado na Figura 1. Portanto, será necessário criar um mecanismo de sincronismo e outro para definição dos pixels.
Abaixo é listado o código da entidade para o bloco de sincronismo.
ENTITY VGASync IS PORT( RESET : IN STD_LOGIC; -- Entrada para reiniciar o estado do controlador F_CLOCK : IN STD_LOGIC; -- Entrada de clock (50 MHz) F_HSYNC : OUT STD_LOGIC; -- Sinal de controle VGA: H_SYNC F_VSYNC : OUT STD_LOGIC; -- Sinal de controle VGA: V_SYNC F_ROW : OUT STD_LOGIC_VECTOR(9 DOWNTO 0); -- Índice da linha que está sendo processada F_COLUMN : OUT STD_LOGIC_VECTOR(10 DOWNTO 0); -- Índice da coluna que está sendo processada F_DISP_ENABLE : OUT STD_LOGIC --Indica a região ativa do frame ); END ENTITY VGASync;
Para definir a operação do módulo, será apresentada a arquitetura dividia em 6 partes. Na listagem abaixo são mostrados os sinais internos do módulo.
-- Sinais de controle Horizontal SIGNAL H_CMP1 : STD_LOGIC; -- Indica se o contador Horizontal está com o valor D SIGNAL H_CMP2 : STD_LOGIC; -- Indica se o contador Horizontal está com o valor D+E SIGNAL H_CMP3 : STD_LOGIC; -- Indica se o contador Horizontal está com o valor D+E+B SIGNAL H_CMP4 : STD_LOGIC; -- Indica se o contador Horizontal está com o valor D+E+B+C SIGNAL HSync_Next : STD_LOGIC; -- Valor de H_SYNC no próximo pulso de clock SIGNAL HSync_Prior : STD_LOGIC; -- Último valor atribuído em H_SYNC SIGNAL HDataOn_Next : STD_LOGIC; -- Indica se o contador Horizontal está na região ativa SIGNAL HDataOn_Prior : STD_LOGIC; -- Último valor atribuído em HDataOn SIGNAL HCount_Next : STD_LOGIC_VECTOR(10 DOWNTO 0); --próximo valor do contador Horizontal SIGNAL HCount_Prior : STD_LOGIC_VECTOR(10 DOWNTO 0); --valor atual do contador Horizontal -- Sinais de controle Vertical SIGNAL V_CMP1 : STD_LOGIC; -- Indica se o contador Vertical está com o valor R SIGNAL V_CMP2 : STD_LOGIC; -- Indica se o contador Vertical está com o valor R+S SIGNAL V_CMP3 : STD_LOGIC; -- Indica se o contador Vertical está com o valor R+S+P SIGNAL V_CMP4 : STD_LOGIC; -- Indica se o contador Vertical está com o valor R+S+P+Q SIGNAL VSync_Next : STD_LOGIC; -- Valor de V_SYNC no próximo pulso de clock SIGNAL VSync_Prior : STD_LOGIC; -- Último valor atribuído em V_SYNC SIGNAL VDataOn_Next : STD_LOGIC; -- Indica se o contador Vertical está na região ativa SIGNAL VDataOn_Prior : STD_LOGIC; -- Último valor atribuído em VDataOn SIGNAL VCount_Next : STD_LOGIC_VECTOR(9 DOWNTO 0); --próximo valor do contador Vertical SIGNAL VCount_Prior : STD_LOGIC_VECTOR(9 DOWNTO 0); --valor atual do contador Vertical
Com base nos valores dos contadores, horizontal e vertical, é mostrado na listagem abaixo a identificação dos parâmetros B, C, D, E, P, Q, R e S.
--============================================= --COMPARADORES --============================================= --CONTADOR = D (800) H_CMP1 <= '1' WHEN HCount_Prior = 799 ELSE '0'; --CONTADOR = D + E (D + E = 800 + 56 = 856) H_CMP2 <= '1' WHEN HCount_Prior = 855 ELSE '0'; --CONTADOR = D + E + B (D + E + B = 800 + 56 + 120 = 976) H_CMP3 <= '1' WHEN HCount_Prior = 975 ELSE '0'; --CONTADOR = D + E + B + C = (D + E + B + C = 800 + 56 + 120 + 64 = 1040) H_CMP4 <= '1' WHEN HCount_Prior = 1039 ELSE '0'; --CONTADOR = R 600 V_CMP1 <= '1' WHEN VCount_Prior = 599 ELSE '0'; --CONTADOR >= R + S 600 + 37 = 637 V_CMP2 <= '1' WHEN VCount_Prior = 636 ELSE '0'; --CONTADOR = R + S + P = 600 + 37 + 6 = 643 V_CMP3 <= '1' WHEN VCount_Prior = 642 ELSE '0'; --CONTADOR = R + S + P + Q = 600 + 37 + 6 + 23 = 666 V_CMP4 <= '1' WHEN VCount_Prior = 665 ELSE '0';
Esses sinais indicam se uma das condições (parâmetros) foi alcançada e são utilizados para determinar o próximo estado dos sinais de sincronismo.
--=============================================
--VALORES DE ENTRADA DOS FFD
--=============================================
--Sincronização - Horizontal
-- HSYNC = 0 após 856 pulsos e permanece em zero até 976 pulsos
HSync_Next <= '0' WHEN H_CMP2 = '1' ELSE --Reset
'1' WHEN H_CMP3 = '1' ELSE --Set
HSync_Prior; --Memória
--Sincronização Vertical
--VSYNC = 0 após 637 pulsos e permanece em zero até 643 pulsos
VSync_Next <= '0' WHEN V_CMP2 = '1' ELSE --Reset
'1' WHEN V_CMP3 = '1' ELSE --Set
VSync_Prior; --Memória
--Região Ativa - Horizontal
HDataOn_Next <= '0' WHEN H_CMP1 = '1' ELSE --Reset
'1' WHEN H_CMP4 = '1' ELSE --Set
HDataOn_Prior; --Memória
--Região Ativa - Vertical
VDataOn_Next <= '0' WHEN V_CMP1 = '1' ELSE --Reset
'1' WHEN V_CMP4 = '1' ELSE --Set
VDataOn_Prior; --Memória
Na listagem abaixo é mostrada a lógica para incrementar/reiniciar os contadores.
--=============================================
--CONTADORES
--=============================================
--Contador - Horizontal
-- O contador é reiniciado após 1040 pulsos
HCount_Next <= (others => '0') WHEN H_CMP4 = '1' ELSE
HCount_Prior + 1;
-- Contador - Vertical
-- O contador é reiniciado após 666 pulsos
-- O contador é incrementado somente após a finalização de uma linha
VCount_Next <= (others => '0') WHEN V_CMP4 = '1' ELSE
VCount_Prior + 1 WHEN H_CMP4 = '1' ELSE
VCount_Prior;
Esses sinais apenas determinam qual será o valor futuro, sendo necessário atualizar o estado atual. No processo listado abaixo, sempre que ocorre uma transição de borda de subida no clock os sinais de controle são atualizados.
--============================================= --Atualiza o sinal de saída dos FFD conforme o --sinal de Clock/Reset --============================================= PROCESS(F_CLOCK, RESET) BEGIN IF (RESET = '0') THEN HCount_Prior <= (others => '0'); VCount_Prior <= (others => '0'); HSync_Prior <= '0'; VSync_Prior <= '0'; HDataOn_Prior <= '0'; VDataOn_Prior <= '0'; ELSIF RISING_EDGE(F_CLOCK) THEN --Contadores HCount_Prior <= HCount_Next; VCount_Prior <= VCount_Next; --Sinais de sincronismo HSync_Prior <= HSync_Next; VSync_Prior <= VSync_Next; HDataOn_Prior <= HDataOn_Next; VDataOn_Prior <= VDataOn_Next; END IF; END PROCESS;
Por fim, esses sinais devem ser atribuídos na saída do bloco de sincronismo. A listagem abaixo mostra a atribuição dos sinais de controle.
--============================================= --SINAIS DE CONTROLE DO MÓDULO VGA --============================================= F_HSYNC <= HSync_Prior; F_VSYNC <= VSync_Prior; F_ROW <= VCount_Prior; F_COLUMN <= HCount_Prior; F_DISP_ENABLE <= HDataOn_Prior AND VDataOn_Prior;
O código completo do bloco de sincronismo VGA é mostrado abaixo.
LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY VGASync IS PORT( RESET : IN STD_LOGIC; -- Entrada para reiniciar o estado do controlador F_CLOCK : IN STD_LOGIC; -- Entrada de clock (50 MHz) F_HSYNC : OUT STD_LOGIC; -- Sinal de controle VGA: H_SYNC F_VSYNC : OUT STD_LOGIC; -- Sinal de controle VGA: V_SYNC F_ROW : OUT STD_LOGIC_VECTOR(9 DOWNTO 0); -- Índice da linha que está sendo processada F_COLUMN : OUT STD_LOGIC_VECTOR(10 DOWNTO 0); -- Índice da coluna que está sendo processada F_DISP_ENABLE : OUT STD_LOGIC --Indica a região ativa do frame ); END ENTITY VGASync; ARCHITECTURE arch OF VGASync IS -- Sinais de controle Horizontal SIGNAL H_CMP1 : STD_LOGIC; -- Indica que o contador Horizontal está com o valor D SIGNAL H_CMP2 : STD_LOGIC; -- Indica que o contador Horizontal está com o valor D+E SIGNAL H_CMP3 : STD_LOGIC; -- Indica que o contador Horizontal está com o valor D+E+B SIGNAL H_CMP4 : STD_LOGIC; -- Indica que o contador Horizontal está com o valor D+E+B+C SIGNAL HSync_Next : STD_LOGIC; -- Valor de H_SYNC no próximo pulso de clock SIGNAL HSync_Prior : STD_LOGIC; -- Último valor atribuído em H_SYNC SIGNAL HDataOn_Next : STD_LOGIC; -- Indica que o contador Horizontal está na região ativa SIGNAL HDataOn_Prior : STD_LOGIC; -- Último valor atribuído em HDataOn SIGNAL HCount_Next : STD_LOGIC_VECTOR(10 DOWNTO 0); --próximo valor do contador Horizontal SIGNAL HCount_Prior : STD_LOGIC_VECTOR(10 DOWNTO 0); --valor atual do contador Horizontal -- Sinais de controle Vertical SIGNAL V_CMP1 : STD_LOGIC; -- Indica que o contador Vertical está com o valor R SIGNAL V_CMP2 : STD_LOGIC; -- Indica que o contador Vertical está com o valor R+S SIGNAL V_CMP3 : STD_LOGIC; -- Indica que o contador Vertical está com o valor R+S+P SIGNAL V_CMP4 : STD_LOGIC; -- Indica que o contador Vertical está com o valor R+S+P+Q SIGNAL VSync_Next : STD_LOGIC; -- Valor de V_SYNC no próximo pulso de clock SIGNAL VSync_Prior : STD_LOGIC; -- Último valor atribuído em V_SYNC SIGNAL VDataOn_Next : STD_LOGIC; -- Indica que o contador Vertical está na região ativa SIGNAL VDataOn_Prior : STD_LOGIC; -- Último valor atribuído em VDataOn SIGNAL VCount_Next : STD_LOGIC_VECTOR(9 DOWNTO 0); --próximo valor do contador Vertical SIGNAL VCount_Prior : STD_LOGIC_VECTOR(9 DOWNTO 0); --valor atual do contador Vertical BEGIN --============================================= --SINAIS DE CONTROLE DO MÓDULO VGA --============================================= F_HSYNC <= HSync_Prior; F_VSYNC <= VSync_Prior; F_ROW <= VCount_Prior; F_COLUMN <= HCount_Prior; F_DISP_ENABLE <= HDataOn_Prior AND VDataOn_Prior; --============================================= --Atualiza o sinal de saída dos FFD conforme o --sinal de Clock/Reset --============================================= PROCESS(F_CLOCK, RESET) BEGIN IF (RESET = '0') THEN HCount_Prior <= (others => '0'); VCount_Prior <= (others => '0'); HSync_Prior <= '0'; VSync_Prior <= '0'; HDataOn_Prior <= '0'; VDataOn_Prior <= '0'; ELSIF RISING_EDGE(F_CLOCK) THEN --Contadores HCount_Prior <= HCount_Next; VCount_Prior <= VCount_Next; --Sinais de sincronismo HSync_Prior <= HSync_Next; VSync_Prior <= VSync_Next; HDataOn_Prior <= HDataOn_Next; VDataOn_Prior <= VDataOn_Next; END IF; END PROCESS; --============================================= --CONTADORES --============================================= --Contador - Horizontal -- O contador é reiniciado após 1040 pulsos HCount_Next <= (others => '0') WHEN H_CMP4 = '1' ELSE HCount_Prior + 1; -- Contador - Vertical -- O contador é reiniciado após 666 pulsos -- O contador é incrementado somente após a finalização de uma linha VCount_Next <= (others => '0') WHEN V_CMP4 = '1' ELSE VCount_Prior + 1 WHEN H_CMP4 = '1' ELSE VCount_Prior; --============================================= --COMPARADORES --============================================= --CONTADOR = D (800) H_CMP1 <= '1' WHEN HCount_Prior = 799 ELSE '0'; --CONTADOR = D + E (D + E = 800 + 56 = 856) H_CMP2 <= '1' WHEN HCount_Prior = 855 ELSE '0'; --CONTADOR = D + E + B (D + E + B = 800 + 56 + 120 = 976) H_CMP3 <= '1' WHEN HCount_Prior = 975 ELSE '0'; --CONTADOR = D + E + B + C = (D + E + B + C = 800 + 56 + 120 + 64 = 1040) H_CMP4 <= '1' WHEN HCount_Prior = 1039 ELSE '0'; --CONTADOR = R 600 V_CMP1 <= '1' WHEN VCount_Prior = 599 ELSE '0'; --CONTADOR = R + S 600 + 37 = 637 V_CMP2 <= '1' WHEN VCount_Prior = 636 ELSE '0'; --CONTADOR = R + S + P = 600 + 37 + 6 = 643 V_CMP3 <= '1' WHEN VCount_Prior = 642 ELSE '0'; --CONTADOR = R + S + P + Q = 600 + 37 + 6 + 23 = 666 V_CMP4 <= '1' WHEN VCount_Prior = 665 ELSE '0'; --============================================= --VALORES DE ENTRADA DOS FFD --============================================= --Sincronização - Horizontal -- HSYNC = 0 após 856 pulsos e permanece em zero até 976 pulsos HSync_Next <= '0' WHEN H_CMP2 = '1' ELSE --Reset '1' WHEN H_CMP3 = '1' ELSE --Set HSync_Prior; --Memória --Sincronização Vertical --VSYNC = 0 após 637 pulsos e permanece em zero até 643 pulsos VSync_Next <= '0' WHEN V_CMP2 = '1' ELSE --Reset '1' WHEN V_CMP3 = '1' ELSE --Set VSync_Prior; --Memória --Região Ativa - Horizontal HDataOn_Next <= '0' WHEN H_CMP1 = '1' ELSE --Reset '1' WHEN H_CMP4 = '1' ELSE --Set HDataOn_Prior; --Memória --Região Ativa - Vertical VDataOn_Next <= '0' WHEN V_CMP1 = '1' ELSE --Reset '1' WHEN V_CMP4 = '1' ELSE --Set VDataOn_Prior; --Memória END ARCHITECTURE arch;
Para gerar os pixels será criado um novo bloco. Esse bloco utiliza a posição do pixel (linha e coluna) e um sinal que indica se a varredura está na região ativa para determinar os sinais RGB. Para o exemplo, foi testado o valor da coluna de forma que a cada 100 pixels será exibida uma combinação de cores.
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY PixelGen IS
PORT(
RESET : IN STD_LOGIC; -- Entrada para reiniciar o estado do controlador
F_CLOCK : IN STD_LOGIC; -- Entrada de clock (50 MHz)
F_ON : IN STD_LOGIC; --Indica a região ativa do frame
F_ROW : IN STD_LOGIC_VECTOR(9 DOWNTO 0); -- Índice da linha que está sendo processada
F_COLUMN : IN STD_LOGIC_VECTOR(10 DOWNTO 0); -- Índice da coluna que está sendo processada
R_OUT : OUT STD_LOGIC; -- Componente R
G_OUT : OUT STD_LOGIC; -- Componente G
B_OUT : OUT STD_LOGIC -- Componente B
);
END ENTITY PixelGen;
ARCHITECTURE arch OF PixelGen IS
SIGNAL RGBp : STD_LOGIC_VECTOR(2 DOWNTO 0); -- Valor atual do pixel
SIGNAL RGBn : STD_LOGIC_VECTOR(2 DOWNTO 0); -- Último valor definido
BEGIN
-- Cada componente deve ser ativada somente se o frame estiver na região ativa
R_OUT <= RGBp(2) AND F_ON;
G_OUT <= RGBp(1) AND F_ON;
B_OUT <= RGBp(0) AND F_ON;
-- Define um novo valor RGB de acordo com índice da coluna
RGBn <= "000" WHEN F_COLUMN = "0000000000" ELSE -- Preto (Coluna = 0)
"001" WHEN F_COLUMN = "0001100100" ELSE -- Azul (Coluna = 100)
"010" WHEN F_COLUMN = "0011001000" ELSE -- Verde (Coluna = 200)
"011" WHEN F_COLUMN = "0100101100" ELSE -- Ciano (Coluna = 300)
"100" WHEN F_COLUMN = "0110010000" ELSE -- Vermelho (Coluna = 400)
"101" WHEN F_COLUMN = "0111110100" ELSE -- Magenta (Coluna = 500)
"110" WHEN F_COLUMN = "1001011000" ELSE -- Amarelo (Coluna = 600)
"111" WHEN F_COLUMN = "1010111100" ELSE -- Branco (Coluna = 700)
RGBp; --Último valor definido
PROCESS(F_CLOCK, RESET)
BEGIN
IF (RESET = '0') THEN
RGBp <= (others => '0');
ELSIF RISING_EDGE(F_CLOCK) THEN
RGBp <= RGBn;
END IF;
END PROCESS;
END ARCHITECTURE arch;
A listagem abaixo mostra a declaração dos dois blocos dentro de um terceiro arquivo. Neste arquivo são estabelecidas as conexões entre os módulos e o acionamento dos pinos de saída da DE0-Nano.
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY DE0_NANO IS
PORT (
CLOCK_50 : IN STD_LOGIC;
Reset : IN STD_LOGIC;
H_SYNC : OUT STD_LOGIC;
V_SYNC : OUT STD_LOGIC;
R : OUT STD_LOGIC;
G : OUT STD_LOGIC;
B : OUT STD_LOGIC);
END DE0_NANO;
ARCHITECTURE arch OF DE0_NANO IS
COMPONENT VGASync IS
PORT(
RESET : IN STD_LOGIC;
F_CLOCK : IN STD_LOGIC;
F_HSYNC : OUT STD_LOGIC;
F_VSYNC : OUT STD_LOGIC;
F_ROW : OUT STD_LOGIC_VECTOR(9 DOWNTO 0);
F_COLUMN : OUT STD_LOGIC_VECTOR(10 DOWNTO 0);
F_DISP_ENABLE : OUT STD_LOGIC
);
END COMPONENT VGASync;
COMPONENT PixelGen IS
PORT(
RESET : IN STD_LOGIC;
F_CLOCK : IN STD_LOGIC;
F_ON : IN STD_LOGIC;
F_ROW : IN STD_LOGIC_VECTOR(9 DOWNTO 0);
F_COLUMN : IN STD_LOGIC_VECTOR(10 DOWNTO 0);
R_OUT : OUT STD_LOGIC;
G_OUT : OUT STD_LOGIC;
B_OUT : OUT STD_LOGIC
);
END COMPONENT PixelGen;
--Índice da linha/coluna atual
SIGNAL CURRENT_ROW : STD_LOGIC_VECTOR(9 DOWNTO 0);
SIGNAL CURRENT_COLUMN : STD_LOGIC_VECTOR(10 DOWNTO 0);
SIGNAL DISP_ENABLE : STD_LOGIC;
BEGIN
--Módulo de sincronismo
VGA : VGASync PORT MAP(
RESET => RESET,
F_CLOCK => CLOCK_50,
F_HSYNC => H_SYNC,
F_VSYNC => V_SYNC,
F_ROW => CURRENT_ROW,
F_COLUMN => CURRENT_COLUMN,
F_DISP_ENABLE => DISP_ENABLE);
--Módulo para gerar os pixels
PIXELS : PixelGen PORT MAP(
RESET => RESET,
F_CLOCK => CLOCK_50,
F_ON => DISP_ENABLE,
F_ROW => CURRENT_ROW,
F_COLUMN => CURRENT_COLUMN,
R_OUT => R,
G_OUT => G,
B_OUT => B);
END arch;
Na Figura 2 são mostrados os dois módulos do controlador VGA.
Os pinos do FPGA que foram utilizados como entrada e saída do controlador são mostrados na Figura 3.
Testando o controlador
Neste exemplo, as saídas definidas do controlador estão localizadas nos pinos 2, 4, 6, 8 e 10 do conector GPIO1 (JP2). Os três pinos definidos para os canais R, G e B estão conectados a resistores de 1kΩ (vide Figura 4).
Na Figura 5 é mostrada a forma de onda do sinal H_SYNC. O intervalo determinado pelo cursor indica o período em que o sinal H_SYNC permanece em zero. Visto que o sinal H_SYNC permanece em zero durante 120 pulsos, o tempo é de 2,4 us.
Abaixo é mostrado o tempo do frame correspondente às regiões C, D e E, isto é, o tempo do frame após o pulso H_SYNC.
O tempo total de uma linha é de 1040 pulsos de clock. Na Figura 7 é mostrado o tempo de varredura de uma linha que corresponde a 20,8 us, isto é, a frequência de varredura da linha é de aproximadamente 48,07 kHz.
Na tabela 2 é mostrado o período de cada parâmetro de uma linha e o tempo total de varredura horizontal.
| Pixels (pulsos de clock) | Tempo (microssegundos) | |
| H_SYNC | 120 | 2,4 |
| Front porch | 56 | 1,12 |
| Back porch | 64 | 1,28 |
| Região ativa | 800 | 16 |
| Linha completa | 1040 | 20,08 |
Sabendo que o tempo de varredura de uma linha é de 20,8 us, o tempo total de varredura de todas as linhas do quadro é de 12,48 ms. O tempo total do quadro pode ser obtido somando o tempo das outras regiões de sincronismo vertical (Tabela 3).
| Linhas | Tempo (milissegundos) – 20,8 us x linhas | |
| V_SYNC | 6 | 0,1248 |
| Front porch | 37 | 0,7696 |
| Back porch | 23 | 0,4784 |
| Região ativa | 600 | 12,48 |
| Frame completo | 666 | 13,8528 |
Da Tabela 3 temos o tempo total do frame que é de aproximadamente 13,85 ms, isto é, a frequência de atualização do quadro é próxima de 72,2 Hz. Na Figura 8 é mostrada a frequência do frame detectado pelo monitor.
Conclusão
Nesta série de artigos foi apresentada uma breve descrição sobre o padrão VGA. A partir da caracterização deste padrão tão conhecido, procurou-se definir o escopo de um controlador bem simples. Com base em dois módulos principais, foi elaborado um projeto utilizando a linguagem VHDL e a placa de desenvolvimento DE0-Nano.
A simplicidade do controlador construído facilita a compreensão do padrão VGA. Um modelo simples como o descrito neste artigo pode ser construído utilizando poucos recursos lógicos, como portas AND (comparadores), contadores e flip-flops.
Fica demonstrado, de forma simples, como exibir uma imagem em um monitor VGA!
Para saber mais
Para aprender mais sobre FPGA confira os artigos de André Prado:
- FPGA;
- Introdução a FPGA em Slides;
- VHDL Básico: Parte 1 – Entidade;
- VHDL Básico: Parte 2 – Arquitetura.
Confira também os artigos do Thiago Lima sobre a DE0-Nano:
Referências
– Tabela 1: https://martin.hinner.info/vga/timing.html
– Imagem destacada: https://en.wikipedia.org/wiki/Mode_13h#/media/File:VGA_palette_with_black_borders.svg













muito obrigado amigo, me ajudou bastante!
Olá, Anderson.
Obrigado pelo retorno!
Sensacional! Tu tem alguma indicação de material desse mesmo assunto, porém voltado para a descrição de hardware em Verilog?