Comenzamos el diseño en nuestro proyecto que hemos llamado contador_BCD, creando una nueva fuente Contador con el codigo en VHDL de un contador BCD de 2 dígitos: decenas y unidades, el cual dispone de una entrada de reloj de 1 Hz, y de entradas adicionales sincrónicas de Clear y Enable, para poderlo reiniciar e inhabilitar cuando se requiera.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Contador is
Port ( clock : in STD_LOGIC;
Clear, Enable : in STD_LOGIC;
Dec,Uni : inout STD_LOGIC_VECTOR (3 DOWNTO 0));
end Contador;
architecture Behavioral of Contador is
begin
PROCESS(clock)
begin
if clock'event and clock = '1' then
if Clear = '1' then
Dec <= "0000"; Uni <= "0000";
elsif Enable = '1' then
if Uni = "1001" then
Uni <= "0000";
if Dec = "1001" then
Dec <= "0000";
else
Dec <= Dec + '1';
end if;
else
Uni <= Uni + '1';
end if;
end if;
end if;
end PROCESS;
end Behavioral;
Las decenas y unidades en BCD del contador se declaran en la entidad como inout (entrada/salida) y son vectores de 4 bits.
En el proceso se considera que en el flanco de subida del reloj, si la entrada Clear es de nivel alto, las decenas y unidades del contador se hacen igual a cero.
Luego se observa en el código que para que el contador trabaje se requiere que la entrada de habilitación Enable esté en nivel alto, estando la entrada Clear en nivel bajo.
Para comprender la esencia del código es importante analizar el siguiente diagrama de flujo:
Al llegar el flanco de subida del reloj de 1 Hz, si el Clear = 0, y el Enable = 1, las unidades del contador se incrementan cada segundo, y al llegar al 9, (1001) en binario, las unidades se resetean y las decenas se incrementan. Esta situación hace que el contador pase del 09 al 10, y así sucesivamente, luego pase del 19 al 20, etc. Al llegar al 99 pasará al 00.
Este código debe salvarse y sintetizarse para ser utilizado luego como componente del programa final que busca visualizar en dos de los displays de 7 segmentos disponibles en la tarjeta Basys 2 el conteo de decenas y unidades desde el 00 al 99.
Requerimos además adicionar una fuente con el código del divisor que convierte el reloj de cristal de 50 Mhz disponible en la tarjeta Basys 2 a la frecuencia de 1 Hz requerido para que el conteo de las unidades se haga cada segundo:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Divisor is
Port ( CLK50Mhz : in STD_LOGIC;
CLK1hz : out STD_LOGIC);
end Divisor;
architecture Behavioral of Divisor is
signal pulso: STD_LOGIC := '0';
signal contador: integer range 0 to 24999999 := 0;
begin
process (CLK50Mhz)
begin
if (CLK50Mhz'event and CLK50Mhz = '1') then
if (contador = 24999999) then
pulso <= NOT(pulso);
contador <= 0;
else
contador <= contador+1;
end if;
end if;
end process;
CLK1hz <= pulso;
end Behavioral;
Este código deberá guardarse y sintetizarse, asignandole módulo de nivel superior Set as Top Module para poderlo hacer.
Como ya se ha estudiado anteriormente en este código se produce un pulso de 1 segundo de periodo de reloj, por cada cincuenta millones de pulsos del cristal de 50 Mhz.
Se requiere ahora como un tercer componente utilizar un multiplexor de 2 a 1, que permita seleccionar las decenas o las unidades para enviarlas a sus displays respectivos:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Mux21 is
Port ( Decenas : in STD_LOGIC_VECTOR (3 downto 0);
Unidades : in STD_LOGIC_VECTOR (3 downto 0);
Selector : in STD_LOGIC;
Salidas : out STD_LOGIC_VECTOR (3 downto 0));
end Mux21;
architecture Behavioral of Mux21 is
begin
Salidas <= Decenas WHEN Selector = '0' ELSE
Unidades ;
end Behavioral;
Este código que debe haberse guardado y sintetizado, permite con el Selector = 0, que las Decenas del contador pasen a las Salidas, y con Selector = 1 lo hagan las unidades.
Se requiere ahora como cuarto componente sintetizar el decodificador que convierta el binario disponible en las decenas y unidades a lo exigido en los leds del display de 7 segmentos, ánodo común, para visualizar los dígitos desde el 0 hasta el 9.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Dec7segm_hex is
port( BINARIO: in STD_LOGIC_VECTOR (3 downto 0);
led: out STD_LOGIC_VECTOR(6 downto 0) );
end Dec7segm_hex;
architecture comportamiento of Dec7segm_hex is
begin
process (BINARIO)
begin
case BINARIO is
when "0000" => LED <= "0000001"; --0
when "0001" => LED <= "1001111"; --1
when "0010" => LED <= "0010010"; --2
when "0011" => LED <= "0000110"; --3
when "0100" => LED <= "1001100"; --4
when "0101" => LED <= "0100100"; --5
when "0110" => LED <= "0100000"; --6
when "0111" => LED <= "0001111"; --7
when "1000" => LED <= "0000000"; --8
when "1001" => LED <= "0001100"; --9
when "1010" => LED <= "0001000"; --A
when "1011" => LED <= "1100000"; --b
when "1100" => LED <= "0110001"; --C
when "1101" => LED <= "1000010"; --d
when "1110" => LED <= "0110000"; --E
when others => LED <= "0111000"; --F
end case;
end process ;
end comportamiento;
Aunque sólo se requiere visualizar los números del 0 al 9, adicionamos una fuente que ya se tenía, con el código dea un decodificador de 7 segmentos para el hexadecimal.
Un quinto componente indispensable para lograr la visualización dinámica que permita ver encendido los dos displays mostrando las decenas y unidades del contador es un RELOJ de 1 KHZ, que produce un periodo de 1 milisegundo. Como la persistencia de la imágen en la retina del ojo humano es de una décima de segundo, este tiempo que es cien veces mas pequeño, nos hará ver encendido el display que en la realidad está apagado.
Este periodo de 1 ms será entonces el Selector del Multiplexor de 2 a 1.
Se utiliza como reloj de 1 Khz un código análogo al del Divisor, sólo que en lugar de dividir por 50 millones, lo hace ahora por 50 mil.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity RELOJ1KHZ is
Port ( CLK50MHZ : in STD_LOGIC;
CLK1KHZ : out STD_LOGIC);
end RELOJ1KHZ;
architecture Behavioral of RELOJ1KHZ is
signal pulso: STD_LOGIC := '0';
signal contador: integer range 0 to 24999 := 0;
begin
process (CLK50Mhz)
begin
if (CLK50Mhz'event and CLK50Mhz = '1') then
if (contador = 24999) then
pulso <= NOT(pulso);
contador <= 0;
else
contador <= contador+1;
end if;
end if;
end process;
Al igual que el Multiplexor 2 a 1 debe trabajar a una frecuencia de 1 KHZ, así mismo lo deben hacer los ánodos que son los que habilitan a traves de transistores PNP a los displays de 7 segmentos.
Esto hace que se requiera un sexto componente, los Anodos de los displays, para que sincronizadamente con el multiplexor tambien esten cambiando cada milésima de segundo.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Anodos_displays is
port( input: in std_logic;
anodo: out std_logic_vector(3 downto 0));
end Anodos_displays;
architecture Behavioral of Anodos_displays is
begin
process(input)
begin
case input is
when '0' => anodo <= "0111";
when others => anodo <= "1011";
end case;
end process;
end Behavioral;
Recuerde que para que el transistor PNP se sature y por consiguiente permita que le llegue la alimentación de 3.3 voltios al ánodo de led del display debe existir un cero lógico entre base y colector del transistor. Con el 1 lógico, el display está desactivado, al estar desconectado el ánodo del display al voltaje de alimentación.
Procedemos despues de haber sintetizado los seis componentes a elaborar el código del circuito Final que obedezca el siguiente esquemático:
En este circuito final en la entidad se deben declarar como entradas el Reloj de 50 Mhz, el Clear y el Enable, y como salidas los cátodos de los displays de 7 segmentos, y los Änodos de los mismos.
Así mismo se utilizan como señales internas para interconectar los seis componentes: X1hz, XDec, XUni, X1khz, y XBin.
El código Final es el siguiente:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity final is
Port ( Reloj50mhz, Clear,Enable : in STD_LOGIC;
Anodos : out STD_LOGIC_VECTOR (3 downto 0);
catodos : out STD_LOGIC_VECTOR (6 downto 0));
end final;
architecture Behavioral of final is
signal X1hz,X1khz: std_logic;
signal XDec: STD_LOGIC_VECTOR (3 downto 0);
signal XUni: STD_LOGIC_VECTOR (3 downto 0);
signal XBin: STD_LOGIC_VECTOR (3 downto 0);
component Divisor
Port ( CLK50Mhz : in STD_LOGIC;
CLK1hz : out STD_LOGIC);
end component;
component Contador
Port ( clock : in STD_LOGIC;
Clear, Enable : in STD_LOGIC;
Dec,Uni : inout STD_LOGIC_VECTOR (3 DOWNTO 0));
end component;
component Mux21
Port ( Decenas : in STD_LOGIC_VECTOR (3 downto 0);
Unidades : in STD_LOGIC_VECTOR (3 downto 0);
Selector : in STD_LOGIC;
Salidas : out STD_LOGIC_VECTOR (3 downto 0));
end component;
component Dec7segm_hex
port( BINARIO: in STD_LOGIC_VECTOR (3 downto 0);
led: out STD_LOGIC_VECTOR(6 downto 0) );
end component;
component RELOJ1KHZ
Port ( CLK50MHZ : in STD_LOGIC;
CLK1KHZ : out STD_LOGIC);
end component;
component Anodos_displays
port( input: in std_logic;
anodo: out std_logic_vector(3 downto 0));
end component;
begin
paso1: Divisor PORT MAP (Reloj50mhz, X1hz);
paso2: Contador PORT MAP (X1hz,Clear,Enable,XDec,XUni);
paso3: Mux21 PORT MAP (XDec,XUni,X1khz,XBin);
paso4: Dec7segm_hex PORT MAP (XBin,catodos);
paso5: RELOJ1KHZ PORT MAP (Reloj50mhz, X1khz);
paso6: Anodos_displays PORT MAP (X1khz,Anodos);
end Behavioral;
Al salvar y sintetizar este código, se observa que este programa fin integra los seis componentes en seis pasos:
Para crear el archivo final.ucf hay que asignar pines como User Constrains (Restricciones de Usuario)
Al editar como texto este archivo de asignacion de pines se obtiene:
Lo que sigue es implementar el diseño, generar el archivo de programación, y una vez encendida la tarjeta Basys 2 configurarla debidamente:
Al efectuar la implementación del diseño aparece una advertencia respecto a lo demasiado angosto del pulso de 1 Hz, lo cual no es inconveniente para nuestro caso.
Luego se llama al ADEPT para la programación de la FPGA con el archivo final.bit.
Se debe verificar el excelente desempeño de la tarjeta al visualizarse en los displays el conteo desde el 00 al 99.