Search code examples
vhdlfpgalattice

Boucing effect vhdl in a fpga


im in internship and my company wants that i learn vhdl for fpga. I'm using lattice diamond to code and compile my project and questasim for simulation. I also have a little board for training : MachXO3LF by Lattice.

I did a project : when i push a button a led is lighting for 2s and then fade. (works in simulation but but with the card because of the bouncing effect.

My mentor gave me a new project : write a code and a test bench to avoid the boucing effect and when i push the button, the led is lighting for 2s and when i push again the button there is no action for 10s.

I dont know how to avoid the bouncing effect with my starting code.... The bouncing effect is the results of false triggering or multiple triggering like the button is pressed multiple times

Here is my code

use ieee.std_logic_1164.ALL;
use ieee.numeric_std.ALL;


entity bouton_led_debounce is 
    Port    (btn    : in std_logic;
             reset  : in std_logic;
             clock  : in std_logic;
             led    : out std_logic
             );
end bouton_led_debounce;

architecture Behavioral of bouton_led_debounce is

signal m1       : std_logic := '1';
signal m2       : std_logic:= '1';
signal int      : std_logic:= '1';
signal buffer_led   : std_logic := '1';
signal cptr     : std_logic_vector (27 downto 0) := (others => '0');

begin   

    process(clock,reset)
    begin
        if(reset = '0') then
            buffer_led  <= '1';
            m1      <= '1';
            m2      <= '1';
            int     <= '1';
            cptr        <= (others => '0');
        elsif rising_edge(clock) then
        
            buffer_led <= '1';
            
            m1  <= btn;
            m2  <= m1;
            int <= m2;
            
            if( (int = '1') and (m2 = '0') ) then
                cptr <= (others => '0');
                cptr(0) <= '1';
                buffer_led <= '0';
            end if;
                    
            if( unsigned(cptr) /= 0) then
                cptr <= std_logic_vector(unsigned(cptr)+1);
                buffer_led  <= '0';
            end if;

            if( unsigned (cptr) = (24000000-1) )then
                cptr <= (others => '0');
            end if;
        end if;
    end process;
    
    led <= buffer_led;

end Behavioral;

And my test bench :

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity bouton_led_debounce_tb is
end bouton_led_debounce_tb;

architecture Behavioral of bouton_led_debounce_tb is

    component bouton_led_debounce is
        port(   btn     : in STD_LOGIC;
            reset   : in STD_LOGIC;
            clock   : in STD_LOGIC;
            led     : out STD_LOGIC);
    end component;


    signal btn : STD_LOGIC := '1' ;
    signal led : STD_LOGIC;
    signal reset : STD_LOGIC := '1';
    signal clock : STD_LOGIC := '0';

begin
    uut: bouton_led_debounce
        port map(
        btn => btn,
        led => led,
        clock => clock,
        reset => reset);
        

btn_sti : process
begin
    btn <= '1'; 
    wait for 500 ns;
    btn <= '0'; 
    wait for 50 ns;
    btn <= '1'; 
    wait for 30 ns;
    btn <= '0'; 
    wait for 50 ns;
    btn <= '1'; 
    wait for 30 ns;
    btn <= '0'; 
    wait for 30 ns;
    btn <= '1';
    wait;
end process btn_sti;

rst_sti : process
begin
    reset <= '1'; 
    wait for 1 us;
    reset <= '0'; 
    wait for 1 us;
    reset <= '1'; 
    wait;
end process rst_sti;


clock_sti : process
begin
    clock <= '0'; 
    wait for 83.33 ns;
    clock <= '1'; 
    wait for 83.33 ns;
end process clock_sti;
    
end Behavioral;

Solution

  • A few comments on your design

    buffer_led  <= '1';
    

    I assume your LED is active low driven? Still, for who ever reads your code this might not be obvious and should be handled accordingly. In your case, as you don't ever read buffer_led I'd use this signal as a representation of the logical signal and do

    led <= not(buffer_led);
    

    instead.

    I don't see any button debouncing in your snipped. What you do with

    m1  <= btn;
    m2  <= m1;
    int <= m2;
    

    is input synchronization what is totally fine, but has nothing to do with debouncing. As mentioned in the comments, debouncing comes in different forms, a rather simple one would be to lower the sampling rate of your button. E.g. create an enable pulse with 1kHz frequency and check only when there's an enable, you can do this inside your process.

    if clken_1kHz then
      m1 <= btn;
      m2 <= m1;
      if m1 = '1' and m2 = '1' then
        int <= '1';
      else
        int <= '0';
      end if;
    end if;
    

    With the above snippet, the int signal would only turn one if the samples m1 and m2 which are 1ms apart both evaluate to 1. Of course this is far from perfect and there are way more sophisticated variations of debouncing, but it should for sure improve your situation. You can also play around with the sampling rate, decrease the 1kHz to e.g. 10Hz to get more debouncing.

    Further, use the signal types that make most sense, there's no added value in tons of conversion, if you have a counter, use unsigned directly instead of converting back and forth. With the int signal defined as above, the rest of your code would be something like

    if int = '1' and cptr(cptr'left) = '0' then 
      led_buffer <= '1';
      cptr <= cptr-1;
    else
      led_buffer <= '0';
      if int = '0' then
        cptr <= to_unsigned(24000000-1, cptr'length);
      end if;
    end if;
    

    by counting down instead of up we can simply check for the underflow, note that we need a counter with one additional bit, but the comparison is also just 1 bit instead of full size. So as long as the button is pressed and the counter has not underflow, the led_buffer is set, once the button is released or the counter has elapsed, we turn off the led and if the button has been released as well we can safely reset the counter.

    disclaimer: haven't tested it, just mind compiled