Как посчитать нажатые клавиши на спартанской плате FPGA

Я использую плату FPGA Spartan 2 и хочу подсчитать нажатые клавиши с клавиатуры, это мой код VHDL:

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;

допустимы только ключи 1 и 0

Я хочу показать значение счетчика в 2 7 сегментах и ​​показать 0 и 1 в двух строках светодиодной матрицы, но есть проблема со счетчиком, я думаю, что проблема в том, что «Key_PUSH» или «RK» меняются много раз, когда я нажимаю клавишу .

Как создать счетчик нажатых клавиш?


person G3ntle_Man    schedule 18.07.2017    source источник
comment
Перекрестная публикация вопроса на нескольких сайтах SE не разрешена, как описано в здесь.   -  person    schedule 19.07.2017


Ответы (1)


Эффект, который вы наблюдаете, называется «подпрыгиванием» переключателя. Вам нужно "отремонтировать" внешний вход.


Как синхронизировать внешний вход

Внешний вход не синхронизирован с доменом внутренних часов. Таким образом, фронты сигнала в пределах времени установки или удержания регистра могут вызвать метастабильность. Вам необходимо синхронизировать ваш ввод с часовым доменом с помощью синхронизатора. Обычно достаточно двухступенчатого синхронизатора.

Пример кода:

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;

Ослабление сигнала

Предполагая, что вход синхронизирован по часам (или синхронизирован, как описано выше). Вы можете отклонить сигнал, убедившись, что он стабилен в течение длительного периода. Т.е. запускает счетчик при нажатии кнопки и пересылает ввод, когда счетчик достигает значения.

Пример кода:

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;

Как считать нажатие клавиши

Вы обнаруживаете нажатие клавиши, обнаруживая переход ввода от 0 к 1.

Пример кода:

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;

Ссылки

Вот несколько ссылок с дополнительными примерами:

person JHBonarius    schedule 18.07.2017
comment
Согласитесь, что для второй ссылки требуется дополнительный FF на входе. - person scary_jeff; 18.07.2017
comment
Помимо необходимости устранения дребезга, этот ответ не описывает, как подсчитывать нажатые клавиши. - person ; 18.07.2017
comment
@ user1155120 текст вопроса предполагает, что ключи уже подсчитываются, поэтому в ответе не нужно предлагать новый метод, просто решите очевидную проблему. По сути, название неправильное, это должно быть не то, как подсчитывать ... (в любом случае это будет закрыто как слишком широкое), а скорее проблема с многократным подсчетом ... - person Chris Stratton; 20.07.2017
comment
@ChrisStratton - Счетчик зафиксирован, это поразительный осциллятор, стробируемый KEY_PUSH. Счетчик, который он завершит, больше похож на бросок кубика, скорость приращения зависит от цепочки переноса для приращения, а конечное значение зависит от начального значения и длительности KEY_PUSH, которая здесь не описана. Ваш комментарий не поразил вас, и JHBonarius не ответил полным, несмотря на голоса за. - person ; 20.07.2017
comment
@ user1155120, к счастью, в stackoverflow вы всегда можете опубликовать лучший ответ;) - person JHBonarius; 20.07.2017
comment
Поскольку вы можете улучшить свое, если у вас есть возможность. - person ; 20.07.2017
comment
@ user1155120 счастливы сейчас? - person JHBonarius; 20.07.2017