Olá pessoal,
Mais um novo projeto com VHDL, nesse post iremos criar um simples e pequeno relógio digital utilizando VHDL.
Primeiramente, abra o Quartus II (a versão utilizada aqui é a 13.1)...
em File > New > New Quartus II Project crie um novo projeto (caso tenha alguma dúvida de como fazer, leia o primeiro tutorial)
Figura 1: Criando um novo projeto para o relógio digital.
Logo após, crie dois novos documentos VHDL File > New > VHDL File.
Dai você pergunta, por que dois? Bom, nesse tutorial a gente vai criar uma função que irá converter números inteiros em um STD_LOGIC_VECTOR com valores correspondente a sua imagem no display de 7 segmentos, e para isso, precisaremos de um novo arquivo VHDL.
Antes de começar a programar, vamos entender como funciona o display de 7 segmentos... primeiramente, vale lembrar que cada display é composto por 7 LEDs (light emitting diode). No nosso caso, o display é conectado em ânodo comum, ou seja, para ligar o LED devemos atribuir um sinal de tensão baixo (zero) e para desligar devemos atribuir um sinal alto (um). Caso o display seja ligado em cátodo comum, basta inverter a lógica.
Figura 2: Esquemático do display de 7 segmentos.
Por exemplo, para "escrever" o número 5, escreveriamos um vetor com o seguinte valor:
| g | f | e | d | c | b | a |
| 0 | 0 | 1 | 0 | 0 | 1 | 0 |
assim, "b" e "e" estariam apagados e o restante ligado.
Ou o número 9 (apenas "e" desligado):
| g | f | e | d | c | b | a |
| 0 | 0 | 1 | 0 | 0 | 0 | 0 |
Para facilitar e diminuir o nosso código criaremos uma função que irá converter os números em seus respectivos códigos para o display. Chamaremos essa função de "int2seg". int2seg deve ser criada em um arquivo contendo uma estrutura determinada, necessitaremos criar um PACKAGE. Com os PACKAGES você pode compartilhar soluções padrões para certos problemas e compartilhar entre diferentes estruturas. Para criar um PACKAGE precisamos:
- inserir as bibliotecas;
- declarar o package e o seu nome;
- criar o package body;
- criar suas funções e/ou procedures;
- inserir seu package em seu código principal.
a estrutura de um package
PACKAGE nome_do_package IS
-- DECLARA AS FUNÇÕES, CONSTANTES, SINAIS, PROCEDURES
END nome_do_package;
PACKAGE BODY nome_do_package IS
-- CRIA SUAS DECLARAÇÕES
END nome_do_package ;
Para criar a função criamos um package com nome PACOTE e dentro dele declaramos nossa função.
FUNCTION int2seg(A: INTEGER) RETURN STD_LOGIC_VECTOR;
Essa função tem como argumento um número inteiro (armazenado como A) e retorna um STD_LOGIC_VECTOR (o código correspondendo para o display de 7 segmentos)
No package body a mesma declaração é utilizada, uma variável chamada result é criada, ela armazenará o valor correto do display e será retornada com o comando RETURN
VARIABLE result: STD_LOGIC_VECTOR(6 downto 0);
BEGIN
CASE A IS
WHEN 0 => result := "1000000";
.
.
.
END CASE;
RETURN result;
END int2seg;
O comando USE insere o package no nosso arquivo com a nossa função.
USE work.PACOTE.all; -- INSERE O PACKAGE
Agora que já podemos escrever no display, precisamos fazer "o tempo correr". Isso é feito por meio do clock. A placa DE0 possui um clock interno de 50Mhz, isso significa temos um período de 20ns de duração. Ou seja, se pudermos contar de 0 até 49999999 contaremos 50000000, se cada contagem demora 20ns, contaremos um segundo. Teoricamente isso é correto, porém na prática, existe um pequeno erro (que não interfere no resultado).
A contagem será feita dentro do bloco PROCESS. Com process somos capazes de utilizar operações sequenciais como if-else, case... dentro do bloco process tudo é executado sequencialmente. Process possui uma "lista de sensibilidade" que irá disparar os eventos dentro dele. No nosso relógio, o process é executado observando o CLOCK_50, que é o clock do FPGA.
PROCESS(CLOCK_50)
O código para o relógio é encontrado abaixo.
____________________________________________________A contagem será feita dentro do bloco PROCESS. Com process somos capazes de utilizar operações sequenciais como if-else, case... dentro do bloco process tudo é executado sequencialmente. Process possui uma "lista de sensibilidade" que irá disparar os eventos dentro dele. No nosso relógio, o process é executado observando o CLOCK_50, que é o clock do FPGA.
PROCESS(CLOCK_50)
O código para o relógio é encontrado abaixo.
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
USE work.PACOTE.all;
ENTITY relogio_digital IS
GENERIC (SEG: INTEGER := 49999999); -- DEFINE O NUMERO DE CLOCKS PARA CONTAR UM SEGUNDO
PORT(
-- AQUI DEFIMOS AS ENTRADAS E SAIDAS
HEX0_D :OUT STD_LOGIC_VECTOR (6 downto 0);
HEX1_D :OUT STD_LOGIC_VECTOR (6 downto 0);
HEX2_D :OUT STD_LOGIC_VECTOR (6 downto 0);
HEX3_D :OUT STD_LOGIC_VECTOR (6 downto 0);
CLOCK_50 :IN STD_LOGIC
);
END relogio_digital;
ARCHITECTURE behavioural OF relogio_digital IS
SIGNAL TEMPORAL: INTEGER RANGE 0 TO SEG;
SIGNAL SS0: INTEGER RANGE 0 TO 59 := 0; -- SEGUNDOS
SIGNAL HH0: INTEGER RANGE 0 TO 9 := 0; --PRIMEIRO CARACTERE DA HORA
SIGNAL HH1: INTEGER RANGE 0 TO 2 := 0; --SEGUNDO CARACTERE DA HORA
SIGNAL MM0: INTEGER RANGE 0 TO 9 := 0; --PRIMEIRO CARACTERE DOS MINUTOS
SIGNAL MM1: INTEGER RANGE 0 TO 5 := 0; --SEGUNDO CARACTERE DOS MINUTOS
BEGIN
-- ATRIBUI OS VALORES DOS SINAIS PARA O DISPLAY DE 7 SEGMENTOS
HEX0_D <= int2seg(MM0);
HEX1_D <= int2seg(MM1);
HEX2_D <= int2seg(HH0);
HEX3_D <= int2seg(HH1);
PROCESS(CLOCK_50)
BEGIN
IF rising_edge(CLOCK_50) THEN -- NA BORDA DE SUBIDA
-- SE O CONTADOR NAO CHEGAR NO FIM DE 1 SEG
IF(TEMPORAL /= SEG) THEN
TEMPORAL <= TEMPORAL + 1; -- INCREMENTA
-- SENAO, RESETA O CONTADOR E ATRIBUI UM SEGUNDO
ELSE
TEMPORAL <= 0;
SS0 <= SS0 + 1;
IF(SS0 = 59) THEN -- 60 segundos eh 1 minuto
SS0 <= 0;
MM0 <= MM0 + 1;
IF(MM0 = 9) THEN -- CASO PASSE 10 minutos
MM0 <= 0; -- RESETA O DISPLAY 0
MM1 <= MM1 + 1; -- INCREMENTA O DISPLAY 1
-- 60 MINUTOS EH 1 HORA E ASSIM POR DIANTE...
IF(MM1 = 5) THEN
MM1 <= 0;
HH0 <= HH0 + 1;
IF(HH0 = 9) THEN
HH0 <= 0;
HH1 <= HH1 + 1;
ELSIF (HH0 = 3 AND HH1 = 2) THEN HH1 <= 0; HH0 <= 0; MM1 <= 0; MM0 <= 0;
END IF;
END IF;
END IF;
END IF;
END IF;
END IF;
END PROCESS;
END behavioural;
________________________________________________________
relogio_digital_pk.vhdl
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
PACKAGE PACOTE IS
FUNCTION int2seg(A: INTEGER) RETURN STD_LOGIC_VECTOR;
END PACOTE;
PACKAGE BODY PACOTE IS
FUNCTION int2seg(A: INTEGER) RETURN STD_LOGIC_VECTOR IS
VARIABLE result: STD_LOGIC_VECTOR(6 downto 0);
BEGIN
CASE A IS
WHEN 0 => result := "1000000";
WHEN 1 => result := "1111001";
WHEN 2 => result := "0100100";
WHEN 3 => result := "0110000";
WHEN 4 => result := "0011001";
WHEN 5 => result := "0010010";
WHEN 6 => result := "0000010";
WHEN 7 => result := "1011000";
WHEN 8 => result := "0000000";
WHEN 9 => result := "0010000";
WHEN OTHERS => result := (OTHERS=>'0');
END CASE;
RETURN result;
END int2seg;
END PACOTE;
________________________________________________________
Salve os arquivos e compile Processing > Start Compilation.
Figura 3: Salvando os arquivos...
Se nenhum erro for reportado, é hora de declarar a pinagem. Isso pode ser feito da maneira antiga (ensinada no tutorial 1) ou importando a pinagem da placa DE0 utilizando os arquivos dentro do DVD fornecido pela terasic junto com sua placa.
Para importar os pinos: Assignments > Import Assignments e escolha o arquivo DE0_Default.qsf (esse arquivo deve ser facilmente encontrado pela internet, caso você não possua o DVD).
Figura 4: Importando todos os pinos da placa DE0.
Compile novamente.
- Salvando seu programa na memória EEPROM.
Para que seu programa possa rodar mesmo depois da placa ser desligada precisamos primeiro declarar o tipo de EEPROM. A DE0 possui um chip Altera EPCS4. Em Assignments > Device clique em Device and Pin Options. Na nova tela marque o checkbox "use configuration device" e escolha o chip EPCS4.
Figura 5: Assignments > Device.
Figura 6: Selecionando a EEPROM.
Compile seu programa novamente.
Na sua placa selecione o modo PROG.
Figura 7: Modo PROG.
Logo após, em Tools > Programmer, selecione a opção Mode: Active Serial Programming e procure pelo arquivo com a extensão .pof.
Figura 8: Programando a placa.
Clique em open e start. Ao completar o processo, troque o modo da placa para RUN e voilà.
Figura 9: Resultado final.
- Incrementos para o relógio...
É fato que o relógio começa no 00:00 e, a não ser que você ligue ele na meia-noite, ele vai funcionar mais como um "cronômetro". Então seria um bom desafio fazer com que o usuário pudesse corrigir o valor do relógio a partir dos botões e chaves da placa. Com esse mesmo código acima eu fiz um relógio capaz de editar a hora, confira no vídeo abaixo.
É isso, qualquer dúvida é só comentar.
Amigo estou com dificuldades para editar o relógio, poderia explicar como fez?
ResponderExcluirE ai cara! Bom... a essência do código desse relógio é o mesmo que é mostrado no post! Diferenças é que, na entity, vc deve declarar os pontos do display de 7 segmentos (HEX0_DP, HEX1_DP, etc...) e os botões da sua placa.
ExcluirNa arquitetura eu fiz um signal que indicava se o relógio estava ou não em modo de edição (um flag, bem simples). Caso estivesse, entrava numa região de código que tinha um contador para contabilizar quantas vezes o usuário apertava no botão e assim selecionar o display de sete segmentos. Ao selecionar o display de sete segmentos que vc quer, um novo contador aparece para contabilizar o número de vezes que o usuário pressiona o outro botão e incrementar os valores da variável correspondente do relógio (MM0,MM1,HH0...). Espero que vc tenha entendido :)
Este comentário foi removido pelo autor.
Excluirmuito complicado.
ResponderExcluirTentei fazer, mas está dando esse erro:
ResponderExcluirError (12007): Top-level design entity "relogio_digiital" is undefined
me ajudem!!!!!!!!
Olá,
Excluirdá uma olhadinha se vc está usando o nome da entity com o mesmo nome do arquivo que vc está salvando. A prioridade é definida pelo título do arquivo, por exemplo, se sua entity principal chama-se "relogio_digiital", salve como "relogio_digiital.vhd" que ela se torna a entity "principal" (a propósito, tem 2 i's no título ai :P)
Obrigado Ícaro, era isso mesmo!!!! Deu certo!
ExcluirEste comentário foi removido pelo autor.
ResponderExcluirOlá Ícaro, teria como vc disponibilizar parte do código da edição do relógio?
ResponderExcluiro meu pacote fica dando erro, ja tentei de tudo e nada.
ResponderExcluirError (10500): VHDL syntax error at PACOTE.vhd(1) near text "FUNCTION"; expecting "entity", or "architecture", or "use", or "library", or "package", or "configuration"
Boa tarde! Teria como disponibilizar o código de pinagem que utilizou?! Não estou conseguindo utilizar as saídas no formato STD_LOGIC_VECTOR!
ResponderExcluirObrigado!