Search code examples
vhdlfpgaspartan

How to count pressed keys on FPGA spartan board


I`m using FPGA Spartan 2 board and want to count the keys pressed from Keyboard this is my VHDL code :

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

ENTITY Keyboard IS
 PORT(CLOCK : IN STD_LOGIC;
      RESET : IN STD_LOGIC;
      RK : IN STD_LOGIC_VECTOR(3 DOWNTO 1);
      DE : OUT STD_LOGIC_VECTOR(3 DOWNTO 1);
      Invalid_Key : OUT STD_LOGIC := '0';
      Seg1 : OUT STD_LOGIC_VECTOR(7 Downto 0);
      Seg2 : OUT STD_LOGIC_VECTOR(7 Downto 0);
      LEDRow1 : OUT STD_LOGIC_VECTOR(7 Downto 0);
      LEDRow2 : OUT STD_LOGIC_VECTOR(7 Downto 0);       
      Key : OUT STD_LOGIC_VECTOR(0 TO 15));
END Keyboard;

Architecture Behavier OF Keyboard IS
 Signal CLK : STD_LOGIC_VECTOR(23 DOWNTO 0);
 Signal KC : STD_LOGIC_VECTOR(1 DOWNTO 0);
 Signal KEY_PUSH : STD_LOGIC_VECTOR(4 DOWNTO 0);
 Signal KeyTemp : STD_LOGIC_VECTOR(1 TO 16) := "0000000000000000";
 Signal Counter : STD_LOGIC_VECTOR(4 downto 0) := "00000";
Begin
 DE(3) <= '0';
 DE(2 DOWNTO 1) <= KC;
 KEY_PUSH <= KC & RK;

 Process(KEY_PUSH)
 begin
  Case KEY_PUSH is
   WHEN "11101" => --0
    if Counter <= 15 then
      Invalid_Key <= '0';
      Counter <= Counter + 1;
     KeyTemp(conv_integer(Counter)) <= '0';           
    else
     Invalid_Key <= '1';
    end if;
   WHEN "00110" => --1
    if Counter <= 15 then
      Invalid_Key <= '0';
     Counter <= Counter + 1;
      KeyTemp(conv_integer(Counter)) <= '1';
    else
     Invalid_Key <= '1';
    end if;

   WHEN "00101" =>
    Invalid_Key <= '1';  -- 2
   WHEN "00011" =>
    Invalid_Key <= '1';  -- 3
   WHEN "01110" =>
    Invalid_Key <= '1';  -- 4
   WHEN "01101" =>
    Invalid_Key <= '1';  -- 5
   WHEN "01011" =>
    Invalid_Key <= '1';  -- 6
   WHEN "10110" =>
    Invalid_Key <= '1';  -- 7
   WHEN "10101" =>
    Invalid_Key <= '1';  -- 8
   WHEN "10011" =>
    Invalid_Key <= '1';  -- 9
   WHEN "11011" => -- #
    Invalid_Key <= '1';  -- #   

   WHEN "11110" => -- *
     Invalid_Key <= '0';
    KeyTemp <= "0000000000000000";
    Counter <= "00000";
   WHEN OTHERS =>   
    Invalid_Key <= '0';
  End Case;

   case Counter is
    when "00000" => -- 0
     Seg1 <= "00111111";
      Seg2 <= "00111111";
    when "00001" => -- 1
     Seg1 <= "00111111";
      Seg2 <= "00000110";
    when "00010" => -- 2
     Seg1 <= "00111111";
      Seg2 <= "01011011";
    when "00011" => -- 3
     Seg1 <= "00111111";
      Seg2 <= "01001111";
    when "00100" => -- 4
     Seg1 <= "00111111";
      Seg2 <= "01100110";
    when "00101" => -- 5
     Seg1 <= "00111111";
      Seg2 <= "01101101";
    when "00110" => -- 6
     Seg1 <= "00111111";
      Seg2 <= "01111101";
    when "00111" => -- 7
     Seg1 <= "00111111";
      Seg2 <= "00100111";
    when "01000" => -- 8
     Seg1 <= "00111111";
      Seg2 <= "01111111";
    when "01001" => -- 9
     Seg1 <= "00111111";
      Seg2 <= "01101111";
    when "01010" => -- 10
     Seg1 <= "00000110";
      Seg2 <= "00111111";
    when "01011" => -- 11
     Seg1 <= "00000110";
      Seg2 <= "00000110";
    when "01100" => -- 12
     Seg1 <= "00000110";
      Seg2 <= "01011011";
    when "01101" => -- 13
     Seg1 <= "00000110";
      Seg2 <= "01001111";
    when "01110" => -- 14
     Seg1 <= "00000110";
      Seg2 <= "01100110";
    when "01111" => -- 15
     Seg1 <= "00000110";
      Seg2 <= "01101101";
    when "10000" => -- 16
     Seg1 <= "00000110";
      Seg2 <= "01111101";
    when others =>
     Seg1 <= "00000000";
     Seg2 <= "00000000";      
   end case;

  LEDRow1 <= KeyTemp(1 to 8);
  LEDRow2 <= KeyTemp(9 to 16);  

  if Counter = 16 then
   Key <= KeyTemp;
  end if;
 End Process;

 Process(CLOCK, CLK)
 begin
  IF (Clock'EVENT AND Clock='1') THEN
   Clk <= Clk + 1;
  END IF;    
 end Process;   

 Process(Reset, CLK(10))
 begin
  IF RESET = '1' THEN
   KC <= "00";
  ELSIF (CLK(10) 'EVENT AND CLK(10)='1') THEN 
   KC <= KC + 1;    
  END IF;
 end Process;   
END Behavier;

only 1 and 0 keys are acceptable

I want to show counter value in 2 7segment and show the 0 and 1s in two line of LED Matrix, but there a problem on counter, I think the problem is "Key_PUSH" or "RK" are changing many times when I press a key.

How I can create a counter for pressed keys ?


Solution

  • The effect you are witnessing is called "bouncing" of the switch. You need to "debounce" the external input.


    How to synchronize an external input

    An external input is not synchronous to the internal clock domain. Thus signal edges within the setup or hold time of a register could cause metastability. You need to synchronize your input to the clock domain using a synchronizer. A two-stage synchronizer is usually sufficient.

    Example code:

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity synchronizer is
        generic(
            nr_of_stages : natural := 2
            );
        port(
            clk : in std_logic;
            asynchronous_input : in std_logic;
            synchronous_output : out std_logic
            );
    end entity;
    
    architecture rtl of synchronizer is
        signal registers : std_logic_vector(nr_of_stages-1 downto 0);
        -- no intialization as this could give a false edge further in the chain.
    begin
        -- build the registers
        register_proc : process(clk)
        begin
            -- connect the registers end to end
            if rising_edge(clk) then
                for i in nr_of_stages-1 downto 1 loop
                    registers(i) <= registers(i-1);
                end loop;
                registers(0) <= asynchronous_input;
            end if;
        end process;
        -- connect the output to the last register
        synchronous_output <= registers(nr_of_stages-1);
    end architecture;
    

    Debouncing the signal

    Assuming the input is clock synchronous (or synchronized, as described above). You can debounce the signal by ensuring it is stable for a prolonged period. I.e. start a counter when a button is pressed and forward the input when the counter reaches a value.

    Example code:

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity debouncer is
        generic(
            clock_frequency : positive := 20e6; -- e.g. 20 MHz
            settle_time : time := 100 ms
            );
        port(
            clk : in std_logic;
            input : in std_logic;
            output : out std_logic
            );
    end entity;
    
    architecture rtl of debouncer is
        constant settle_time_in_clocks : positive := integer(real(clock_frequency) * settle_time / 1 sec); -- MHz to ms
        signal timer : natural range settle_time_in_clocks-1 downto 0 := settle_time_in_clocks-1;
    begin
        timer_proc : process(clk)
        begin
            if rising_edge(clk) then
                if input = '0' then
                    -- not asserted: reset the timer and output
                    timer <= settle_time_in_clocks-1;
                    output <= '0';
                elsif timer = 0 then
                    -- timer finished, set the output
                    output <= '1';
                else
                    -- count down
                    timer <= timer - 1;
                end if;
            end if;
        end process;
    end architecture;
    

    How to count a key press

    You detect a key press by detecting a 0-to-1 transition of the input.

    Example code:

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity kpcnt is
        port(
            clk : in std_logic;
            rst : in std_logic;
            input_from_debouncer : in std_logic -- assumed to be synchronous to clk
            -- some output to be defined
            );
    end entity;
    
    architecture rtl of kpcnt is
        signal input_delay : std_logic;
        signal input_rising_edge : std_logic;
        use ieee.numeric_std.all;
        signal kpcounter : unsigned(7 downto 0) := (others => '0');
    begin
        -- create delayed input signal
        delay_input : process(clk)
        begin
            if rising_edge(clk) then
                input_delay <= input_from_debouncer;
            end if;
        end process;
        -- detect 0->1 transition
        input_rising_edge <= '1' when input_from_debouncer = '1' and input_delay = '0' else '0';
        -- count the number of 0->1 transitions
        kpcounter_proc : process(clk)
        begin
            if rising_edge(clk) then
                if rst = '1' then
                    kpcounter <= (others => '0');
                elsif input_rising_edge = '1' then
                    kpcounter <= kpcounter + 1;
                end if;
            end if;
        end process;    
    end architecture;
    

    Links

    Here are some links with additional examples: