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.

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:
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:
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:
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:
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:
...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:
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'
 
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.
 
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
 
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: 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.
 
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.
 
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
 
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)?
 
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?
 
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
 
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
        radix   dec

        #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     *
;******************************************************************
        radix   dec
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:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top