问题描述
我使用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;
链接
这里有一些链接,还有其他示例:
- debouncing .
- 这个不错的网页,讲述了如何为fpga cope .尽管我会在输入处添加一个额外的触发器以进行适当的时钟域同步.
问题描述
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:
- 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.