sábado, 19 de julho de 2014

[FPGA] Tutorial 4 - LEDs pulsantes em VHDL

Olá pessoal,
Estamos aqui mais uma vez para mostrar um outro pequeno projeto feito com a DE0 Board, os LEDs pulsantes. Esse efeito consiste em aumentar a intensidade do brilho de cada LED da placa gradativamente e, ao atingir o brilho máximo, diminuir. Se você não conhece e/ou não entendeu, é só checar o vídeo abaixo.



A primeira pergunta que deve surgir na sua cabeça para criar esse efeito provavelmente é: como controlar a intensidade do brilho de um LED usando apenas lógica digital, aonde não existe meio termo (ligado/desligado, zero ou um)?

Primeiramente, para responder essa pergunta temos que arremeter a um efeito chamado persistência da visão. Esse efeito faz com que luz capturada (vista) pelo olho humano fique "presa" na retina dos nossos olhos um pouco mais de tempo. Caso esteja interessado em ler mais sobre isso, o wikipédia explica.

OK, entendi... a luz fica na retina um pouco mais de tempo, mas e dai? 
Bom, graças a isso, podemos controlar a intensidade dos LEDs desligando e ligando eles em diferentes "velocidades". Por exemplo, se em 20 mili-segundos eu deixo todos os LEDs ligados, o brilho deles nesse período de tempo é constante e máximo. Se dentro dos próximos 20 mili-segundos eu consigo desligar por 10 mili-segundos e ligar pelos restantes 10 mili-segundos a impressão para quem vê é de que o brilho foi atenuado. 

É basicamente isso, ligar e desligar em diferentes tempos de forma que tenhamos uma "média" como resultado para o brilho. Em eletrônica essa técnica é conhecida como PWM (do inglês, Pulse-Width Modulation), não entrarei em detalhes sobre PWM porque não interfere muito no resultado e levaria muuuito tempo para cobrir todo o assunto, mas se você deseja saber mais, basta pesquisar.


O projeto.

Crie um novo projeto no Quartus II (no caso, v. 13.1) e complete com todas as informações necessárias. 

Nota: qualquer dificuldade em como criar um projeto, pin assignments ou qualquer outra coisa relacionada a criação do projeto, recorra aos tutoriais antigos.

Figura 1: Criando um novo projeto.

Crie 3 novos arquivos VHDL. Um chamado fading_led.vhd, outro fading_freq.vhd e fading_pwm.vhd.

Como foi dito, estaremos usando três arquivos VHDL, cada arquivo terá uma função: o arquivo _freq.vhd irá ser o divisor de frequência e o _pwm.vhd fará o controle do liga/desliga dos leds. Esses arquivos devem ser conectados e para que isso seja possível, o uso do COMPONENT é indispensável. O component diz como as portas são usadas e conectadas. 
O component é declarado dentro do architecture e inicializados pelo PORT MAP, como estaremos vendo a seguir.

fading_freq.vhd

-- inicialização das bibliotecas
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
-- criacao da entidade fading_freq
ENTITY fading_freq IS
-- contador para div. de freq. aprox. 8 miliseg.
GENERIC(TEMPO: INTEGER := 400000);
-- entradas/saidas
PORT(
-- clock de entrada
CLK: IN STD_LOGIC;
-- indica a porcentagem de tempo q fica ligado
CONT: OUT INTEGER RANGE 0 TO 100
);
END fading_freq;

ARCHITECTURE behavioral OF fading_freq IS
SIGNAL temporal: INTEGER RANGE 0 TO TEMPO;
-- 0 = fade in | 1 = fade out
SIGNAL fade_inout: STD_LOGIC := '0'; 
-- valor de saida de CONT
SIGNAL cnt: INTEGER RANGE 0 TO 100;
BEGIN
CONT <= cnt;
PROCESS(CLK)
BEGIN
-- a cada borda de subida do clock
IF rising_edge(CLK) THEN
-- se o TEMPO for alcançado
IF (temporal = TEMPO) THEN
temporal <= 0;
-- se cnt = 99%
IF (cnt = 99) THEN
-- executa fade out
fade_inout <= '1';
-- se cnt = 1%
ELSIF (cnt = 1) THEN
-- executa fade in
fade_inout <= '0';
END IF;
-- fade in, incrementa tempo ligado
-- fade out, decrementa.
CASE fade_inout IS
WHEN '0' => cnt <= cnt + 1;
WHEN '1' => cnt <= cnt - 1;
END CASE;
-- continua contando TEMPO
ELSE
temporal <= temporal + 1;
END IF;
END IF;
END PROCESS;
END behavioral;

