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;
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