去抖动按钮按下导致连续状态转换
De-bounced button press resulting in successive state transitions
我正在对使用按钮作为输入信号的 FPGA 进行编程。它有一个有限状态机,有 11 个状态,使用特定的按钮按下可以从一个状态转换到另一个状态。
例如,在我的设计中,状态 s0 通过按下按钮进入状态 s1。这与从状态 s1 到 s2 和从状态 s2 到 s3 的转换情况相同。这个状态转换系统是在我的 VHDL 代码中使用 case 语句实现的。
LED 在每个状态下都会亮起,以跟踪开发板当前所处的状态。
我的问题是,当处于状态 s0 时 my_btnL = '1' 为真时,电路板显示它已移至状态 s3。
我认为正在发生的事情是,它确实会进入状态 s1 和 s2,但在状态 s0 中按下的相同按钮也会在状态 s1 和 s2 中被读取。这种情况发生得如此之快,以至于电路板没有足够的时间来显示状态 s1 和 s2 的 LED 指示。它在状态 s3 停止,因为状态 s3 使用不同的按钮移动到状态 s4。
所以我的问题是如何使按钮按下信号具有上升沿和下降沿,以便仅在一种状态下读取单个按钮按下,而不是在其后的状态下读取?
按钮信号已去抖动,但这只会使信号成为均匀的方波。
在下面的代码中,btnC、btnL、btnR……是按钮:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;
entity digital_lock is
Port (
my_btnC, clk, my_btnU, my_btnR, my_btnL, my_btnD: in std_logic;
my_sw: in std_logic_vector(3 downto 0);
hex0, hex1, hex2, hex3: out std_logic_vector (3 downto 0);
my_led: out std_logic_vector(15 downto 0)
);
end digital_lock;
architecture Behavioral of digital_lock is
type state IS (s0, s1, s2, s3, s4 ,s5 ,s6, s7, s8, s9, s10,s11);
signal my_state: state;
signal my_status: unsigned(1 downto 0);
signal num1, num2, num3, key1, key2, key3: std_logic_vector(3 downto 0);
signal number, final_key: std_logic_vector(11 downto 0);
begin
FSM: process(clk, my_btnC)
begin
if(my_btnC ='1') then
my_state <= s0;
elsif rising_edge(clk) then
case my_state is
when s0 =>
my_status <= "00";
my_led <= "1100000000000000";
hex3 <= "0000";
hex2 <= "0000";
hex1 <= "0000";
hex0 <= "0000";
if(my_btnL ='1') then
my_state <= s1;
else
my_state <= s0;
end if;
when s1 =>
key1 <= my_sw;
hex0 <= key1;
my_led <= "0000000000000001";
if(my_btnL='1') then
my_state <= s2;
else
my_state <= s1;
end if;
when s2 =>
key2 <= my_sw;
hex0 <= key2;
my_led <= "0000000000000010";
if(my_btnL ='1') then
my_state <= s3;
else
my_state <= s2;
end if;
when s3 =>
key3 <= my_sw;
hex0 <= key3;
my_led <= "0000000000000011";
if(my_btnR= '1') then
my_state <= s4;
else
my_state <= s3;
end if;
when s4 =>
final_key(11 downto 8) <= key1;
final_key(7 downto 4) <= key2;
final_key(3 downto 0) <= key3;
my_led <= "0000000000000100";
if(my_btnU ='1') then
my_state <= s5;
else
my_state <= s4;
end if;
when s5 =>
num1 <= my_sw;
hex0 <= num1;
my_led <= "0000000000000101";
if(my_btnD ='1') then
my_state <= s0;
elsif (my_btnL ='1') then
my_state <= s6;
else
my_state <= s5;
end if;
when s6 =>
num2 <= my_sw;
hex0 <= num2;
my_led <= "0000000000000110";
if(my_btnD ='1') then
my_state <= s0;
elsif(my_btnL ='1') then
my_state <= s7;
else
my_state <= s6;
end if;
when s7 =>
num3 <= my_sw;
hex0 <= num3;
my_led <= "0000000000000111";
if(my_btnD ='1') then
my_state <= s0;
elsif(my_btnR = '1') then
my_state <= s8;
else
my_state <= s7;
end if;
when s8 =>
number(11 downto 8) <= num1;
number(7 downto 4) <= num2;
number(3 downto 0) <= num3;
my_led <= "0000000000001000";
if(number = final_key) then
my_state <= s9;
else
my_state <= s10;
end if;
when s9 =>
my_led <= "1111111111111111";
if(my_btnD = '1') then
my_state <= s0;
else
my_state <= s9;
end if;
when s10 =>
my_status <= my_status + 1;
if(my_status >= 3) then
my_state <= s11;
elsif(my_status < 3) then
my_state <= s5;
end if;
when s11 =>
my_led <= "0000000000000000";
hex0 <= "1111";
hex1 <= "1111";
hex2 <= "1111";
hex3 <= "1111";
my_state <= s11;
end case;
end if;
end process;
end Behavioral;
在与状态机相同的时钟域中,用于去抖动信号的边沿检测器可以使用带有信号输入的触发器和检测输入上的首选状态(在输入边沿之后)的门来完成) 而触发器处于其他状态。
signal my_btnL_event: std_logic;
signal my_btnLd: std_logic; -- architecture declarative items
process (clk)
begin
if rising_edge(clk) then
my_btnLd <= my_btnL;
end if;
my_btnL_event <= my_btnL and not my_btnLd;
使用 my_btnL_event 代替 my_btnL 的状态转换。
请注意,这需要 my_btnL 无效才能再次生效,前提是有足够的去抖动。
my_btnL_event 赋值可以用多种方式表达,例如通过 if 语句或条件信号赋值。
我正在对使用按钮作为输入信号的 FPGA 进行编程。它有一个有限状态机,有 11 个状态,使用特定的按钮按下可以从一个状态转换到另一个状态。
例如,在我的设计中,状态 s0 通过按下按钮进入状态 s1。这与从状态 s1 到 s2 和从状态 s2 到 s3 的转换情况相同。这个状态转换系统是在我的 VHDL 代码中使用 case 语句实现的。
LED 在每个状态下都会亮起,以跟踪开发板当前所处的状态。
我的问题是,当处于状态 s0 时 my_btnL = '1' 为真时,电路板显示它已移至状态 s3。
我认为正在发生的事情是,它确实会进入状态 s1 和 s2,但在状态 s0 中按下的相同按钮也会在状态 s1 和 s2 中被读取。这种情况发生得如此之快,以至于电路板没有足够的时间来显示状态 s1 和 s2 的 LED 指示。它在状态 s3 停止,因为状态 s3 使用不同的按钮移动到状态 s4。
所以我的问题是如何使按钮按下信号具有上升沿和下降沿,以便仅在一种状态下读取单个按钮按下,而不是在其后的状态下读取?
按钮信号已去抖动,但这只会使信号成为均匀的方波。
在下面的代码中,btnC、btnL、btnR……是按钮:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;
entity digital_lock is
Port (
my_btnC, clk, my_btnU, my_btnR, my_btnL, my_btnD: in std_logic;
my_sw: in std_logic_vector(3 downto 0);
hex0, hex1, hex2, hex3: out std_logic_vector (3 downto 0);
my_led: out std_logic_vector(15 downto 0)
);
end digital_lock;
architecture Behavioral of digital_lock is
type state IS (s0, s1, s2, s3, s4 ,s5 ,s6, s7, s8, s9, s10,s11);
signal my_state: state;
signal my_status: unsigned(1 downto 0);
signal num1, num2, num3, key1, key2, key3: std_logic_vector(3 downto 0);
signal number, final_key: std_logic_vector(11 downto 0);
begin
FSM: process(clk, my_btnC)
begin
if(my_btnC ='1') then
my_state <= s0;
elsif rising_edge(clk) then
case my_state is
when s0 =>
my_status <= "00";
my_led <= "1100000000000000";
hex3 <= "0000";
hex2 <= "0000";
hex1 <= "0000";
hex0 <= "0000";
if(my_btnL ='1') then
my_state <= s1;
else
my_state <= s0;
end if;
when s1 =>
key1 <= my_sw;
hex0 <= key1;
my_led <= "0000000000000001";
if(my_btnL='1') then
my_state <= s2;
else
my_state <= s1;
end if;
when s2 =>
key2 <= my_sw;
hex0 <= key2;
my_led <= "0000000000000010";
if(my_btnL ='1') then
my_state <= s3;
else
my_state <= s2;
end if;
when s3 =>
key3 <= my_sw;
hex0 <= key3;
my_led <= "0000000000000011";
if(my_btnR= '1') then
my_state <= s4;
else
my_state <= s3;
end if;
when s4 =>
final_key(11 downto 8) <= key1;
final_key(7 downto 4) <= key2;
final_key(3 downto 0) <= key3;
my_led <= "0000000000000100";
if(my_btnU ='1') then
my_state <= s5;
else
my_state <= s4;
end if;
when s5 =>
num1 <= my_sw;
hex0 <= num1;
my_led <= "0000000000000101";
if(my_btnD ='1') then
my_state <= s0;
elsif (my_btnL ='1') then
my_state <= s6;
else
my_state <= s5;
end if;
when s6 =>
num2 <= my_sw;
hex0 <= num2;
my_led <= "0000000000000110";
if(my_btnD ='1') then
my_state <= s0;
elsif(my_btnL ='1') then
my_state <= s7;
else
my_state <= s6;
end if;
when s7 =>
num3 <= my_sw;
hex0 <= num3;
my_led <= "0000000000000111";
if(my_btnD ='1') then
my_state <= s0;
elsif(my_btnR = '1') then
my_state <= s8;
else
my_state <= s7;
end if;
when s8 =>
number(11 downto 8) <= num1;
number(7 downto 4) <= num2;
number(3 downto 0) <= num3;
my_led <= "0000000000001000";
if(number = final_key) then
my_state <= s9;
else
my_state <= s10;
end if;
when s9 =>
my_led <= "1111111111111111";
if(my_btnD = '1') then
my_state <= s0;
else
my_state <= s9;
end if;
when s10 =>
my_status <= my_status + 1;
if(my_status >= 3) then
my_state <= s11;
elsif(my_status < 3) then
my_state <= s5;
end if;
when s11 =>
my_led <= "0000000000000000";
hex0 <= "1111";
hex1 <= "1111";
hex2 <= "1111";
hex3 <= "1111";
my_state <= s11;
end case;
end if;
end process;
end Behavioral;
在与状态机相同的时钟域中,用于去抖动信号的边沿检测器可以使用带有信号输入的触发器和检测输入上的首选状态(在输入边沿之后)的门来完成) 而触发器处于其他状态。
signal my_btnL_event: std_logic;
signal my_btnLd: std_logic; -- architecture declarative items
process (clk)
begin
if rising_edge(clk) then
my_btnLd <= my_btnL;
end if;
my_btnL_event <= my_btnL and not my_btnLd;
使用 my_btnL_event 代替 my_btnL 的状态转换。
请注意,这需要 my_btnL 无效才能再次生效,前提是有足够的去抖动。
my_btnL_event 赋值可以用多种方式表达,例如通过 if 语句或条件信号赋值。