PDA

View Full Version : Beginner in need of help !!



robbo78
- 15th January 2015, 13:13
Hi everyone, I am completely new to both these forums & pic programming. I am working with the 12f683 which I have managed to flash led 's read potentiometers etc, very basic stuff.

Now to my question. I want to turn an LED on/off with a single tap of a tactile button. So press & release button: led turns on, then repeat to turn led off.

I knio how to turn an LED on/off by holding the button for on & releasing for off, but I can't work out how to do the above.

Any help would be greatly appreciated, I intend to over come any hiccups, not give up.

Thanks,
Rob

Amoque
- 15th January 2015, 13:27
Not sure how much help you need. I'm assuming you know input/ output, high/ low...

Google "Pull down switch" and see how the level goes from high to low when the button is pressed (there is a pull up as well); use standard input technique to read the level change when the switch is pressed. To control the LED output, I think an if/ then will work well:

If Button press = 0 and LED = off then [turn on LED]
If Button press = 0 and LED = on then [turn off LED]

You should also look at "debounce" as there is some issue to mechanical switches.

Also, without some programming to limit the speed, the switch will turn the LED on/ off very quickly. A pause will work (to give the user time to release the button) or, you can look for a "1" on the switch (released) before allowing another button press to register.

HTH,

HenrikOlsson
- 15th January 2015, 13:30
Hi Rob,
Welcome to the forum! As you can imaging there are several ways to do this (and most other things). Amoque shows you one, here's another (which also tries to take care of the debounce):

LED VAR GPIO.0
Switch VAR GPIO.1

ANSEL = 0 ' No analog inputs
CMCON = 7 ' Comparators disabled
TRISIO = %11111110 ' GPIO.0 as output

Main:
IF Switch = 1 THEN ' If the button is pressed...
TOGGLE LED ' ...we toggle the LED.
Pause 20 ' Then we wait for contact bounce on make to die, may need tweaking
WHILE Switch = 1 : WEND ' Then we wait for the user to actually release the button
Pause 20 ' Finally we wait for contact bounce on release to die, may need tweaking
ENDIF
Goto Main

/Henrik.

robbo78
- 15th January 2015, 13:34
Hi Guys, thanks for the help. I will go away & have a play with your examples :)

Thanks again,
Rob

robbo78
- 15th January 2015, 16:35
Hi Rob,
Welcome to the forum! As you can imaging there are several ways to do this (and most other things). Amoque shows you one, here's another (which also tries to take care of the debounce):

LED VAR GPIO.0
Switch VAR GPIO.1

ANSEL = 0 ' No analog inputs
CMCON = 7 ' Comparators disabled
TRISIO = %11111110 ' GPIO.0 as output

Main:
IF Switch = 1 THEN ' If the button is pressed...
TOGGLE LED ' ...we toggle the LED.
Pause 20 ' Then we wait for contact bounce on make to die, may need tweaking
WHILE Switch = 1 : WEND ' Then we wait for the user to actually release the button
Pause 20 ' Finally we wait for contact bounce on release to die, may need tweaking
ENDIF
Goto Main

/Henrik.

Thanks for this Henrik, I found this the best way. Another question, what if I wanted to run a different command to just turning on the LED, what about if I wanted to start the LED flashing upon button tap? Could I do this with the same method?

Thanks,
Rob

HenrikOlsson
- 15th January 2015, 19:12
Hi,

> what if I wanted to run a different command to just turning on the LED
Don't understand this one, sorry...

> what about if I wanted to start the LED flashing upon button tap?
Well, yes. But as soon as you mention blinking it starts to get a Little bit more complicated. Why?
Because then you need delays in there, during which (if using PAUSE) the button won't be polled so it'll feel unresponsive. Of course there are ways around this but the correct one depends on what you want to do after we get that LED to blink....

Try this (I haven't, hope it works....):

LEDState VAR BYTE
Time VAR WORD

LED_OFF CON 0
LED_ON CON 1
LED_FLASH CON 2

Rate CON 250 ' Time between LED toggles when blinking

Main:
IF Switch = 1 THEN ' If the button is pressed...
LEDState = LEDState + 1
IF LEDState = 3 THEN LEDState = 0
Pause 20 ' Then we wait for contact bounce on make to die, may need tweaking
WHILE Switch = 1 : WEND ' Then we wait for the user to actually release the button
Pause 20 ' Finally we wait for contact bounce on release to die, may need tweaking
ENDIF

IF (LEDState = LED_OFF) or (LEDState = LED_ON) THEN
LED = LEDState ' Turn LED on or off, solid.
ENDIF

IF LEDState = LED_FLASH THEN
Time = Time + 1
IF Time = Rate THEN
TOGGLE LED
Time = 0
ENDIF
PAUSEUS 1000
ENDIF

Goto Main

/Henrik.

robbo78
- 19th January 2015, 17:05
Hi again, I have been trying to play around with interrupts, both instant asm (which barely makes any sense to me) & basic 'on interrupt'.

I have wrote a very basic code, however what I cant get my head around is how to incorporate the button method we discussed before into an interrupt? So how can one push & release of the button enter an interrupt loop & wait there, until I push & release the button again which would exit back to the main loop? Here is the small code I have written.

Thanks again,
Rob




ANSEL = 0 'digital I/O
CMCON0 = 7 'comparator off
INTCON = %00010000 'enable pin 4 interrupt
TRISIO = %00010000 'pin 4 input

LED VAR GPIO.0 'pin 7 led
Switch VAR GPIO.4 'pin 3 button

ON INTERRUPT GOTO int1

main:
HIGH LED
PAUSE 500
LOW LED
PAUSE 500
GOTO main

DISABLE
int1:
TOGGLE LED
INTCON.1 = 0
RESUME
ENABLE

HenrikOlsson
- 19th January 2015, 17:31
Hi Rob,
If you want the button to enable/disable the blining LED then you can't toggle the LED itself in the interrupt and then just continue to blink it in the main program loop. What you need is flag or semaphore telling the main loop if the LED is supposed to blink or not.


BLINK VAR BIT

ON INTERRUPT GOTO int1

main:
If Blink = 1 THEN
HIGH LED
PAUSE 500
LOW LED
PAUSE 500
ENDIF
GOTO main

DISABLE
int1:
Blink = !Blink ' Invert the state of the semapore
RESUME
ENABLE

Please note that the PAUSE statement is basically delaying the Int1 routine from executing until the PAUSE statement finishes (that's the drawback of ON INTERRUPT). So if you push the button just as the PAUSE 500 start to exectute the ISR won't execute until 500ms later. This won't matter in this case but please keep that in mind when you start doing more complicated stuff.

Also, the above does not incorporate any debounce, you may want to add that.

/Henrik.

robbo78
- 19th January 2015, 17:55
Hi Henrik, thanks for explaing that to me it will help me a lot :) I am learning more & more as I go on :D

Rob


Hi Rob,
If you want the button to enable/disable the blining LED then you can't toggle the LED itself in the interrupt and then just continue to blink it in the main program loop. What you need is flag or semaphore telling the main loop if the LED is supposed to blink or not.


BLINK VAR BIT

ON INTERRUPT GOTO int1

main:
If Blink = 1 THEN
HIGH LED
PAUSE 500
LOW LED
PAUSE 500
ENDIF
GOTO main

DISABLE
int1:
Blink = !Blink ' Invert the state of the semapore
RESUME
ENABLE

Please note that the PAUSE statement is basically delaying the Int1 routine from executing until the PAUSE statement finishes (that's the drawback of ON INTERRUPT). So if you push the button just as the PAUSE 500 start to exectute the ISR won't execute until 500ms later. This won't matter in this case but please keep that in mind when you start doing more complicated stuff.

Also, the above does not incorporate any debounce, you may want to add that.

/Henrik.

robbo78
- 20th January 2015, 17:01
Hi, please can someone tell me how I disable MCLR on gp3 (my pic is 12f683). Ive been researching for the past 2 hours & I have got no closer to disabling it, I just want GP3 as a normal input pin, not this MCLR crap!

If anyone knows could you please help?

Rob

HenrikOlsson
- 20th January 2015, 17:32
Hi,
MCLR is disabled by clearing the correct "fuse" in the CONFIG word. With PBP3 you set the CONFIG word(s) with the #CONFIG/#ENDCONFIG compiler directives (more on that in the manual). Your PBP3 installation folder has a subfolder named DEVICE_REFERENCES in which you'll find a file for each and every device the compiler supports. The file contains information about the various CONFIG options and also what defaults PBP uses if you don't include any in your code.

For a 12F683 the defaults are:

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_ON & _CP_OFF
#ENDCONFIG


If you want to disable MCLR and keep everything else then copy/paste this into your program:

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _CP_OFF
#ENDCONFIG


/Henrik.

robbo78
- 20th January 2015, 17:46
Hi,
MCLR is disabled by clearing the correct "fuse" in the CONFIG word. With PBP3 you set the CONFIG word(s) with the #CONFIG/#ENDCONFIG compiler directives (more on that in the manual). Your PBP3 installation folder has a subfolder named DEVICE_REFERENCES in which you'll find a file for each and every device the compiler supports. The file contains information about the various CONFIG options and also what defaults PBP uses if you don't include any in your code.

For a 12F683 the defaults are:

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_ON & _CP_OFF
#ENDCONFIG


If you want to disable MCLR and keep everything else then copy/paste this into your program:

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _CP_OFF
#ENDCONFIG


/Henrik.

Your a life saver mate. I knew I had to write to the config but I just could not find out which way to do it, I was nearly there I was putting @config instead though!

Thanks again,
Rob

robbo78
- 21st January 2015, 08:46
Hi,
MCLR is disabled by clearing the correct "fuse" in the CONFIG word. With PBP3 you set the CONFIG word(s) with the #CONFIG/#ENDCONFIG compiler directives (more on that in the manual). Your PBP3 installation folder has a subfolder named DEVICE_REFERENCES in which you'll find a file for each and every device the compiler supports. The file contains information about the various CONFIG options and also what defaults PBP uses if you don't include any in your code.

For a 12F683 the defaults are:

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_ON & _CP_OFF
#ENDCONFIG


If you want to disable MCLR and keep everything else then copy/paste this into your program:

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _CP_OFF
#ENDCONFIG


/Henrik.

Hi Henrik, I am going to start my first project which will hopefully teach me new things along the way. I have question for you before I start though, I am hoping you can give a short answer to this.

Basically I want different modes (say 10) which are each cycled through via a tap of a tactile button (so mode 1, tap button, mode 2, tap, mode 3, etc) & each mode has its own led flashing at a different speed.

My question is, which way is the best to do this? With interrupts or something else? I would also like the chip to watch for other button presses as well not just this one sequence of modes, so its not going to be just a simple sequnce of button tap to next mode etc.

So I need a way of watching for multiple input changes all the time, I am expecting interrupts to be the answer after reading about, but just want to be sure :)

Hope you can help,
Rob

HenrikOlsson
- 21st January 2015, 10:16
Hi Rob,
There are several ways - as is the case most of the time.
Interrupts are probably the way to go but there may be other ways.

Do you have a particular chip in mind, is it still the 12F683? I think not since you mention 10 modes, each with its own LED.....
How many switches do you need to monitor?

/Henrik.

robbo78
- 21st January 2015, 10:20
Hi Henrik, I will still be using the pic12f683. This is just a starter project which I am hoping to take to something bigger, but I will be flashing 1 led, the 10 modes are all outputting to the one led. I would like to sense 2 inputs, 1 tactile button & 1 using adc to mintor coltage change.

Thanks,
Rob

robbo78
- 21st January 2015, 12:09
My last post messed up a bit at the end, it was supposed to be monitor voltage lol. Anyway given the things I would like, do you think interrupts are the best route? I would end up adding more outputs to this (in the end 4 outputs, 2 inputs).

Thanks again,
Rob

robbo78
- 21st January 2015, 13:46
Ive decided to go the interrupt route... I am stuck already. Here is my simple code, why isnt my interrupt working?


#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

DEFINE OCS 4 '4mhz ocsillator
ANSEL = 0 'all I/O digital
CMCON0 = 7 'comparator off
LED VAR GPIO.0 'LED pin 7
LED2 VAR GPIO.1 'LED2 pin 6

ON INTERRUPT GOTO mode1 'interrupt handler is mode1
INTCON = %10001000 'enable gp3 interrupt

main:
ENABLE
HIGH LED 'led on
PAUSE 500 'delay 0.5 second
LOW LED 'led off
PAUSE 500 'delay 0.5 second
GOTO main 'repeat

DISABLE 'disable interrupts in handler
mode1:
HIGH LED2 'led2 on
PAUSE 50
LOW led2 'led2 off
PAUSE 50
RESUME 'return to where left off
ENABLE 'enable interrupts

mackrackit
- 21st January 2015, 15:57
Normally in the interrupt routine this is needed.
INTCON.1 = 0
But the data sheet for this chip reads like this is needed.
INTCON.0 = 0

Then at the beginning of your code below
INTCON = %10001000
add
IOC.3=1

I think...

robbo78
- 21st January 2015, 16:29
Normally in the interrupt routine this is needed.
INTCON.1 = 0
But the data sheet for this chip reads like this is needed.
INTCON.0 = 0

Then at the beginning of your code below
INTCON = %10001000
add
IOC.3=1

I think...

Brilliant, that got it working. I take it IOC.3 = 1 tells the chip when to interrupt & on what pin?

So INTCON = %10001000 tells it to enable interrupts on pin 3 & IOC.3 = 1 tells it to interrupt on pin 3 change ?

Thanks for your help,
Rob

Tabsoft
- 21st January 2015, 16:42
This should work for you. It works in my simulator.

Also, since PBP's ON INTERRUPT only checks to see if an interrupt has occurred between PBP commands, you may want to use a loop counter in your main routine and use short pauses. This is how I revised your code.

Comments are added to the code below.

Good Luck.



'PIC 12F683
#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

'DEFINE OCS 4 '4mhz ocsillator
DEFINE OSC 4 '4mhz ocsillator
ANSEL = 0 'all I/O digital
CMCON0 = 7 'comparator off
LED VAR GPIO.0 'LED pin 7
LED2 VAR GPIO.1 'LED2 pin 6

loop1 var byte ' Loop 1 Counter

ON INTERRUPT GOTO mode1 'interrupt handler is mode1
INTCON = %10001000 'enable GIE and GPIE; clear GPIF
IOC = %00001000 ' enable IOC3 (GPIO3 Interrupt on change)

ENABLE
main:
do
for loop1 = 1 to 50
HIGH LED 'led on
'PAUSE 500 'delay 0.5 second
pause 10 '10 ms
next loop1

for loop1 = 1 to 50
LOW LED 'led off
'PAUSE 500 'delay 0.5 second
pause 10 '10 ms
next loop1

'GOTO main 'repeat
loop

DISABLE 'disable interrupts in handler
mode1:
if INTCON.0 = 1 then 'one of the GPIO<5:0> pins changed state (must be cleared in software)

HIGH LED2 'led2 on
PAUSE 50
LOW led2 'led2 off
PAUSE 50

endif
INTCON = %10001000 'enable GIE and GPIE; clear GPIF

RESUME 'return to where left off
ENABLE 'enable interrupts

end 'got here by mistake

robbo78
- 21st January 2015, 17:30
This should work for you. It works in my simulator.

Also, since PBP's ON INTERRUPT only checks to see if an interrupt has occurred between PBP commands, you may want to use a loop counter in your main routine and use short pauses. This is how I revised your code.

Comments are added to the code below.

Good Luck.



'PIC 12F683
#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

'DEFINE OCS 4 '4mhz ocsillator
DEFINE OSC 4 '4mhz ocsillator
ANSEL = 0 'all I/O digital
CMCON0 = 7 'comparator off
LED VAR GPIO.0 'LED pin 7
LED2 VAR GPIO.1 'LED2 pin 6

loop1 var byte ' Loop 1 Counter

ON INTERRUPT GOTO mode1 'interrupt handler is mode1
INTCON = %10001000 'enable GIE and GPIE; clear GPIF
IOC = %00001000 ' enable IOC3 (GPIO3 Interrupt on change)

ENABLE
main:
do
for loop1 = 1 to 50
HIGH LED 'led on
'PAUSE 500 'delay 0.5 second
pause 10 '10 ms
next loop1

for loop1 = 1 to 50
LOW LED 'led off
'PAUSE 500 'delay 0.5 second
pause 10 '10 ms
next loop1

'GOTO main 'repeat
loop

DISABLE 'disable interrupts in handler
mode1:
if INTCON.0 = 1 then 'one of the GPIO<5:0> pins changed state (must be cleared in software)

HIGH LED2 'led2 on
PAUSE 50
LOW led2 'led2 off
PAUSE 50

endif
INTCON = %10001000 'enable GIE and GPIE; clear GPIF

RESUME 'return to where left off
ENABLE 'enable interrupts

end 'got here by mistake



Hi Tabsoft, that worked perfectly. I can see what you mean by using loops instead of pauses, the interrupt is much more reactive.

Now I just need to know how to stay in that interrupt routine until I tap the button again.

Thanks,
Rob

HenrikOlsson
- 21st January 2015, 18:46
Hi Rob,

Now I just need to know how to stay in that interrupt routine until I tap the button again.
Even though that would work for this particular application that's generally not what you want to do (ie stay in the interrupt routine). Instead you do as I showed earlier, use a flag/semaphore in the interrupt service routine which the main code reads and determines what to do.

The same thing applies to your scenario with ten states. You have a variable which you increment in the interrupt (ie once very time you press the button), once it gets to 10 you reset it to zero. Then, in the main program loop you simply check the value of this variable and act accordingly. With a bit of thought and some math you might even USE the variables value directly as the delay in your loop to blink the LED.

As for the analog input it's important to understand that there is no mechanism to automatically run the ADC and detect (ie interrupt) when the voltage at the input changes. You need to write code to sample the input once every minute, second, millisecond or whatever the requirement may be.

Finally, please don't quote the full message just posted. There's really no need to have the very latest post quoted in full.

/Henrik.

Tabsoft
- 21st January 2015, 18:49
Rob,

Glad it worked for you.

I'm not sure if you want/need to stay in the ISR as I don't know your final objective.

I would suggest using a flag to determine the state of the IOC.3 interrupt coming from your button.
Then in the ISR change the state of the flag each time the interrupt occurs and then get out of the ISR.
You can then setup a subroutine for what you wanted to accomplish in the ISR.
Finally in your main loop check the state of the flag and gosub to the new subroutine.

Here is a working example. Expanded on what I posted last time.



'PIC 12F683
#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

'DEFINE OCS 4 '4mhz ocsillator
DEFINE OSC 4 '4mhz ocsillator
ANSEL = 0 'all I/O digital
CMCON0 = 7 'comparator off
LED VAR GPIO.0 'LED pin 7
LED2 VAR GPIO.1 'LED2 pin 6

loop1 var word ' Loop 1 Counter (word for 0 to 65535 increments)
loop2 var byte ' Loop 2 Counter (byte used for 0 to 255 increments)
ModeFlag var bit ' Bit to determine what state of IOC.3 is

'*****Initlaze Vars*****
loop1 = 0
loop2 = 0
ModeFlag = 0
'***********************

ON INTERRUPT GOTO mode1 'interrupt handler is mode1
INTCON = %10001000 'enable GIE and GPIE; clear GPIF
IOC = %00001000 ' enable IOC3 (GPIO3 Interrupt on change)

ENABLE
main:
do
for loop1 = 1 to 500
HIGH LED 'led on
'PAUSE 500 'delay 0.5 second
pause 1 '1 ms
if ModeFlag = 1 then
gosub moderoutine
endif

next loop1

for loop1 = 1 to 500
LOW LED 'led off
'PAUSE 500 'delay 0.5 second
pause 1 '1 ms
if ModeFlag = 1 then
gosub moderoutine
endif

next loop1

'GOTO main 'repeat
loop

moderoutine:
do while ModeFlag = 1
high LED2 'LED2 On
for loop2 = 0 to 50
pause 1 '1ms
next loop2

low LED2 'LED2 Off
for loop2 = 0 to 50
pause 1 '1ms
next loop2
loop

return

DISABLE 'disable interrupts in handler
mode1:
if INTCON.0 = 1 then 'one of the GPIO<5:0> pins changed state (must be cleared in software)

'HIGH LED2 'led2 on
'PAUSE 50
'LOW led2 'led2 off
'PAUSE 50

'Set the ModeFlag
ModeFlag = ~ ModeFlag
endif
INTCON = %10001000 'enable GIE and GPIE; clear GPIF

RESUME 'return to where left off
ENABLE 'enable interrupts

end 'got here by mistake

robbo78
- 21st January 2015, 19:20
Thanks a lot for the help guys, I appreciate it :) I understand now how isr's work now.

Thanks,
Rob

HenrikOlsson
- 22nd January 2015, 06:05
Hi,
Another aproach is to use the interrupt mechanism as a way to "latch" the buttonpress but then not actually enable the interrupt itself but instead poll the interrupt flag to determine if the button was pressed.


State VAR BYTE

Main:

If INTCON.1 = 1 THEN ' GP2 input interrupt
INTCON.1 = 0 ' Clear flag
State = State + 1
If State = 3 THEN State = 0
ENDIF

SELECT CASE State

Case 0
HIGH LED
PAUSE 50
LOW LED
PAUSE 50

CASE 1
HIGH LED
PAUSE 100
LOW LED
PAUSE 100

CASE 2
HIGH LED
PAUSE 150
LOW LED
PAUSE 150
GOTO MAIN

robbo78
- 22nd January 2015, 06:48
Hi, I like that way as well. Although at some point I need to add a nap command with wdt off so it wakes when a voltage is higher than 0.60v & naps when voltage is less then, so I would have to use an interupt anyway at some point?

Thanks,
Rob

robbo78
- 22nd January 2015, 10:35
Right I have changed the code from what Tabsoft last posted, so now there are 3 modes each blinking led at a different rate depending on the mode. Every tap of the button is supposed to goto the next mode.

Something weird is happening though, each tap of the button is reversing the mode lol. So instead of going forwards to the next mode, its going backwards (should go mode 1 > mode 2, but is going Mode 1 > mode 3 > mode 2).

Any ideas? here is the code -



'PIC 12F683

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

'DEFINE OCS 4 '4mhz ocsillator
DEFINE OSC 4 '4mhz ocsillator
ANSEL = 0 'all I/O digital
CMCON0 = 7 'comparator off
LED VAR GPIO.0 'LED pin 7

loop1 var word ' Loop 1 Counter (word for 0 to 65535 increments)
ModeFlag var bit ' Bit to determine what state of IOC.3 is

'*****Initlaze Vars*****
loop1 = 0
ModeFlag = 0
'***********************

ON INTERRUPT GOTO modeselect 'interrupt handler is modeselect
INTCON = %10001000 'enable GIE and GPIE; clear GPIF
IOC = %00001000 ' enable IOC3 (GPIO3 Interrupt on change)

ENABLE
mode1:
do
for loop1 = 1 to 1000
HIGH LED 'led on
'PAUSE 1000 'delay 1 second
pause 1 '1 ms
if ModeFlag = 1 then
gosub mode2
endif

next loop1

for loop1 = 1 to 1000
LOW LED 'led off
'PAUSE 1000 'delay 1 second
pause 1 '1 ms
if ModeFlag = 1 then
gosub mode2
endif

next loop1

'GOTO mode1 'repeat
loop

ENABLE
mode2:
do
for loop1 = 1 to 500
HIGH LED 'led on
'PAUSE 500 'delay 0.5 second
pause 1 '1 ms
if ModeFlag = 1 then
gosub mode3
endif

next loop1

for loop1 = 1 to 500
LOW LED 'led off
'PAUSE 500 'delay 0.5 second
pause 1 '1 ms
if ModeFlag = 1 then
gosub mode3
endif

next loop1

'GOTO mode2 'repeat
loop

ENABLE
mode3:
DO
for loop1 = 1 to 50
HIGH LED 'led on
'PAUSE 50 'delay 0.05 second
pause 1 '1 ms
if ModeFlag = 1 then
gosub mode1
endif

next loop1

for loop1 = 1 to 50
LOW LED 'led off
'PAUSE 50 'delay 0.05 second
pause 1 '1 ms
if ModeFlag = 1 then
gosub mode1
endif

next loop1

'GOTO mode3 'repeat
loop

DISABLE 'disable interrupts in handler
modeselect:
if INTCON.0 = 1 then 'one of the GPIO<5:0> pins changed state (must be cleared in software)

'next mode

'Set the ModeFlag
ModeFlag = ~ ModeFlag
endif
INTCON = %10001000 'enable GIE and GPIE; clear GPIF

RESUME 'return to where left off
ENABLE 'enable interrupts

end 'got here by mistake

robbo78
- 22nd January 2015, 10:40
UPDATE: I've just worked it out I think, its because I haven't got any debounce in there so every 'tap' could be jumping a couple of modes instead of seeing it as 1 button tap & jumping 1 mode.

Where do I actually add the debounce though? I couldn't add it into the sub modes as it would wreck the routine timing wouldn't it?

HenrikOlsson
- 22nd January 2015, 11:17
Hi,
No debounce may very well be part of the problem but in THIS case I think the contact bounce is what actually makes it sort of "work".
The real issue, I believe, is that your "loops" looks for ModeFlag being equal to 1 and switches mode. But the flag is not being cleared. So, if you push the button once, the ISR sets the flag. If there are no bounce the flag will be set all the time and the program will keep cycling between the different modes since the flag is never cleared. That's a problem with the implementation.

What's more severe is that you're using GOSUB without a matching RETURN. What you want in this case is GOTO.

Make the ISR set (and only set) the ModeFlag, implement some debounce in the ISR.
In the main loop you check the ModeFlag and IF it's set you first clear it and then act accordingly (ie. jump to the next mode).

Also, there's no need to set the LED high or low 1000 times (ie do it inside the FOR_NEXT loop), one time is enough, I'd rewrite the loop like:


DO
HIGH LED
for loop1 = 1 to 1000
pause 1 '1 ms
IF ModeFlag = 1 THEN
ModeFlag = 0
GOTO Mode 2
ENDIF
next loop1

LOW LED 'led off
for loop1 = 1 to 1000
pause 1 '1 ms
IF ModeFlag = 1 THEN
ModeFlag = 0
GOTO Mode 2
ENDIF
next loop1
LOOP

/Henrik.

robbo78
- 22nd January 2015, 11:46
Hi Henrik, thanks for helping. I have changed to the code to do as you said: goto's instead of gosub's & made the ISR set flag only (i think).

It all seems to work better now, however I still have a problem with the debounce as I think it is actually 'the problem' now instead of actually making the code work as you stated. The isr sees the release of the button as an interrupt & sets a flag again which skips modes. So how do I go about adding the debounce without slowing things down?

Isnt there a way to only interrupt or tell the ISR to flag only if the button is depressed & not when released? Here is the updated code -


'PIC 12F683

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

'DEFINE OCS 4 '4mhz ocsillator
DEFINE OSC 4 '4mhz ocsillator
ANSEL = 0 'all I/O digital
CMCON0 = 7 'comparator off
LED VAR GPIO.0 'LED pin 7

loop1 var word ' Loop 1 Counter (word for 0 to 65535 increments)
ModeFlag var bit ' Bit to determine what state of IOC.3 is

'*****Initlaze Vars*****
loop1 = 0
ModeFlag = 0
'***********************

ON INTERRUPT GOTO modeselect 'interrupt handler is modeselect
INTCON = %10001000 'enable GIE and GPIE; clear GPIF
IOC = %00001000 ' enable IOC3 (GPIO3 Interrupt on change)

ENABLE
mode1:
do
for loop1 = 1 to 1000
HIGH LED 'led on
'PAUSE 1000 'delay 1 second
pause 1 '1 ms
if ModeFlag = 1 then
ModeFlag = 0
goTO mode2
ENDIF

next loop1

for loop1 = 1 to 1000
LOW LED 'led off
'PAUSE 1000 'delay 1 second
pause 1 '1 ms
if ModeFlag = 1 then
Modeflag = 0
goTO mode2
endif

next loop1

'GOTO mode1 'repeat
loop

ENABLE
mode2:
do
for loop1 = 1 to 500
HIGH LED 'led on
'PAUSE 500 'delay 0.5 second
pause 1 '1 ms
if ModeFlag = 1 then
modeflag = 0
goTO mode3
endif

next loop1

for loop1 = 1 to 500
LOW LED 'led off
'PAUSE 500 'delay 0.5 second
pause 1 '1 ms
if ModeFlag = 1 then
modeflag = 0
goTO mode3
endif

next loop1

'GOTO mode2 'repeat
loop

ENABLE
mode3:
DO
for loop1 = 1 to 50
HIGH LED 'led on
'PAUSE 50 'delay 0.05 second
pause 1 '1 ms
if ModeFlag = 1 then
modeflag = 0
goTO mode1
endif

next loop1

for loop1 = 1 to 50
LOW LED 'led off
'PAUSE 50 'delay 0.05 second
pause 1 '1 ms
if ModeFlag = 1 then
modeflag = 0
goTO mode1
endif

next loop1

'GOTO mode3 'repeat
loop

DISABLE 'disable interrupts in handler
modeselect:
if INTCON.0 = 1 then 'one of the GPIO<5:0> pins changed state (must be cleared in software)
'next mode

'Set the ModeFlag
ModeFlag = 1
ENDIF

INTCON = %10001000 'enable GIE and GPIE; clear GPIF
RESUME 'return to where left off
ENABLE 'enable interrupts

end 'got here by mistake

HenrikOlsson
- 22nd January 2015, 15:43
Hi,
You're quite right, it's because you're using the IOC (Interrupt On Change), it'll trip the interrupt both when you push the button and when you release the button. (As well as on all the edges of the contact bounce). Also, when you're using IOC you need to remember to read the port (even if you're not using the value) in order to clear the mismatch condition.

I'd wire the button to GP2 and use the "normal" INT instead of IOC.

/Henrik.

robbo78
- 22nd January 2015, 17:25
So if I want to use IOC, I would need to read the button or any other type of input such as the adc?

I'm only asking as I need to be able to use interrupt on like 5 inputs, not just 1. Although gp2 will use adc not any others if that would be easier.

Thanks,
Rob

HenrikOlsson
- 22nd January 2015, 17:57
Hi Rob,
The IOC trips the interrupt on any change of the state on a digital pin. An interrupt will trip when the pin goes low and again when the pin goes high - provided you read the state of the pin in between. So in the ISR you need to read the state of switch and determine if it was pushed or released (and don't forget about the contact bounce). If it's determined that the interrupt tripped because the button was released you don't set the MODEFLAG.

And, once again, the ADC can NOT generate an interrupt when the analog voltage on the input changes. It won't work with IOC. Any pin configured as analog input will always read '1' and will never trip the IOC or INT interrupts.
Depending on what you want to do you might be able to use the comparator, they can be setup to trip an interrupt when their respective output changes state.

/Henrik.

robbo78
- 22nd January 2015, 20:15
Hi Henrik, thanks for explaining that it makes things a lot clearer as to how I'm going to run the code. What I would like to do is have one of the inputs (gp2), taking a reading from ADC every say 0.5 - 1 seconds.

Basically I want the chip to enter low power mode along with the device. When the device is off, one of the component pins goes to 0.00v, then when it is turned on it goes to 0.70v & stays at that. So I would like the ADC to monitor this, then when the voltage drops below 0.60v the chip is in low power mode, then when the voltage is higher than 0.60v I want it to come out of low power mode.

I hope this can be done in some way,
Rob

Tabsoft
- 22nd January 2015, 21:40
Rob,

I don't think the ADC module will accomplish what you are attempting as Henrik stated in his last reply.
You should look into the Comparator of the PIC, again as Henrik suggested.
:D

HenrikOlsson
- 23rd January 2015, 06:32
You can use the ADC to sample the voltage but the PIC needs to be awake for the ADC to operate and, again, the ADC doesn't run continously and can not automatically generate an interrupt when a given voltage or threshold is detected. You'd need to wake the PIC up every now and then, sample thevoltage at the pin and based on that determine if you should stay awake or go back to sleep. No problems doing that but to me it looks like you'll be better served by using comparator module. Using that you can put the PIC to sleep and it will automatically wake up when the comparator output flips.

/Henrik.

robbo78
- 23rd January 2015, 06:51
Oh I see, yes it sounds like the comparator will be the best then. Anyway thanks for your help guys, I have learned a lot.

I will be back lol

Rob

robbo78
- 23rd January 2015, 18:38
Back again already, Comparators are mind boggling !!

Firstly I have no idea how to even set the mode of the comparator, let alone working out & setting the VRC. I have been researching all day, the only info that looks helpful is in C so it is all gobbledygook to me as I have only just started learning basic.

I am not looking for someone to spoon feed me a code, I just want to be able to understand it for myself. What does this mean from the data sheet CM<2:0> = 011? How do you get CMCON0 = 2 from that? If I could understand the data sheet enough to put it into basic then it would help me a lot.

Just being able to turn on an LED when the voltage goes under 1.00v using a pot would be a massive accomplishment for me at the moment, as I could understand how it all works.

Any help on this would be great.

Thanks,
Rob

Archangel
- 23rd January 2015, 19:01
What does this mean from the data sheet CM<2:0> = 011?

It means bits 2 , 1, 0 are set 011
very common nomenclature to use the colon to be inclusive of all the bits in between 2:0

Tabsoft
- 23rd January 2015, 19:05
Rob,

Yes Comparators can be confusing the 1st time using them on the PIC MCU.

First, have a read through this link.
http://www.cuteminds.com/index.php/electronics/picmicro/picprorudiments/123-comparators

It explains simply how the comparator
on the PIC12F683 can be used. Ignore the C language stuff, but pay attention
to the setup of the Comparator, especially the Second Case.
This case describes what you will probably implement.

This will give you the info on how the Comparator will work by polling the
Comparator output, NOT USING INTERRUPTS.

Once you have this digested, then you can move on to the Interrupt portion.

Use the equation for VRR=1 (Low Range): (VR3:VR0/24) x Vdd to set the "Threshold Voltage"

E.g.
Vdd = +5Vdc
VR3:VR0 = 5 (%1001)

Putting this into the equation above gives this CVref "Threshold voltage"

(5/24) x 5 = 1.042 Vdc

This will be the reference voltage that will be used to compare the input voltage to.

General Rules when using the Comparator:
1. Set the appropriate pins to analog for the Comparator with ANSEL
2. Set the direction of the pins appropriately with TRISIO
3. Configure the Compare Module using CMCON0
4. Configure the reference voltage using VRCON
To use the comparator, poll the value of the status bit in CMCON0.

See if this gets you started.

Regards,

robbo78
- 23rd January 2015, 19:22
Thanks Tabsoft for that info & trying to help. I will go over all that until it becomes clear, I refuse to give up!

Thanks again,
Rob

robbo78
- 24th January 2015, 09:20
Okay I think I have made sense of this now :) - correct me if I'm wrong.

So if I wanted to set the comparator up with say an internal reference & output (cm<2:0> = 011), I would set the cmcon0 register as CMCON0 = %00000011 ?

Now to the VRR -

I think Im right in saying that the comparator uses the main VDD+ pin on the chip to compare the internal reference to? What I dont understand is this -

IF VRR=1 and VR3 ... VR0 = 1111 (15) with a VDD of 5V the Vref will be (15/24)*5 = 3,125V

^^ Where are they getting that 15 from? Also where do you get the 24 from?

IF VRR=0 and VR3 ... VR0 = 1111 (15) with a VDD of 5V the Vref will be 5/4+(15/32)*5 = 3,593V

^^ Also why has the 24 changed to a 32 just from vrr=1 changing to a 0?

One last question, Am I right in thinking that you have to use the CIN- pin on the chip to provide the voltage to check? Cant you use a comparator on any pin of the chip (apart from gp3, vdd & vss).

Thanks again,
Rob

robbo78
- 24th January 2015, 09:52
Update: Ive looked in the data sheet & found out the reason for dividing by either 24 or 32, if your working out low range then : 24, high range : 32. Thats another thing understood.

So the last thing I am struggling to understand now is, how do you get the number from th bits?

VR<3:0> = 1111 (15). < How on earth do you get 15 from just bits 1111?

This is the only thing that I'm struggling to understand now.

Thanks,
Rob

richard
- 24th January 2015, 10:14
the low range internal vref gives you 15 steps of 0.208 volt steps starting at 0v
the high range internal vref gives you 15 steps of 0.156 volt steps starting at 1.25v

if you don't need the cout as an actual physical output CM<2:0> = 110 is a better choice
and lets you chose pin 6 or 7 as your input (select with cmcon0.3 "CIS")
why waste pins on a 8 pin chip

0000=0
0001=1
0010=2
0011=3
...................etc etc
its called binary

robbo78
- 24th January 2015, 10:21
Edit: just re-read your post. I need to be able to read a pin & output on it at the same time (obviously with pauses, so do I need to use comparator with output?

Hi Richard, I just came back here from doing a bit of googleing. It took me about 2 mins to figure out that its just binary, maybe if I had paid more attention in Maths I would know this!

I now understand the voltage steps as well, everything is coming together now, its actually quite simple once understood.

I still need to know if you can use a comparator with internal reference on any pin or does it have to be the CIN- pin?

Cheers,
Rob

richard
- 24th January 2015, 10:28
if you don't need the cout as an actual physical output CM<2:0> = 110 is a better choice
and lets you chose pin 6 or 7 as your input (select with cmcon0.3 "CIS")

input to comparator is [ cin+ pin 7] or [cin- pin 6] with CM<2:0> = 110 its your choice

there are other modes but in all cases cin is only on pins 6,7

richard
- 24th January 2015, 10:31
if you need or can use the comparator output use CM<2:0> = 101
it still lets you chose pin 6 or 7 as your input (select with cmcon0.3 "CIS")

robbo78
- 24th January 2015, 10:39
Okay Richard thanks for your help.

So lets say I wanted an LED to be OFF if the voltage on cin+ is below 1.1v, & LED to be ON if the voltage is higher than 1.1v. Would I use this -

VR3 ... VR0 = 1000 (8) with a VDD of 3.3V the Vref will be (8/24) x 3.3 = 1.1V

I hope this is right lol

richard
- 24th January 2015, 11:41
there is not enough info to answer

what comparator mode ?
what cis setting
is vrr set
what cinv seting
how is led connected ?

robbo78
- 24th January 2015, 12:21
Richard, I'm specifically talking about just the VRR nothing else. I just want to know if I've got the workings out right. If you need the whole code to tell me if that part is right or not then I can do that no problem.

richard
- 24th January 2015, 12:26
that is correct it will set the reference to 1.1 for a vdd of 3.3v

robbo78
- 24th January 2015, 12:46
Thats great :) thanks for the help, now I can have a go at the code.

robbo78
- 24th January 2015, 13:37
Okay I have had a go & failed :( What am I doing wrong?


'PIC12f683

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

DEFINE OSC 4 '4mhz ocsillator
ANSEL = %00000010 'pin 6 analog
CMCON0 = %00000100 'comparator mode
VRCON = %00001000 'voltage reference
TRISIO = %00000010 'pin 6 input

POTEN VAR CMCON0.1 'pot pin 6
LED VAR GPIO.2 'led pin 5

PAUSE 20

main:
IF CMCON0.1 = 1 THEN
HIGH LED
ELSE
LOW LED
ENDIF
GOTO main

Tabsoft
- 24th January 2015, 14:49
Rob,

Glad to see your working through the design and issues.
You are very close.:smile:

Here are a couple of things you need to correct.
1. VRCON: Look at the register description in "Register 8-3" on page 53 of the datasheet.
In your program you are setting VRCON like this: VRCON = %00001000 'voltage reference
There are more bits in the register that you need to take into account.
A. You have bit7=0 which does this "0 = CVREF circuit powered down, no IDD drain and CVREF = VSS". This turns OFF the internal reference voltage.
You need to change this to "1", 1 = CVREF circuit powered on, to turn on the internal reference voltage circuit.
B. You have bit5 = 0 which does this "VRR: CVREF Range Selection bit 0=High Range"
This changes your CVref equation and result to this: "VRR = 0, CVREF = (VDD/4) + (VR3:VR0 X VDD/32)", which when you plug in your values you get this
for your reference voltage. (3.3/4) + (8/32) = (0.825) + (0.25) = 1.075 Vdc. Now this may be what you were actually trying to do, if so then your are good.

2. In your main loop you are attempting to test the output of the Comparator like this: "IF CMCON0.1 = 1 THEN"...
Look at the CMCON0 register description in "Register 8-1" on page 47 of the datasheet and compare that with the "CMCON0.1 = 1" above.
You are testing bit1 of the CMCON0 register, that is what the ".1" does. xxxx.1 says I want to look at bit1 of xxxx.
Is this the bit you need to check for the Comparator Output? If you're not sure have a look at "FIGURE 8-3" on page 49 and look at the figure for the
CM Mode you are using "100". That diagram indicates what the Comparator Output is by name.

Check and correct this.

3. Lastly, and somewhat obscure in the DS, you need to determine how you want the Comparator to test and output.
As I understand you want to check the input pin voltage against your "1.075 Vdc" reference you are setting up internally and have the following outcomes:
If the input is "Lower than 1.075 Vdc" you want the LED to be OFF and you want the LED to be "ON when the input is Higher than 1.075 Vdc".

To get these results you need to take a look at a couple of items in the datasheet.
A. Look at CMCON0, "Register 8-1" on page 47, and review the information for bit6 and bit4.
Also look at "TABLE 8-1" on page 48.
Between these two, you should be able to piece together how you need to configure the operation to get your desired "Output" of the Comparator.

You are getting close, hopefully these additional points will get you further on your way.

Regards,

robbo78
- 24th January 2015, 15:00
Hi Tabsoft, thanks a lot for your help I appreciate it. I will go back through everything you have stated & set it up right this time. I did think there were other bits to change but didnt want to mess as I didnt know what I was doing, but ai should now with your info.

Thanks again, I will let you know how it goes.
Rob

Tabsoft
- 24th January 2015, 16:16
Rob,

One correction in my earlier post.

The CVref calculation for High Range is CVREF = (VDD/4) + (VR3:VR0 X VDD/32).
Using your input values of VDD=3.3 and VR3:VR0=8, the calculation will come out like this.
(3.3/4) + (8 x (3.3/32) = (0.825) + (8 x 0.103125) = (0.825) + (0.825) = 1.65 Vdc

If you use Low Range then here is the info:
CVREF = (VR3:VR0/24) x VDD = (8/24) x 3.3 = 0.333 x 3.3 = 1.1 Vdc

Regards,

robbo78
- 25th January 2015, 11:56
Hi Tabsoft,

Right I have changed the bits in the VRCON to the right stuff, so I think that is right now. I did change bits in the CMCON0 but none of it seemed to make a difference, so I have put it back to how it was. I'm thinking either I'm testing the wrong bit still or my actual hardware setup is wrong.

Maybe you can point something out?

BTW I am using the low range :) Also this is correct - As I understand you want to check the input pin voltage against your "1.1" reference you are setting up internally and have the following outcomes: If the input is "Lower than 1.075 Vdc" you want the LED to be OFF and you want the LED to be "ON when the input is Higher than 1.075 Vdc".

Code -


'PIC 12F683


#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF
#ENDCONFIG


DEFINE OSC 4 '4mhz ocsillator
ANSEL = %00000010 'pin 6 analog
CMCON0 = %00000100 'comparator mode
VRCON = %10101000 'voltage reference
TRISIO = %00000010 'pin 6 input


POT1 VAR CMCON0.2 'pot pin 6
LED VAR GPIO.2 'led pin 5


PAUSE 20


main:
IF POT1 = 1 THEN
HIGH LED
ELSE
LOW LED
ENDIF
GOTO main

Thanks again,
Rob

Tabsoft
- 25th January 2015, 16:49
Rob,

Yes you're not testing the correct bit for the actual output of the Comparator..

Look at my post again where I listed the things you need to correct. Look at item 2.
I point you to where you can find the answer of which bit in which register to test.
I also point you to where you can determine what the output of the comparator will be for different configurations and physical connections.

Have a read of this info again, I'm sure you'll get it.

Regards:smile:

robbo78
- 25th January 2015, 19:42
Hi Tabsoft, okay had another read at first it was just going over my head. Am I right in saying I need to read bit 6 of CMCON0? So CMCON0.6?

Thanks,
Rob

Tabsoft
- 25th January 2015, 20:06
Exactly!:biggrin:

CMCON0 Register:
bit 6 COUT: Comparator Output bit.
This is the logic bit output of the Comparator.

CM: Comparator Mode = 100
7686

With this setup the comparator is comparing VIN- (CIN-/GP1) to VIN+ (which is not an external pin but it is "From CVREF Module", internal voltage reference)
So you need to look at the description of COUT in the CMCON0 register listed below AND you need to look at Table 8-1 in the DS.
This will help you work out what value CMCON0.6 will be based upon a given input on GP1.
Take note that CINV is a configuration bit within the CMCON0 register that determines the logic polarity of COUT.

COUT: Possible values
When CINV = 0:
1 = VIN+ > VIN-
0 = VIN+ < VIN

When CINV = 1:
1 = VIN+ < VIN-
0 = VIN+ > VIN

robbo78
- 25th January 2015, 21:36
Brilliant! Thanks for the info again your the best! I should be able to get this working how I want it to now, then & only then will I move onto interupt with it.

Thanks again,
Rob

robbo78
- 26th January 2015, 12:17
Hi Tabsoft, I got the comparator working perfectly :D so I decided to move onto adding an interrupt, which believe it or not hasn't worked. Here is the code I have fully written, maybe you can help where I have gone wrong?

Thanks,
Rob

You will probably have a laugh at this, as its probably completely wrong lol



'PIC 12F683

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

PAUSE 20 'wait for hardware to settle

DEFINE OSC 4 '4mhz ocsillator
ANSEL = %00000010 'pin 6 analog
CMCON0 = %00000100 'comparator mode
VRCON = %10101000 'voltage reference
TRISIO = %00000010 'pin 6 input

ON INTERRUPT GOTO SLSELECT 'interrupt handler is slselect
INTCON = %11001000 'enable GIE and GPIE; clear GPIF
IOC = %00000010 ' enable IOC1 (GPIO.1 Interrupt on change)
PIE1 = %00001000 'enable comparator interrupt

POT1 VAR CMCON0.6 'read potentiometer
LED VAR GPIO.2 'led pin 5

ENABLE
main:
HIGH LED 'light led
GOTO main 'repeat

DISABLE 'disable interrupts in handler
SLSELECT:
IF POT1 = 1 THEN 'if <1.1v sleep
NAP 1
ELSE
ENDIF

INTCON = %10001000 'enable GIE and GPIE; clear GPIF
PIR1 = %00000000 'reset CMIF
RESUME 'where left off
ENABLE 'enable interrupts

Tabsoft
- 26th January 2015, 14:34
Glad to hear if Rob.

You did not say what is happening only that it is not working.
I took a look at your code and several things jump out at me.

1. You are jumping into the deep end of the pool with testing "nap"
If would take a smaller step first. Set a flag in the ISR and resume. Then in the main act on the flag you set in the ISR.
This way you can see the effects of the ISR logic.
2. You have IOC.1 enabled, which is GP1, which you are using for the comparator. You need to comment out IOC.1.
3. You are not testing for which Interrupt is firing inside of your ISR. It is almost always a good practice to determine what interrupt caused the jump to the ISR.
4. At the end of your ISR you are not setting the INTCON register they way you think. I believe you have a typo in the value you are assigning to it.


I changed your code to the following which shows the changes.




'PIC 12F683

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

PAUSE 20 'wait for hardware to settle

DEFINE OSC 4 '4mhz ocsillator
ANSEL = %00000010 'pin 6 analog
CMCON0 = %00000100 'comparator mode
VRCON = %10101000 'voltage reference
TRISIO = %00000010 'pin 6 input

ON INTERRUPT GOTO SLSELECT 'interrupt handler is slselect
INTCON = %11001000 'enable GIE and GPIE; clear GPIF
'IOC = %00000010 ' enable IOC1 (GPIO.1 Interrupt on change)
PIE1 = %00001000 'enable comparator interrupt

POT1 VAR CMCON0.6 'read potentiometer
LED VAR GPIO.2 'led pin 5

bitTest var bit 'Debug test bit. Set/Clear in ISR and Check in maiin

bitTest = 0

ENABLE
main:

'HIGH LED 'light led
LED = bitTest

GOTO main 'repeat

DISABLE 'disable interrupts in handler
SLSELECT:
'IF POT1 = 1 THEN 'if <1.1v sleep
'NAP 1
'ELSE
'ENDIF
if PIR1.3 = 1 then 'Comparator Interrupt fired (must be cleared before resume)
bitTest = POT1
endif

'INTCON = %10001000 'enable GIE and GPIE; clear GPIF
INTCON =%11001000
PIR1 = %00000000 'reset CMIF
RESUME 'where left off
ENABLE 'enable interrupts


See if this gets you along further.

robbo78
- 26th January 2015, 20:43
Hi Tabsoft, okay thanks for all that. I will go back to flagging. I think I was
Getting a bit ahead of myself lol.

Let you know how it goes :)

robbo78
- 29th January 2015, 14:56
Hi, okay I've been playing about with comparator interrupt now & think I have got the hang of it using flags. The code below is something little I came up with :)

Now I think I would like to implement the nap command now, or do you think I should learn something else before jumping to the nap command?

Thanks again,
Rob


'PIC 12F683

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF
#ENDCONFIG

PAUSE 50 'wait for hardware to settle

DEFINE OSC 4 '4mhz ocsillator
ANSEL = %00000010 'pin 6 analog
CMCON0 = %00000100 'comparator mode
VRCON = %10101000 'voltage reference
TRISIO = %00000010 'pin 6 input

ON INTERRUPT GOTO int1 'interrupt handler is int1
INTCON = %11001000 'enable GIE and GPIE; clear GPIF
PIE1 = %00001000 'enable comparator interrupt

POT1 VAR CMCON0.6 'read potentiometer
LED VAR GPIO.2 'led pin 5
loop1 var word 'loop1 counter

bitTest var bit 'Debug test bit. Set/Clear in ISR and Check in main

bitTest = 0
Loop1 = 0

ENABLE
main:

IF bittest = 1 THEN
GOTO FLASH
ENDIF

LOW LED

GOTO main


ENABLE
FLASH:



do
for loop1 = 1 to 1000
HIGH LED
PAUSE 1

NEXT LOOP1

for loop1 = 1 to 1000
LOW LED
PAUSE 1

NEXT LOOP1

IF bittest = 0 THEN
GOTO main
ENDIF

LOOP


DISABLE 'disable interrupts in handler
int1:

if PIR1.3 = 1 then 'Comparator Interrupt fired (must be cleared before resume)
BITtest = pot1
ENDIF

INTCON =%11001000
PIR1 = %00000000 'reset CMIF
RESUME 'where left off
ENABLE 'enable interrupts

Tabsoft
- 29th January 2015, 16:59
Rob,

Looks like you got it working.:)

One little thing though.
The Pause command is based upon the "DEFINE OSC" value to calculate the pause interval correctly.
It is a good practice to have your "DEFINE OSC" statement up top in the source code before you use any time sensitive commands such as "Pause x".

The way you have your program setup the following occurs, which I presume you have seen in practice on your physical system.
The LED on GP2(pin5) will stay LOW (OFF) while the voltage input on GP1(pin6) is above your reference voltage.
The LED GP2(pin5) will blink in 1sec intervals while the voltage input on GP1(pin6) is below your reference voltage.

This works fine on my simulator.

Good job!

It looks like you're ready for a "nap" now.:D

robbo78
- 29th January 2015, 17:29
Hi Tabsoft, thanks for comfirming :) also thanks again for all your help, I wouldnt have got to this point if it wasnt for you. I havent got a simulator so I just test straight on the BB.

I will drop the pause below the osc value, thanks for that tip :)

Do I implement the nap command in the same way as flashing an LED? So if bittest = 1 : nap 1, if bittest = 0 : goto main? Something like that ?

Thanks again,
Rob

Tabsoft
- 29th January 2015, 20:29
Rob,

Why don't you check this out.



'PIC 12F683

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF
#ENDCONFIG


DEFINE OSC 4 '4mhz ocsillator
ANSEL = %00000010 'pin 6 analog
CMCON0 = %00000100 'comparator mode
VRCON = %10101000 'voltage reference
TRISIO = %00000010 'pin 6 input

PAUSE 50 'wait for hardware to settle

ON INTERRUPT GOTO int1 'interrupt handler is int1
INTCON = %11001000 'enable GIE and GPIE; clear GPIF
PIE1 = %00001000 'enable comparator interrupt

POT1 VAR CMCON0.6 'read potentiometer
LED VAR GPIO.2 'led pin 5
loop1 var word 'loop1 counter

bitTest var bit 'Debug test bit. Set/Clear in ISR and Check in main


high LED 'Start with LED ON
pause 500 'Pause .5 secs so you can see the LED turn on

bitTest = POT1 'Take initial reading of input voltage state
Loop1 = 0

ENABLE
main:

IF bitTest = 1 THEN 'input voltage is < reference voltage
low LED
nap 1
else 'bitTest = 0 - input voltage is > reference voltage
GOTO FLASH
ENDIF

GOTO main


ENABLE
FLASH:
do
for loop1 = 1 to 1000
HIGH LED
PAUSE 1
NEXT LOOP1

for loop1 = 1 to 1000
LOW LED
PAUSE 1
NEXT LOOP1

IF bitTest = 1 THEN
GOTO main
ENDIF

LOOP


DISABLE 'disable interrupts in handler
int1:

if PIR1.3 = 1 then 'Comparator Interrupt fired (must be cleared before resume)
bitTest = pot1
ENDIF

INTCON =%11001000
PIR1 = %00000000 'reset CMIF
RESUME 'where left off
ENABLE 'enable interrupts

HenrikOlsson
- 30th January 2015, 06:45
I will drop the pause below the osc value, thanks for that tip
Like Tabsoft wrote, you generally want your DEFINEs at the top of the program but it's for readabillity. In reallity it makes no difference because they are not runtime commands.

Another little note

for loop1 = 1 to 1000
HIGH LED
PAUSE 1
NEXT LOOP1

What's the purpose of setting the LED high inside the loop?
There's nothing inherently wrong in doing it but it does take (a little) bit of time.

Also, now that you're starting to get the hang of it you might want to consider ditching the HIGH/LOW commands and simply set the pin directly, LED=1, LED=0. For this to work you need to clear the corresponding TRIS-bit first (TRISO.2 = 0 ' Set GPIO.2 to output). Doing it this way executes faster and consumes less codespace since it doesn't waste resources clearing the TRIS bit each and every time (which is what HIGH/LOW does for you).

Again, you're not doing anything "wrong" - it all works fine, I'm just pointing out a couple of things you could improve and take with you to the next project.

/Henrik.

robbo78
- 30th January 2015, 17:15
Thanks guys, I will have a play :)

Rob

robbo78
- 31st January 2015, 18:37
Hi guys, okay I've been having a mess today & I am quite happy with myself. I managed to use the comparator successfully, now I have added a button into the routine.

It is all doing what I want it to but I am having this big issue with de-bouncing :( how do I sort this out, what is the best way? Do I need to read the button in a different way?

Here is the code -


'PIC 12F683

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF
#ENDCONFIG


DEFINE OSC 4 '4mhz ocsillator
ANSEL = %00000001 'pin 7 analog
CMCON0 = %00011110 'comparator mode
VRCON = %10100000 'voltage reference
TRISIO = %00101111 'pin 2,4,5,6,7 input

PAUSE 50 'wait for hardware to settle

ON INTERRUPT GOTO int1 'interrupt handler is int1
INTCON = %11001000 'enable GIE and GPIE; clear GPIF
PIE1 = %00001000 'enable comparator interrupt
IOC = %00001000 'pin 3 ioc

RTPOT VAR CMCON0.6 'right trigger pot
RTBITTEST var bit 'trigger debug test bit. Set/Clear in ISR and Check in main

BUT VAR GPIO.3 'tactile button
BUTBITTEST VAR BIT 'button test bit


RTBITTEST = RTPOT 'initial reading

BUTBITTEST = 0 'read 0 to start


ENABLE
main:

IF RTBITTEST = 1 THEN
GOTO RAPID
ENDIF

IF RTBITTEST = 0 THEN
GOTO SLEEPY
ENDIF

GOTO main


ENABLE
RAPID:

LOW GPIO.0
PAUSE 70
TRISIO.0 = 1
PAUSE 70

IF RTBITTEST = 0 THEN
GOTO SLEEPY
ENDIF

IF BUTBITTEST = 1 THEN
GOTO FLASH
ENDIF

GOTO RAPID


ENABLE
SLEEPY:

HIGH GPIO.5
PAUSE 1000
TRISIO.5 = 1
PAUSE 1000

IF RTBITTEST = 1 THEN
GOTO main
ENDIF

GOTO SLEEPY


ENABLE
FLASH:

HIGH GPIO.5
PAUSE 300
TRISIO.5 = 1
PAUSE 300

IF BUTBITTEST = 1 THEN
GOTO RAPID
ENDIF

GOTO FLASH



DISABLE 'disable interrupts in handler
int1:

if PIR1.3 = 1 then 'Comparator Interrupt fired (must be cleared before resume)
RTBITTEST = RTPOT
ENDIF

IF IOC.3 = 1 THEN
BUTBITTEST = BUT
ENDIF

INTCON =%11001000
PIR1 = %00000000 'reset CMIF
IOC = %00000000 'reset IOC.3
RESUME 'resume to where left off
ENABLE 'enable interrupts

Tabsoft
- 31st January 2015, 23:48
Rob,
You really don't state what the problem is, only that you see a problem.

A quick scan of the code leads to too many questions for me.
What are you trying to accomplish in the different subroutines Rapid, Sleepy and Flash?
You seem to be using two GPIO pins, one for Rapid (GPIO.0) and another (GPIO.5) for Sleepy and Flash.
Another question is why in each of the subroutines are you setting the GPIO high, pausing and then configuring the same GPIO as an input?

Without more info, it is futile to offer an opinion.