Problem with buttons - sounds old, but...

P

Pleg

Guest
Hi everybody, I was trying to play with the push buttons of a proto board,
to see if I'm able to get the buttons work correctly --- I'm not ^__^;
I've already looked to some old answers, but still I can't figure out the
solution.

What I wanted to do is: I've a 7 segments display, and I want to show a
digit on it, starrting with 0. Button A increases the digit, button B
decreases it. My first attempt gave me the "bad synchronous description
error", because I wrote


count_up_down: process(debounced_button_A,debounced_button_B)
begin
if(debounced_button_A'event and debounced_button_A='0') then
case current_state_1 is
when zero => next_state_1 <= one;
...
end case;
elsif(debounced_button_B'event and debounced_button_B='0') then
case current_state_1 is
when zero => next_state_1 <= nine;
...
end case;
end if;
end process count_up_down;


Now, I understand the error, and I've already found some answers in a mex of
one year ago, suggesting to use synchronous description (using the clock),
something like

if clk'event and clk = '1' then
if debounced_button_A = '0' then
...
elsif debounced_button_B = '0' then
...

but I don't think it's the same: I want to be sensitive to edges in the
buttons state, not the clock! I mean, I've a clock at 100 MHz, if I use the
above code, when I push button A (for example) it'll stay down for a lot of
clock cycles, my display will increase the digit one million times, right?

I tried then with


flag <= debounced_button_A and debounced_button_B;
process(flag,debounced_button_A,debounced_button_B)
begin
if(flag'event and flag='0') then
if(debounced_button_A='0') then
case current_state_1 is
when zero => next_state_1 <= one;
...
end case;
elsif(debounced_button_B='0') then
case current_state_1 is
when zero => next_state_1 <= nine;
...
end case;
else null;
end if;
end if;
end process;


In my mind, now the process is sensitive to the edge of only one signal (the
"flag", which "summarize" both buttons), and then in the if clause I see
which one of the buttons was pushed. I simulated it with modelsim and it
works correctly, but once loaded on the FPGA it doesn't work! The display
never changes :-(


BTW, also the display is a little mistery: I want to initialize the display
state to 0, and so I write

signal current_state_1, next_state_1: display_state := zero;

where "type display_state is (zero, one, two, three, four, five, six, seven,
eight, nine);"

But the display starts with an invalid state... there's no number on it,
just a "-", that is what I want it to show when something's wrong. Why
doesn't it intialize correctly?

I've tried with just one button (it counts only forward), and, though it
doesn't initialize, it works (I've added a clause that says that when the
state is unknown, it has to go to five at the push of the button. So, it
starts with "-", then when I push the button it goes to 5, and then counts
correctly).


I hope somebody was patient enough to read all this and can help me :)




Pleg
 
You have to make a circuit that debounces your button, and then detects a
rising edge... And you have to do this synchronously. You have to have one
clock, one reset, and everything has to be synchronous to the clock. and
only ever reset once, asynchronously.

So anyways, I think if you go back to basics you'll get this working in no
time, firstly forget the state machine, it's too complicated at this stage.

Try something like this.

Button_A_Debounce: process (clock, reset)
begin
if reset = RESET_ACTIVE then
button_a_debounced <= '0';
elsif rising_edge (clock) then
-- some code to make button_a_debounced & not show glitches (filter for
10ms)
end if;
end process

do the same for Button_B

Then you want to detect the rising_edge of a and b...
Detect_A_Rising_Edge : process (clock, reset)
begin
if reset = RESET_ACTIVE then
button_a_delayed <= '0';
elsif rising_edge (clock) then
button_a_delayed <= button_a_debounced;
end if;
button_a_rising_edge <= button_a_debounced AND NOT button_a_delayed

Same for B

Then you want a process to control a counter, it is reset with RESET,and
changed only when one of the rising edges is true (think synchronous enable)

Then finally a process which converts your internal counter value to the
seven segemt display value.

My help stops here, but this should get you going. =)
HTH
Ben





"Pleg" <25mV@300.K> wrote in message
news:%eBRf.10079$nz4.5887@tornado.fastwebnet.it...
Hi everybody, I was trying to play with the push buttons of a proto board,
to see if I'm able to get the buttons work correctly --- I'm not ^__^;
I've already looked to some old answers, but still I can't figure out the
solution.

What I wanted to do is: I've a 7 segments display, and I want to show a
digit on it, starrting with 0. Button A increases the digit, button B
decreases it. My first attempt gave me the "bad synchronous description
error", because I wrote


count_up_down: process(debounced_button_A,debounced_button_B)
begin
if(debounced_button_A'event and debounced_button_A='0') then
case current_state_1 is
when zero => next_state_1 <= one;
...
end case;
elsif(debounced_button_B'event and debounced_button_B='0') then
case current_state_1 is
when zero => next_state_1 <= nine;
...
end case;
end if;
end process count_up_down;


Now, I understand the error, and I've already found some answers in a mex
of
one year ago, suggesting to use synchronous description (using the clock),
something like

if clk'event and clk = '1' then
if debounced_button_A = '0' then
...
elsif debounced_button_B = '0' then
...

but I don't think it's the same: I want to be sensitive to edges in the
buttons state, not the clock! I mean, I've a clock at 100 MHz, if I use
the
above code, when I push button A (for example) it'll stay down for a lot
of
clock cycles, my display will increase the digit one million times, right?

I tried then with


flag <= debounced_button_A and debounced_button_B;
process(flag,debounced_button_A,debounced_button_B)
begin
if(flag'event and flag='0') then
if(debounced_button_A='0') then
case current_state_1 is
when zero => next_state_1 <= one;
...
end case;
elsif(debounced_button_B='0') then
case current_state_1 is
when zero => next_state_1 <= nine;
...
end case;
else null;
end if;
end if;
end process;


In my mind, now the process is sensitive to the edge of only one signal
(the
"flag", which "summarize" both buttons), and then in the if clause I see
which one of the buttons was pushed. I simulated it with modelsim and it
works correctly, but once loaded on the FPGA it doesn't work! The display
never changes :-(


BTW, also the display is a little mistery: I want to initialize the
display
state to 0, and so I write

signal current_state_1, next_state_1: display_state := zero;

where "type display_state is (zero, one, two, three, four, five, six,
seven,
eight, nine);"

But the display starts with an invalid state... there's no number on it,
just a "-", that is what I want it to show when something's wrong. Why
doesn't it intialize correctly?

I've tried with just one button (it counts only forward), and, though it
doesn't initialize, it works (I've added a clause that says that when the
state is unknown, it has to go to five at the push of the button. So, it
starts with "-", then when I push the button it goes to 5, and then counts
correctly).


I hope somebody was patient enough to read all this and can help me :)




Pleg
 
On Tue, 14 Mar 2006 16:24:00 +0100, "Pleg" <25mV@300.K> wrote:

Hi everybody, I was trying to play with the push buttons of a proto board,
to see if I'm able to get the buttons work correctly --- I'm not ^__^;
I've already looked to some old answers, but still I can't figure out the
solution.

What I wanted to do is: I've a 7 segments display, and I want to show a
digit on it, starrting with 0. Button A increases the digit, button B
decreases it.
Let me try to make some general comments. About the buttons: when you
push the button it usually bounces many times till it gets to a stable
value so when you read it with a high speed clock you need to filter
out all those small bounces. A robust way of doing it is to use
several synchronizing flops to the button inputs to get rid of
metastable sampling cases (basically a shift register which samples
the button) and then use a small counter to filter all 1-0-1 changes
and wait for it to be stable. When you see a stable 1-0 change (or
vice versa) you generate a valid flag and a value.
As to the LED, you need to generate a power on reset value for the
display state. Your registers are waking up in a state you don't
control. You need to apply a reset or figure out what the reset value
of the registers are for your fpga and assign a valid display for that
configuration.
HTH.
 
flag <= debounced_button_A and debounced_button_B;
process(flag,debounced_button_A,debounced_button_B)
begin
if(flag'event and flag='0') then
if(debounced_button_A='0') then
case current_state_1 is
when zero => next_state_1 <= one;
...
end case;
elsif(debounced_button_B='0') then
case current_state_1 is
when zero => next_state_1 <= nine;
...
end case;
else null;
end if;
end if;
end process;
Have you considered deleting the two buttons from the sensitivity list ? I'm
not an expert in VHDL but as far as i can tell, you only want the process to
trigger when flag changes, and then look at the buttons, but the buttons
themselves should not trigger the process directly.
 
Pleg wrote:

but I don't think it's the same: I want to be sensitive to edges in the
buttons state, not the clock!
Read this whole thread, then for a related example,
see the rising level counter source here:
http://home.comcast.net/~mike_treseler/

-- Mike Treseler
 
Having dealt with buttons in designs for 7 years now, both in
microprocessors and FPGA's, there are a few general things you should
know. One, not all buttons are alike. Some have relatively short bounce
periods, while others seem to ring forever. Most protoboards use
pushbutton microswitches, which are generally fairly good about bounce
- but if you have a scope, you should try to measure it. Otherwise, you
are trying to fix a problem you can't see.

Generally speaking, you should sample the input at a rate approximately
the same as your expected bounce period (which is why you need to know
what it is). If you see three or more samples in a row, then you can be
relatively sure that you are seeing the final state - and not a glitch.

For example, I have a breadboard project using standard pushbutton
microswitches. They tend to bounce for approximately 250-500us. So, I
set up a clock divider to bring my tick rate down to about 500us, and
feed the samples into a register pipeline. If all three registers are
high, then I indicate a high for the button. This means that it takes
1.5ms to indicate a button press - which is more than fast enough for a
user interface. If you are especially conservative, you can make the
tick period longer - but you will begin to notice the delay in pressing
the button and seeing something happen at about 100-150ms. (I have a
commercial programmable thermostat with this problem)

Note, you can use the "tick" pulse for multiple debouncers to save on
gates. I actually wrote a "generic" debounce circuit which takes the
number of switches as a generic, so I can handle them all at once. This
way, each additional button only costs three FF's and a LUT.

Typically, buttons will also bounce when you release them, but this is
less of a problem as you typically only care when the button is
pressed, not when it is released. If you need reasonably precise timing
of both events, then use the same method to detect lows as well.

The other alternative is to use a RC low-pass filter and feed the
output into a Schmitt trigger. Again, you need to know the approximate
bounce period to properly size your filter components, but you are
reasonably guaranteed a "clean" input to your FPGA.
 
You have to make a circuit that debounces your button, and then detects a
rising edge...
And that's what I did (or, at least, what I intended to do). That's why, for
example, the signals from the buttons were named "debounced_button_A" and
not "button_A".


Then you want a process to control a counter, it is reset with RESET,and
changed only when one of the rising edges is true (think synchronous
enable)

Then finally a process which converts your internal counter value to the
seven segemt display value.
And that's what I had in mind, the problem was to make it synchronous with
the clock and not with the buttons (which looked more natural to me... I
want the transitions when I push the buttons).



Pleg
 
As to the LED, you need to generate a power on reset value for the
display state. Your registers are waking up in a state you don't
control.
Yes, that's what I don't understand completely. I thought that when I write,
for example,

signal pin_counter: std_logic_vector(15 downto 0) := X"0000";

my signal gets initialized at that value (in the case, all zeroes). It
doesn't happen, right? Never, for no signal, right? I never noticed before.
Anyway, I added a reset process (on the push of a button), and now
everything seems to work fine.
Thank you for the suggestion!


Pleg
 
Have you considered deleting the two buttons from the sensitivity list ?
Yes I tried, but nothing changed :) And reading the rest of this thread I
guess I understand why.


Pleg
 
[cut]
Thanks for the long explanation... makes me feel a little less stupid :)
What I ended up doing is probably not efficient, but at least it works in
the way I intended to: both buttons work :) without glitches or bouncing,
and if I keep one of them pressed the numbers go up (or down) in a fast but
controllable way.
I just wait for the first transition in any button, and when it occurs, I
stop monitoring the buttons for about 200 ms; after that, I come back
monitoring the buttons. This is it:


process(slow_clk,reset)
begin
if(reset='1') then
next_state_1 <= zero;
elsif(slow_clk'event and slow_clk='1') then
case push_buttons_state is
when waiting =>
push_buttons_counter <= push_buttons_counter+1;
if(push_buttons_counter=DEBOUNCE_PUSH_BUTTON_TIME) then
push_buttons_state <= idle;
end if;

when idle =>
if(button_B='0') then
case current_state_1 is
when zero => next_state_1 <= one;
....
when nine => next_state_1 <= zero;
end case;
push_buttons_counter <= B"0_0000_0000";
push_buttons_state <= waiting;
elsif(button_A='0') then
case current_state_1 is
when zero => next_state_1 <= nine;
....
when nine => next_state_1 <= eight;
end case;
push_buttons_counter <= B"0_0000_0000";
push_buttons_state <= waiting;
end if;

when others => push_buttons_state <= idle;
end case;
end if;
end process;




The other alternative is to use a RC low-pass filter and feed the
output into a Schmitt trigger. Again, you need to know the approximate
bounce period to properly size your filter components, but you are
reasonably guaranteed a "clean" input to your FPGA.

Yes, that's what I prefer doing. Unfortunately, I'm working on a proto
board, with buttons and everything already integrated in it.


Thank you for the help!


Pleg
 
Initial conditions like this are not taken into account for synthesis...
(when they're in the signal declaration line)

"Pleg" <25mV@300.K> wrote in message
news:qSdSf.14049$nz4.3255@tornado.fastwebnet.it...
As to the LED, you need to generate a power on reset value for the
display state. Your registers are waking up in a state you don't
control.

Yes, that's what I don't understand completely. I thought that when I
write,
for example,

signal pin_counter: std_logic_vector(15 downto 0) := X"0000";

my signal gets initialized at that value (in the case, all zeroes). It
doesn't happen, right? Never, for no signal, right? I never noticed
before.
Anyway, I added a reset process (on the push of a button), and now
everything seems to work fine.
Thank you for the suggestion!


Pleg
 

Welcome to EDABoard.com

Sponsor

Back
Top