You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Basys3 FPGA上用VHDL实现有限状态机 解决单次按键多触发问题

问题原因

你的猜测是对的,同时还有额外的亚稳态风险,问题核心分为两点:

  1. 异步输入未同步:按键是外部异步信号,直接输入同步时钟域的逻辑会有亚稳态风险,导致采样结果不稳定。
  2. 电平触发逻辑不合理:Basys3默认系统时钟为100MHz,单次按键按下的时长通常在100ms以上,这段时间内会经过上千万个时钟周期,你当前的状态机只要检测到P为高就跳转,自然会被触发多次。

解决方法

你需要对按键输入做三级处理:同步→消抖→边沿检测,最终生成单周期的触发脉冲,只有脉冲有效时才允许状态跳转。

  • 两级同步:用两个寄存器打拍,把异步的按键输入同步到时钟域,消除亚稳态
  • 按键消抖:检测按键电平稳定持续20ms以上(覆盖机械按键的抖动区间),输出稳定的按键电平信号
  • 边沿检测:对消抖后的稳定信号检测上升沿,生成一个仅持续1个时钟周期的触发脉冲,保证按一次按键仅触发一次状态跳转

修改后代码示例

library IEEE;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;

ENTITY trasher is
PORT (
      clock :   IN STD_LOGIC;
      P     :   IN STD_LOGIC;
      reset :   IN STD_LOGIC;
      LED3, LED1,LED2,LED0     :    OUT STD_LOGIC);
END ENTITY;

Architecture RTL of trasher is
TYPE State_type IS (A, B, C, D);
SIGNAL State : State_Type;

-- 按键处理相关信号
SIGNAL p_sync1, p_sync2 : STD_LOGIC; -- 两级同步寄存器
SIGNAL p_debounced : STD_LOGIC; -- 消抖后信号
SIGNAL p_debounced_prev : STD_LOGIC; -- 消抖信号打拍,用于边沿检测
SIGNAL key_pulse : STD_LOGIC; -- 单周期触发脉冲
SIGNAL debounce_cnt : unsigned(20 downto 0); -- 100MHz下21位计数器最大计数2^21=2097152,对应约20.97ms,覆盖消抖区间
CONSTANT DEBOUNCE_THRESHOLD : unsigned(20 downto 0) := to_unsigned(2000000, 21); -- 20ms阈值

BEGIN 
  -- 1. 按键两级同步
  PROCESS(clock, reset)
  BEGIN
    IF reset = '1' THEN
      p_sync1 <= '0';
      p_sync2 <= '0';
    ELSIF rising_edge(clock) THEN
      p_sync1 <= P;
      p_sync2 <= p_sync1;
    END IF;
  END PROCESS;

  -- 2. 按键消抖
  PROCESS(clock, reset)
  BEGIN
    IF reset = '1' THEN
      debounce_cnt <= (others => '0');
      p_debounced <= '0';
    ELSIF rising_edge(clock) THEN
      IF p_sync2 /= p_debounced THEN
        debounce_cnt <= debounce_cnt + 1;
        IF debounce_cnt = DEBOUNCE_THRESHOLD THEN
          p_debounced <= p_sync2;
          debounce_cnt <= (others => '0');
        END IF;
      ELSE
        debounce_cnt <= (others => '0');
      END IF;
    END IF;
  END PROCESS;

  -- 3. 上升沿检测,生成单周期脉冲
  PROCESS(clock, reset)
  BEGIN
    IF reset = '1' THEN
      p_debounced_prev <= '0';
    ELSIF rising_edge(clock) THEN
      p_debounced_prev <= p_debounced;
    END IF;
  END PROCESS;
  key_pulse <= p_debounced AND NOT p_debounced_prev;

  -- 状态机逻辑,仅在key_pulse有效时跳转
  PROCESS (clock, reset) 
  BEGIN 
    IF (reset = '1') THEN
        State <= A;
    ELSIF rising_edge(clock) THEN
        CASE State IS
            WHEN A => 
                IF key_pulse='1' THEN 
                    State <= B; 
                END IF; 
            WHEN B => 
                IF key_pulse='1' THEN 
                    State <= C; 
                END IF; 
            WHEN C => 
                IF key_pulse='1' THEN 
                    State <= D; 
                END IF; 
            WHEN D=> 
                IF key_pulse='1' THEN 
                    State <= B; 
                -- 若不需要松手自动回A可删除下方ELSE分支
                ELSE 
                    State <= A; 
                END IF; 
            WHEN others =>
                State <= A;
        END CASE; 
    END IF; 
  END PROCESS;
  
  -- 输出逻辑不变
  LED0 <= '1' WHEN State=A ELSE '0';
  LED1 <= '1' WHEN State=B ELSE '0';
  LED2 <= '1' WHEN State=C ELSE '0';
  LED3 <= '1' WHEN State=D ELSE '0';
END rtl;

内容的提问来源于stack exchange,提问作者coding-newb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 22:42:03