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.

Push-buttons and PICs

Status
Not open for further replies.

NJ Roadmap

New Member
I was just wondering how everybody interfaces push-buttons to PICs? I'm just trying to choose between a normal digital I/O interface, an interrupt (INTn) pin, or an interrupt-on-change pin (KBIn). I'm using a PIC18F4520.

I was thinking of using the KBI pins purely because they're useful for scroll-type buttons which may be pressed for long periods of time since the RBIF bit continues to stay set as long as a key is pressed. Also theres the issue of debouncing which I am yet to address. But I'm open to suggestions so this is why I'm here!
 
Interupt on change can work, but on the first interupt you have to disable further interupts for a debounce period, which means you'll need to use a timer. Or you could just use a timer to run a poleing routine to pole an entire I/O port and a few variables to only change the state of the button variable if the button state has been stable for 2 or more readings (the more readings you integrate the less chance you'll have bounce problems)
 
Debouncing is the least of your problems because it is the easiest to solve. I always use software debouncing. Once a change is detected, call a debounce delay and when it returns check if the state is the same prior to calling the debouce delay. If same state, then button is valid, else ignore button. The debounce delay can range from 1 to 50 milliseconds and is button dependent, for instance, tact switches give damn the a super clean 1 microsecond raise and no noise but other switches can look like ekg signals.

In all my apps, I can afford to not use buttons in interrupts because I call my magical CHECK_BUTTON routine inside all major routines. CHECK_BUTTON returns within 4 to 6 clock cycles if no button is press, so you see its not a big overhead. I use interrupts for more important things instead like keeping track of operation time and external hardware interface monitoring, basically things not available to users.
 
Last edited:
I use polling to read buttons. I find a 10mS interrupt is good enough to do prefect debounce. If I was reading keys on PORTB, I would do the following to debounce,

; this assumes that keys are active high.
Temp = Port
Keys = Temp xor PreviousValue
; any bit that is 1 has changed since last time.
Keys = Keys and Temp
; get rid of key releases
; any bit that is one is a new key press
PreviousValue = Temp

In code this would be,
Code:
	movfw	PORTB		;read port
	movwf	Temp		;Save it
	xorwf	Previous,W	;Keys that have changed will be 1
	andwf	Temp,W		;Get rid of keys that were released = 1 to 0
	movwf	Keys		;Save the value for use elsewhere
	movfw	Temp		;Copy port value to previous 
	movwf	Previous	;for next time around.

If the keys are active low,

Code:
	comf	PORTB,W		;read port and invert it.
	movwf	Temp		;Save it
	xorwf	Previous,W	;Keys that have changed will be 1
	andwf	Temp,W		;Get rid of keys that were released = 1 to 0 
	movwf	Keys		;Save the value for use elsewhere
	movfw	Temp		;Copy port value to previous 
	movwf	Previous	;for next time around.

Mike.
Edit, changed the comment and added the active low code.
 
Last edited:
Using timer based interrupts to poll and process multiple switches in parallel is a very viable method.

I would build on Mike's (Pommie's) example 10-msec interrupt code by using a slightly modified version of Scott Dattalo's debounce vertical counter sample code. This code provides eight independent 30-msec debounce counters which debounce on both the "press" and "release" switch states and it's so small (13 words) that it should be considered even if you only have one or two switches in the project.

Code:
;
;  <> process up to eight switches in parallel
;  <> eight independent 2-bit 30.0-msec "debounce" vertical counters
;  <> momentary switch operation (test then clear a SWITCH bit
;     in Main)
;  <> toggle switch emulation (push to toggle a SWITCH bit from
;     off-to-on or from on-to-off) perfect for lighted switches
;
;  a switch is "debounced" or "filtered" after it's sampled four
;  times at the same level spanning a 30-msec period.
;
;  execute this code every 10.0-msec interrupt cycle
;
;  13 instructions/13 words
;
ISR_Debounce
;
;  get new press, release, or bounce state "change" bits in W
;
        comf    PORTB,W         ; read active low switches        |B0
        movwf   SWKEYS          ; save live switch press data     |B0
        xorwf   SLATCH,W        ; get delta 'live' and 'latch'    |B0
;
;  reset vertical counters for bouncing or inactive switches
;
        andwf   VCBIT0,f        ;                                 |B0
        andwf   VCBIT1,f        ;                                 |B0
;
;  get timed-out counter bits in W
;
        andwf   VCBIT0,W        ;                                 |B0
        andwf   VCBIT1,W        ;                                 |B0
;
;  update debounced switch state latch (each '1' represents
;  a 30.0-msec debounced switch press)
;
        xorwf   SLATCH,f        ; update debounced state latch    |B0
;
        andwf   SWKEYS,W        ; get "new" switch press bits     |B0
;
;  toggle SWITCH pressed flags for processing by MAIN program
;
;  MAIN should test SWITCH flags for emulated "toggle" switches
;  or test then clear SWITCH flags for "momentary" switches
;
        xorwf   SWITCH,f        ; toggle SWITCH flags for MAIN    |B0
;
;  increment the 2-bit vertical counters (unconditionally)
;
        movf    VCBIT0,W        ;                                 |B0
        xorwf   VCBIT1,f        ; b1 ^= b0                        |B0
        comf    VCBIT0,f        ; b0 = ~b0                        |B0
;
 
Last edited:
Pommie said:
Code:
    movfw    PORTB        ;read port
    movwf    Temp        ;Save it
    xorwf    Previous,W    ;Keys that have changed will be 1
    andwf    Temp,W        ;Get rid of keys that were released = 0 to 1 
    movwf    Keys        ;Save the value for use elsewhere
    movfw    Temp        ;Copy port value to previous 
    movwf    Previous    ;for next time around.
Mike.
Mike, I would change the movwf Keys instruction to either iorwf Keys,F or xorwf Keys,F otherwise the Keys variable will be overwritten each interrupt cycle and you probably should be using it as a latch for MAIN.
 
Mike said:
Mike, I would change the movwf Keys instruction to either iorwf Keys,F or xorwf Keys,F otherwise the Keys variable will be overwritten each interrupt cycle and you probably should be using it as a latch for MAIN.

Yes, changing the movwf would make a lot of sense.

I have not encountered the lost key problem as I normally have a bit of code after the above which implements a key delay and key repeat. This causes the value to be copied into another variable.

Mike.
 
I didn't mean to nit-pick Mike (Pommie). That's a very good example of code to parallel process up to 8 switches. The switch state latch is essential for producing "new" press or release data and ignoring the opposite state change. Here are my versions of the same algorithm but I fully debounce the switches before performing this logic;

Code:
;
;  detect "new" switch "press" (active high switch data)
;
        movf    SWDATA,W        ; live debounced switch bits
        xorwf   SLATCH,W        ; delta "live" and "latch"
        andwf   SWDATA,W        ; each '1' is a "new" press
        xorwf   SWITCH,F        ; update flags for MAIN
;
        movf    SWDATA,W        ; live debounced switch bits
        movwf   SLATCH          ; update switch state latch
;
;
Code:
;
;  detect "new" switch "release" (active high switch data)
;
        movf    SWDATA,W        ; live debounced switch bits
        xorwf   SLATCH,W        ; delta "live" and "latch"
        andwf   SLATCH,W        ; each '1' is a "new" release
        xorwf   SWITCH,F        ; update flags for MAIN
;
        movf    SWDATA,W        ; live debounced switch bits
        movwf   SLATCH          ; update switch state latch
;
You could also shave 1 word from that "new press" algorithm by updating the switch state latch differently;

Code:
;
;  detect "new" switch "press" (active high switch data)
;
        movf    SWDATA,W        ; live debounced switch bits
        xorwf   SLATCH,W        ; delta "live" and "latch"
        xorwf   SLATCH,F        ; update switch state latch
        andwf   SWDATA,W        ; each '1' is a "new" press
        xorwf   SWITCH,F        ; update flags for MAIN
;
And here's a simple way to produce a switch press beep for audible feedback that relies on W containing '1' bits for any new switch press;

Code:
;
;  detect "new" switch "press" (active high switch data)
;
ISR_switch
        movf    SWDATA,W        ; live debounced switch data
        xorwf   SLATCH,W        ; delta "live" and "latch"
        xorwf   SLATCH,F        ; update switch state latch
        andwf   SWKEYS,W        ; any "new" switch press?
        skpz                    ; no, skip, else
        bsf     BEEP,4          ; send 32-msec short 'beep'
        xorwf   SWITCH,F        ; update flags for MAIN
;
;  500-Hz tone using 1-msec interrupts
;
ISR_beep
        movf    BEEP,W          ; beep timer set?
        bz      ISR_next        ; no, branch, else
        movf    PORTA,W         ; toggle piezo spkr on RA0
        xorlw   b'00000001'     ;
        movwf   PORTA           ;
        decf    BEEP,F          ; decrement BEEP msec counter
;
ISR_next
There you have it. Some very simple and powerful examples for polling and processing multiple switches using periodic interrupts. I hope they inspire some members to investigate the use of interrupts.

Mike, K8LH
 
Last edited:
Hi All, just wondering if you could look at something for me and advise on why its not working... I'm using the code that Mike (pommie) posted above.

the situation is, I have 8 push button switches, which are interfaced with a 16F873A pic via a 4014 shift register. I have a register called "input_data", which, suprisingly, is where the data from the shift register is stored. I then have one called "output_data" where the state of my switches is outputted onto 8 LED's... basically i want the LED's to light the first time a switch is pressed, and then turn off when they are pressed again...

Code:
<bit bashing data from 8 switches, through 4014 into "Incoming_data">
	movfw	incoming_data		
	movwf	temp_data		
	xorwf	Previous,W	
	andwf	Temp_data,W	 
	iorwf	output_data		
	movfw	Temp_data		
	movwf	Previous

<bit bashing "output_data" to a 4094 to 8 led's>

When I use the above code I get no output on the LED's, any ideas / suggestions as to why?

Cheers

Sam J
 
Hi Sam,

A couple comments if I may?

If you've got "live" switch press data already in incoming_data (each '1' bit should indicate a switch press) you shouldn't have to make another copy of it. For toggle switch emulation where you press a switch to toggle its output latch bit from on-to-off or from off-to-on you want to use an xorwf instruction on the output latch. Something like this;

Code:
;
        movf    incoming_data,W ; get live switch press bits
        xorwf   Previous,W      ; the '1' bits indicate a change
        andwf   incoming_data,W ; the '1' bits indicate 'new' presses
        xorwf   output_data,F   ; toggle output flag bits

        movf    incoming_data,W ; get live switch press bits
        movwf   Previous        ; update switch state latch
;
If your incoming_data is active low (a '0' indicates a switch press) then you might complement the data as in this example;

Code:
;
        comf    incoming_data,F ; complement switch press bits  <---
        movf    incoming_data,W ; get live switch press bits
        xorwf   Previous,W      ; the '1' bits indicate a change
        xorwf   Previous,F      ; update switch state latch
        andwf   incoming_data,W ; the '1' bits indicate 'new' presses
        xorwf   output_data,F   ; toggle output flag bits
;
This code is normally run in your ISR (interrupt service routine) and your MAIN program would test and clear a switch flag bit in output_data for normal push button switches or simply test a switch flag bit for switches emulating a "toggle" switch.

Good luck with your project. Mike, K8LH
 
Last edited:
Mike said:
Code:
;
;  detect "new" switch "press" (active high switch data)
;
        movf    SWDATA,W        ; live debounced switch bits
        xorwf   SLATCH,W        ; delta "live" and "latch"
        xorwf   SLATCH,F        ; update switch state latch
        andwf   SWDATA,W        ; each '1' is a "new" press
        xorwf   SWITCH,F        ; update flags for MAIN
;

Nice use of the xor instruction.

Just for any newbies following this thread, the first 3 lines of the above code do the equivalent of,
W=SWDATA xor SLATCH
SLATCH = SWDATA
This is a clever method and it is not obvious how it works. Anyone interested should read up on the xor instruction and follow the code through with a few examples.

I like the beep code and will almost certainly use it in a future project.

Whoops, I just noticed that the code posted earlier is incorrectly commented as being for active low switches. I'll edit that earlier post.

Mike.
 
Sam Jelfs said:
Hi All, just wondering if you could look at something for me and advise on why its not working... I'm using the code that Mike (pommie) posted above.

the situation is, I have 8 push button switches, which are interfaced with a 16F873A pic via a 4014 shift register. I have a register called "input_data", which, suprisingly, is where the data from the shift register is stored. I then have one called "output_data" where the state of my switches is outputted onto 8 LED's... basically i want the LED's to light the first time a switch is pressed, and then turn off when they are pressed again...

If copying input_data to output_data results in the LEDs lighting then the code mike k8lh posted should work.

I may have confused the matter as I incorrectly labeled the above code as being for active low switches.

Mike.
 
Status
Not open for further replies.

Latest threads

Back
Top