Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Using a push button to cycle though routines - Anyone know of an example?

Status
Not open for further replies.

Andy1845c

Active Member
I am looking for an example in ASM of how I can use a single push button to cycle through routines on a pic. I am using a 16F690 or a 16F677, but mostly just want to see how it can be done, not rip it off.

Pretty much what I want is to use 1 button to change led patterns. I did a project once before where I used seperate I/O ports to select patterns, but in this case I want to try somthing new.

I'm pretty green with ASM yet, but its all slowly making more and more sense.
 
Since you already have done before. Hint: as the button is pushed/pressed, increment on one GPR, from the GPR call a look up table for the patterns, after returning from the table then send to output.
 
think of it like counting. If you press a button have it add to a variable
like:

if button1 = 1 then var = var + 1

then for the main code put like

if var1 = 01 do this
if var1 = 02 then do that
if var1 = 03 then do something else

In asm its like a

Code:
Start:
BTFSC   PORTA,1
INCF    VAR1
...
..
.
movlw   0x01
CPFSEQ  VAR1
bra     $+4
goto    do1

movlw   0x02
CPFSEQ  VAR1
bra     $+4
goto    do2
...
..
.
goto    Start
 
Wow, thanks for the quick replys!

I was thinking it would involve incrementing a register.

CPFSEQ? bra? What are those commands? I haven't run across either yet. They are not in the instruction set thing I printed out off the Microchip site.

The other thing I am wondering (and yes, I am getting ahead of myself here) is what if my patterns takes a second or two to complete, and the button is pressed mid pattern? Is there anything I can do software-wise, or will I have to add a capacitor and pull the pin high rather then being able to pull it low?
 
sorry didnt notice "16F677" ill write some small sample in 1 minute...
You can have the button on as a interrupt this way it stops mid way or when ever its pushed . The only thing is you still need checks every now and then to see if the data has changed.
 
To be honest i never use 16F pics but i think this should work fine now sure if it has to be $+2 or $+4

Code:
Start:
BTFSC   PORTA,1
INCF    VAR1
...
..
.
movlw   0x01
XORWF   VAR1,f
BTFSC   VAR1,0
goto     $+4
goto    do1

movlw   0x02
XORWF   VAR1,f
BTFSC   VAR1,1
goto     $+4
goto    do2

movlw   0x03
XORWF   VAR1,f
BTFSC   VAR1,0
goto    $+4
BTFSC   VAR1,1
goto    $+4
goto    do3
...
..
.
goto    Start
 
Last edited:
CPFSEQ? bra? What are those commands? I haven't run across either yet. They are not in the instruction set thing I printed out off the Microchip site.
If you look in the MPASM help file under the heading 12-Bit/14-Bit Instruction Width Pseudo-Instructions, you see some pseudo-instructions which make writing in assembler easier. While there is no bra, there is b, for branch, which is the same as goto. The CPFSEQ (ComPare File Skip if EQual) could be replaced by a subwf and a BZ.
AtomSoft:
You should replace the:
XORWF VAR1,f
BTFSC VAR1,1
with
XORWF VAR1,w
SKPZ

So as to not corrupt the VAR1 for the next instruction.
 
Last edited:
I'll use the table, as I said:
Code:
Start
    btfss    PORTA, 0    ;check for input
    goto    $-1
    call    Delay        ;delay for switch debounce
    incf    var1, f
    movf    var1, w
    call    Table
    movwf    PORTB        ;output to LEDs
    goto    Start        ;do it again

Table
    addwf    PCL, f
    retlw    b'00001111'
    retlw    b'11110000'
    ;
    ;
    ;followed by the patterns

You will only need to add the patterns into the look up table.
 
You could try a simple state machine. Each press goes to the next routine and is pretty easy in ASM ?

loop1
is button pressed ?
no = goto loop1
yes = set pattern 1
loop1r
is button released ?
no = goto loop1r
loop2
is button pressed ?
no = goto loop2
yes = set pattern 2
loop2r
is button released ?
no = goto loop2r
loop3
is button pressed ?
no = goto loop3
yes = set pattern 3
loop3r
is button released ?
no = goto loop3r
goto loop1
 
