Contador de segundos em VHDL com 7 segmentos 4 dígitos

Um contador de segundos é um bom exemplo para explorarmos alguns pontos do desenvolvimento de uma descrição de hardware. O contador aqui desenvolvido está em formato decimal (0000~9999).

O projeto foi desenvolvido com o kit de desenvolvimento Cyclone IV, disponível no mercado nacional, os detalhes deste kit podem ser visto no artigo Placa de FPGA com Cyclone IV.

A figura abaixo traz a representação do RTL (register transfer level) do nível mais alto do projeto (top level), nele podemos notar que o projeto está dividido em seis sub módulos de três componentes diferentes.

Contador

O primeiro módulo tem como função efetuar a contagem do ciclo de clock. No momento que este atingir o valor equivalente em Hz, o contador incrementa os dígitos em uma unidade, representando que um segundo se passou e distribui os dígitos em formato BCD ao próximo módulo. Por exemplo:

valor atual = 5639

saída esperada = 0101 0110 0011 1001

A descrição do hardware pode ser vista abaixo:

library ieee ;
    use ieee.std_logic_1164.all ;
    use ieee.numeric_std.all ;

entity counter is
  port (
    clock_i     : in std_logic;
    reset_i     : in std_logic;
    -- output four digits
    digit1_o    : out std_logic_vector(3 downto 0);
    digit2_o    : out std_logic_vector(3 downto 0);
    digit3_o    : out std_logic_vector(3 downto 0);
    digit4_o    : out std_logic_vector(3 downto 0)
  ) ;
end counter ; 

architecture rtl of counter is

    -- 1 sec clock signals
    constant    value_1HZ_c         : natural := 50000000; -- frequenci clock value.
    signal      clk_1s              : std_logic;
    signal      cnt_s               :  integer   :=  0 ;

    -- signals to count every digit 0001 up to 9999
    signal      unit_counter        : integer := 0;
    signal      tens_counter        : integer := 0; 
    signal      hundred_counter     : integer := 0;
    signal      thousand_counter    : integer := 0;

begin
    
    -- process for increment 1 sec clock
    process(clock_i)
        begin
        if rising_edge(clock_i) then
                if  cnt_s = value_1HZ_c - 1 then
                    cnt_s  <= 0;
                    clk_1s <= '1';
                else
                    cnt_s  <= cnt_s + 1;
                    clk_1s <= '0';
                end if;
        end if;
    end process;

    -- process for increment unit value,
    -- reset retur all values to 0
    process(clk_1s, reset_i)
    begin
        if  reset_i = '0' then
            unit_counter     <= 0;
            tens_counter     <= 0;
            hundred_counter  <= 0;
            thousand_counter <= 0;
        else
            if rising_edge(clk_1s) then
                unit_counter <= unit_counter + 1;
                if unit_counter >= 9 then
                    unit_counter <= 0;
                    tens_counter <= tens_counter + 1;
                    if tens_counter >= 9 then
                        tens_counter <= 0;
                        hundred_counter <= hundred_counter + 1;
                        if hundred_counter >= 9 then
                            hundred_counter <= 0;
                            thousand_counter <= thousand_counter + 1;
                            if thousand_counter >= 9 then
                                thousand_counter <= 0;
                            end if;
                        end if;
                    end if;
                end if;
            end if;
        end if;
    end process;

    digit1_o <= std_logic_vector(to_unsigned(unit_counter     , digit1_o'length));
    digit2_o <= std_logic_vector(to_unsigned(tens_counter     , digit2_o'length));
    digit3_o <= std_logic_vector(to_unsigned(hundred_counter  , digit3_o'length));
    digit4_o <= std_logic_vector(to_unsigned(thousand_counter , digit4_o'length));

end architecture ;

Note que no momento que o signal unit_counter atinge o valor de 10, esta retorna para 0 e incrementa o outro signal tens_counter. O processo se repete para os demais dígitos.

O bloco possui ainda um reset, para retornar ao valor 0.

Conversor BCD para 7- segmentos

Este é um módulo básico, presente em quase todos os cursos de FPGA disponíveis. Consiste de uma entrada em BCD e uma saída em 7 segmentos.

Neste projeto, foram utilizados quatro componentes desse, um para cada dígito.

library ieee ;
    use ieee.std_logic_1164.all ;
    use ieee.numeric_std.all ;

entity bcd_seven_seg is
  port (
    clock_i : in  std_logic;
    bcd_i   : in  std_logic_vector(3 downto 0);
    seven_o : out std_logic_vector(6 downto 0)
  ) ;
end bcd_seven_seg ; 

architecture rtl of bcd_seven_seg is
begin
    process (clock_i)
    begin
        if rising_edge(clock_i)then
            case bcd_i is
                when "0000" => seven_o <= "0000001"; -- "0"     
                when "0001" => seven_o <= "1001111"; -- "1" 
                when "0010" => seven_o <= "0010010"; -- "2" 
                when "0011" => seven_o <= "0000110"; -- "3" 
                when "0100" => seven_o <= "1001100"; -- "4" 
                when "0101" => seven_o <= "0100100"; -- "5" 
                when "0110" => seven_o <= "0100000"; -- "6" 
                when "0111" => seven_o <= "0001111"; -- "7" 
                when "1000" => seven_o <= "0000000"; -- "8"     
                when "1001" => seven_o <= "0000100"; -- "9" 
                when "1010" => seven_o <= "0000010"; -- a
                when "1011" => seven_o <= "1100000"; -- b
                when "1100" => seven_o <= "0110001"; -- C
                when "1101" => seven_o <= "1000010"; -- d
                when "1110" => seven_o <= "0110000"; -- E
                when others => seven_o <= "0000001"; -- F
            end case;
        end if;
    end process;
end architecture ;

Mux 4 dígitos

O terceiro componente deste projeto está representado por um multiplexador, responsável por integrar os quatro dígitos ao display, variando o anodo, ativando um a cada ciclo de aproximadamente 330µs.

A cada ciclo a saída, o digit_o e o anode_o variam seus valores em quatro fases, sendo uma para unidade, outra para dezena, mais uma para centena e uma última para milhar.

library ieee ;
    use ieee.std_logic_1164.all ;
    use ieee.std_logic_unsigned.all ;

entity four_digit_mux is
  port (
    clock_i     : in  std_logic;
    reset_i     : in  std_logic;
    digit1_i    : in  std_logic_vector (6 downto 0);
    digit2_i    : in  std_logic_vector (6 downto 0);
    digit3_i    : in  std_logic_vector (6 downto 0);
    digit4_i    : in  std_logic_vector (6 downto 0);
    segment_o   : out std_logic_vector (6 downto 0);
    anode_o     : out std_logic_vector (3 downto 0)
  ) ;
end four_digit_mux ; 

architecture arch of four_digit_mux is
    signal period_display           : std_logic_vector (17 downto 0);
    signal led_activating_counter   : std_logic_vector (1 downto 0);
begin
    -- process add value in period_display
    process (clock_i,reset_i)
    begin
        if reset_i = '0' then
            period_display <= (others => '0');
        elsif rising_edge(clock_i)then
            period_display <=  period_display + 1;
        end if;
    end process;
    
    -- chance value each 25M/65536 = 2.6 ms
    led_activating_counter <= period_display(17 downto 16);
    process (led_activating_counter)
    begin
        case led_activating_counter is
            when "11" =>
                anode_o     <= "0111";
                segment_o   <= digit1_i;
            when "10" =>
                anode_o     <= "1011";
                segment_o   <= digit2_i;
            when "01" =>
                anode_o     <= "1101";
                segment_o   <= digit3_i;
            when others =>
                anode_o     <= "1110";
                segment_o   <= digit4_i;
        end case;
    end process;
end architecture;

Contador de 4 dígitos (top level)

O top level deste projeto não possui nenhuma lógica em sua arquitetura, apenas a instância dos componentes e sua integração através dos signals.

library ieee ;
    use ieee.std_logic_1164.all ;
    use ieee.numeric_std.all ;

entity counter_4_digt is
  port (
    clock_i  : in  std_logic;
    reset_i  : in  std_logic;
    digit_o  : out std_logic_vector(6 downto 0);
    anode_o  : out std_logic_vector(3 downto 0)
  ) ;
end counter_4_digt ; 

architecture rtl of counter_4_digt is
    signal digit1_bcd  , digit2_bcd  , digit3_bcd  , digit4_bcd     : std_logic_vector(3 downto 0);
    signal digit1_seven, digit2_seven, digit3_seven, digit4_seven   : std_logic_vector(6 downto 0);

    component counter is
    port (
        clock_i     : in  std_logic;
        reset_i     : in  std_logic;
        digit1_o    : out std_logic_vector(3 downto 0);
        digit2_o    : out std_logic_vector(3 downto 0);
        digit3_o    : out std_logic_vector(3 downto 0);
        digit4_o    : out std_logic_vector(3 downto 0)
    ) ;
    end component;

    component bcd_seven_seg is
        port (
          clock_i : in  std_logic;
          bcd_i   : in  std_logic_vector(3 downto 0);
          seven_o : out std_logic_vector(6 downto 0)
        ) ;
      end component ;

    component four_digit_mux is
        port (
          clock_i     : in  std_logic;
          reset_i     : in  std_logic;
          digit1_i    : in  std_logic_vector (6 downto 0);
          digit2_i    : in  std_logic_vector (6 downto 0);
          digit3_i    : in  std_logic_vector (6 downto 0);
          digit4_i    : in  std_logic_vector (6 downto 0);
          segment_o   : out std_logic_vector (6 downto 0);
          anode_o     : out std_logic_vector (3 downto 0)
        ) ;
    end component ; 

begin
    counter_1 : counter 
    port map ( 
        clock_i  => clock_i, 
        reset_i  => reset_i, 
        digit1_o => digit1_bcd, 
        digit2_o => digit2_bcd, 
        digit3_o => digit3_bcd, 
        digit4_o => digit4_bcd
    );

    bcd_1 : bcd_seven_seg port map (clock_i => clock_i, bcd_i => digit1_bcd, seven_o => digit1_seven);
    bcd_2 : bcd_seven_seg port map (clock_i => clock_i, bcd_i => digit2_bcd, seven_o => digit2_seven);
    bcd_3 : bcd_seven_seg port map (clock_i => clock_i, bcd_i => digit3_bcd, seven_o => digit3_seven);
    bcd_4 : bcd_seven_seg port map (clock_i => clock_i, bcd_i => digit4_bcd, seven_o => digit4_seven);

    four_digit_mux_1 : four_digit_mux 
    port map(
        clock_i     => clock_i,
        reset_i     => reset_i,
        digit1_i    => digit1_seven,
        digit2_i    => digit2_seven,
        digit3_i    => digit3_seven,
        digit4_i    => digit4_seven,
        segment_o   => digit_o,
        anode_o     => anode_o
    );
end architecture ;

Testbench e forma de onda

Abaixo pode ser visto o Test Bench desenvolvido para simular este projeto. O Testbench, traduzido literalmente como bancada de teste, é uma descrição auxiliar (mas indispensável), que permite simular o funcionamento da descrição de hardware.

library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

use std.textio.all;
use std.env.finish;

entity counter_4_digit_tb is
end counter_4_digit_tb;

architecture sim of counter_4_digit_tb is

    constant clk_hz : integer := 100e6;
    constant clk_period : time := 1 sec / clk_hz;

    signal clk : std_logic := '1';
    signal rst : std_logic := '1';
    signal digit_tb : std_logic_vector(6 downto 0);
    signal anode_tb : std_logic_vector(3 downto 0);

begin

    clk <= not clk after clk_period / 2;

    DUT : entity work.counter_4_digt(rtl)
    port map (
        clock_i => clk,
        reset_i => rst,
        digit_o => digit_tb,
        anode_o => anode_tb
    );

    SEQUENCER_PROC : process
    begin
        wait for clk_period * 2;
        rst <= '1';
    end process;

end architecture;

Podemos notar na forma de onda da simulação o fato de que a partir do 1s um dígito apresenta valor 1 e os demais permanecem como 0.

Conexão com os pinos

Abaixo podemos ver a planilha de conexão com os pinos físicos do kit de desenvolvimento.

Conclusão

Esta proposta bem simples consegue nos fornecer a ideia de como integrar componentes em VHDL. Caso queira ver mais a fundo, os links do projeto estão disponíveis neste link!

Se interessou e não sabe por onde começar? Recomendo a publicação Projetos para Começar com HDL.

Referencias

Foto de capa tirada por Akshaylals

VHDL code for Seven-Segment Display on Basys 3 FPGA

Comentários:
Notificações
Notificar
1 Comentário
recentes
antigos mais votados
Inline Feedbacks
View all comments
Domingos Tomé
Domingos Tomé
05/06/2021 04:09

Trabalho muito bom. Parabéns!

Home » Hardware » Contador de segundos em VHDL com 7 segmentos 4 dígitos

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: