PDA

View Full Version : Change On Interrupt, PIC16F884



elec_mech
- 12th November 2008, 17:00
Hello Folks,

I'm either missing something amazingly minor or something is going terribly wrong, likely the former. I'm using a PIC16F884 and attempting to use the interrupt-on-change feature. I've hooked up a momentary switch, pulled high and tied to ground when the switch is pressed and connected it to PORTB.0. I have two LEDs, one that blinks constantly and one to let me know when the interrupt routine has been activated. I've attached a simple drawing of the circuit under test. Eventually I want to use four switches on ports B0-B3.

The problem is, I can never seem to get the interrupt to work, i.e. the second LED never comes on. I've gone over the manual and the forum, tried a few things, and have come up nil. Any advice or suggestions is greatly appreciated. Code is below. Thank you in advance.


INCLUDE "modedefs.bas"
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low
asm ; The following code is assembly, not Basic

__CONFIG _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF & _LVP_OFF
; Use internal oscillator & make both, OSC pins I/Os, turn off watchdog timer, enable Power-up timer,
; code protection off, brown-out off, disable switch over mode, turn off failsafe monitor,
; low voltage programming off
bcf WDTCON, SWDTEN ; Disable the watchdog timer
bsf OSCCON, IRCF2 ; Set the internal oscillator to 8MHz
bsf OSCCON, IRCF1 ; Set the internal oscillator to 8MHz
bsf OSCCON, IRCF0 ; Set the internal oscillator to 8MHz
bcf ADCON0, ADON ; Make all analog pins digital I/Os
bcf CM1CON0, C1ON ; Disable comparator C1
bcf CM2CON0, C2ON ; Disable comparator C1
MOVLW 0x00 ; Set w = 00000000
MOVWF ANSEL ; Set all analog pins on port a to digital I/O
MOVLW 0x00 ; Set w = 00000000
MOVWF ANSELH ; Set all analog pins on port b to digital I/O
endasm ' End assembly code
define OSC 8 ' Tell the program the oscillator is running at 8 MHz
TRISA = %00000000 ' Make all port a pins outputs
TRISB = %00001111 ' Make port b pins 0-3 inputs and the rest as outputs
TRISC = %00000000 ' Make all port c pins outputs
TRISD = %00000000 ' Make all port d pins outputs
TRISE = %00000000 ' Make all port e pins outputs
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low
'Variables**************************************** ************************************************** *
b0 VAR byte ' Required byte variable for toggle button
'************************************************* ************************************************** *
INITIALIZE: ' Initialize Routine
INTCON.0 = 0 ' Clear the interrupt-on-change flag
On Interrupt goto Halt ' Once an active interrupt port is enabled, go to the Halt routine
INTCON.7 = 1 ' Enable global interrupts
IOCB = %00000001 ' Enable interrupt-on-change on RB0-RB3
INTCON.3 = 1 ' Enable interrupt-on-change on Port b
GOTO MAIN ' Go to Main routine
'************************************************* ************************************************** *
MAIN:
' PORTB = $00 ' Set all port b pins to low
b0 = PORTB ' temp is some variable
HIGH PORTE.0
PAUSE 500
LOW PORTE.0
PAUSE 500
GOTO MAIN
'************************************************* ************************************************** ***
DISABLE ' Disable all interrupts
Halt: ' Halt Routine
HIGH PORTE.1
b0 = PORTB ' temp is some variable
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME Main ' Go to Main routine
ENABLE ' Enable all active interrupts
'************************************************* ************************************************** ***


END

Bruce
- 12th November 2008, 20:52
One major problem is you're not switching to the proper register banks when attempting to
write to various registers.

Why not just let PBP handle all this for you? ADCON0=xx, OSCCON=xx, it's very easy. If you
prefer to do this all in assembler, you'll need to get very familiar with bank switching, and
read the datasheet so you know which banks things are located in.

elec_mech
- 13th November 2008, 13:08
:eek:

Bruce,

Thank you! I completely forgot about bank switching! Here is the revised code if it helps anyone. I assume I don't need to worry about bank switching for the config settings.

INCLUDE "modedefs.bas"
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low

asm ; The following code is assembly, not Basic
__CONFIG _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF & _LVP_OFF
; Use internal oscillator & make both, OSC pins I/Os, turn off watchdog timer, enable Power-up timer,
; code protection off, brown-out off, disable switch over mode, turn off failsafe monitor,
; low voltage programming off
endasm ' End assembly code

ADCON0.0 = 0 ; Make all analog pins digital I/Os
CM1CON0.7 = 0 ; Disable comparator C1
CM2CON0.7 = 0 ; Disable comparator C2
OSCCON.6 = 1 ; Set the internal oscillator to 8MHz
OSCCON.5 = 1 ; Set the internal oscillator to 8MHz
OSCCON.4 = 1 ; Set the internal oscillator to 8MHz
WDTCON.0 = 0 ; Disable the watchdog timer
ANSEL = %00000000 ; Set all analog pins on port a to digital I/O
ANSELH = %00000000 ; Set all analog pins on port b to digital I/O

define OSC 8 ' Tell the program the oscillator is running at 8 MHz
TRISA = %00000000 ' Make all port a pins outputs
TRISB = %00001111 ' Make port b pins 0-3 inputs and the rest as outputs
TRISC = %00000000 ' Make all port c pins outputs
TRISD = %00000000 ' Make all port d pins outputs
TRISE = %00000000 ' Make all port e pins outputs
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low
'Variables**************************************** ************************************************** *
b0 VAR byte ' Required byte variable for toggle button
'************************************************* ************************************************** *
INITIALIZE: ' Initialize Routine
INTCON.0 = 0 ' Clear the interrupt-on-change flag
On Interrupt goto Halt ' Once an active interrupt port is enabled, go to the Halt routine
INTCON.7 = 1 ' Enable global interrupts
IOCB = %00000001 ' Enable interrupt-on-change on RB0-RB3
INTCON.3 = 1 ' Enable interrupt-on-change on Port b
GOTO MAIN ' Go to Main routine
'************************************************* ************************************************** *
MAIN:
' PORTB = $00 ' Set all port b pins to low
b0 = PORTB ' temp is some variable
HIGH PORTE.0
PAUSE 500
LOW PORTE.0
PAUSE 500
GOTO MAIN
'************************************************* ************************************************** ***
DISABLE ' Disable all interrupts
Halt: ' Halt Routine
HIGH PORTE.1
LOW PORTE.0
b0 = PORTB ' temp is some variable
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME Done ' Go to Main routine
ENABLE ' Enable all active interrupts
'************************************************* ************************************************** ***
Done:

END

Bruce
- 13th November 2008, 14:23
This can cause a lot of headaches sometimes due to read-modify-write;

HIGH PORTE.1 ' reads whole port, flips this bit, writes whole port
LOW PORTE.0 ' same, but if RE1 isn't high yet, RE1 is read & writen back as 0

Try just toggling the LED on RE1 to verify that you're entering the interrupt routine.

Another potential show-stopper is directing RESUME to Done, which halts everything.

Try something like this;


INITIALIZE: ' Initialize Routine
IOCB = %00000001 ' Enable interrupt-on-change on RB0
INTCON = %10001000 ' Global & interrupt-on-change on Port b enabled
'*************************************************
On Interrupt goto Halt ' on interrupt go to the Halt routine

MAIN:
HIGH PORTE.0
PAUSE 500
LOW PORTE.0
PAUSE 500
GOTO MAIN
'*************************************************
DISABLE ' do NOT place interrupt checking code below
Halt: ' Halt Routine
WHILE PORTB.0=0 ' wait for button release & end mismatch condition on portb
WEND
TOGGLE PORTE.1
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME ' Go to Main routine
ENABLE ' Enable all active interrupts

elec_mech
- 13th November 2008, 14:59
Bruce,

Thank you. It acts a little flaky, e.g., sometimes the LED on porte.1 would stay on two consecutive button presses or off, due to debounce. I looked at some other threads and found a 50ms pause. Here is the code again with the debounce which is working like a charm. Thank you for your help.

INCLUDE "modedefs.bas"
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low

asm ; The following code is assembly, not Basic
__CONFIG _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF & _LVP_OFF
; Use internal oscillator & make both, OSC pins I/Os, turn off watchdog timer, enable Power-up timer,
; code protection off, brown-out off, disable switch over mode, turn off failsafe monitor,
; low voltage programming off
endasm ' End assembly code

ADCON0.0 = 0 ; Make all analog pins digital I/Os
CM1CON0.7 = 0 ; Disable comparator C1
CM2CON0.7 = 0 ; Disable comparator C2
OSCCON.6 = 1 ; Set the internal oscillator to 8MHz
OSCCON.5 = 1 ; Set the internal oscillator to 8MHz
OSCCON.4 = 1 ; Set the internal oscillator to 8MHz
WDTCON.0 = 0 ; Disable the watchdog timer
ANSEL = %00000000 ; Set all analog pins on port a to digital I/O
ANSELH = %00000000 ; Set all analog pins on port b to digital I/O

define OSC 8 ' Tell the program the oscillator is running at 8 MHz
TRISA = %00000000 ' Make all port a pins outputs
TRISB = %00001111 ' Make port b pins 0-3 inputs and the rest as outputs
TRISC = %00000000 ' Make all port c pins outputs
TRISD = %00000000 ' Make all port d pins outputs
TRISE = %00000000 ' Make all port e pins outputs
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low
'Variables**************************************** ************************************************** *
b0 VAR byte ' Required byte variable for toggle button
'************************************************* ************************************************** *
INITIALIZE: ' Initialize Routine
INTCON.0 = 0 ' Clear the interrupt-on-change flag
On Interrupt goto Halt ' Once an active interrupt port is enabled, go to the Halt routine
INTCON = %10001000 ' Enable global interrupts, Enable interrupt-on-change on Port b
IOCB = %00000001 ' Enable interrupt-on-change on RB0-RB3
PORTE.1 = 0 ' Make sure LED on PORTE.1 is off
GOTO MAIN ' Go to Main routine
'************************************************* ************************************************** *
MAIN:
HIGH PORTE.0
PAUSE 500
LOW PORTE.0
PAUSE 500
GOTO MAIN
'*************************************************
DISABLE ' do NOT place interrupt checking code below
Halt: ' Halt Routine
WHILE PORTB.0=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 50 ' Debounce delay
TOGGLE PORTE.1 ' Toggle LED on PORTE.1
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME ' Go to Main routine
ENABLE ' Enable all active interrupts

elec_mech
- 13th November 2008, 16:31
One more question: How about detecting multiple switches with interrupt-on-change? I've tried the modified code below as well as the commented out code on its own. This works, but not reliably. I'm using those momentary red square switches from RadioShack and the LEDs only change about half the time, i.e., the program isn't detecting the button press. I've tried pressing the switches hard (fully down) quickly, but with the same hit rate of half. If I press and hold each button for a second or two, it works fine, but I don't want to force the user to press and hold a switch to do something. I tried playing with the debounce times and even adding a ceramic capacitor, 0.1uF and 0.01uF across the switch contacts with no luck. I tried searching the forum, but the only interrupt examples I've come across are for single button applications. Any suggestions?

INCLUDE "modedefs.bas"
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low

asm ; The following code is assembly, not Basic
__CONFIG _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF & _LVP_OFF
; Use internal oscillator & make both, OSC pins I/Os, turn off watchdog timer, enable Power-up timer,
; code protection off, brown-out off, disable switch over mode, turn off failsafe monitor,
; low voltage programming off
endasm ' End assembly code

ADCON0.0 = 0 ; Make all analog pins digital I/Os
CM1CON0.7 = 0 ; Disable comparator C1
CM2CON0.7 = 0 ; Disable comparator C2
OSCCON.6 = 1 ; Set the internal oscillator to 8MHz
OSCCON.5 = 1 ; Set the internal oscillator to 8MHz
OSCCON.4 = 1 ; Set the internal oscillator to 8MHz
WDTCON.0 = 0 ; Disable the watchdog timer
ANSEL = %00000000 ; Set all analog pins on port a to digital I/O
ANSELH = %00000000 ; Set all analog pins on port b to digital I/O

define OSC 8 ' Tell the program the oscillator is running at 8 MHz
TRISA = %00000000 ' Make all port a pins outputs
TRISB = %00001111 ' Make port b pins 0-3 inputs and the rest as outputs
TRISC = %00000000 ' Make all port c pins outputs
TRISD = %00000000 ' Make all port d pins outputs
TRISE = %00000000 ' Make all port e pins outputs
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low
'Variables**************************************** ************************************************** *
b0 VAR byte ' Required byte variable for toggle button
'************************************************* ************************************************** *
INITIALIZE: ' Initialize Routine
INTCON.0 = 0 ' Clear the interrupt-on-change flag
On Interrupt goto Halt ' Once an active interrupt port is enabled, go to the Halt routine
INTCON = %10001000 ' Enable global interrupts, Enable interrupt-on-change on Port b
IOCB = %00000011 ' Enable interrupt-on-change on RB0-RB3
PORTE.1 = 0 ' Make sure LED on PORTE.1 is off
PORTE.2 = 0 ' Make sure LED on PORTE.2 is off
GOTO MAIN ' Go to Main routine
'************************************************* ************************************************** *
MAIN:
HIGH PORTE.0
PAUSE 500
LOW PORTE.0
PAUSE 500
GOTO MAIN
'*************************************************
DISABLE ' do NOT place interrupt checking code below
Halt: ' Halt Routine
IF PORTB.0 = 0 THEN
WHILE PORTB.0=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
HIGH PORTE.1
LOW PORTE.2
ENDIF
IF PORTB.1 = 0 THEN
WHILE PORTB.1=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
LOW PORTE.1
HIGH PORTE.2
ENDIF

' IF PORTB.0 = 0 THEN
' ' PAUSE 20
' ' IF PORTB.0 = 0 THEN
' HIGH PORTE.1
' LOW PORTE.2
' ' ENDIF
' ENDIF
'
' IF PORTB.1 = 0 THEN
' ' PAUSE 20
' ' IF PORTB.1 = 0 THEN
' LOW PORTE.1
' HIGH PORTE.2
' ' ENDIF
' ENDIF

INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME ' Go to Main routine
ENABLE ' Enable all active interrupts

Pic_User
- 13th November 2008, 17:19
Hi elec_mech,
Posting code directly into the text portion of the forum makes it difficult to follow.

paste your code here
One step better but a lot more work:

Show us your Colors.

define osc 8 ' tell the program the oscillator is running at 8 mhz



4.16. DEFINE may be used to change the predefined oscillator value...
These definitions must be in all upper case, exactly as shown.
If not, the compiler may not recognize them.
No error message will be produced for defines the compiler does not recognize.

DEFINE OSC 8-Adam-

elec_mech
- 13th November 2008, 17:43
Adam,

I see what you mean with the DEFINE, oops! Thanks for the catch. I see what you mean with colors. Is this better?

INCLUDE "modedefs.bas"
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low

asm ; The following code is assembly, not Basic
__CONFIG _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF & _LVP_OFF
; Use internal oscillator & make both, OSC pins I/Os, turn off watchdog timer, enable Power-up timer,
; code protection off, brown-out off, disable switch over mode, turn off failsafe monitor,
; low voltage programming off
endasm ' End assembly code

ADCON0.0 = 0 ; Make all analog pins digital I/Os
CM1CON0.7 = 0 ; Disable comparator C1
CM2CON0.7 = 0 ; Disable comparator C2
OSCCON.6 = 1 ; Set the internal oscillator to 8MHz
OSCCON.5 = 1 ; Set the internal oscillator to 8MHz
OSCCON.4 = 1 ; Set the internal oscillator to 8MHz
WDTCON.0 = 0 ; Disable the watchdog timer
ANSEL = %00000000 ; Set all analog pins on port a to digital I/O
ANSELH = %00000000 ; Set all analog pins on port b to digital I/O

DEFINE OSC 8 ' Tell the program the oscillator is running at 8 MHz
TRISA = %00000000 ' Make all port a pins outputs
TRISB = %00001111 ' Make port b pins 0-3 inputs and the rest as outputs
TRISC = %00000000 ' Make all port c pins outputs
TRISD = %00000000 ' Make all port d pins outputs
TRISE = %00000000 ' Make all port e pins outputs
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low
'Variables**************************************** ************************************************** *
b0 VAR byte ' Required byte variable for toggle button
'************************************************* ************************************************** *
INITIALIZE: ' Initialize Routine
INTCON.0 = 0 ' Clear the interrupt-on-change flag
On Interrupt goto Halt ' Once an active interrupt port is enabled, go to the Halt routine
INTCON = %10001000 ' Enable global interrupts, Enable interrupt-on-change on Port b
IOCB = %00000011 ' Enable interrupt-on-change on RB0-RB3
PORTE.1 = 0 ' Make sure LED on PORTE.1 is off
PORTE.2 = 0 ' Make sure LED on PORTE.2 is off
GOTO MAIN ' Go to Main routine
'************************************************* ************************************************** *
MAIN:
HIGH PORTE.0
PAUSE 500
LOW PORTE.0
PAUSE 500
GOTO MAIN
'*************************************************
DISABLE ' do NOT place interrupt checking code below
Halt: ' Halt Routine
IF PORTB.0 = 0 THEN
WHILE PORTB.0=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
HIGH PORTE.1
LOW PORTE.2
ENDIF
IF PORTB.1 = 0 THEN
WHILE PORTB.1=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
LOW PORTE.1
HIGH PORTE.2
ENDIF

' IF PORTB.0 = 0 THEN
' ' PAUSE 20
' ' IF PORTB.0 = 0 THEN
' HIGH PORTE.1
' LOW PORTE.2
' ' ENDIF
' ENDIF
'
' IF PORTB.1 = 0 THEN
' ' PAUSE 20
' ' IF PORTB.1 = 0 THEN
' LOW PORTE.1
' HIGH PORTE.2
' ' ENDIF
' ENDIF

INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME ' Go to Main routine
ENABLE ' Enable all active interrupts

Pic_User
- 13th November 2008, 17:53
Adam,
I see what you mean with the DEFINE, oops! Thanks for the catch. I see what you mean with colors. Is this better?
It is better! But the idea is to copy from your editor, paste into a code box. So the indents and formatting look more like the original. Sometimes the forum changes the formatting. I was not sure the lowercase define was in your original or being changed by the forum. It changes a lot of things “for you” :) I wish I knew more about PBP to really help you on your real question.
-Adam-

mister_e
- 13th November 2008, 19:49
with code bracket
I type


i now paste my code here
then i type

we got

asm ; The following code is assembly, not Basic
__CONFIG _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF & _LVP_OFF
; Use internal oscillator & make both, OSC pins I/Os, turn off watchdog timer, enable Power-up timer,
; code protection off, brown-out off, disable switch over mode, turn off failsafe monitor,
; low voltage programming off
endasm ' End assembly code

define OSC 8 ' Tell the program the oscillator is running at 8 MHz
OSCCON.6 = 1 ; Set the internal oscillator to 8MHz
OSCCON.5 = 1 ; Set the internal oscillator to 8MHz
OSCCON.4 = 1 ; Set the internal oscillator to 8MHz

PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low

TRISA = %00000000 ' Make all port a pins outputs
TRISB = %00001111 ' Make port b pins 0-3 inputs and the rest as outputs
TRISC = %00000000 ' Make all port c pins outputs
TRISD = %00000000 ' Make all port d pins outputs
TRISE = %00000000 ' Make all port e pins outputs

ADCON0.0 = 0 ; Make all analog pins digital I/Os
CM1CON0.7 = 0 ; Disable comparator C1
CM2CON0.7 = 0 ; Disable comparator C2
WDTCON.0 = 0 ; Disable the watchdog timer
ANSEL = %00000000 ; Set all analog pins on port a to digital I/O
ANSELH = %00000000 ; Set all analog pins on port b to digital I/O

INCLUDE "modedefs.bas"

'Variables**************************************** *************************************************
b0 VAR byte ' Required byte variable for toggle button


'************************************************* *************************************************
INITIALIZE: ' Initialize Routine
INTCON.0 = 0 ' Clear the interrupt-on-change flag
On Interrupt goto Halt ' Once an active interrupt port is enabled, go to the Halt routine
INTCON = %10001000 ' Enable global interrupts, Enable interrupt-on-change on Port b
IOCB = %00000011 ' Enable interrupt-on-change on RB0-RB3
PORTE.1 = 0 ' Make sure LED on PORTE.1 is off
PORTE.2 = 0 ' Make sure LED on PORTE.2 is off


'************************************************* ************************************************** *
MAIN:
HIGH PORTE.0
PAUSE 500
LOW PORTE.0
PAUSE 500
GOTO MAIN


'*************************************************
DISABLE ' do NOT place interrupt checking code below

Halt: ' Halt Routine
IF PORTB.0 = 0 THEN
WHILE PORTB.0=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
HIGH PORTE.1
LOW PORTE.2
ENDIF

IF PORTB.1 = 0 THEN
WHILE PORTB.1=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
LOW PORTE.1
HIGH PORTE.2
ENDIF

INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME ' Go to Main routine
ENABLE ' Enable all active interrupts

with colors, following the following thread
http://www.picbasic.co.uk/forum/showthread.php?t=6221&highlight=show+colors

we got

<font color="#000000"> <font color="#000080">ASM </font><font color="#008000">; The following code is assembly, not Basic
</font><font color="#000080">__CONFIG _CONFIG1, _INTOSCIO &amp; _WDT_OFF &amp; _PWRTE_ON &amp; _CP_OFF &amp; _BOR_OFF &amp; _IESO_OFF &amp; _FCMEN_OFF &amp; _LVP_OFF
</font><font color="#008000">; Use internal oscillator &amp; make both, OSC pins I/Os, turn off watchdog timer, enable Power-up timer,
; code protection off, brown-out off, disable switch over mode, turn off failsafe monitor,
; low voltage programming off
</font><font color="#000080">ENDASM </font><font color="#008000">' End assembly code

</font><font color="#000080">DEFINE </font>OSC 8 <font color="#008000">' Tell the program the oscillator is running at 8 MHz
</font>OSCCON.6 = 1 <font color="#008000">; Set the internal oscillator to 8MHz
</font>OSCCON.5 = 1 <font color="#008000">; Set the internal oscillator to 8MHz
</font>OSCCON.4 = 1 <font color="#008000">; Set the internal oscillator to 8MHz

</font>PORTA = $00 <font color="#008000">' Set all port a pins to low
</font>PORTB = $00 <font color="#008000">' Set all port b pins to low
</font>PORTC = $00 <font color="#008000">' Set all port c pins to low
</font>PORTD = $00 <font color="#008000">' Set all port d pins to low
</font>PORTE = $00 <font color="#008000">' Set all port e pins to low

</font>TRISA = %00000000 <font color="#008000">' Make all port a pins outputs
</font>TRISB = %00001111 <font color="#008000">' Make port b pins 0-3 inputs and the rest as outputs
</font>TRISC = %00000000 <font color="#008000">' Make all port c pins outputs
</font>TRISD = %00000000 <font color="#008000">' Make all port d pins outputs
</font>TRISE = %00000000 <font color="#008000">' Make all port e pins outputs

</font>ADCON0.0 = 0 <font color="#008000">; Make all analog pins digital I/Os
</font>CM1CON0.7 = 0 <font color="#008000">; Disable comparator C1
</font>CM2CON0.7 = 0 <font color="#008000">; Disable comparator C2
</font>WDTCON.0 = 0 <font color="#008000">; Disable the watchdog timer
</font>ANSEL = %00000000 <font color="#008000">; Set all analog pins on port a to digital I/O
</font>ANSELH = %00000000 <font color="#008000">; Set all analog pins on port b to digital I/O

</font><font color="#000080">INCLUDE </font>&quot;modedefs.bas&quot;

<font color="#008000">'Variables**************************************** *************************************************
</font>b0 <font color="#000080">VAR BYTE </font><font color="#008000">' Required byte variable for toggle button


'************************************************* *************************************************
</font>INITIALIZE: <font color="#008000">' Initialize Routine
</font>INTCON.0 = 0 <font color="#008000">' Clear the interrupt-on-change flag
</font><font color="#000080">ON INTERRUPT GOTO </font>Halt <font color="#008000">' Once an active interrupt port is enabled, go to the Halt routine
</font>INTCON = %10001000 <font color="#008000">' Enable global interrupts, Enable interrupt-on-change on Port b
</font>IOCB = %00000011 <font color="#008000">' Enable interrupt-on-change on RB0-RB3
</font>PORTE.1 = 0 <font color="#008000">' Make sure LED on PORTE.1 is off
</font>PORTE.2 = 0 <font color="#008000">' Make sure LED on PORTE.2 is off


'************************************************* ************************************************** *
</font>MAIN:
<font color="#000080">HIGH </font>PORTE.0
<font color="#000080">PAUSE </font>500
<font color="#000080">LOW </font>PORTE.0
<font color="#000080">PAUSE </font>500
<font color="#000080">GOTO </font>MAIN


<font color="#008000">'*************************************************
</font><font color="#000080">DISABLE </font><font color="#008000">' do NOT place interrupt checking code below

</font>Halt: <font color="#008000">' Halt Routine
</font><font color="#000080">IF </font>PORTB.0 = 0 <font color="#000080">THEN
WHILE </font>PORTB.0=0 <font color="#008000">' wait for button release &amp; end mismatch condition on portb
</font><font color="#000080">WEND
PAUSE </font>20 <font color="#008000">' Debounce delay
</font><font color="#000080">HIGH </font>PORTE.1
<font color="#000080">LOW </font>PORTE.2
<font color="#000080">ENDIF

IF </font>PORTB.1 = 0 <font color="#000080">THEN
WHILE </font>PORTB.1=0 <font color="#008000">' wait for button release &amp; end mismatch condition on portb
</font><font color="#000080">WEND
PAUSE </font>20 <font color="#008000">' Debounce delay
</font><font color="#000080">LOW </font>PORTE.1
<font color="#000080">HIGH </font>PORTE.2
<font color="#000080">ENDIF

</font>INTCON.0 = 0 <font color="#008000">' Clear the interrupt-on-change flag
</font><font color="#000080">RESUME </font><font color="#008000">' Go to Main routine
</font><font color="#000080">ENABLE </font><font color="#008000">' Enable all active interrupts
</font>

Read your datasheet about Interrupt on change. I haven't done myself, but I bet you should read the whole, save it to a variable, then play with this variable.

Probably not a bad idea, to do the same thing before ON INTERRUPT.. read the whole port, save it to a dummy variable, etc.

HTH

elec_mech
- 13th November 2008, 19:49
Alright, I got smart and have an LED coming on whenever the interrupt routine is entered and guess what? The program always goes into the interrupt routine whenever I press a button. The bad news is that the program doesn't seem to be able to determine which switch is pressed fast enough. Again, if I hold the switch down for a second or more, it knows which switch is pressed. But more often than not, I just press the button as anyone would and I see the LED come on indicating the interrupt routine was entered, but the other LEDs don't toggle most of the time. I suspect that the program is dragging somewhere, but I don't know why the program can pick up a change on PORTB but doesn't have the time to determine which input was tripped. Any suggestions? Modified code below.


'************************************************* ************************************************** *
MAIN:
' HIGH PORTE.0
PAUSE 500
LOW PORTE.0
' PAUSE 500
GOTO MAIN
'*************************************************
DISABLE ' do NOT place interrupt checking code below
Halt: ' Halt Routine
HIGH PORTE.0
IF PORTB.0 = 0 THEN
WHILE PORTB.0=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
HIGH PORTE.1
LOW PORTE.2
ENDIF
IF PORTB.1 = 0 THEN
WHILE PORTB.1=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
LOW PORTE.1
HIGH PORTE.2
ENDIF

INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME Main ' Go to Main routine
ENABLE ' Enable all active interrupts

Bruce
- 14th November 2008, 00:05
You have 500mS delays before your interrupt can even be processed. Unless someone
presses & holds a button, it may be quite a long time before you enter your interrupt
handler.

Reduce your pause times by using a loop with PAUSEUS until your 500mS delay period
expires. Then you only have a few uS before PBP will jump to your int handler, and you
can read the whole port in the first line, save it to a variable, then make a decision on
which switch was pressed.

Charles Linquis
- 14th November 2008, 01:46
Just a suggestion - use Darrel Taylor's Instant Interrupts. If you use that, you don't have to worry about PAUSE. The interrupt routine will be entered whenever the button is pushed - even in the middle of a PAUSE routine. In the end, it is a much cleaner solution than ON INTERRUPT GOTO - believe me.

elec_mech
- 14th November 2008, 13:57
Ahh, I was wondering if that might impact it. So, an external interrupt will not interrupt the program while executing a pause command? Is that due to the way PBP works? If I were to go so far as to program in assembly, it's been a while, I would assume I could interrupt a timer function with an external interrupt. Okay though, I see what you mean.

Alright, I gave it shot and eureka, it works! Thank you Bruce! Here's the updated code.


INCLUDE "modedefs.bas"
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low

asm ; The following code is assembly, not Basic
__CONFIG _CONFIG1, _INTOSCIO & _WDT_OFF & _PWRTE_ON & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF & _LVP_OFF
; Use internal oscillator & make both, OSC pins I/Os, turn off watchdog timer, enable Power-up timer,
; code protection off, brown-out off, disable switch over mode, turn off failsafe monitor,
; low voltage programming off
endasm ' End assembly code

ADCON0.0 = 0 ; Make all analog pins digital I/Os
CM1CON0.7 = 0 ; Disable comparator C1
CM2CON0.7 = 0 ; Disable comparator C2
OSCCON.6 = 1 ; Set the internal oscillator to 8MHz
OSCCON.5 = 1 ; Set the internal oscillator to 8MHz
OSCCON.4 = 1 ; Set the internal oscillator to 8MHz
WDTCON.0 = 0 ; Disable the watchdog timer
ANSEL = %00000000 ; Set all analog pins on port a to digital I/O
ANSELH = %00000000 ; Set all analog pins on port b to digital I/O

DEFINE OSC 8 ' Tell the program the oscillator is running at 8 MHz
TRISA = %00000000 ' Make all port a pins outputs
TRISB = %00001111 ' Make port b pins 0-3 inputs and the rest as outputs
TRISC = %00000000 ' Make all port c pins outputs
TRISD = %00000000 ' Make all port d pins outputs
TRISE = %00000000 ' Make all port e pins outputs
PORTA = $00 ' Set all port a pins to low
PORTB = $00 ' Set all port b pins to low
PORTC = $00 ' Set all port c pins to low
PORTD = $00 ' Set all port d pins to low
PORTE = $00 ' Set all port e pins to low
'Variables**************************************** ************************************************** *
b0 VAR byte ' Required byte variable for toggle button
i var word
temp var byte
'************************************************* ************************************************** *
INITIALIZE: ' Initialize Routine
INTCON.0 = 0 ' Clear the interrupt-on-change flag
On Interrupt goto Halt ' Once an active interrupt port is enabled, go to the Halt routine
INTCON = %10001000 ' Enable global interrupts, Enable interrupt-on-change on Port b
IOCB = %00000011 ' Enable interrupt-on-change on RB0-RB3
PORTE.1 = 0 ' Make sure LED on PORTE.1 is off
PORTE.2 = 0 ' Make sure LED on PORTE.2 is off
GOTO MAIN ' Go to Main routine
'************************************************* ************************************************** *
MAIN:
HIGH PORTE.0
FOR i = 1 to 5000
PAUSEUS 100
NEXT
LOW PORTE.0
FOR i = 1 to 5000
PAUSEUS 100
NEXT
GOTO MAIN
'*************************************************
DISABLE ' do NOT place interrupt checking code below
Halt: ' Halt Routine
temp = PORTB
IF temp.0 = 0 THEN
WHILE PORTB.0=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
HIGH PORTE.1
LOW PORTE.2
ENDIF
IF temp.1 = 0 THEN
WHILE PORTB.1=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
LOW PORTE.1
HIGH PORTE.2
ENDIF
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME ' Resume wherever program left off at
ENABLE ' Enable all active interrupts

Sorry, I tried the Save As in html to get the color, but I can't save it as html from MPLAB.

I assume the while loop and 20ms debounce pause are still good to have.

Charles, I tried using Darrel Teylor's Instant Interrupts without success. I attempted to use the RBC_INT. It didn't work and I read that that command is only designed for ports b4-7. Then I attempted to use INT0_INT and INT1_INT for ports b0 and b1, respectively, but no luck there either. I'm not sure if I'm doing something wrong or if I need configure something. I love the concept, but I wanted to figure out how to fix the PBP interrupt version I'm familar with before I try figuring out a new method. I'm open to suggestions if you have any. Thank you for the suggestion. I definitely want to play with Darrel's solution once I've got this licked.

Archangel
- 14th November 2008, 16:13
Ahh, I was wondering if that might impact it. So, an external interrupt will not interrupt the program while executing a pause command? Is that due to the way PBP works? If I were to go so far as to program in assembly, it's been a while, I would assume I could interrupt a timer function with an external interrupt. Okay though, I see what you mean.

Alright, I gave it shot and eureka, it works! Thank you Bruce! Here's the updated code.




It's due to PBP interrupts being a Psudo interrupt, it detects the hardware interrupt, puts a request on the stack and waits for an opportunity, Whereas an assembly interrupt is like cops, break in and announce it's arrival.

elec_mech
- 14th November 2008, 17:11
Joe, thanks for the info!

Okay, related to interrupts, I've run into another problem. I go into my interrupt routine, detect a button on a port change and then tell the program to resume at a new routine based on which button was pressed. The first button works fine, but whenever I press the second button, it seems to ignore/skip the resume (colored below) and go on to the end of the interrupt routine. I added a toggle to an LED to indicate where the program was going. It works before the colored resume statement and after the end if statement, but not immediately after the resume statement. I also tried it at the beginning of the Score_player2 routine, which of course didn't work. I tried a quick search on the forum, but didn't come across anything similar, at least not yet. Any reason PBP would skip or ignore a resume statement? I've just included the interrupt routine - the rest is very similar to the code posted above.

I don't know if this is an indication of another problem, but I'm multiplexing two seven segment digits and whenever I press either button, one of the digits goes out, sometimes digit A and sometimes digit B regardless of the button pressed, and comes back on whenever I release the button.


'************************************************* ************************************************** ***
DISABLE ' do NOT place interrupt checking code below
Halt: ' Halt Routine
temp = PORTB
TOGGLE PORTE.1
IF temp.0 = 0 THEN
WHILE PORTB.0=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME SCORE_PLAYER1
ENDIF
IF temp.1 = 0 THEN
WHILE PORTB.1=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME SCORE_PLAYER2
ENDIF
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME Main ' Go to Main routine
ENABLE ' Enable all active interrupts
'************************************************* ************************************************** ***

mister_e
- 14th November 2008, 17:13
Not a good idea to me to use label with RESUME as you never know where the program was before to jump in the ISR. You should jump (goto) at the end if your ISR, then your program will return where he was before.

elec_mech
- 14th November 2008, 17:25
Steve,

I'd be lying if I said I completely understood what you meant, but I noticed that I was forcing the program to resume at the start of the Main routine and allowed it to just resume at the end of the ISR. It works! Thank you for your advice. I still have one of the digits going off whenever I press a button, but I'll investigate that further. Likely a hardware issue.


'************************************************* ************************************************** ***
DISABLE ' do NOT place interrupt checking code below
Halt: ' Halt Routine
temp = PORTB
TOGGLE PORTE.1
IF temp.0 = 0 THEN
WHILE PORTB.0=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME SCORE_PLAYER1
ENDIF
IF temp.1 = 0 THEN
WHILE PORTB.1=0 ' wait for button release & end mismatch condition on portb
WEND
PAUSE 20 ' Debounce delay
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME SCORE_PLAYER2
ENDIF
INTCON.0 = 0 ' Clear the interrupt-on-change flag
RESUME ' Resume program running before interrupt occurred
ENABLE ' Enable all active interrupts
'************************************************* ************************************************** ***