viernes, 24 de octubre de 2014

Letrero con matriz de leds en FPGA

Se trata de obtener por pantallazos en una matriz de leds de 7 filas, 5 columnas los caracteres que se muestran: 
  Las 7 filas de la matriz, se conectan a 7 salidas de la FPGA dispuesta en la tarjeta Digilent en sus puertos de expansión.  Los cátodos de los leds, en las columnas se manejan a través de un contador de anillo de 5 bits, activo por nivel bajo.   Sólo una columna se energiza cada vez, pero al hacer la secuencia del contador de anillo muy rápidamente cada 0,01 segundo, el ojo humano, por la persistencia de la imágen en la retina, ve todas las 5 columnas como si estuvieran todas energizadas, y pareciese que todos los Leds que generan el caracter estuvieran encendidos, pero en realidad sólo se estan encendiendo columnna por columna, de acuerdo al CERO que envía el Contador de Anillo a todos los 7 leds de cada columna y aterrizarlos.  Por medio de la FPGA  se envían los UNOS para que se enciendan los leds requeridos para que vaya formando el caracter. Por ejemplo, para formar la U, inicialmente la memoria debe enviarle a los leds de la primera columna 1111110, luego 0000001 a la segunda columna al tiempo que se energiza esa columna por el contador de anillo, después eso mismo a la tercera y cuarta columna a medida que el contador de anillo se desplaza, y al final a los 0,05 segundos, cuando se energiza la quinta columna, la más a la derecha, la memoria debe enviar nuevamente 1111110,  y esto debe  hacerlo repetitivamente unas 20 veces, para que el caracter, en este caso la U se observe durante 1 segundo ( 0,05 * 20 = 1). Luego se continúa en igual forma con los otros dos caracteres, la A y la N, dejando en blanco durante 1 segundo el display, y luego seguir la secuencia  desplegando el mensaje UAN blanco, UAN blanco,.. indefinidamente.

Se utilizó una matriz miniatura 

En la implementación se utlizan 3 puertos de expansión dispuestas en la tarjeta Digilent Basys 2:
Se pueden observar en el gráfico anterior las resistencias de protección contra corto circuitos a las salidas, lo mismo que Diodos de protección ESD a tierra contra descargas electrostáticas.

Veamos la conexión de pines:

Veamos ahora el programa final en VHDL:

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

entity Letrero_UAN is
    Port ( CLOCK50MHZ : in  STD_LOGIC;
           COL : out  STD_LOGIC_VECTOR (4 downto 0);
           FIL : out  STD_LOGIC_VECTOR (6 downto 0));
end Letrero_UAN;

architecture Behavioral of Letrero_UAN is
signal Xclk: STD_LOGIC;
signal Xclk_letras: STD_LOGIC;
signal Xq  : STD_LOGIC_VECTOR (2 downto 0);
signal Xcol: STD_LOGIC_VECTOR (4 downto 0);
signal XSL : STD_LOGIC_VECTOR (1 downto 0);

component Divisor 
    Port ( CLK50Mhz : in  STD_LOGIC;
           CLK1khz   : out STD_LOGIC);
end component;

component Cont 
    Port ( Clk : in  STD_LOGIC;
             Q : out  STD_LOGIC_VECTOR (2 downto 0));
 end component;


component anillo
    Port ( Entrada : in  STD_LOGIC_VECTOR (2 downto 0);
           Salida : out  STD_LOGIC_VECTOR (4 downto 0));
end component;

component Memoria 
      Port ( Selector_letra: in STD_LOGIC_VECTOR (1 downto 0);
       Direccion : in  STD_LOGIC_VECTOR (4 downto 0);
           Datos : out  STD_LOGIC_VECTOR (6 downto 0));
end component;

component Cont_letras 
    Port ( CLK_letras : in  STD_LOGIC;
           Q : out  STD_LOGIC_VECTOR (1 downto 0));
end component;

component Reloj_letras 
    Port ( CLK50Mhz : in  STD_LOGIC;
           CLK1hz   : out STD_LOGIC);
end component;

begin
paso1: Divisor port map (CLOCK50MHZ, Xclk);
paso2: Cont    port map (Xclk, Xq); 
paso3: anillo  port map (Xq, Xcol);
paso4: Memoria port map (XSL,Xcol, FIL );
paso5: Reloj_letras port map (CLOCK50MHZ, Xclk_letras);
paso6: Cont_letras port map (Xclk_letras, XSL);

