Reloj digital en VHDL

Reloj en VHDL

 

Si ya hemos hablado de cómo generar un reloj de 1 Hz y también hemos visto cómo controlar un display de siete segmentos con VHDL, el siguiente paso lógico es construir un circuito para un reloj digital de 24 horas. Sólo es cuestión de unir lo ya aprendido en los dos anteriores artículos y añadir algo de lógica de control.

Vamos a construir un circuito descrito con VHDL que tiene una sola entrada llamada clk50mhz, que es la señal de reloj (de 50 Mhz en este caso) que va a comandar todo el sistema. Como salida hay dos buses, uno llamado display, que obviamente se encarga de iluminar los leds correspondientes de cada display de siete segmentos, y otra salida llamada cur_display que selecciona qué display, de los cuatro posibles, se ilumina en cada momento. El código es el siguiente.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity digital_clock_top is
port (
  clk50mhz: 	in STD_LOGIC;
  display: 	out STD_LOGIC_VECTOR(7 downto 0);
  cur_display:	out STD_LOGIC_VECTOR(3 downto 0)
);
end digital_clock_top;

architecture beh of digital_clock_top is
  constant max_count: INTEGER := 25000000; -- 50000000/2
  constant max_refresh_count: INTEGER := 100000; -- 50Mzh/100000=500Hz
  signal count: INTEGER range 0 to max_count;
  signal refresh_count: INTEGER range 0 to max_refresh_count;
  signal refresh_state: STD_LOGIC_VECTOR(1 downto 0) := (others => '0');
  signal clk_state: STD_LOGIC := '0';
  signal display_sel: STD_LOGIC_VECTOR(3 downto 0) := (others => '0');
  shared variable hora1, hora2, min1, min2: INTEGER range 0 to 10 := 0;
  shared variable segundos: INTEGER range 0 to 59 := 0;

  function digito(numero:INTEGER) return STD_LOGIC_VECTOR is
	variable salida: STD_LOGIC_VECTOR(7 downto 0);
  begin
	case numero is
		when 0 => salida := "11000000"; -- 0 
		when 1 => salida := "11111001"; -- 1
		when 2 => salida := "10100100"; -- 2
		when 3 => salida := "10110000"; -- 3
		when 4 => salida := "10011001"; -- 4
		when 5 => salida := "10010010"; -- 5
		when 6 => salida := "10000011"; -- 6
		when 7 => salida := "11111000"; -- 7 
		when 8 => salida := "10000000"; -- 8
		when 9 => salida := "10010000"; -- 9
		when others => salida := "11111111";
	end case;
	return(salida);
  end digito;
	
  begin
    cur_display <= display_sel;

    gen_clock: process(clk50mhz, clk_state, count)
    begin
	if clk50mhz'event and clk50mhz='1' then
		-- contador 1HZ
		if count < max_count then 
			count <= count + 1;
		else
			clk_state <= not clk_state;
			count <= 0;
		end if;
		
		-- contador 500Hz (para refresco del display)
		if refresh_count < max_refresh_count then
			refresh_count <= refresh_count + 1;
		else
			refresh_state <= refresh_state + 1;
			refresh_count <= 0; 
                end if; 
        end if; 
    end process; 

  show_display: process(refresh_state) 
        begin -- selección del display 
            case refresh_state is 
                when "00" => display_sel <= "1110"; -- display 0 
                when "01" => display_sel <= "1101"; -- display 1 
                when "10" => display_sel <= "1011"; -- display 2 
                when "11" => display_sel <= "0111"; -- display 3 
                when others => display_sel <= "1111"; 
            end case; 
            
            -- mostrar hora 
            case display_sel is 
                when "1110" => display <= digito(hora2); -- display 0 
                when "1101" => display <= digito(hora1); -- display 1 
                when "1011" => display <= digito(min2); -- display 2 
                when "0111" => display <= digito(min1); -- display 3 
                when others => display <= "11111111"; 
	    end case;
	
	    -- parpadeo del punto
	    if display_sel="1101" then
		display(7) <= clk_state;
	    end if;
  end process;
	
  persecond: process (clk_state)
  begin
	if clk_state'event and clk_state='1' then
		
		-- contador de segundos
		if segundos < 59 then
			segundos := segundos + 1;
		else 
			segundos := 0;
			min1 := min1 + 1; -- +1 minuto
		end if;
		
		-- segundo dígito minutero
		if min1 = 10 then
			min2 := min2 + 1;
			min1 := 0;
		end if;
		
		-- primer dígito hora
		if min2 = 6 then
			hora1 := hora1 + 1;
			min2 := 0;
		end if;
		
		-- segundo dígito hora
		if hora1 = 10 then
			hora2 := hora2 + 1;
			hora1 := 0;
		end if;
			
		if hora2=2 and hora1=4 then
			hora2 := 0;
			hora1 := 0;
		end if;
	end if;
  end process;
	
end beh;

Casi todo el código ha sido ya explicado en los anteriores artículos. Se mantienen los dos contadores: el de 1 Hz que indica el paso de cada segundo, y el de 500 Hz usado para el refresco del display. Si comparas este código con los anteriores, verás que el contador de 1 Hz ahora cuenta hasta 25000000 en lugar de hasta 50000000 como en el código anterior (justo la mitad). Lo cierto es que en el código anterior creamos un reloj que cambiaba de estado cada segundo, es decir, la señal se mantenía en alto un segundo y luego en bajo otro segundo, y así sucesivamente. En otras palabras, el primer segundo empezaba a contar en el primer flanco de subida, el segundo en el siguiente flanco de bajada, y así en cada flanco de subida y de bajada. Como detectar los flancos de subida y bajada es un engorro, vamos a marcar el paso de cada segundo con el flanco de subida, así que necesitamos un contador que se active en la mitad del tiempo que necesitábamos antes. Ahora, en el tiempo en el que se produce un cambio de estado, con el nuevo contador se producen dos, por lo tanto, habrá un flanco de subida cada segundo.

Pulso de reloj

La otra parte nueva es la lógica de control de los dígitos. Se ha usado un contador para cada dígito evitando así añadir circuitos específicos para realizar operaciones matemáticas complejas. Cuando el primer contador que gestiona el primer dígito del minutero llega a 10, se vuelve a poner a cero y se suma 1 al contador que gestiona el segundo dígito del minutero, y así con el resto de dígitos. La lógico es bastante sencilla y se ve claramente en el proceso persecond del código VHDL.

Para la implementación he usado la misma placa de los artículos anteriores, basada en la FPGA EP1C3T144. Estas son las conexiones de entrada y salida en la FPGA.

 

reloj VHDL pin planner

 

 

2 Comentarios en "Reloj digital en VHDL"

  1. Omar Rodriguez Rojas | en | Responder

    Hola, en VHDL no deberías usar funciones de alto nivel como el case o if, se supone que todo el diseño digital se debe hacer a partir de bloques combinatorios y bloques secuenciales. Entiendo que el programa traduce las funciones de alto nivel a la lógica mas simple, pero ten en cuenta que no estamos escribiendo software cuando “programamos” en VHDL, estamos creando circuitos lógicos a partir de un lenguaje de descripción de hardware.

    • Alberto García | en | Responder

      De hecho, el case y el if SON lógica combinatoria que se traduce (según el caso) en, por ejemplo, un multiplexor. En realidad no es un programa, sino una descripción hardware.

      Saludos.

Deja un comentario.

Tu dirección de correo no será publicada.


*