A Special "Thanks" to Myke Predko for submitting the following project/article. About the Author Myke Predko is the author of " Programming and Customizing the PIC Microcontroller", the "Handbook of Microcontrollers" and "Programming and Customizing the 8051 Microcontroller" as well as the soon to be released "PC PhD" and "PC Interfacing Pocketbook" which are all published by McGraw-Hill.As well as writing books on electronics and programming, Myke works for Celestica, Inc. in the area of New Products Test Engineering. His wife, Patience, and he have three children, Joel, Elliot and Marya. You can contact Myke by E-mail at: myke@passport.ca or visit his web site at: http://www.myke.com Button Debouncing
I apologise for the tardiness of this article; for the next little while, I will probably have to scale them back to once every two weeks or so. The reason is that I am proof reading the "Galley Proofs" of my new book, "PC PhD". This book discusses the PC's hardware and how to develop interfaces (along with the required software) for it. I'll be putting more information about it on my web page as the August release date comes up. In my last article, I presented you with a simple circuit as a first application. I want to continue with this circuit to demonstrate five different methods of software based "debouncing". A typical switch connection used in a digital application is:
In this circuit, the Microcontroller input pin is held high until the button is pressed (the switch is "closed"). When the switch is closed, then a direct path to ground is made with the Microcontroller Pin being "pulled" to Ground. "Bouncing" is the term used to describe what happens when the switch is closed or opened. Instead of a single, square edge as you may expect, a switch change consists of many short spikes which are a result of the button's contacts "bouncing" during the transition. This is shown in the diagram below:
These spikes can cause a microcontroller to detect many button pressing events. As is shown in the diagram above, when "Button Pressed", the signal actually received by the microcontroller's input pin looks like the series of "spikes" that I have shown. These spikes are characteristic to the button and are generally present for about 10 milliseconds. In the diagram above, I have shown the Logic "High", "Low" and "Threshold" levels. The "High" and "Low" values should be easy to understand, but I wanted to spend a moment on the "Threshold" level. This is the voltage level in which the microcontroller detects the difference between a "High" or a "Low" input. For TTL circuits, this value is generally about 1.4 Volts while for CMOS circuits (as are used in the PICMicro), the threshold is normally one half Vcc, or 2.4 to 2.6 Volts. While 10 milliseconds is quite short for us humans, it is actually quite long for a digital circuit and the period between spikes is often enough for the application to respond to the request and set up to wait for another button press, which comes as another keystroke. To the user, it will seem as if the Microcontroller is responding to multiple button presses for each individual one. From the Microcontroller's perspective, this is exactly what is happening. To prevent this rapid repeat of button press operations a "debounce" routine like the one shown in psuedo-code below is used to wait for the button transitions to stop for 20 msecs before acknowledging that a button has been pressed: WaitForButton: In this code, first the button going low (being pressed) is polled. When it goes low, a 20 msec delay is set up and then the button is polled until either the button goes high again or the 20 msec delay times out. If the button goes high again (the timeout has not occured), then the whole process starts again until the button is low for 20 msecs. To demonstrate how debouncing works, I decided to use the same circuit as last week:
|
||||
| In the application code that I present here,
instead of turning on one of the LEDs when the button is pressed, I toggle the state of
the LED each time the button is pushed. The psuedo-code for the application is: main() In this code, the PICMicro waits for the button to stabilize at a "high" condition before waiting for a valid press ("Low"). Note that I have an LED always on; I use this as an indicator for me to know the application is in fact running. In "bounce.asm", I used the following code for debouncing the button going low: LowLoop
; Return Here until "0" Debounced This code doesn't actually follow the psuedo code listed above. It really operates like: WaitForButton: Which obviously doesn't follow structured programming techniques and could cause problems with some "C" compilers. The PICMicro assembly language code works quite well and to make it more "portable" to other applications, I rewrote it as a Macro in "bounce2.asm": Debounce macro HiLo, Port, Bit 1. It can be used for waiting for the "up stroke" as well as the "down stroke". In the "Bounce" code, I had to repeate the code twice based on whether or not I was debouncing the wait for high or the wait for low. In Bounce2, I could use the same macro for the same function. 2. The Macro can be used for any Pin (set for "input") in the PICMicro. The "Bounce" code has to be edited for use with other pins. 3. I put in conditional code for debugging. If the "Debug" label is "defined", then a short loop (which takes the same number of instruction words) is used. This is useful when working with simulators like "MPLAB" in which the 20 msec delay can be quite onerous to wait through. The "InitDlay" constant is calculated using the formula: TimeDelay = (((InitDlay - 1 ) * 256) * 7) / (Frequency / 4) or InitDlay = ((TimeDelay * (Frequency / 4)) / (256 * 7)) + 1 This means that with an "InitDlay" value of 12, a 19.7 msec debounce delay will be produced for a PICMicro running a 4 MHz Clock. I should point out that this macro will not work with low-end PICMicros because of the "addlw 1" instruction. This code can be modified with my "Increment w" routine that can be found on my PICMicro Snippet Page, but if you are going to use "w increment" code, then make sure you increase the number of nops to 4 in the Macro and multiply by "9" instead of "7" when calculating "InitDlay". A very interesting way of debouncing is used by the Parallax Basic Stamp. The "Button" instruction checks the defined pin each time the instruction is executed. If the pin is active ("Downstate" and "Targetstate"), then a counter ("Variable") is incremented. If the Pin is not active, then the counter is cleared. When the counter reaches the "Delay" value, execution jumps to the button response address, else the execution flows right through to the next location. The most typical way of implementing this function is before a delay which then loops back to the function to check the button again. ButtonLoop: I have defined the following "Button" Macro for the
PICMicro with the Parameters: The Macro itself probably looks quite complex at first glance: Button macro Port, Pin, Downstate, Delay, Rate,
Variable, Targetstate, Address But it really is quite simple to use and allows you to create applications that can provide an interrupt-like response to buttons and debouncing without using interrupts. I should point out that the "Button" macro will work for low-end as well as mid-range PICMicros. With the different ways of implementing polling (interruptless) debounce loops pretty well exhausted, I want to take a look at debouncing switches using interrupts. My first look at this avenue was to create debounce code that would cause an interrupt when the "RBO/Int" Pin changed state to "low" or the timer timed out after 20 msecs. This is "bounce3.asm". I'm not going to discuss Bounce3 too much other than to say that it converts the work done by the first two polling debounce routines into interrupt handlers. As such, it works quite well, but I don't feel that this method is that optimal because it uses more resources (the timer, interrupt controller and more instructions) to provide the same level of function as was present before. The last two Interrupt based Debounce Routines do have a lot to add to the function of the code. The first "bounce4.asm" sets one of two flags if the button state has stabilized to the point where it can beconsidered "debounced". This simplifies the code to just polling the "Hi" and "Lo" bits set by the Interrupt handler to: Loop When you look at the "Button4" interrupt handler, you'll see that it is actually quite complex with it working through which level change is going to happen next. To try and simplify that, I modified "Button4" into "bounce5.asm" which uses the "Port Change Interrupt". In this mode, if any of the PORTB pins 4 through 7 are used for "input", then an interrupt request will be made if the pins change state. This eliminates the need to "swap" the RB0/Int pin Interrupt direction bits in the "OPTION" Register and also gives up to four buttons to play with instead of two. The actual code uses two "flag" registers ("Hi" and "Lo") which are polled for each of the four possible buttons. In my code I used "#defines" for the "Lo" and "Hi" bits, so the code presented above is exactly the same for Bounce5. The interrupt handler code is: org 4 Note that when you implement this code that you have to change the button's position from RB0 (pin 6) to RB4 (pin 10) on the PIC16F84. Also note that when using the Port Change Interrupt, PORTB should never be read except in the interrupt handler body. Reads outside the interrupt handler body could result in missed Port Change Interupt requests. This is why I use a variable kept current with the PORTB output values instead of reading PORTB directly for the LED toggles as I do in the other applications. So, with this (rather long) article, you now have six different ways of implementing software button debouncing, each with it's own advantages and disadvantages. A few comments about testing out this code. The first one is, I used a YAP (along with the "YAP Windows" code discussed below) for the circuit. This worked out very well because I was able to test and debug all six applications with only having to change the button between Bounce5 and the other applications. If you're going to use the "YAP", make sure you always use the correct speed. I found that running at the incorrect speeds made the debounce routines work strangely (causing some short lived panic until I understood what I screwed up on). Secondly, as you look through the code you'll see that I
included a "Debug" "define" for the delay loops. This was done to
speed through the debounce loops while simulating in MPLAB. For the Button Input, I used
the "Asynchronous Input" to MPLAB and this slows it down considerably. If you
are going to use the "Asynchronous Input" debug dialog box for your application,
I highly recommend that you add this to your applications if you have significant delays
that you want to step through. A couple of times I found that I forgot to take out the
"Debug" define (which means I rename it so it isn't recognized by the
conditional code) and the operating code work strangely - although if I reduced The last point I want to make is, I didn't include any hardware debouncing. In the past I haven't had a lot of luck with these, but I am going to try a 0.1 uF cap and a 100K resistor to filter button input into a Schmidt trigger input. When I get some results, I'll post them here. Coming up and other News
This past week, I created a Visual Basic Front end for the YAP programmer that is presented in "Programming and Customizing the PIC Microcontroller". This front end is available for download from Wirz Electronics. The file to be downloaded is a 1.6 MB "zip" file. During download you can "open" it with "WinZip" and then execute "setup.exe". The set up operation will load the correct device drivers as well as the application on your PC. To execute the application, click on "YAP Windows" from "Start" on your PC's taskbar. One of the big features of the YAP and one that can really be taken advantage of with the "YAP Windows" code is the ability to send and receive serial messaging to and from the application. In my next article, I will present a number of ways in which the 16F84 can communicate with the "YAP Windows" application using "NRZ" ("Non-Return to Zero") serial communications. These NRZ routines can also be used outside of the YAP in applications for communicating with other devices, such as your PC using RS-232. I recently bought a model train set for my daughter and was surprised to discover how primitive the electronic controls are for trains; unless you are willing to spend huge bucks on a "DCC" system. Looking at the traditional controls which are probably the same as what your parents played with, I wondered if there was some opportunities for me to come up with better ways of controlling and monitoring the trains and the tracks. What I will probably start with includes: This week on myke.comSince it has been a few weeks since my last article, I thought I should give you a quick list of the past books and movies of the week. If you love movies, then you should probably take a look at Ed Sikov's "On Sunset Boulevard" which is a biography of Billy Wilder and a discussion of his movies. It is a fascinating look at the man that tried to push Hollywood to become more adventurous and then discovered that mainstream Hollywook movies passed him by. If you are interested in "true" history, then you would probably be interested in "Black Hawk Down", the harrowing story of the 99 Army Rangers caught having to spend the night in downtown Mogadishu after a raid to arrest clan leaders. The battle is described as the "biggest firefight since Vietnam" and will give you a much greater respect for the men (and women) who go overseas to help others. Lastly, this week I have "Hannibal", Thomas Harris' long awaited sequel to "Silence of the Lambs". It is a compelling, shocking story that you will be thinking about for a long time. Find out more at my Book of the Week Page. In response to the popularity of the latest Austin Power's movie, I thought I'd put in a movie that <i>I</i> loved as a teenager; "Hooper" starring Burt Reynolds and Sally Fields. My thirteen year old grudgingly admits that it is as good as Austin Powers (which he's seen twice so far). Previously, the video of the week was Mike Nichol's "Primary Colors". A few weeks ago I presented "The Newsroom" as one of the best shows ever on TV because of it's final, one hour episode, "The Election" in which the laid off news team get their stupid, vain and sexually disfunctional anchor elected as an Ontario Liberal MPP. Well, since I saw this episode for the first time, two things have happened; the first is, we had an election here in Ontario that was more ridiculous than anything you could put on a screen and secondly, I saw "Primary Colors". I am not a big fan of John Travolta, but he does do an excellent job as a Bill Clinton clone that can't keep his pants zipped up. Emma Thompson does her typically excellent job as his continually humiliated wife that you spend the whole movie wondering how much she knows. Take a look at it at my Video Corner. This week's survey is "Who is the funniest guy in the movies"? Drop by my Survey Page and tell me if I'm all wet about Mike Meyers. I hope this article is useful for you and if you have any questions, please send them to me at: myke@passport.ca Myke Copyright and Warranty Statement This article is presented on an "AS IS" basis. I have tested the circuit and code that I have presented here and I am confident that it works on the hardware that I have used. Different hardware may result in different results. If you have problems with the circuit or software, I would be happy to talk with you about it, but I cannot guarantee that it will work for you under all circumstances. The text, drawings, circuit and software are Copyright © Myke Predko, 1999.For technical support with this project please contact Myke Predko at: myke@passport.ca The information contained here cannot be reproduced without the author's permission. Program the PIC in simple BASIC using the PicBasic Compiler. Visit: http://www.rentron.com/PicBasic2.htm
|