VHDL实现引擎启动控制FSM:解决单次输入多时钟识别问题
VHDL FSM按键单次输入识别解决方案
问题背景
需实现一个VHDL有限状态机(FSM)作为引擎启动控制器:
- 当按键按A-B-B顺序输入时,输出
ut(1) = 1(引擎启动) - 输入错误顺序时,输出
ut(0) = 1,需复位后才能重新输入 - 当前问题:单次按键会被多个时钟周期识别为多次输入,导致状态跳转异常
问题核心原因
按键按下/释放时的电平状态会持续多个时钟周期,当前FSM直接检测电平状态,只要时钟沿到来时按键处于按下状态就会触发状态跳转,导致一次按键被多次识别。若为实际硬件场景,按键还会存在10~20ms的机械抖动,进一步加剧该问题。
解决方案
通过边沿检测确保单次按键动作仅被识别一次,若适配实际硬件,可在边沿检测前增加按键消抖逻辑。以下是带边沿检测的实现方案:
修改后的FSM代码
entity engine is Port ( ab : in STD_LOGIC_VECTOR(1 downto 0); clock : in STD_LOGIC; ut : out STD_LOGIC_VECTOR(1 downto 0); reset : in STD_LOGIC); end engine; architecture Behavioral of engine is type state_type is (S0, S1, S2, S3, S4); signal state, next_state : state_type; -- 寄存上一周期按键状态,用于边沿检测 signal ab_prev : STD_LOGIC_VECTOR(1 downto 0); -- 有效按键触发信号:仅在按键从释放变为按下时置1 signal a_press, b_press : STD_LOGIC; begin -- 边沿检测与状态更新进程 process(clock, reset) begin if reset = '1' then next_state <= S0; ab_prev <= "00"; a_press <= '0'; b_press <= '0'; elsif rising_edge(clock) then -- 更新上一周期按键状态 ab_prev <= ab; -- 检测A按键上升沿(从释放到按下) a_press <= '1' when (ab = "10" and ab_prev /= "10") else '0'; -- 检测B按键上升沿(从释放到按下) b_press <= '1' when (ab = "01" and ab_prev /= "01") else '0'; state <= next_state; case state is when S0 => if a_press = '1' then -- 仅在A按下边沿触发跳转 next_state <= S1; end if; when S1 => if b_press = '1' then -- 仅在B按下边沿触发跳转 next_state <= S2; end if; when S2 => if b_press = '1' then next_state <= S3; elsif a_press = '1' then next_state <= S4; end if; when S3 => next_state <= S3; when S4 => next_state <= S4; end case; end if; end process; -- 输出逻辑进程 process(state) begin case state is when S0 => ut <= "00"; when S1 => ut <= "00"; when S2 => ut <= "00"; when S3 => ut <= "10"; -- 正确顺序,引擎启动 when S4 => ut <= "01"; -- 错误顺序 end case; end process; end Behavioral;
调整后的测试平台代码
ENTITY egninetest IS END egninetest; ARCHITECTURE behavior OF egninetest IS COMPONENT engine PORT( ab : IN std_logic_vector(1 downto 0); clock : IN std_logic; ut : OUT std_logic_vector(1 downto 0); reset : IN std_logic ); END COMPONENT; --Inputs signal ab : std_logic_vector(1 downto 0) := (others => '0'); signal clock : std_logic := '0'; signal reset : std_logic := '0'; --Outputs signal ut : std_logic_vector(1 downto 0); -- Clock period definitions constant clock_period : time := 15 ns; BEGIN uut: engine PORT MAP ( ab => ab, clock => clock, ut => ut, reset => reset ); -- Clock process definitions clock_process :process begin clock <= '0'; wait for clock_period; clock <= '1'; wait for clock_period; end process; -- 模拟按键按下后释放,触发边沿检测 stim_proc: process begin reset <= '1'; wait for 40 ns; reset <= '0'; -- 测试错误输入:按下B后释放 ab <= "01"; wait for 30 ns; ab <= "00"; wait for 55 ns; reset <= '1'; -- 复位 wait for 40 ns; reset <= '0'; -- 测试正确输入序列A-B-B -- 按下A后释放 ab <= "10"; wait for 30 ns; ab <= "00"; wait for 40 ns; -- 按下B后释放 ab <= "01"; wait for 30 ns; ab <= "00"; wait for 40 ns; -- 按下B后释放 ab <= "01"; wait for 30 ns; ab <= "00"; wait; end process; END;
关键修改说明
- 添加
ab_prev信号寄存上一周期按键状态,用于判断边沿变化 - 定义
a_press和b_press脉冲信号,仅在按键从释放变为按下时置1,确保单次按键仅产生一次有效触发 - FSM状态跳转条件改为检测脉冲信号,避免持续电平导致多次状态跳转
内容的提问来源于stack exchange,提问作者flacko
相关产品推荐
相关产品推荐

