bounce baby bounce
Zap-o
Posts: 452
Fellas I wonder if there is a better way to de bounce the three buttons I am working with. here is what I got so far.
All the buttons are tied high until pressed. Its working but the response is a bit sloppy. Any ideas and other ways I could go about this would be great. I am basically incrementing a number on a display when the y up arrow button is pressed and decrementing a number when the down button is pressed. The on/off toggles an led.
Pub CheckButtons Button_temp := INA [noparse][[/noparse] 0..2 ] Case Button_temp %111 : Button_idx := 0 %011 : Button_idx++ IF (Button_idx => 1500) !outa [noparse][[/noparse] Led ] Button_idx := 0 %101 : Button_idx++ IF (Button_idx => 950) Scrap := Math.Fadd(Scrap,0.01) UpDateDisplay Button_idx := 0 %110 : Button_idx++ IF (Button_idx => 950) Scrap := Math.Fsub(Scrap,0.01) UpDateDisplay Button_idx := 0
All the buttons are tied high until pressed. Its working but the response is a bit sloppy. Any ideas and other ways I could go about this would be great. I am basically incrementing a number on a display when the y up arrow button is pressed and decrementing a number when the down button is pressed. The on/off toggles an led.
Comments
Edited to replace vanishing brackets with curly braces.
Rich H
Post Edited (W9GFO) : 6/18/2009 2:58:28 AM GMT
Well ... the implementation depends on what your button-reading loop is doing. Is it only reading the buttons or does it have other tasks? In other words: Is it OK that the CheckButtons function is blocking or not. If it is allowed to be blocking it's easy:
1. Read INA
2. Run whatever you want to run
3. Wait for 1ms for debouncing - bouncing usually only takes some 100usec
4. a) wait until the button is released or b) a key-repeat-rate timeout·has been·reached
if you only implement a) then it's blocking as long as the button is pushed
if you implement a) and b) it's blocking until the timeout has been reached
If blocking is not allowed at all CheckButton needs a state so that it knows what to do next:
state 0 => read INA (initial state), state 1 => debounce, state 2 => wait for release/key-repeat-rate
with the following transitions:
state 0 and no button pushed: return immediately
state 0 and button is pushed: do whatever has to be done and set state to 1 (store cnt for time measurement)
state 1 and 1ms not passed: return immediately
state 1 and 1ms passed: set state to 2 (store cnt in case you want the key repeat)
state 2 and button not released and time not passed: return immediately
state 2 and button released or time passed: set state to 0
Post Edited (MagIO2) : 6/18/2009 7:16:43 AM GMT
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Style and grace : Nil point
It's still a waste of a COG in my opinion to have a simple button-checker running in it's own COG doin nothing else. If you need your application to be responsive, then make the CheckButtons function non-blocking and call it whenever possible. Usually the main program of user-interface-driven applications wastes a lot of time simply for waiting. If you have a number-crunching application which runs for hours, why not add the CheckButtons call in the number crunching loop?
Other way would be to add this kind of code into a driver - say the keyboard driver for example. So, whenever the driver waits for the next command it simply checks the buttons as well. Implementation of the state machine would simply be self-modifying code in this case.
Post Edited (MagIO2) : 6/18/2009 11:39:17 AM GMT
www.ganssle.com/debouncing.pdf
Leon
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Amateur radio callsign: G1HSM
Suzuki SV1000S motorcycle
And if you run out of cogs, identify tasks, which can be done by one cog. Or attach a second propeller. But when you leave the paradigm of parallel processing, you run into the debris you call "the rest of the world" .
So "Hello World" in Spin is "hello debris"
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
cmapspublic3.ihmc.us:80/servlet/SBReadResourceServlet?rid=1181572927203_421963583_5511&partName=htmltext
And even if you have a cog left over, why would you want to consume additional power just for running a button checker COG, when the button checker code can be run in one of the COGs that wait most of the time?
You say "And if you run out of cogs, identify tasks, which can be done by one cog." - so, I'm only one step ahead when I say "Don't waste a COG for that from the beginning".
Usually you start with implementation of a project and you don't exactly know where you will end. In terms of COG usage you don't know how much COGs you need for other stuff in advance : ".... oh ... I could add feature x and y to my project. That would be fun." If you now have to rewrite your button input because you have to free the COG again it might be more difficult than doin it clever in the beginning.
That's only my personal opinion ... so, feel free to do it the wrong way ;o)
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Style and grace : Nil point
I am going to try to use a timer of some kind today Ill keep you all updated.
At the risk of drawing MAGIO2's ire, I did exactly what you propose to do. I agree with him that dedicating an entire cog to watching for buttons is wasteful. I was charged with re-doing a piece of equipment. The biggest complaint with the old equipment was the buttons weren't responsive. So my first design decision was not to miss any presses. And I did it by dedicating an entire cog. In my application the when a button is pressed, it drives a line high.
And here's an example dealing with those buttons and a 4x20 LCD
Peter
Waste of COG, waste of memory, waste of runtime ... all nicely packed in one little program. Let me guess: You learned programming on megabyte machines and did not realize yet, that the propeller has less memory.
Now .. let's start to analyse:
MAIN_MENU
=========
I guess the first lines of code repeat in the other MENU functions as well (SECOND_MENU and COUNTDOWN_MENU). So, what I would have done is create a function PRINT_MENU which prints any menu. So, all the .cls, .gotoxy and .str function calls are only needed once even if you have multiple menus. The strings including the positions can be arranged in a dat section. So, when you call PRINT_MENU you only pass the adress of the menu you want to print.
repeat loop .... what does it do? Most of the time NOTHING but wait for the button variables to change. So, please tell me why can't you call a non-blocking getButton function there? No need for an extra·COG that watches the buttons,·your main·COG has plenty of time to do this. The code behind the CLEAR_PRESSES, UP_PRESSES and DOWN_PRESSES is done in microseconds, so the buttons will be pretty responsive.
In the other menus you add the same getButton, so no problem there as well.
I guess SET_INTERVAL needs it's own button handling, so getButton will be needed there too.
And GPS_SCREEN looks like you have a loop there which reads GPS data and displays that. Loop ... loop ... hmmm ... easy to call the getButton there as well for stopping the loop, go back to the menu or whatever.
In each of the functions you have to check for CLEAR_PRESSES, UP_PRESSES... anyway if you want it to respond to the buttons. So, what's the difference to calling getButton?
CLEAR_PRESSES, UP_PRESSES, DOWN_PRESSES ... at least one byte each where one bit would be enough.
BUTTONS
=======
Please read the PDF that Leon mentioned. Your BUTTON function only debounces but it doesn't protect against EMI (my suggestions from the earlier posts didn't as well). Switching on a fluorescent lamp·might be·enough to·confuse your application.
Why do you call delay in each if? It would be enough to call it in the end once.
This would be an equivalent to your implementation with no noticable differences regarding look and feel. (But it's still not how I'd do it) Maybe I'll find some time this weekend to
·code a demo for you.
·
I'm glad I did at least one thing right
You have to admit that at least there was efficiency in that.
I don't mind your deconstruction of my code. Just for the record, I didn't learn programming on a megabyte machine. I've been learning it on the Prop (although some may argue with how successful I've been at that). I appreciate that rather then saying what an awful example of coding I've provided, you are willing to make a few suggestions. For that I thank you.
I'm aware of the Prop's memory limitations, but also feel that readable and simple code makes life easier for me when writing, debugging, and maintaining code. Which is why I opted for byte long variables for the buttons rather than having to mask them off. But I don't think that's what's at the heart of your critique and the example you provided shows using bits can still be a clear way of coding.
As I mentioned in my original post, my charge was not to miss button presses. I knew full well I this design would have one entire cog simply waiting for buttons to be pressed. And I can honestly say that I did so knowingly (please don't have a heart attack). Not because I fail to recognize the value or scarcity of resources on the Prop but because I knew I could get the rest of my program into the remaining space, and more importantly, because I couldn't/didn't think of a better way. I'm not sure the example you showed
can solve my particular problem. If it can, I hope you're patient (and merciful [noparse];)[/noparse] ) enough to show me how. I think there are 2 problems:
1. When the user enters the COUNTDOWN or GPS screens (menu may not be a good choice of name) the display needs to update even while no buttons are pressed. I believe that the way you coded it below, once I call BUTTON I don't return until a button is pressed (which would achieve my stated goal of not missing button presses) but it would leave the GPS screen (for instance) stuck until the next button press, wouldn't it? The GPS screen needs to refresh every second to show the new time,date, position, etc. Likewise for the COUNTDOWN screen.
2. I don't see how, using a nonBlocking getButton function, you can be sure to check the current state of the buttons frequently enough to assure you haven't missed one. The things the main cog needs to do while in screens other than the MAIN_MENU may require that cog's attention for many, even hundreds of milliseconds. The arrangement I have allows the prop to notice buttons presses while the main cog is away doing something else. And because it's running in a separate cog it is watching for button presses ALL THE TIME. I realize my MAIN_MENU method (and others like it) only do something ABOUT those presses when they get around to it. But that's the whole point. It can be busy when a button is pressed (and released) but it finds out about it when it gets back. I'm not sure I see a nonBlocking solution to that. If you can show me, I'll be more than happy to incorporate that into my code (with appropriate credit, of course).
The only line from the first chunk that repeats (exactly as it is) in each menu is the cls. That's certainly shouldn't get it's own function. However you seem to suggest a way that I could populate a DAT section with whatever I want printed, then simply print that. That's what I wanted to do originally- have the different methods put what they wanted printed into a "buffer" and then simple call a method which prints that buffer to the LCD. Is this what you're getting at? I didn't get it working so I moved back to each method printing strings to the LCD where and when it was needed.
You suggest moving the delay in my BUTTONS method from within each IF statement to the end. But I don't want to wait every time through the repeat loop (that would defeat the purpose of putting this method in it's own cog). I simply want to wait a bit only WHEN a button has been pressed. I'm using this as a software debounce. If I move it to the end I save a couple lines of code, but to make it work the same way I need another IF statement. You'll have a hard time convincing me that another if and Delay statement at the end of the BUTTONS method are any more efficient than what I've got.
I may be asking too much, but if you're going to pan my code I can hope you offer a functionally equivalent alternative
Peter
Guess with all the topics this will be what forum users call 'hitchhiking a thread'
Why is everybody so negative ... deconstructing your code ... I don't deconstruct, I step on it to reach the next higher level ... and it's not an awful example, it's a good lesson ;o)
As I stated, the code I quickly wrote down is equivalent to yours, which means that you still have to run BUTTONS in it's own COG. The delay of 100ms does not hurt even if you delay when no button is pressed. The loop is watching the port 10 times per second. That's usually fast enough at least at my age and when you don't plan to use it as a fire button in an ego shooter. The advantage is that waitcnt saves power and the loop is easier to read. (One of the advantages you seem to like ;o)
"The GPS screen needs to refresh every second to show the new time,date, position, etc. Likewise for the COUNTDOWN screen." So, refreshing the screen needs how much COG-time? 100%? Don't think so. So even those parts of the program should have plenty of time to do the check-button job from time to time at a rate where you don't miss a button.
Later this evening I might go and code a bit for this problem. So long ...
I'm glad you can read the humor into my posts. It's hard to know what people's intent/nature is without intonation but you seem to have picked up the correct tone from my post.
I suppose you are right; this thread has been hijacked, but an example of how to do what you propose would put us back to Zap-o's original question. Stable and dependable monitoring of buttons with a software debounce.
I didn't realize that the BUTTONS method you proposed was intended to run in a separate cog.
I still think moving the delay outside the if produces a different behavior. Your loop does look at the state of the buttons 10x per second, but it spends almost all of its time waiting (I realize this reduces current consumption, and therefore saves resources). But this construction spends only a small fraction of its time reading the button states. My version spends nearly all its time looking at the state of the buttons and only waits if a button is pressed. I realize for buttons these may be functionally equivalent and that's enough for me to consider it (for these buttons). But for other signals (say a 1PPS from GPS which is only 100uS wide) your construction could miss a signal that we wanted to catch since the entire signal could have come and gone in the time the cog is sitting in the delay.
Believe it or not this isn't a ego shooter, it's a seismic detonation unit. And the fire button is monitored in PASM in its own cog. No way you are talking me out of that. Checking the fire line only 10x per second is not good enough and would not pass the engineering group even though there are hardware checks before actually firing.
What you've posted so far is educational and points out a possible shortcut in the code but so far isn't an improvement enough for me to give up what I've got working. I'm looking forward to a non blocking button read that won't miss button presses and any suggestions/example you might have for speeding/simplifying getting messages to the display.
Like most folks, I'm here to learn (and teach even if it's what NOT to do).
Regards,
Peter
But you know what you do currently? You massively changed scope of the problem. We started with a simple button for doing some menu stuff or somthing like that ... nothing really critical.
Anyway ... I don't really understand your concerns about the 100ms waittime. I measured the time of faster button pushes and the signal duration was ~200-300ms. For a push at normal speed I measured 600 to 900ms. So, you won't miss a real life button push .. but who knows ... maybe if you trigger a detonation you are in a hurry.
Quick fix for that :
PUB BUTTONS
repeat
buttons |= INA[noparse][[/noparse] 3..0 ]
DelayMS( 10-150*(buttons==0) )
Now it waits 10ms if no button pushed and 160ms if button is pushed. (You see why I said -150... ?)
"1PPS from GPS which is only 100uS wide". I declare that being totally out of scope ;o)
This is a unit still in development so no, the functionality has not yet passed the engineering group. I've read Leon's link and fully agree that a debounce (something I thought was being implemented in software but was not) is necessary. Thank you for helping me see I was not doing what I thought I was. There is a hardware debounce FYI.
I measured the response time of the buttons we are using. One can successfully press and release in less < 50msec, which should be recognized as a press. So while your original code waited around for 100ms, users started wondering if I took seriously their complaints about missing button presses. I see your newest snippet does better, (and yes I know that 150*zero equals and adding 10 still equals 10; I don't mind instruction and I appreciate your suggestions, but please don't talk down to me). However the better and better your snippet gets (with regard to response time) the more it looks like my original one : ( .
I'm still looking forward to the non Blocking read....
p
Hope this is of interest for others as well ;o)
If your menu·does not have empty lines, then you can save the y coordinate in the dat section and use the counter i·in the gotoxy instead. (Hey ... that saves a byte per menu-entry!)
< END HITCHHIKE >
And I'm not sure if you understood the question correctly. What I wanted to point out is why I said 10 MINUS 150... and not 10 PLUS 150... Bet there are newbies out here which would be confused about that. You know .. I like question and answer games .. and usually prefere giving tips over giving completed code. Because things you find out by yourself are much better to remember later on.
Rich H
The red part is a boolean expression which has the result true or false.
false = 0··makes the expression to 10 - 150*0
true = -1 makes the expression to 10 - 150*-1· or ·10+150 in the end.
Sorry if I took offense where none was intended. I was feeling a bit defensive because everytime I post something I get ripped rather than rather than constructively criticized.
I'm not sure where you got the idea that there is no EMI protection. EMI considerations are too important in this application to be left to software alone. The 4 buttons that my example code deals with have a hardware debounce. The two buttons (ARM and CHARGE) which are both required to be pressed while a shot is fired, are 12" apart (requiring 2 hands) in protected button shells (to avoid accidental pressing) and are Double pole-double throw to avoid accidental firing because of bounce issues and are NOT handled by the code above. EMI is certainly considered in the hardware design as is ESD. There are no cell phones allowed at the shooting site. Not because they will accidentally set off a charge, but because they distract people who CAN and MIGHT set off a charge.
Now that I've defended our design, let's get on to more interesting things....
< CONTINUE HITCHHIKE >
The string-to-LCD printing code you provided is certainly not something I'd thought of. I went with what was obvious, mostly because it was obvious and because it was straightforward (in case that's not obvious enough). After reading over your example several times, I understand how it works but I'm not sure what one gains by doing it this way. Maybe this isn't the right metric, but printing out things the way I did it requires 102 bytes; your's 85. There's no denying that your code takes less space. Well done. It's less clear to me what's going on. For maintainability (by me and by others that may work with this code in the future), I'll gladly give up the 17 bytes to have something more readable. What's the reason to chose one over the other?
< END HITCHHIKE >
In an effort to get the thread back on topic and help Zap-o (and me), let's get back to the original challenge: a way to read button presses that is dependable and doesn't require its own cog. I'll admit you've met the challenge when you've provided code or pseudo-code which:
1. does not need its own cog
2. will catch button presses as narrow as 50ms
3. and may not be callable by the main cog for times as long as 100ms.
You may call that last restriction unfair, but that's the problem I was faced with. Maybe I should say that's what my solution provides (and I need it). I never said the methods called from my main menu were only display methods and returned immediately. You assumed that somewhere back there when you said "what a waste of resources..." . I didn't give all my requirements, because I was simply trying to give Zap-o an example that works for my application.
I don't think this is a simple task. I avoided it by giving up on the first requirement and I'm lucky enough to have the cogs available to do it. I look forward to seeing what you come up with. I agree with your philosophy that using cogs unnecessarily is wasteful and poor coding practice. (But I do value some things over efficiency (like readability when resources allow for it - such as the printing code discussed above)). I'd be happy to incorporate what you come up with in my project as long as it's not susceptible to EMI
I'm gone all weekend, but I'm eager to see what people come up with....
Regards,
Peter