Button Debouncer - red-bote/VHDL_Demos GitHub Wiki

Demonstration of accumulator template with button input. The intent of the design is to demonstrate buttons that could act on momentary presses and releases, and to also detect a held down button. This is a first attempt to write up a general purpose button debouncer to use in other planned example projects.

images/accumulator/OgajnO.png

The top-level VHDL sets up 2 instances of a de-bounced button with one btn_debounce instance acting on a button "make" event (momentary press) and the other de-bouncer acts on the "break" event (moment of button release). The accumulator increment is then set on either condition and the accumulator output shown on the LEDs. accumulator_2 from xilinx_xstug_examples is used and allows the accumulator register widths to be specified as a Generic port.

architecture Behavioral of rtl_top is
    signal acc_cnt : std_logic_vector (COUNT_BITS-1 downto 0);
    signal acc_add : std_logic_vector (COUNT_BITS-1 downto 0);

    signal btn_U_deb : std_logic := '0';
    signal btn_D_deb : std_logic := '0';
begin

    u_btn_U_deb : entity work.btn_debounce
    Port map (
        clk => clk,
        btn_inp => btnU,
        btn_pressed => open,
        btn_make => open,
        btn_break => btn_U_deb
    );

    u_btn_D_deb : entity work.btn_debounce
    Port map (
        clk => clk,
        btn_inp => btnD,
        btn_pressed => open,
        btn_make => btn_D_deb,
        btn_break => open
    );

    acc_add <= x"1" when btn_U_deb = '1' or btn_D_deb = '1' else (others => '0');

    u_accum : entity work.accumulators_2
    generic map (
        WIDTH => COUNT_BITS)
    port map (
        clk  => clk,
        rst => reset,
        D => acc_add, -- in  std_logic_vector(WIDTH-1 downto 0);
        Q => acc_cnt -- out std_logic_vector(WIDTH-1 downto 0)
    );

    led(COUNT_BITS-1 downto 0) <= acc_cnt;

end Behavioral;

The code below is the crude attempt using a few registers to buffer the button sample. It creates a button action that increments a counter. The counter should increment 1 for each press (or release, depending upon the action desired). The momentary make almost works glitch-free, but occasionally the counter increments a couple times on a button press rather than just once. The break action surprisingly seems to operate without error and counter is incremented just once per button press (upon release of button).

    p_clk_pr : process(clk) is
    begin
        if rising_edge(clk) then
            btn_U_q3 <= btn_U_q2;
            btn_U_q2 <= btn_U_q1;
            btn_U_q1 <= btn_U_q0;
            btn_U_q0 <= btn_raw;

            make_flag <= '0';

            if btn_raw = '1' and btn_U_q0 = '1' and btn_U_q1 = '1' and btn_U_q2 = '1' and btn_U_q3 = '1' and b_dn_flag = '0'
            then
                make_flag <= '1';
                b_dn_flag <= '1';
            elsif btn_raw = '0' and btn_U_q0 = '0' and btn_U_q1 = '0' and btn_U_q2 = '0' and btn_U_q3 = '0' then
                b_dn_flag <= '0';
            end if;

            if btn_U_q0 = '0' and btn_U_q1 = '0' and btn_U_q2 = '1' and btn_U_q3 = '1' then
                break_flag <= '1';
            else
                break_flag <= '0';
            end if;
        end if;
    end process p_clk_pr;

Debounce with shift-register

Recalling the idea of holding a string of samples in a series of latches or a shift register to allow the button contacts to settle. A serial-in/parallel-out shift register is used in place of the flip-flops used in the crude example. A given instance of btn_debounce handles a single button. The btn_make and btn_break outputs provide a momentary condition of the button that registers for 1-clock cycle. On each rising clock, the state of the button input port is sampled and clocked into the 8-bit wide shift register. Comparing the shift register parallel out value to 0xFF is considered fully debounced and pressed. On a value of 0x00 the button is considered fully debounced and released. The pressed_flag allows the transition of the button condition to be detected so that the momentary make or break status can be determined.

architecture Behavioral of btn_debounce is
    signal break_flag : std_logic;
    signal make_flag : std_logic;
    signal pressed_flag : std_logic := '0';
    -- parallel output of shift register
    signal btn_po_reg : std_logic_vector(7 downto 0);
begin
    u_shift_reg : entity work.shift_registers_5
    port map (
        C => clk,
        SI => btn_inp,
        PO => btn_po_reg -- out std_logic_vector(7 downto 0));
    );

    p_clk_pr : process(clk) is
    begin
        if rising_edge(clk) then

            make_flag <= '0';
            break_flag <= '0';

            if btn_po_reg = x"FF" and pressed_flag = '0'
            then
                make_flag <= '1';
                pressed_flag <= '1';
            elsif btn_po_reg = x"00" and pressed_flag = '1'
            then
                break_flag <= '1';
                pressed_flag <= '0';
            end if;
        end if;
    end process p_clk_pr;

    btn_make <= make_flag;
    btn_break <= break_flag;
    btn_pressed <= pressed_flag;
end Behavioral;

The make action of the push button turns out to be more problematic to detect without bouncing. The mechanics of the low-cost push-buttons on the Basys 3 board allow the button state to be toggled by wiggling your finger around on the button without fully releasing it. In the debounce circuit design presented, the break detection seems to be completely effective for the purpose of reliably triggering a single increment of the accumulator on an individual button press.

Counter with Adder and Register

The accumulator from the previous example code can be replaced with discrete register and adder components for a more-or-less functionally equivalent counter. (registers_5 initializes to "1111" on reset). There is an additional signal to connect Q from registers_5 to A of adders_1.

    u_cntr_reg : entity work.registers_5
    port map (
        C => clk,
        CE => '1',
        PRE => reset,
        D => acc_cnt, -- in std_logic_vector (3 downto 0);
        Q => adder_in_a -- out std_logic_vector (3 downto 0));
    );

    u_uns_adder : entity work.adders_1
    generic map (
        WIDTH => COUNT_BITS)
    port map (
        A => adder_in_a,
        B => acc_add,
        SUM => acc_cnt
    );

Next