The other thing I am wondering (and yes, I am getting ahead of myself here) is what if my patterns takes a second or two to complete, and the button is pressed mid pattern? Is there anything I can do software-wise, or will I have to add a capacitor and pull the pin high rather then being able to pull it low?

You place your pattern changing button routine inside the delay routine.When each press increment a register & set a flag.
Ex:
When pattern changing button press
if increment a register is 1 then set flag 1
if increment a register is 2 then set flag 2

Check this flag & call the pattern.Your main routine will look something like this.

Code:
Main	btfsc	Pattern_Flag,0
	call	PAttern1
	btfsc	Pattern_Flag,1
	call	PAttern2
	btfsc	Pattern_Flag,2
	call	PAttern3
	btfsc	Pattern_Flag,3
	call	PAttern4
	btfsc	Pattern_Flag,4
	call	PAttern5
	goto	Main
 
Wow, thanks for all the replys and example code! I hope I have some time tonight after work to get started trying this on protoboard. I'm sure I will be back with more questions then.....


You place your pattern changing button routine inside the delay routine.When each press increment a register & set a flag.

Ahhh, never thought about that!:D Shows how green i really am. Thanks!
 
If I were making this device In wouldn't hesitate to do use 'interrupt on change PIORTA/PORTB' to capture button presses. You may need to debounce the button, so do that in the 'interrupt service routine.' As for the main body I cannot add much to the above discussion.

If you can make the incremented variable apart of the pattern algorithm then you don't need any of this look-up table business. Whether this is feasible depends on the nature of your patterns.
 
I finally found some time t work on this. I decided to start out a bit simpler and skip the XOR for the moment.

The code below works okay, but somtimes jumps 2 lights per button press. Is there some little tweak I can make to make it work a little smoother? I don't know how to do an interrupt on change yet, and tried adding the button press part of the code to the delay routine, but that was almost worse.

Code:
#include <p16F677.inc>
    __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
    cblock 0x20
Delay1                   ; Define two file registers for the
Delay2
Delay1B                   ; Define two file registers for the
Delay2B
var1
pattern
count
                  ; delay loop
     endc
      
     org 0
Start:
     bsf       STATUS,RP0     
     movlw     b'00000001'
     movwf     TRISB          ; PORTB 7 input
     
     clrf      TRISC               ; make IO PortC all output
     bcf       STATUS,RP0          ; back to Register Page 0
     clrf var1
 incf var1, f
Main:
 
 btfsc var1,7
 goto Pattern7
 btfsc var1,6
 goto Pattern6
 btfsc var1,5
 goto Pattern5
 btfsc var1,4
 goto Pattern4
 btfsc var1,3
 goto Pattern3
 btfsc var1,2
 goto Pattern2
 btfsc var1,1
 goto Pattern1
 btfsc var1,0
 goto Pattern0
 
 
 
Pattern0:
 
 clrf PORTC
 bsf  PORTC,0
 call Delay 
 clrf PORTC
 call Delay
 goto Main
Pattern1:
 clrf PORTC
 bsf  PORTC,1
 call Delay 
 clrf PORTC
 call Delay
 goto Main
Pattern2:
 clrf PORTC
 bsf  PORTC,2
 call Delay 
 clrf PORTC
 call Delay
 goto Main
Pattern3:
 clrf PORTC
 bsf  PORTC,3
 call Delay 
 clrf PORTC
 call Delay
 goto Main
Pattern4:
clrf PORTC
 bsf  PORTC,4
 call Delay 
 clrf PORTC
 call Delay
 goto Main
Pattern5:
 clrf PORTC
 bsf  PORTC,5
 call Delay 
 clrf PORTC
 call Delay
 goto Main
Pattern6:
clrf PORTC
 bsf  PORTC,6
 call Delay 
 clrf PORTC
 call Delay
 goto Main
Pattern7:
 clrf PORTC
 bsf  PORTC,7
 call Delay 
 clrf PORTC
 call Delay
 goto Main

 
Delay:
  btfss PORTB,7
  rlf  var1, f
  
  movlw  d'125'
     movwf  Delay2
     movlw  d'250' 
     movwf  Delay1
OndelayLoop:
 
     decfsz    Delay1,f            ; Waste time.  
     goto      OndelayLoop         ; The Inner loop takes 3 instructions per loop * 256 loopss = 768 instructions
  
     decfsz    Delay2,f            ; The outer loop takes and additional 3 instructions per lap * 256 loops
     goto      OndelayLoop         ; (768+3) * 256 = 197376 instructions / 1M instructions per second = 0.197 sec.
     return                              ; call it a two-tenths of a second.
DelayB:
  
  movlw  d'100'
     movwf  Delay2B
     movlw  d'2' 
     movwf  Delay1B
OndelayLoopB:
 
     decfsz    Delay1B,f            ; Waste time.  
     goto      OndelayLoopB         ; The Inner loop takes 3 instructions per loop * 256 loopss = 768 instructions
  
     decfsz    Delay2B,f            ; The outer loop takes and additional 3 instructions per lap * 256 loops
     goto      OndelayLoopB         ; (768+3) * 256 = 197376 instructions / 1M instructions per second = 0.197 sec.
     return                              ; call it a two-tenths of a second.
      
    
     end
 
It seems that your code will allow the routine to cycle if the key is held down. In which case the length of time the key press lasts can cause the cycle to advance twice. Perhaps you should wait for a 'no keys pressed' state before returning to the Main routine.
 
Consider using a switch state latch to detect only the "new press" portion of the switch cycle. When you exclusive-or a sampled bit with the same bit in the switch state latch you'll get a '0' bit result if the new sample and the latch are the same or a '1' bit result if they're different. If you AND a '1' bit result with the new sample bit you'll get a '0' bit result if you're detecting a "new release" or a '1' bit result if you're detecting a "new press". This allows you to use very simple code to ignore all but a "new press" (you can press the key as long as you like and you won't get another "new press" condition);

Code:
;
;  sample the active low switch on RB7 and bump the 'var1' variable
;  on a "new press"
;
Delay
        comf    PORTB,W         ; sample switch port
        andlw   b'10000000'     ; mask off non-switch pins
        xorwf   slatch,W        ; a difference (press or release)?
        bz      swdone          ; no, branch, else
        xorwf   slatch,F        ; update switch state latch
        andwf   slatch,W        ; a debounced "new press"?
        bz      swdone          ; no, branch (a "new release"), else
        rlf     var1,W          ; rotate our switch variable
        rlf     var1,F          ;
swdone
There's a rather good single-switch debounce example tutorial mentioned in the first post of this Forum.Microchip thread that may be worth studying; **broken link removed**

If you needed a "blocking" type GetSw routine then you would want to include the delay between samples within the routine. Perhaps something like this;

Code:
;
;  single switch example
;
GetSw
        movlw   20              ; reset debounce counter
        movwf   db_cnt          ; to 20 msecs
sample
        DelayCy(1*msec)         ; use 1 msec sample interval
        comf    PORTB,W         ; sample active low switch
        andlw   b'10000000'     ; on rb7
        xorwf   slatch,W        ; a difference (press or release)?
        bz      GetSw           ; no, branch (reset counter), else
        decfsz  db_cnt,f        ; debounced? yes, skip, else
        goto    sample          ; sample again
        xorwf   slatch,F        ; update debounced switch state latch
        andwf   slatch,W        ; a debounced "new press"?
        bz      sample          ; no, branch (a "new release"), else
        return                  ; return only for a "new press"
The example above actually debounces both the "press" and the "release" portions of the switch cycle but only returns to your main program on detecting a "new press". If the switch changes state or "bounces" between 1 msec samples before the 20 msec debounce counter gets to zero the counter is reset for another 20 msecs.

While the switch state latch mechanism shown above can easily handle up to eight switches in parallel, the debounce counter can only be used effectively for a single switch. When you need to debounce and manage multiple switches you can actually use something called "vertical counters" to implement up to eight independent debounce counters to end up with a relatively simple (and small) routine.

Happy Holidays.

Regards, Mike
 
Last edited:
Status
Not open for further replies.

Latest threads

Back
Top