Reset And Panic Buttons, Buttons
Debouncing
The monitor
program in my home brew computer has a nice debug feature. The NMI (Non-Maskable Interrupt) is
used to service the so called 'panic' button. The role of a panic button in a microprocessor system is simple - if your program gets stuck or runs away, you can press the panic button
connected to /NMI pin of the processor and the service routine
assigned for NMI will store the current program status on the stack, including the return address, dump CPU
registers and do whatever else you programmed it to do and then drop you back in the monitor program prompt thus giving
you back control without the need to reset the system. You can then
use monitor program functions to examine or alter memory of the faulty program and even
continue the faulty program execution from the moment of NMI
interrupt, if you think you corrected the cause of the problem.
I used that button often to recover
from failed / hung up code or to return from 3-rd party applications
I ran on my system (e.g.: Tiny Basic) to the monitor prompt. The
continuation of the execution didn't work too well though simply
because I didn't have the so called debouncing circuit implemented
for the panic button.
What is debouncing and why do I need to
debounce a button, you might ask?
When you deal with electronic circuits,
which involve fast switching signals, you need to realize that in
real life applications, things like buttons / switches, wires,
connectors, resistors, capacitors, inductors and so on are not
perfect. Wires have a resistance and capacitance. Resistors have a capacitance and inductance. Inductors have a resistance. If wires carry
high frequency signal, there is interference. In case of mechanical
switches the problem is that their contacts bounce off each other –
literally and very fast. When you press a button, two metal surfaces
come in contact with each other. These surfaces are not perfect. They
may be not aligned perfectly. They are not perfectly flat. Over a
very small amount of time there is a mechanical high frequency
oscillation when these surfaces come in contact and detach from each
other before they finally settle. This period of unstable / undefined connection between these surfaces is called bouncing.
To you this looks like a single button push. If
you had a battery and light bulb circuit that such button closes, you
would just see that the light comes on – and when you release
button, the light comes off. To a micro controller or a
microprocessor which are usually clocked with relatively high
frequency and process the data much quicker than human perception
allows to notice, the moment of you pressing the button appears like
tens, hundreds or even thousands of separate signal switching events.
However we want to register just one clean push. How to solve this
dilemma?
There are two major ways – solve it
with hardware or with software.
In our case software debouncing will
not work, because NMI signal is Non-Maskable (can't be turned off)
and is triggered by a low coming signal edge – button contacts
bouncing results in a series of high-to-low edges of signals which
will trigger a new NMI each time such edge is detected, assuming
there was enough time between them to be detected by CPU. Therefore
we cannot just mask interrupts at the entrance of NMI service and put
a delay on it to ignore the oscillations.
We have to debounce the button in
hardware.
One of the cheap and reliable methods
to do this which I used in my circuit is to combine RC filter with
hysteresis by the means of Schmitt trigger TTL gate, e.g.: inverting gates found inside 74LS14 or 74HC14 TTL chip. (it has 6 of such gates)
The RC circuit at input to the gate provides
low-pass filter to cut off high frequency oscillations resulting from bouncing and Schmitt
trigger gate ensures that the on / off levels are separated by a dead
zone – pretty much in the same manner that your A/C or heating
system thermostat does.
Below is the result. Note that I also
debounced the Reset button, however this is not as critical as NMI
since /RES in MOS6502 is triggered (and held) by low level. You'll
notice though that the capacitor in RC circuit for Reset button is of
much higher value than for the Panic button. It is because there is
unstable period when you turn on the power to microprocessor system
when voltage and currents in the system oscillate before they reach
stabilized levels. It is critical that after you turn on the power to
the system, the initial reset pulse comes some time after power to
all the system components has stabilized. It (/RES signal) also needs
to come after any possible unstable oscillations from NMI debouncing
circuit, otherwise the system may freeze at startup. It also needs to be at TTL high level when stable, since /RES is active low - thus two inverting gates are used, while it does not matter for NMI sigal which is triggered by the signal's edge, not by its level.
Now I can enjoy reliable panic button function to debug my software or temporarily switch to monitor program to load data, change memory contents etc.
Thanks for reading.
MK 6/5/2020
Credits:
Embed With Elliot: "DEBOUNCE YOUR NOISY BUTTONS, PART I"
References:
Schmitt Trigger Gate
DM74LS14: Hex Inverter with Schmitt Trigger Inputs