Polling for button inputs while delaying other actions?
Donnie Barnes
Posts: 6
I'm going to do some more complex stuff, but for the sake of argument, let's say I have a serial LCD and a button connected to the BS2p. The serial LCD has a backlight on/off command you can send it. I also have an AD592 temp probe connected.
Now, I can talk to the LCD fine. I can get temp info from the AD592 fine. I can sample the AD592 every five seconds and display the results on the LCD fine. What I can't quite figure out is how to poll the button state and use that info to set the backlight state very well. I got something that sort of works, but I think I need to see some fairly similar sample code that *really* works to better know what I'm doing. This lack of interrupt thing is what's getting me. Any pointers to sample code? Or general instruction?
An ugly code snippet of what I have:
That sort of works, but you can still miss the button press easily and you can't press the button more than once per second. There's got to be a better way. Basically, I only want to update the temp every five seconds, but I want to be able to turn the backlight on and off any time (and this will lead into more complex stuff, but I have to get this issue "better" first).
--Donnie
Now, I can talk to the LCD fine. I can get temp info from the AD592 fine. I can sample the AD592 every five seconds and display the results on the LCD fine. What I can't quite figure out is how to poll the button state and use that info to set the backlight state very well. I got something that sort of works, but I think I need to see some fairly similar sample code that *really* works to better know what I'm doing. This lack of interrupt thing is what's getting me. Any pointers to sample code? Or general instruction?
An ugly code snippet of what I have:
Main: LOW 5 ' Discharge the capacitor. RCTIME 5, 0, rct ' Time for the volts to rise to 1.3 V. TK = Kal/rct*10 + (Kal//rct*10/rct) ' Calculate Kelvin TC = TK - 273 ' and Celsius. TF = ((TC*18) + 320) / 10 ' and Fahrenheit SEROUT TX, LcdBaud, [noparse][[/noparse]LcdLine1, DEC TC, " degrees C", LcdLine2, DEC TF, " degrees F"] FOR COUNTER = 1 TO 200 : IF IN7 = 0 THEN IF BLSTATE = 1 THEN SEROUT TX, LcdBaud, [noparse][[/noparse]LcdBLOn] BLSTATE = 0 PAUSE 1000 ELSE SEROUT TX, LcdBaud, [noparse][[/noparse]LcdBLoff] BLSTATE = 1 PAUSE 1000 ENDIF ELSE PAUSE 25 ENDIF NEXT GOTO Main END
That sort of works, but you can still miss the button press easily and you can't press the button more than once per second. There's got to be a better way. Basically, I only want to update the temp every five seconds, but I want to be able to turn the backlight on and off any time (and this will lead into more complex stuff, but I have to get this issue "better" first).
--Donnie
Comments
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
--Donnie
For the temp update, keep a variable with the loop count at the last temp update. Each time through the loop, look at the elapsed time. If it's greated than some value, update the LCD and reset the saved loop count to the current count.
You can add other "timed" actions in the same or similar ways. You can add other buttons as well. As the loop gets busier, you may need to either go to a faster processor or go to a tiered setup where you check some things every time through the loop, but check some things maybe every 4 times through the loop or every 16 times. That way you might check AA on one time through the loop, BB on the second time, CC on the third time, and DD on the fourth time. ZZ you might check every time through the loop, etc.
·
·· You beat me to the punch on mentioning removing the PAUSE commands.· ·
·
Donnie,
·
·· What I would do is implement a passive counter variable that is reset whenever the button is pushed.· That same event also cause the backlight to come on…Once the timer reaches a certain value you turn the backlight off.· You can use the MAX operator to limit the count and keep it from rolling over.· This same counter can also implement your debounce that Mike mentioned.
·
·· So think of it like this.· You have your loop running with no delays in it.· Read the sensor, update the display, check for button presses and increment the counter.· While incrementing the counter you limit it to some value less than its maximum value.·· You will have a conditional check for two things…the first is if the button is pressed.· When it is you can reset the counter, wait for so many ‘ticks’ to go by (this is your debounce) then turn on the backlight and continue the loop.· Your other conditional is simply waiting for the counter to reach its upper limit (which will be less than the max value you set).· When that has been reached the backlight turns off.· Ticks refers to passes through the loop, in this case, and the timing is somewhat indeterminate without experimenting.· I hope this offers some useful information.· Take care.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
Problem is that I think it executes too fast on the iterations where it doesn't update the screen and such. I now get a semi-accurate first reading, and then garbage for every reading after that. If I put a DEBUG in there to spit out a line of info on every iteration, the data stays fairly good (I presume because the overhead to spit that entire line of text out to the serial port is fairly high compared to a few compare instructions and then a loop). The backlight debouncing and operation seems okay, though perhaps needs a bit more tweaking on the timing part.
Oddly enough, if I comment out the loop around the SEROUT, then the data doesn't get munged at all. But the backlight timing is now way off (and doesn't seem repairable with the current setup) and won't work. Of course, the screen updates a lot more than I'd really like, too.
Thoughts? I'm sure my code could be more elegant, but I'm also guessing I'm still missing something. Or maybe my capacitor type was a bad choice...
--Donnie
Any way you choose above, I think I would approach this by making the loop about the button itself. I would have a counter set up to count "iterations" of the loop and then through trial/error discover how many iterations (ticks) of the loop=1 second, and make that a "trigger" value.
Then setup a "seconds" counter would be iterated whenever the "ticks" is equal to the "trigger" value. When the Seconds counter reaches "5" (or whatever # of seconds you prefer as the delay between temperature updates) then run a suibroutine that reads the temp and updates the LCD.
Inside the loop itself, have a flag for Back Light status (a bit sized variable will do). If the button is pressed, change the state of the backlight and toggle the Back Light Status bit. The Back Light Status bit is used to make sure the command for the LCD (turn backlight on/off) is only sent ONE TIME for each change of button state. I have banged together some code that shjould illustrate the point (and might work!). Note: This is untested!
Hope this helps!
Vern
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Post Edited (Vern) : 12/14/2006 3:28:45 AM GMT
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com
Post Edited (Tracy Allen) : 12/14/2006 8:20:29 AM GMT
·
·· I am impressed…I usually don’t explain passive counters very well, but your display routine uses it perfectly.· It will update the LCD every 1500 iterations through the loop.· The problem I see is with the button routine.· DEBOUNCE will be at an indeterminate value when you press the button and the check for a value is inside of a conditional, which means it may not activate most times.·
·
·· Using DEBUG can you tell if the data is getting garbled outside the LCD Print routine?· Or does adding the DEBUG line fix what you see on the LCD as well?· Can you post a fully working version of this code?· As a side-note are you sure about your value of 5 in your RCTIME command?
EDIT: Just noticied Tracy's post...If you have a BS2p that might be something to look into as well.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
I originally thought the POLL thing was cool, then upon reading more (and getting further responses) liked the idea of not using separate programs in different slots. That seems a bit hacky to me. But I didn't read further at the time on POLL to know I didn't *need* to use separate program slots. I do have a bs2p, so Tracy's solution does look pretty good. I'll try that.
As for the 5 in my RCTIME, that came straight from the sample code for the AD592 on the Parallax web site. The constant from the sample code seems to need to vary fairly wildly depending on how tight these loops are, though, which makes me wonder about my electronics side a little. Could be that my capacitor isn't what I think it is, and I could also have a lot of noise affecting things on the professional developer board (I didn't clip legs on the resistor or capacitor, etc). I haven't tried calibrating it with the cold bath yet, but I'm just going by getting reasonably close to what I think the room temp is.
Oh, I'm also not sure why in my code DEBOUNCE would ever be indeterminate. I set it to zero to start with, I increment it every tick (or trip through the main loop), and I reset it to zero if it's held long enough. What am I missing there?
--Donnie
·
·· What appears to be wrong with the debounce section is that yes, you are incrementing the counter and yes, you do reset it, but only if it was at 60 when it entered the loop.· Say during the loop the DECOUNCE counter is at 61 when you press the button…now your code will have to loop all the way through 65476 more iterations to get to the inner loop that resets.· Then, depending on how long you hold the button down it may actually run that loop a few times while the button is down.· You don’t actually want to increment your DEBOUNCE counter until the button is pressed.· It should be reset when the button is first pressed, or optionally, when released.
·
·· I didn’t catch the reference to the AD592, so maybe there is a small problem with your circuit or it could be the tolerance of the capacitor.· I hope this helps.· Take care.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chris Savage
Parallax Tech Support
I see what you meant about how DEBOUNCE could get incremented by the "dirt" on the up/down side. With this it seems like it can only get incremented if the previous loop through was positive, which should actually do some debouncing rather than just going with 85 possibly-non-contiguous instances of pushed state. Right? (PUSHED is a Bit that was set to zero initially, too.)
This seems to work better, but still occasionally "misses" a button press (and I've futzed with the DEBOUNCE limit anywhere from 25 to 150...25 does seem to short and flashes if you hold the button very long and 150 seems too long as you *have* to hold it a bit to change state, but no number seems "just right." Obviously I could still have a dumb error in my code. [noparse]:)[/noparse]
--Donnie
·
For example, add a cmos latch to your button and tie the latch's output to the STAMP pin you are reading. The latch would be configured to put +5vdc on its output when it is pressed, and then 0vdc the next time it is pressed. This simple toggling of the latch's output would repeat as the button is pressed. The important thing, though, is the state of the latch's output would remain constant until the button is pressed again.
·
You could then in your program read the state of the latch - WHENEVER YOUR CODE GETS THE OPPORTUNITY- and store it. After the first time, it is read, you then read it and if it is different from the last time you read it, you know the button was pushed- you do something with your code, reset the previous saved value, and continue on (you don't worry about the actual state of the button or latch, only that it has changed from the last time you checked it). This makes the timing issue you mentioned much less of an issue.
I do have a degree in computer engineering (which is much like EE), but the problem is I haven't had to use much of the programming or hardware side of that in many years now. It's coming back to me, albeit more slowly than I'd like.
--Donnie
The BS2p is faster than the BS2. Putting the LOW after, instead of before, stops the charging toward 5 volts and assures that the pin is at zero volts the next time around. Otherwise the capacitor continues charging to a level that depends on how much other stuff there is in the program and the time between the LOW 5:RCTIME is not enought to completely discharge that. It is also important to use a capacitor with low soakage. The capacitor should be a film type and the wiring fairly short and direct for best results.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Tracy Allen
www.emesystems.com