COL <= Xcol;

end Behavioral;

El esquemático RTL es el siguiente:

A continuación  el código VHDL de  cada  componente:

-- DIVISOR 1 KHZ

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;
           CLK1khz   : out STD_LOGIC);
end Divisor;

architecture Behavioral of Divisor 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;
 CLK1khz <=  pulso;
end Behavioral;

................................................................................................................................................

-- CONTADOR DEL 0 AL 4
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity Cont is
    Port ( Clk : in  STD_LOGIC;
             Q : out  STD_LOGIC_VECTOR (2 downto 0));
            
end Cont;

architecture Behavioral of Cont is
signal count: STD_LOGIC_VECTOR (2 downto 0);
begin
process (Clk) 
begin
   if count = "101" then 
      count <= "000";
   elsif Clk ='1' and Clk'event then
             count <=  count + 1;
   end if;

end process;
Q <= count;

end Behavioral;

................................................................................................................................................

-- CONTADOR DE ANILLO
-
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;


entity anillo is
    Port ( Entrada : in  STD_LOGIC_VECTOR (2 downto 0);
           Salida : out  STD_LOGIC_VECTOR (4 downto 0));
end anillo;

architecture Behavioral of anillo is

begin
process(Entrada)
begin

            case Entrada is
            when "000" => Salida <= "01111";
            when "001" => Salida <= "10111";
            when "010" => Salida <= "11011";
            when "011" => Salida <= "11101";
            when "100" => Salida <= "11110";
when others => Salida <= "00000";
         end case;

end process;


end Behavioral;

................................................................................................................................................

-- DIVISOR DE 1 HZ 

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

entity Reloj_letras is
    Port ( CLK50Mhz : in  STD_LOGIC;
           CLK1hz   : out STD_LOGIC);
end Reloj_letras;

architecture Behavioral of Reloj_letras 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;

................................................................................................................................................

-- CONTADOR PARA  DESPLEGAR LAS LETRAS UAN

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


entity Cont_letras is
    Port ( CLK_letras : in  STD_LOGIC;
           Q : out  STD_LOGIC_VECTOR (1 downto 0));
end Cont_letras;

architecture Behavioral of Cont_letras is
signal count: STD_LOGIC_VECTOR (1 downto 0);
begin
process (CLK_letras) 
begin
   if (CLK_letras = '1' and CLK_letras'event) then
      count <= count + 1;
   end if;
end process;

 Q <= count;


end Behavioral;
................................................................................................................................................

-- MEMORIA CON LAS LETRAS A DESPLEGAR

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


entity Memoria is
    Port ( Selector_letra: in STD_LOGIC_VECTOR (1 downto 0);
       Direccion : in  STD_LOGIC_VECTOR (4 downto 0);
           Datos : out  STD_LOGIC_VECTOR (6 downto 0));
end Memoria;


architecture Behavioral of Memoria is
begin

process (Selector_letra,Direccion)
begin
if Selector_letra = "00"  then


         case Direccion is   -- Letra U
            when "01111" => Datos <= "1111110";
            when "10111" => Datos <= "0000001";
            when "11011" => Datos <= "0000001";
            when "11101" => Datos <= "0000001";
when "11110" => Datos <= "1111110";
            when others => Datos <=  "0000000";
         end case;

elsif Selector_letra = "01"  then


          case Direccion is    -- Letra A
            when "01111" => Datos <= "0111111";
            when "10111" => Datos <= "1001000";
            when "11011" => Datos <= "1001000";
            when "11101" => Datos <= "1001000";
when "11110" => Datos <= "0111111";
            when others => Datos <=  "0000000";
         end case;


elsif Selector_letra = "10"  then


  case Direccion is    -- Letra N
            when "01111" => Datos <= "0111111";
            when "10111" => Datos <= "1000000";
            when "11011" => Datos <= "1000000";
            when "11101" => Datos <= "1000000";
when "11110" => Datos <= "0111111";
            when others => Datos <=  "0000000";
         end case;
-- end process ;

else 
   

  case Direccion is  -- Espacio en Blanco
            when "01111" => Datos <= "0000000";
            when "10111" => Datos <= "0000000";
            when "11011" => Datos <= "0000000";
            when "11101" => Datos <= "0000000";
when "11110" => Datos <= "0000000";
            when others => Datos <=  "0000000";
         end case;


End if;

end process ;


end Behavioral;

.....................................................................................................................................................

Fotografía: