# Single pole double throw to momentary using PIC

Status
Not open for further replies.

#### RMIM

##### Member
Hi,

(I’m very new to PICs. Also I have no programming experience of any kind to speak of.)

The problem I need help with is as follows:

I have 4 single pole double throw switches (Standard UK wall light switches in fact (2-ways)). I need to get a pulse to go to another circuit every time the switch is thrown. I don’t need to know the position of the switch.

At the moment I can only seem to solve the problem for one switch only (I have not learnt all the 35 instructions yet, perhaps there’re other commands that might help)

For one SW I came up with
The common goes to one of the inputs on the PIC. One terminal is connected to 1 the other to 0

TP1: Test Position1 If 1 Jump Line [btfss]
Goto TP1
Call Pulse
Goto TP2

TP2: Test Position2 If 0 Jump Line [btfsc]
Goto TP2
Call Pulse
Goto TP1

Pulse: Pulse subroutine. Return

end

But my code just sits there waiting for the switch to be thrown, Im not sure how to get it to do that for 3 others too. I need it to jump out of the loop and check the others, return back to switch one and start the checking all over again.

--------
Another solution I came up with was just to have an LED and capacitor in series. Throw the switch and you would get the LED to pulse. Throw the switch the other way and it would discharge the capacitor through a second LED, giving you the other pulse. (Trouble is I did not know where to tap off this output to my other circuit. Well I just tried to use an oscilloscope to look for a voltage jump when I threw the switch, I could not really see one. Maybe if I used resistors in place of the LED’s I would have seen a bigger jump?
--------

Thanks

Last edited:

#### marcbarker

##### New Member
I presume wall the light switches are not functioning as actual light switches. If they were, it's a little difficult to interface a PIC development process to live mains voltages! I wonder why you'd specfically mentioned that the switches were wall light switches, and not referred to them as 'SPCO switch'? edit: aka SPDT

That aside, it sounds like you wish your PIC code to detect a change in any one of 4 switches?

Hardware: Enable on-chip pullups. Choose 4 of these input. Wire each the 4 switch 'on' terminals to each of these inputs. Connect the 4 'common' switch terminals to PIC ground.

Software: Read all the switches at the same time! When it is desired to detect if any switch had been thrown, begin this routine. Read the appropriate PORTx into the W register. Mask off the bits you're not interested in. Compare this 4 bit word against what the 4-bit word was last time. If it's different, call Pulse.

I hope this helps.

Last edited:

#### RMIM

##### Member
HI marcbarker,

Thanks for help.

Why mention the wall switches? Just in case there was some property of wall switches that might help out. In case the wall switches are not SPDT like I said, someone would then correct me. Also in case someone told me to just change them for real momentary switches.

The wall switches have to give the illusion that they are wall switches. They will be switching lights - but wirelessly. I'm trying to interface them to a remote key fob. The key fob has 4 momentary switches. It transmits wirelessly to the receiver which toggles. I will rig the receiver to relays or opto triac isolator+triac then to the lights/fan. Why? To save from having to chase 4 sets of cable.

You used the term SPCO. Google came up with Saint Paul Chamber Orchestra.
More searching came up with SP.centre off or SP change over.

I'll have to do some more work before I understand the hint on the software you gave me. But will that allow me to know which actual switch changed over - I need the pulse to go to 1 of 4 outputs.

Thanks

(Not performing this alteration in the UK by the way. Not breaking any laws)

Last edited:

#### MrAl

##### Well-Known Member
Hi,

I would think your two capacitor idea would work too. Discharge the caps
with a relatively high value resistor and that should work. Make sure there
is also some load so that the pulse will die back down after some short time
period, or else it will stay high for a relatively long time.
For example, two 0.22uf and 1meg discharge resistors and one 10k load resistor provides
a pulse width of about 200us at a level of 4.5v or higher with a 5v power supply.

One other little caution here is that the switches made for ordinary house
wiring are made for 120v or higher voltage. At low voltage like 5v some of
them dont work very well. Sometimes you can measure an open with an
ohmmeter even when the switch is closed. Keep an eye out for this, and
you may have to buy brand new switches to get around this and hope that
they dont oxidize too fast.

One thing that works even better though is a pair of NPN transistors, each ones
base driven by one capacitor with resistors, and the output collectors
wire OR'd together to provide negative going pulses (from 5v to 0v instead
of 0v to 5v). This is pretty cheap too and works nicely.
If your circuit can pick up the 200us pulses reliably the other circuit
should work too though.

Last edited:

#### RMIM

##### Member
Thanks for that MrAl - I was hoping someone would comment on the capacitor idea. Good to know it should work.

edit: I just noticed, yours uses 2 capacitors, mine 1.

Thanks for the info on the switches - if something goes wrong might have taken me ages to work out it was just the switches.

Last edited:

#### marcbarker

##### New Member
...will that allow me to know which actual switch changed over[?] - I need the pulse to go to 1 of 4 outputs.

Yes. If each switch is wired to an individual input pin, it's possible to tell which switch was thrown in your software.

yes, mains switches are optimised for switching high power and can oxidise with low voltages and current. Electrical signalling switches have gold-plated contacts so they don't tarnish.

Last edited:

#### kpatz

##### New Member
Assuming the 4 switches are connected to the first 4 bits of PORTB, you could use code like this. If they're connected to a different port or different bits within the port, change the equ definitions at the top to correspond.

Code:
;***** VARIABLE DEFINITIONS (examples)

;  Assume switches are on portb, switch1-4 specify the bits
switchport  equ		PORTB
switch1		equ     0x0
switch2     equ		0x1
switch3		equ		0x2
switch4		equ		0x3

; example of using Shared Uninitialized Data Section
INT_VAR     UDATA_SHR
hold		RES 1          ; Place to store last polled switch position

;**********************************************************************
RESET_VECTOR    CODE    0x0000      ; processor reset vector
goto    start           ; go to beginning of program

INT_VECTOR  CODE    0x004       ; interrupt vector location

INTERRUPT
;  put interrupt handling code here (there's none in this example)
retfie              ; return from interrupt

MAIN_PROG   CODE

start

movfw switchport   ; Get initial switch positions
movwf hold    ; and place them in the hold area
loop
movfw switchport			; Get current switch positions
xorwf hold,w		; Exclusive-OR with hold area to get changed bits
andlw 0x0f			; mask out unused bits
btfsc STATUS, Z     ; If no bits changed, loop
goto   loop
movwf hold			; put W into hold area so we can btfsc it
btfsc hold,switch1		; If switch 1 flipped, call pulse1
call   pulse1
btfsc hold, switch2	; If switch 2 flipped, call pulse2
call   pulse2
btfsc hold, switch3	; If switch 3 flipped, call pulse3
call   pulse3
btfsc hold, switch4	; If switch 4 flipped, call pulse4
call   pulse4
goto  start			;Reset hold area and poll switches again

pulse1	nop			; Generate pulse for switch1
return

pulse2	nop			; Generate pulse for switch2
return

pulse3	nop			; Generate pulse for switch3
return

pulse4	nop			; Generate pulse for switch4
return

END                       ; directive 'end of program'

#### RMIM

##### Member
Thanks for that kpatz. Hopefully I will understand your code soon. Will need to look up hold RES 1. and INT_VAR UDATA_SHR. Know a good web page that explains that stuff for beginners?

marcbarker: how long do you think the switches will last before playing up? Assuming they are switched no more than 4 times per day? We talking months or years?

Also how much does a pic guzzle (mA) when it’s just running the code and not actually pulsing? As this project has to run on batteries. I was recently shocked when I found out a 9v battery has a much lower mAH capacity than AA batteries. Trouble with AA batteries is that you need 4 which take up more room.

#### gaspode42

##### Member
Relocatable Code

Hi RMIM

The commands that you are questioning are part of what is called 'relocatable code'.
Have a look at lesson 16 on this site which gives a good introduction to the subject.

Best regards,

Rupert

#### RMIM

##### Member
cheers rupert - would have taken years to work that one out!

#### kpatz

##### New Member
The RES command (which is a directive to the assembler, not an actual PIC opcode) reserves a space in memory (the file registers). Instead of having to assign addresses (file registers in PIC land) to each variable, you give your variable a name ("hold" in my case) and how many bytes it needs, and the compiler handles the details of assigning an available file register location.

References to the variable name are mapped by the assembler to the corresponding file register location. Essentially this is what makes the code "relocatable". If you combine this code with some other code that also uses file registers, as long as the names are unique, there will be no conflicts. Whereas, if you use a specific register (0x20 for example), and you bring in additional code that also uses 0x20 for its own purpose, you'd have a conflict.

Also how much does a pic guzzle (mA) when it’s just running the code and not actually pulsing? As this project has to run on batteries. I was recently shocked when I found out a 9v battery has a much lower mAH capacity than AA batteries. Trouble with AA batteries is that you need 4 which take up more room.
The best way to know is to look at the datasheet for the particular PIC you're using. Probably 10-20 mA depending on the PIC and how many of its internal features you use. Clock speed is a factor too, a lower clock speed will generally draw less power, and for a simple application like yours, you don't need the PIC running at 20 MHz.

There are ways to reduce the power consumption using sleep mode and timers/interrupts. For example, on the midrange PICs you can set them up to generate an interrupt when one of the input pins changes states (when you flip one of your 4 switches). This way, instead of looping waiting for a switch to change states, the PIC can sleep until a switch is flipped. When you flip a switch, the PIC wakes up, generates the pulse you need, then goes back to sleep. Then the average power consumption is just a few microamps.

The PIC16F505 is the cheapest PIC with enough I/O pins for your application (12, you need 8). It draws 175 microamps when running and 100 nanoamps (0.1 microamp) when sleeping. It doesn't support interrupts, but it does support wake-on-pin-change which you could use to have the chip sleep until you throw one of the switches. A 9V battery should last a VERY long time. Actually, since this PIC will run down to 2 volts, you could use 2 AA, AAA or N cells, or even a lithium coin battery.

P.S. (again)... use high-value pullups (say around 100k-470k) to minimize power draw though them when a switch is closed. I don't know what the value of the "weak pullups" in the PICs are, but if they're in the tens of kilohms range, they'll draw several microamps of current if a switch keeps a pin pulled to ground, which can shorten battery life.

Last edited:

#### marcbarker

##### New Member
marcbarker: how long do you think the switches will last before playing up? Assuming they are switched no more than 4 times per day? We talking months or years?

Also how much does a pic guzzle (mA) when it’s just running the code and not actually pulsing? .

Not switching them makes them worse. There's an industry standard reference book about relays, from memory in it says that switches for power need to have at least xx mA, sorry I can't remember the xx.

The PIC can be configured to use microwatts of power, it can spend most of its time asleep and waking up just long enough to sniff the inputs and go back to sleep again.

#### RMIM

##### Member
The RES command (which is a directive to the assembler, not an actual PIC opcode) reserves a space in memory (the file registers). Instead of having to assign addresses (file registers in PIC land) to each variable, you give your variable a name ("hold" in my case) and how many bytes it needs, and the compiler handles the details of assigning an available file register location.

Thanks for the help. I worked through your code, fiddled with it a bit and it seems to be working. Would have taken me a while to come across that Exclusive-OR trick – thanks for that.

#### marcbarker

##### New Member
that Exclusive-OR trick .

That's the magic bit, it's the "differentiator"

#### RMIM

##### Member
The PIC can be configured to use microwatts of power, it can spend most of its time asleep and waking up just long enough to sniff the inputs and go back to sleep again.

HI - can some one help me with the sleep command? (And point out any errors or better way of doing things – at the moment [that’s before I added the sleep command] the pic does what it’s supposed to do). I have not tested it with the sleep command yet – plus my DMM does not have current. How do I know it’s sleeping? Or the only way to know is to use a current meter?

What I need help with are the following

bsf 8Bh,7 ;global interrupts on
bsf 8Bh,3 ;RB port change on
bcf 0Bh,0 ;RB port change flag clear

The data sheet says ‘when at least one of the RB<7:4> change state
What does RB<7:4> mean? Pin 7,6,5,4?? I set my inputs on 0,1,2,3 will it still work?

Thanks

---------

Code:
LIST   P=PIC16F628A
#include	<p16F628A.inc>
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_OFF &_INTRC_OSC_NOCLKOUT & _MCLRE_OFF & _LVP_OFF
;	 __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_OFF &_INTRC_OSC_NOCLKOUT & _MCLRE_ON & _LVP_OFF

org	0x0000	;org sets the origin, 0x0000 for the 16F628,
;this is where the program starts running
movlw	0x07
movwf	CMCON		;turn comparators off (make it like a 16F84)

STATUS equ 03h

TRISA equ 85h
PORTA equ 05h

TRISB equ 86h
PORTB equ 06h

COUNT1 equ 0Ch                 ;First counter for our delay
COUNT2 equ 0Fh
TEN equ 3Ch
COUNT3 equ 0Eh
MEM equ 2Ch

clrf     PORTA        ;all of porta low
clrf     PORTB        ;all of portb low

;----------------------------------------------------------------------------------

bsf             03h,5				;BANK 1

movlw           b'11111111'
movwf           TRISB
bcf				81h,7				;pull ups on
bsf				8Bh,7				;global interrupts on
bsf				8Bh,3				;RB port change on
movlw			b'00000000'
movwf			TRISA
bcf             03h,5               ;BANK 0

start

movfw PORTB ;READ THE INITIAL SWITCH POSITIONS
movwf MEM	;SAVE THEM TO MEM

loop
;call delay
movfw PORTB 		;READ CURRENT SW POSTIONS
xorwf MEM,w 		;ex-OR with mem save to w. Any change will give a 1
andlw b'00001111' 	;masks last 4 ports (affects status)
btfsc STATUS,Z		;if any bits have changed z will flag causing jump
;Z: Zero bit
;1 = The result of an arithmetic or logic operation is zero
;0 = The result of an arithmetic or logic operation is not zero

bcf	0Bh,0			;RB port change flag clear
sleep
goto loop			;keeps looping till there is a change

movwf MEM			;move what's in the working register to mem

btfsc MEM,0
call pulse0

btfsc MEM,1
call pulse1

btfsc MEM,2
call pulse2

btfsc MEM,3
call pulse3

goto start

pulse0	movlw b'00000001' ;RA0
call pulse
return

pulse1	movlw b'10000000' ;RA7
call pulse
return

pulse2	movlw b'01000000' ;RA6
call pulse
return

pulse3 	movlw b'00001000' ;RA3
call pulse
return

pulse   movwf PORTA
call setten
call delay
movlw b'00000000'
movwf PORTA
return

delay			decfsz     COUNT1,1       ;Subtract 1 from 255
goto       delay
decfsz    COUNT2,1        ;Subtract 1 from 255
goto 	delay 	        ;Go back to the start of our loop.
decfsz		COUNT3,1		;This delay counts down from ;255 to zero, 255 times
goto 		delay
return

setten			movlw 8
movwf COUNT3
Return

end

#### kpatz

##### New Member
The data sheet says ‘when at least one of the RB<7:4> change state What does RB<7:4> mean? Pin 7,6,5,4?? I set my inputs on 0,1,2,3 will it still work?
No, only pins (bits) 7-4 on PORTB will trigger an interrupt and wake on change. Just put your switch inputs on those pins and you'll be good to go.

A good way to know if the sleep is working is to put it in your code. Disable interrupts, and see if the PIC still responds to the switches. If it does, it's not sleeping. If not, it is sleeping. Then enable interrupts and see if it responds to the switches while sleeping.

You sure your DMM doesn't measure current (uA/mA)?

#### RMIM

##### Member
HELLLLLLL!!!!! Now I have to resolder.

Yes very sure my DMM does not have it. It has transistor gain test, Capacitance, Frequency, Volts, Diode, Resistance - but no current. I'm looking for one to buy. Sadly I can't find a small auto-ranging that has current as well as Transistor and capacitance.

Was my use of
bsf 8Bh,7 ;global interrupts on
bsf 8Bh,3 ;RB port change on
bcf 0Bh,0 ;RB port change flag clear
correct?

#### kpatz

##### New Member
Was my use of
bsf 8Bh,7 ;global interrupts on
bsf 8Bh,3 ;RB port change on
bcf 0Bh,0 ;RB port change flag clear
correct?
You're better off using the declarations in the include file for the registers and bits instead of constants.

Code:
bsf INTCON,GIE   ; global interrupts on
bsf INTCON,RBIE  ; RB port change interrupt on
bcf INTCON,RBIF  ; port change flag clear

You could also do all three of those with 2 lines:

Code:
movlw b'10001000'  ; Set GIE, RBIE, and clear all others
movwf INTCON

#### Mike - K8LH

##### Well-Known Member
RMIM,

You don't have to use an interrupt service routine to take advantage of the interrupt-on-change feature to wake up from sleep. Here's an untested example program to demonstrate. This program uses the debounced and filtered "new press" bits to toggle your pulse outputs on RA3..RA0 and uses the debounced "new release" switch state to return to sleep.

Regards, Mike

Code:
        LIST    P=PIC16F628A

#include        <p16F628A.inc>

__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_OFF &_INTRC_OSC_NOCLKOUT & _MCLRE_OFF & _LVP_OFF
;       __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_OFF &_INTRC_OSC_NOCLKOUT & _MCLRE_ON & _LVP_OFF

swnew   equ     0x20            ; new switch press bits
swold   equ     0x21            ; switch state latch
delayhi equ     0x22            ; DelayCy() sub-system variable

;******************************************************************
;  K8LH DelayCy() subsystem macro generates four instructions     *
;******************************************************************
clock   equ     4               ; 4, 8, 12, 16, 20 (MHz), etc.
usecs   equ     clock/4         ; cycles/microsecond multiplier
msecs   equ     clock/4*1000    ; cycles/millisecond multiplier

DelayCy macro   delay           ; 11..327690 cycle range
movlw   high((delay-11)/5)+1
movwf   delayhi
movlw   low ((delay-11)/5)
call    uDelay-((delay-11)%5)
endm

;******************************************************************
;  main program                                                   *
;******************************************************************
org     0x0000
v_reset
movlw   0x07            ;                                 |B0
movwf   CMCON           ; comparators off                 |B0
clrf    PORTA           ; clear port A output latches     |B0
clrf    PORTB           ; clear port B output latches     |B0
bsf     STATUS,RP0      ; bank 1                          |B1
movlw   b'11111111'     ;                                 |B1
movwf   TRISB           ; port B all inputs               |B1
movlw   b'00000000'     ;                                 |B1
movwf   TRISA           ; port A all outputs              |B1
bcf     OPTION_REG,7    ; weak pull-ups on                |B1
bcf     STATUS,RP0      ; bank 0                          |B0
bsf     INTCON,RBIE     ; IOC interrupt enable            |B0
clrf    swold           ; clear switch state latch        |B0
;
;  main program loop
;
suspend
movf    PORTB,W         ; clear IOC mismatch condition    |B0
bcf     INTCON,RBIF     ; clear IOC interrupt flag bit    |B0
sleep                   ; sleep, wait for IOC interrupt   |B0
nop                     ;                                 |B0
dbounce
DelayCy(24*msecs)       ; sample at 24 msec intervals     |B0
;
;  swnew  __---___---___---___---___  fresh switch sample
;  swold  ___---___---___---___---__  switch state latch
;  swnew  __-__-__-__-__-__-__-__-__  changes, press or release
;  swnew  __-_____-_____-_____-_____  new press bits
;
comf    PORTB,W         ; sample active low switches      |B0
andlw   b'11110000'     ; on RB7..RB4 pins                |B0
xorwf   swold,W         ; changes, press or release?      |B0
bz      dbounce         ; no, branch, else                |B0
xorwf   swold,F         ; update switch state latch       |B0
andwf   swold,W         ; filter out new release bits     |B0
bz      suspend         ; branch for new release, else    |B0
movwf   swnew           ; save "new press" bits           |B0
;
;  optional 32-msec 500-Hz "new press" beep (spkr on RA4, 6, or 7)
;
;       movlw   32              ;                                 |B0
;       movwf   beepctr         ;                                 |B0
;beep   movf    PORTA,W         ;                                 |B0
;       xorlw   1<<spkr         ;                                 |B0
;       movwf   PORTA           ;                                 |B0
;       DelayCy(1*msecs-6)      ; interval for 500-Hz tone        |B0
;       decfsz  beepctr,F       ; done? yes, skip, else           |B0
;       goto    beep            ;                                 |B0

swapf   swnew,W         ; swap new press bits in WREG     |B0
xorwf   PORTA,F         ; toggle output (pulse on)        |B0
DelayCy(100*usecs-2)    ; pulse width minus 2 cycles      |B0
swapf   swnew,W         ; swap new press bits in WREG     |B0
xorwf   PORTA,F         ; toggle output (pulse off)       |B0
;       goto    dbounce         ; debounce release before sleep   |B0
goto    suspend         ; debounce release after sleep    |B0

;******************************************************************
;  K8LH DelayCy() subsystem 16-bit uDelay(11..327690 cycle) sub'  *
;                                                                 *
;  9 words, 1 RAM variable, 14-bit core                           *
;******************************************************************
nop                     ; entry for (delay-11)%5 == 4     |B0
nop                     ; entry for (delay-11)%5 == 3     |B0
nop                     ; entry for (delay-11)%5 == 2     |B0
nop                     ; entry for (delay-11)%5 == 1     |B0
uDelay  addlw   -1              ; subtract 5 cycle loop time      |B0
skpc                    ; borrow? no, skip, else          |B0
decfsz  delayhi,F       ; done?  yes, skip, else          |B0
goto    uDelay          ; do another loop                 |B0
return                  ;                                 |B0
;******************************************************************
end

Last edited:

#### RMIM

##### Member
Thanks kpatz

Thanks Mike - I will have to work through that.

Status
Not open for further replies.

Replies
7
Views
1K
Replies
3
Views
2K
Replies
3
Views
1K
Replies
11
Views
3K
Replies
24
Views
2K