#picaxe 08M2
symbol sw1pin = pinC.2 ' Switch input
symbol doubletime = 10 ' Time value for doubleclick
symbol longtime = 25 ' Time value for longpress
symbol sw1 = bit8 ' sw1 copy
symbol sw1mem = bit9 ' sw1 memory
symbol sw1Press1 = bit10 ' Set on first press of doubleclick
symbol sw1timerShort = w3 ' Timer for doubleclick
symbol sw1timerLong = w4 ' Timer for longpress
' Code starts here
' ----------------
sertxd("-- Start --",cr,lf)
do ' Main loop
' Copy pinstate to bit
sw1 = sw1pin
' Detect button press
if sw1 = 1 and sw1mem = 0 then
' Button pressed
sertxd("++Pressed ",#sw1timerShort,cr,lf)
if sw1Press1 = 0 then ' This could be first press of doubleclick
sw1timerShort = doubletime ' Start doubleclick timer
sw1Press1 = 1 ' Set click memory
sertxd("First press ",#sw1timerShort,cr,lf)
else ' This is second press of doubleclick
sertxd("Second press ",#sw1timerShort,cr,lf)
sw1Press1 = 0
if sw1timerShort > 0 then ' If timeout not expired
' Switch doubleclicked
sertxd("++Doubleclicked ",#sw1timerShort,cr,lf)
sw1timerShort = 0 ' Clear doubleclick timer
endif
endif
endif
' Detect button release
if sw1 = 0 and sw1mem = 1 then
' Switch released
sertxd("++Released ",#sw1timerShort,cr,lf)
sw1timerLong = 0 ' Reset longtimer
endif
sw1mem = sw1
' Doubleclick timer
if sw1timerShort > 0 then
dec sw1timerShort
if sw1timerShort = 0 then
sw1Press1 = 0
endif
endif
' Longpress detect
if sw1 = 1 then
inc sw1timerLong
if sw1timerLong > longtime then
sw1timerLong = 0 ' Reset longtimer
sw1Press1 = 0 ' Reset doubleclick flag
sertxd("++LongPressed ",#sw1timerShort,cr,lf)
endif
endif
'
'
'
'
'
loop
#picaxe 08M2
#no_data
symbol key = pinc.2
symbol timeout = 10 ; For simulator, longer for real time
symbol elapsed = b1
symbol pressed = 1
symbol released = 0
main:
elapsed = 0
do : loop until key = released ; Ensure we start with button released
do : loop until key = pressed ; Wait for button to be pressed
do
inc elapsed
if elapsed > timeout then long
loop until key = released ; Fall through if button released (short press)
do
inc elapsed
if elapsed > timeout then short ; Not pressed again before the timeout
loop until key = pressed ; Fall though if double
double:
sertxd("Double Click",cr,lf)
goto main
long:
sertxd("Long Press",cr,lf)
goto main
short:
sertxd("Single Press",cr,lf)
goto main
A few years ago I wrote a program to handle a range of press lengths of a couple of switches using a 100mS Timer interrupt (28X2). As you can imagine, the code was considerably more complicated than the solutions already offered!
I used interrupts to handle the switch timing because my foreground task/s were quite time sensitive and I didn't want the foreground tasks to be slowed significantly when a key was pressed.
Thank you for this, I will try it out and report back in due course.Hi,
It depends when you need to (or can) receive a "notification" of the button state(s). For example the leading edge cannot tell you anything about the type of press, but you might want to be notified to sound a tone/click or light a LED, etc.. Similarly, the first (short) release cannot tell you if there is a double-click to come.
Which got me thinking if a single timing period is all that might be needed. So here's my "minimalist" version (but maybe hippy will do better).
Could have written it as a subroutine to get rid of those pesky GOTOs, but you might want to do more than just send a SERTXD.Code:#picaxe 08M2 #no_data symbol key = pinc.2 symbol timeout = 10 ; For simulator, longer for real time symbol elapsed = b1 symbol pressed = 1 symbol released = 0 main: elapsed = 0 do : loop until key = released ; Ensure we start with button released do : loop until key = pressed ; Wait for button to be pressed do inc elapsed if elapsed > timeout then long loop until key = released ; Fall through if button released (short press) do inc elapsed if elapsed > timeout then short ; Not pressed again before the timeout loop until key = pressed ; Fall though if double double: sertxd("Double Click",cr,lf) goto main long: sertxd("Long Press",cr,lf) goto main short: sertxd("Single Press",cr,lf) goto main
Cheers, Alan.
Thank you for this code. I will try it out and report back to the forum thread in case others have the same problem.Hi mrm,
This is some code to get you started. It does not use interrupts, and should run on any chip.
The two time values are counts which are updated each time the main loop executes. You will need to select suitable values by trial and error.
The sertxd lines starting with ++ are where you could put calls to subroutines
It runs in the simulator, so I suggest you try it there and get a feel for how it works, and if it does what you need.
Cheers,
Buzby
Rich (BB code):#picaxe 08M2 symbol sw1pin = pinC.2 ' Switch input symbol doubletime = 10 ' Time value for doubleclick symbol longtime = 25 ' Time value for longpress symbol sw1 = bit8 ' sw1 copy symbol sw1mem = bit9 ' sw1 memory symbol sw1Press1 = bit10 ' Set on first press of doubleclick symbol sw1timerShort = w3 ' Timer for doubleclick symbol sw1timerLong = w4 ' Timer for longpress ' Code starts here ' ---------------- sertxd("-- Start --",cr,lf) do ' Main loop ' Copy pinstate to bit sw1 = sw1pin ' Detect button press if sw1 = 1 and sw1mem = 0 then ' Button pressed sertxd("++Pressed ",#sw1timerShort,cr,lf) if sw1Press1 = 0 then ' This could be first press of doubleclick sw1timerShort = doubletime ' Start doubleclick timer sw1Press1 = 1 ' Set click memory sertxd("First press ",#sw1timerShort,cr,lf) else ' This is second press of doubleclick sertxd("Second press ",#sw1timerShort,cr,lf) sw1Press1 = 0 if sw1timerShort > 0 then ' If timeout not expired ' Switch doubleclicked sertxd("++Doubleclicked ",#sw1timerShort,cr,lf) sw1timerShort = 0 ' Clear doubleclick timer endif endif endif ' Detect button release if sw1 = 0 and sw1mem = 1 then ' Switch released sertxd("++Released ",#sw1timerShort,cr,lf) sw1timerLong = 0 ' Reset longtimer endif sw1mem = sw1 ' Doubleclick timer if sw1timerShort > 0 then dec sw1timerShort if sw1timerShort = 0 then sw1Press1 = 0 endif endif ' Longpress detect if sw1 = 1 then inc sw1timerLong if sw1timerLong > longtime then sw1timerLong = 0 ' Reset longtimer sw1Press1 = 0 ' Reset doubleclick flag sertxd("++LongPressed ",#sw1timerShort,cr,lf) endif endif ' ' ' ' ' loop
Hi Alan,... the first (short) release cannot tell you if there is a double-click to come. ...
_______ _______ _______
Switch : ___________| |_______________________________________________| |__________| | _____________________________
^----------------------^ ^----------------------^
| | | |
Start Dclick timer Timer done Start Dclick timer Timer done
( Single click ) ( Double click )
Yes; that's the only way to do it.I'm thinking there maybe a way to separate single and double clicks by delaying the result until we are sure what it is
#Picaxe 08M2
#Terminal 4800
#No_Data
Symbol BTN = pinC.3
Symbol PUSHED = 1
Symbol LONG_TIMEOUT = 500
Symbol GAP_TIMEOUT = 500
Symbol NO_PUSH = 0
SYmbol SHORT_PUSH = 1
Symbol LONG_PUSH = 2
Symbol DOUBLE_PUSH = 3
Symbol timeout = w1 ; b3:b2
Symbol action = b4
MainLoop:
Do
Gosub CheckButtonPush
Select case action
Case SHORT_PUSH : SerTxd( "Short Push", CR, LF )
Case LONG_PUSH : SerTxd( "Long Push", CR, LF )
Case DOUBLE_PUSH : SertXd( "Double Push", CR, LF )
End Select
Loop
CheckButtonPush:
If action = DOUBLE_PUSH Then
Do : Loop Until BTN <> PUSHED
End If
action = NO_PUSH
If BTN = PUSHED Then
timeout = LONG_TIMEOUT
Do
timeout = timeout Min 10 - 10
Pause 10
Loop Until BTN <> PUSHED
If timeout = 0 Then
action = LONG_PUSH
Else
action = SHORT_PUSH
End If
timeout = GAP_TIMEOUT
Do
timeout = timeout - 10
Pause 10
Loop Until timeout = 0 Or BTN = PUSHED
If timeout <> 0 Then
action = DOUBLE_PUSH
End If
End If
Return
Thank you for this. Will try it out and report back for the record. Thank you also to Allycat, inglewoodpete and Buzby for the helpful input.Yes; that's the only way to do it.
Code:#Picaxe 08M2 #Terminal 4800 #No_Data Symbol BTN = pinC.3 Symbol PUSHED = 1 Symbol LONG_TIMEOUT = 500 Symbol GAP_TIMEOUT = 500 Symbol NO_PUSH = 0 SYmbol SHORT_PUSH = 1 Symbol LONG_PUSH = 2 Symbol DOUBLE_PUSH = 3 Symbol timeout = w1 ; b3:b2 Symbol action = b4 MainLoop: Do Gosub CheckButtonPush Select case action Case SHORT_PUSH : SerTxd( "Short Push", CR, LF ) Case LONG_PUSH : SerTxd( "Long Push", CR, LF ) Case DOUBLE_PUSH : SertXd( "Double Push", CR, LF ) End Select Loop CheckButtonPush: If action = DOUBLE_PUSH Then Do : Loop Until BTN <> PUSHED End If action = NO_PUSH If BTN = PUSHED Then timeout = LONG_TIMEOUT Do timeout = timeout Min 10 - 10 Pause 10 Loop Until BTN <> PUSHED If timeout = 0 Then action = LONG_PUSH Else action = SHORT_PUSH End If timeout = GAP_TIMEOUT Do timeout = timeout - 10 Pause 10 Loop Until timeout = 0 Or BTN = PUSHED If timeout <> 0 Then action = DOUBLE_PUSH End If End If Return
GOTOs are generally regarded as an arch crime against logic these days but sometimes they are the simplest way particularly when the program is only 20 lines. The main complaint I have with picaxe basic is that you cannot pass arguments to subroutines (unless someone knows better) but controlled use of goto's is fine.Hi,
It depends when you need to (or can) receive a "notification" of the button state(s). For example the leading edge cannot tell you anything about the type of press, but you might want to be notified to sound a tone/click or light a LED, etc.. Similarly, the first (short) release cannot tell you if there is a double-click to come.
Which got me thinking if a single timing period is all that might be needed. So here's my "minimalist" version (but maybe hippy will do better).
Could have written it as a subroutine to get rid of those pesky GOTOs, but you might want to do more than just send a SERTXD.Code:#picaxe 08M2 #no_data symbol key = pinc.2 symbol timeout = 10 ; For simulator, longer for real time symbol elapsed = b1 symbol pressed = 1 symbol released = 0 main: elapsed = 0 do : loop until key = released ; Ensure we start with button released do : loop until key = pressed ; Wait for button to be pressed do inc elapsed if elapsed > timeout then long loop until key = released ; Fall through if button released (short press) do inc elapsed if elapsed > timeout then short ; Not pressed again before the timeout loop until key = pressed ; Fall though if double double: sertxd("Double Click",cr,lf) goto main long: sertxd("Long Press",cr,lf) goto main short: sertxd("Single Press",cr,lf) goto main
Cheers, Alan.
That can be faked if one is prepared to get creative ...The main complaint I have with picaxe basic is that you cannot pass arguments to subroutines (unless someone knows better)
#Define PrintSquared(n) DummyRoutine : b1 = n : Gosub Do_PrintSquared
For b0 = 1 To 10
Gosub PrintSquared(b0)
Next
End
Do_PrintSquared:
w1 = b1 * b1
SerTxd( #b1, " squared is ", #w1, CR, LF )
Return
DummyRoutine:
Return
Symbol number = b0
Symbol PrintSquared.arg = b1
Symbol PrintSquared.sqr = w1
For number = 1 To 10
PrintSquared.arg = number
Gosub PrintSquared
Next
End
PrintSquared:
PrintSquared.sqr = PrintSquared.arg * PrintSquared.arg
SerTxd( #PrintSquared.arg, " squared is ", #PrintSquared.sqr, CR, LF )
Return
Unfortunately #MACRO does not seem to exist on MacAXEpad?That can be faked if one is prepared to get creative ...
The main issue is the PICAXE isn't a 'stack machine', everything is global. Though PUSH/POP and PUSHRAM/POPRAM can be used on the X2's and there are also tricks with @ptr and @bptr to be had.Code:#Define PrintSquared(n) DummyRoutine : b1 = n : Gosub Do_PrintSquared For b0 = 1 To 10 Gosub PrintSquared(b0) Next End Do_PrintSquared: w1 = b1 * b1 SerTxd( #b1, " squared is ", #w1, CR, LF ) Return DummyRoutine: Return
Local variables can sort of be faked, though not entirely elegantly ...
With clever use of #INCLUDE, #DEFINE and #MACRO commands one can hide implementation details away from the main program, have the main program looking like parameter passing is allowed.Code:Symbol number = b0 Symbol PrintSquared.arg = b1 Symbol PrintSquared.sqr = w1 For number = 1 To 10 PrintSquared.arg = number Gosub PrintSquared Next End PrintSquared: PrintSquared.sqr = PrintSquared.arg * PrintSquared.arg SerTxd( #PrintSquared.arg, " squared is ", #PrintSquared.sqr, CR, LF ) Return
In most cases though it's more effort than its worth.
This works brilliantly and very simple to implement.Yes; that's the only way to do it.
Code:#Picaxe 08M2 #Terminal 4800 #No_Data Symbol BTN = pinC.3 Symbol PUSHED = 1 Symbol LONG_TIMEOUT = 500 Symbol GAP_TIMEOUT = 500 Symbol NO_PUSH = 0 SYmbol SHORT_PUSH = 1 Symbol LONG_PUSH = 2 Symbol DOUBLE_PUSH = 3 Symbol timeout = w1 ; b3:b2 Symbol action = b4 MainLoop: Do Gosub CheckButtonPush Select case action Case SHORT_PUSH : SerTxd( "Short Push", CR, LF ) Case LONG_PUSH : SerTxd( "Long Push", CR, LF ) Case DOUBLE_PUSH : SertXd( "Double Push", CR, LF ) End Select Loop CheckButtonPush: If action = DOUBLE_PUSH Then Do : Loop Until BTN <> PUSHED End If action = NO_PUSH If BTN = PUSHED Then timeout = LONG_TIMEOUT Do timeout = timeout Min 10 - 10 Pause 10 Loop Until BTN <> PUSHED If timeout = 0 Then action = LONG_PUSH Else action = SHORT_PUSH End If timeout = GAP_TIMEOUT Do timeout = timeout - 10 Pause 10 Loop Until timeout = 0 Or BTN = PUSHED If timeout <> 0 Then action = DOUBLE_PUSH End If End If Return
Yes, for quite some time now, I've just reserved b1 and w1 as "tempb" and "tempw" to pass parameters (and for any other "local" purposes) which seems much the same in practice. But this also works on (the faster) PE5 and Axepad.In most cases though it's more effort than its worth.