________________________

fading_pwm.vhd

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;

ENTITY fading_pwm IS
-- entradas e saidas
PORT(
-- clock, 
CLK: IN STD_LOGIC;
-- valor do contador vindo do divisor de freq
CONTADOR: IN INTEGER RANGE 0 TO 100;
-- saida para os LEDs
PWM_SAIDA: OUT STD_LOGIC_VECTOR (9 downto 0)
);
END fading_pwm;

ARCHITECTURE behavioral OF fading_pwm IS
-- contador pwm para comparar com contador do divisor
SIGNAL pwm_cont: INTEGER RANGE 0 TO 100;
-- ligar/desligar os LEDs
SIGNAL pwm_signal: STD_LOGIC := '0';
BEGIN
PROCESS(CLK)
BEGIN
-- na borda de subida do clock
IF rising_edge(CLK) THEN
-- se o contado chegou no máximo
IF (pwm_cont = 99) THEN
-- zera
pwm_cont <= 0;
-- senão, conta...
ELSE
pwm_cont <= pwm_cont + 1;
END IF;
END IF;
-- se o contador pwm for menor que o contador do divisor
IF(pwm_cont < CONTADOR) THEN
-- liga o led
pwm_signal <= '1';
-- senao, desliga
ELSE 
pwm_signal <= '0';
END IF;
-- sao 10 LEDs com o valor de pwm_signal
PWM_SAIDA <= (OTHERS => pwm_signal);
END PROCESS;
END behavioral;
________________________

fading_led.vhd

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
-- entity fading_led
ENTITY fading_led IS
PORT(
-- LEDs da placa são saidas
LEDG: OUT STD_LOGIC_VECTOR (9 downto 0);
-- e o clock da placa, entrada.
CLOCK_50:IN STD_LOGIC
);
END fading_led;

-- aqui serão declaradas e inicializados os components
ARCHITECTURE behavioral OF fading_led IS
-- o component para o primeiro entity, _freq
-- eh igual ao entity fading_freq
COMPONENT fading_freq IS
PORT(
CLK: IN STD_LOGIC;
CONT: OUT INTEGER RANGE 0 TO 100
);
END COMPONENT;
-- o component para o segundo entity, _pwm
-- eh igual ao entity fading_pwm
COMPONENT fading_pwm IS
PORT(
CLK: IN STD_LOGIC;
CONTADOR: IN INTEGER RANGE 0 TO 100;
PWM_SAIDA: OUT STD_LOGIC_VECTOR (9 downto 0)
);
END COMPONENT;
-- inicializa um sinal para o contador passar
-- de fading_freq para fading_pwm
SIGNAL cnt: INTEGER RANGE 0 TO 100;
BEGIN
-- inicialização dos components usando PORT MAP
-- cnt passa por fading_freq e fading_pwm
fading_freq_i: fading_freq PORT MAP(CLOCK_50, cnt);
fading_pwm_i: fading_pwm PORT MAP(CLOCK_50, cnt, LEDG);
END behavioral;

________________________

O pin assignment é bem simples, utilizaremos apenas os LEDs verdes como saída e o clock da placa como entrada (chamados de LEDG e CLOCK_50).


Se você deseja mudar o tempo, basta brincar com a constante TEMPO criada no arquivo fading_freq.vhd. Para calcular o valor é simples. 
A placa possui um clock de 50Mhz, então, se você deseja 60Hz.


TEMPO = 50 000 000/ 60 
TEMPO ~ 833 333.

Assim você terá um tempo de 1/60 segundos. Ou seja, 16.67ms.
Também é recomendável estudar um pouco sobre PWM, essa técnica de modulação é muito importante para controle dos mais imagináveis e inimagináveis procedimentos. Realmente muito importante, bem relevante. 

É basicamente isso, até a próxima :D