How to count pressed keys on FPGA spartan board

2020-01-30 02:20发布

问题:

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 ?

回答1:

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:

  • Debouncing.
  • This nice webpage on how to write debounce logic for an FPGA incl code. Although I would add an extra flipflop at the input for proper clock domain synchronization.