如何计算FPGA spartan板上的按下的键?[英] How to count pressed keys on FPGA spartan board

本文是小编为大家收集整理的关于如何计算FPGA spartan板上的按下的键?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我使用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个段中显示反值,并在两行LED矩阵中显示0和1,但是在计数器上存在问题,我认为问题是" key_push"或" rk",当我在我的情况下多次改变按键.

如何为按键创建计数器?

推荐答案

您目睹的效果称为开关的"弹跳". 您需要"审问"外部输入.


如何同步外部输入

外部输入与内部时钟域不同步.因此,在设置内或保持寄存器时间内的信号边缘可能会导致标准化.您需要使用同步器将输入与时钟域同步. a 两阶段的同步器通常足够.

示例代码:

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;

拒绝信号

假设输入是时钟同步的(或同步,如上所述).您可以通过确保长时间稳定来对信号进行审问. IE.按下按钮时启动计数器并在计数器达到值时向前转发输入.

示例代码:

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;

链接

这里有一些链接,还有其他示例:

本文地址:https://www.itbaoku.cn/post/2090949.html

问题描述

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 ?

推荐答案

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: