Maker Pro
Maker Pro

16F628 Interrupt problem - help!

M

Mark Jones

Jan 1, 1970
0
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Rob wrote:
| I've got what I think is an interrupt problem with a simple optical
encoder
| / PIC 16F628 circuit.
|
| The PIC loops around and sends the encoder count values out when a
counter
| in the main loop expires. The encoder is read and the count updated
when the
| encoder input generates a falling edge interrupt on RBO. It all
works fine,
| however every now and again it will crash and cease sending out the
count
| data. This only seems to happen if the encoder is changing position.
I think
| the problem may be do do with my context saving.
|
| The encoder is a quadrature optical unit, which is powered up
intermittently
| at around 2kHz. Some glue logic grabs the status and presents it to
the PIC.
| A falling edge on RBO means a step has occurred, with the direction
| indicated by the second input on RB3. All of this works fine.
|
|
| Clock
| |
| Power switch
| |
| Encoder --> Glue Logic --> PIC - RB0 (Step) / RB3 (Dir)
|
|
|
| I've pasted the relevant code sections below - can anyone see where I am
| going wrong?
|
| Many thanks.
| rob
|
|
;##########################################################################
| ORG 04H ;INTERRUPT VECTOR
| ;SAVE CONTEXT BEFORE SERVICING INTERRUPT
| MOVWF W_TEMP ;COPY W TO W_TEMP REGISTER
| SWAPF STATUS,W ;COPY STATUS REG INTO W
| CLRF STATUS ;BANK ZERO
| MOVWF STATUS_TEMP ;SAVE STATUS IN STATUS_TEMP
|
| MOVF PCLATH,W ;COPY PCLATH INTO W
| MOVWF PCLATH_TEMP ;SAVE PCLATH IN PCLATH_TEMP
| CLRF PCLATH ;CLR PCLATH - GOTO PAGE 0, ONLY REQ IF USING PAGES 1,2
AND
| /OR 3
| MOVF FSR,W ;COPY FSR TO W
| MOVWF FSR_TEMP ;COPY FSR FROM W TO FSR_TEMP
| ;CONTEXT SAVED
|
| ;IDENTIFY INTERRUPT SOURCE
| ;BTFSC INTCON,INTF ;IS INT DUE TO A RB0 TRANSITION?
| BCF INTCON,INTE ;DISABLE RBO INTERRUPT
| CALL ISR_ENC ;YES - SO SERVICE ENCODER STEP INTERRUPT
|
| ;RE-ENABLE PORTB INTERRUPT AFTER SERVICING...........
| BCF INTCON,INTF
| BSF INTCON,INTE
| ;NO MORE INTERRUPTS
|
| ISR_DONE ;RESTORE CONTEXT PREVIOUSLY SAVED
| MOVF PCLATH_TEMP,W ;RESTORE PCLATH
| MOVWF PCLATH
| SWAPF STATUS_TEMP,W ;RESTORE STATUS
| MOVWF STATUS ;RESTORES TO ORIGINAL BANK
| SWAPF W_TEMP,F ;SWAP W_TEMP
| SWAPF W_TEMP,W ;W_TEMP INTO W
| MOVF FSR_TEMP,W ;RESTORE FSR
| MOVWF FSR
| RETFIE ;RETURN FROM INTERRUPT - ALSO RESETS GIE BIT CLEARED BY
INTERRUPT
|
;##########################################################################
|
| ;MAIN LOOP
|
|
| CALL INIT_INTERRUPTS
| HERE
| INCFSZ TX_CNT1,1 ;loop until ctr overflows and then send out
count data
| GOTO HERE
| INCFSZ TX_CNT2,1
| GOTO HERE
| CALL CNT2SER ;CONVERTS HEX COUNT TO ASCII & SENDS OUT SERIAL
| GOTO HERE
|
|
|
;##########################################################################
| ISR_ENC ;SERVICE ENCODER STEP INTERRUPT - ENCODER CHECK ROUTINE
|
;##########################################################################
| BTFSS DIR ;IS DIR HIGH?
| GOTO CCW ;DIR=0 SO STEP IS CCW
| GOTO CW ;DIR=1 SO STEP IS CW
|
|
| CW ;INCREMENT 24 BIT COUNTER
| ;/TEST
| LED2_PULSE ;PULSES LED2
| LED1_ON
| ;/END TEST
| INCFSZ CTR0,1
| RETURN
| INCF CTR1,1
| RETURN
|
| CCW ;DECREMENT 24 BIT COUNTER
| ;/TEST
| LED2_PULSE ;PULSES LED2
| LED1_OFF
| ;/END TEST
|
| DECF CTR0,1
| INCFSZ CTR0,0 ;TEST FOR FF 'UNDERFLOW'
| RETURN ;NO UNDERFLOW
| DECF CTR1,1
| RETURN ;NO UNDERFLOW
|
|
;##########################################################################
| INIT_INTERRUPTS
|
;##########################################################################
| GOBANK1 ;CONFIG RB0 EDGE ETC
| BCF OPTION_REG,INTEDG ;CLEARED FOR FALLING EDGE INTERRUPT
| GOBANK0
| BCF INTCON,INTF ;ENSURE EXT INT FLAG IS CLEAR
| BSF INTCON,INTE ;ENABLE RB0 INT
| CLRF PIR1 ;CLEAR INT FLAGS
| BSF INTCON,GIE ;ENABLE GIE <7>,
| RETURN
|
;##########################################################################
|
|

~ Your LED2_PULSE and LED1_OFF, what do they do?

~ The optical encoder, is it measuring something fast? Precise? Or is
it just being used as a knob? (100ms sample rate is much easier to
deal with than 2kHz.)

~ The general rule of thumb regarding ISR's & PICs is A) 95% of the
time they are not needed, B) they should be very short, and C) all
port activity has to be double-buffered. (Think, what happens if an
interrupt occurs while a port is being modified?)

~ That said, if the encoder is suppling a very fast interrupt, what
happens if another interrupt occurs before the line
~ BCF INTCON,INTE ;DISABLE RBO INTERRUPT
~ ?

- -- The answer is fractals. http://www.lilavois.com/nick/fractals/.
Chaos theory. http://www.softology.com.au/voc.htm. Infinite
complexity. Everything that we see around us is somehow related to an
infnitely complex, unbalanced dynamic. But what could that be?
~ Mark Jones / Helios Studios
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.0 (MingW32)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFB34RJUoq45ERxHvoRAvgKAJ42I9VHGwHJRixBgPiCbjE+B9TERwCfc2Wr
fWQ28XAUMRvoPfzP8utT3qk=
=YUFk
-----END PGP SIGNATURE-----
 
A

Anthony Fremont

Jan 1, 1970
0
Rob said:
I've got what I think is an interrupt problem with a simple optical encoder
/ PIC 16F628 circuit.

The PIC loops around and sends the encoder count values out when a counter
in the main loop expires. The encoder is read and the count updated when the
encoder input generates a falling edge interrupt on RBO. It all works fine,
however every now and again it will crash and cease sending out the count
data. This only seems to happen if the encoder is changing position. I think
the problem may be do do with my context saving.

The encoder is a quadrature optical unit, which is powered up intermittently
at around 2kHz. Some glue logic grabs the status and presents it to the PIC.
A falling edge on RBO means a step has occurred, with the direction
indicated by the second input on RB3. All of this works fine.


Clock
|
Power switch
|
Encoder --> Glue Logic --> PIC - RB0 (Step) / RB3 (Dir)



I've pasted the relevant code sections below - can anyone see where I am
going wrong?

Many thanks.
rob

The biggest problem I see is that you clobber W when restoring the FSR
value before returning from the interrupt. You absolutely have to
save/restore context carefully or strange things happen. W must be the
last thing restored.

To save context, I use this:
movwf W_TEMP ;Save everything
swapf STATUS, W
movwf STATUS_TEMP
movfw FSR
movwf FSR_TEMP
movfw PCLATH
movwf PCLATH_TEMP

These variables must be located in ram that is mirrored accross the
banks, this can be accomplished like this:

;
; ISR Register Save Areas
;
cblock 0x70
W_TEMP
STATUS_TEMP
FSR_TEMP
PCLATH_TEMP
endc

Then, to restore context, use this code in exactly this order:
;------------------------------------------------------
IntExit
movfw PCLATH_TEMP
movwf PCLATH
movfw FSR_TEMP
movwf FSR
swapf STATUS_TEMP, W
movwf STATUS
swapf W_TEMP, F
swapf W_TEMP, W
retfie
 
A

Anthony Fremont

Jan 1, 1970
0
Rob said:
Snipped original post content




Thanks for the reply Mark.

The LEDs are just being turned on/off for test purposes. The encoder runs
very slowly - it is to be used to measure a slowly changing mechanical
position, << 500 Hz encoder pulse rate (500 slot encoder wheel). The
application is battery powered hence the need for the extra glue logic to
power up and sample the encoder periodically.

Re point C, can you please elaborate on "double buffering" port
read/writes - I don't understand what you mean by this.

He was referring to keeping a "shadow" ram location that represents the
value of an ouput port. You then set and reset individual bits within
the shadow register and move the whole shadow register to the output
port, instead of flipping bits directly on the output port. This avoids
potential Read/Modify/Write issues having to do with the way the PIC
ports are designed. These issues are generally associated with highly
loaded output pins and/or fast clock speeds.

When you flip an output bit on a PIC, the whole port is read, a single
bit is modified, and the whole port is re-written. With heavily loaded
pins, or in the case of not allowing enough settling time after changing
a pins state, the pin state can be misread. It will consequently be
re-written with the incorrect value resulting in the appearance of
output pins mysteriously changing themselves when other pins on the same
port are changed.
I was of the understanding that as soon as an interrupt occurs the GIE bit
was cleared - preventing futher interrupts being registered, so the problem
of an interrupt occuring before the line "BCF INTCON,INTE" would not be an
issue - am I incorrect on this?

Your interpretation is correct, Mark appears to be somewhat unfamiliar
with PICs. In fact your clearing and setting of INTE in the ISR is not
really necessary, since no other interrupts will be serviced until GIE
is set again (by retfie).

Be aware that the PIC's stack is quite small. Your ISR is using two of
the eight locations available.
 
N

nospam

Jan 1, 1970
0
Rob said:
I've pasted the relevant code sections below - can anyone see where I am
going wrong?

On restore, you restore STATUS selecting the pre-interrupt bank
then restore W and FSR which you saved to bank zero. If you take an
interrupt while other than bank 0 is selected the interrupt will corrupt W
and FSR.
 
C

CBarn24050

Jan 1, 1970
0
errupts", " reenable interrupt after servicing", the counter direction routine.
Your 24 counter appears to be 16 bits. Your problem is most likely to be in the
convertion routines which you havent shown, I suspect that they are table read
routines. These allways need some care.
 
N

nospam

Jan 1, 1970
0
nospam said:
On restore, you restore STATUS selecting the pre-interrupt bank
then restore W and FSR which you saved to bank zero. If you take an
interrupt while other than bank 0 is selected the interrupt will corrupt W
and FSR.

My mistake, think W is OK, FSR is still a problem.
 
M

Mark Jones

Jan 1, 1970
0
Anthony said:
He was referring to keeping a "shadow" ram location that represents
the value of an ouput port. You then set and reset individual
bits within the shadow register and move the whole shadow register
to the output port, instead of flipping bits directly on the output
port. This avoids potential Read/Modify/Write issues having to do
with the way the PIC ports are designed. These issues are
generally associated with highly loaded output pins and/or fast
clock speeds.

When you flip an output bit on a PIC, the whole port is read, a
single bit is modified, and the whole port is re-written. With
heavily loaded pins, or in the case of not allowing enough settling
time after changing a pins state, the pin state can be misread. It
will consequently be re-written with the incorrect value resulting
in the appearance of output pins mysteriously changing themselves
when other pins on the same port are changed.




Your interpretation is correct, Mark appears to be somewhat
unfamiliar with PICs.

No, it's just that I avoid ISR's because of all the inherent trouble
with them. It's been so long, I'd forgotten GIE was automatically
cleared. It's just as easy most of the time to check for things in the
main program loop anyways.

Also check out JAL, it's a friendly pascal-like language for PICs
(which will do assembler also if you like.)
http://groups.yahoo.com/group/jallist/ - binary from
sourceforge.net/projects/jal/

In fact your clearing and setting of INTE in
the ISR is not really necessary, since no other interrupts will be
serviced until GIE is set again (by retfie).

Be aware that the PIC's stack is quite small. Your ISR is using
two of the eight locations available.

Also sorry about the strange message formatting... was tinkering with
PGP stuff.

-- "The one truth of the universe is: infinite recursion. That is all."
 
R

Rob

Jan 1, 1970
0
I've got what I think is an interrupt problem with a simple optical encoder
/ PIC 16F628 circuit.

The PIC loops around and sends the encoder count values out when a counter
in the main loop expires. The encoder is read and the count updated when the
encoder input generates a falling edge interrupt on RBO. It all works fine,
however every now and again it will crash and cease sending out the count
data. This only seems to happen if the encoder is changing position. I think
the problem may be do do with my context saving.

The encoder is a quadrature optical unit, which is powered up intermittently
at around 2kHz. Some glue logic grabs the status and presents it to the PIC.
A falling edge on RBO means a step has occurred, with the direction
indicated by the second input on RB3. All of this works fine.


Clock
|
Power switch
|
Encoder --> Glue Logic --> PIC - RB0 (Step) / RB3 (Dir)



I've pasted the relevant code sections below - can anyone see where I am
going wrong?

Many thanks.
rob

;##########################################################################
ORG 04H ;INTERRUPT VECTOR
;SAVE CONTEXT BEFORE SERVICING INTERRUPT
MOVWF W_TEMP ;COPY W TO W_TEMP REGISTER
SWAPF STATUS,W ;COPY STATUS REG INTO W
CLRF STATUS ;BANK ZERO
MOVWF STATUS_TEMP ;SAVE STATUS IN STATUS_TEMP

MOVF PCLATH,W ;COPY PCLATH INTO W
MOVWF PCLATH_TEMP ;SAVE PCLATH IN PCLATH_TEMP
CLRF PCLATH ;CLR PCLATH - GOTO PAGE 0, ONLY REQ IF USING PAGES 1,2 AND
/OR 3
MOVF FSR,W ;COPY FSR TO W
MOVWF FSR_TEMP ;COPY FSR FROM W TO FSR_TEMP
;CONTEXT SAVED

;IDENTIFY INTERRUPT SOURCE
;BTFSC INTCON,INTF ;IS INT DUE TO A RB0 TRANSITION?
BCF INTCON,INTE ;DISABLE RBO INTERRUPT
CALL ISR_ENC ;YES - SO SERVICE ENCODER STEP INTERRUPT

;RE-ENABLE PORTB INTERRUPT AFTER SERVICING...........
BCF INTCON,INTF
BSF INTCON,INTE
;NO MORE INTERRUPTS

ISR_DONE ;RESTORE CONTEXT PREVIOUSLY SAVED
MOVF PCLATH_TEMP,W ;RESTORE PCLATH
MOVWF PCLATH
SWAPF STATUS_TEMP,W ;RESTORE STATUS
MOVWF STATUS ;RESTORES TO ORIGINAL BANK
SWAPF W_TEMP,F ;SWAP W_TEMP
SWAPF W_TEMP,W ;W_TEMP INTO W
MOVF FSR_TEMP,W ;RESTORE FSR
MOVWF FSR
RETFIE ;RETURN FROM INTERRUPT - ALSO RESETS GIE BIT CLEARED BY INTERRUPT
;##########################################################################

;MAIN LOOP


CALL INIT_INTERRUPTS
HERE
INCFSZ TX_CNT1,1 ;loop until ctr overflows and then send out count data
GOTO HERE
INCFSZ TX_CNT2,1
GOTO HERE
CALL CNT2SER ;CONVERTS HEX COUNT TO ASCII & SENDS OUT SERIAL
GOTO HERE


;##########################################################################
ISR_ENC ;SERVICE ENCODER STEP INTERRUPT - ENCODER CHECK ROUTINE
;##########################################################################
BTFSS DIR ;IS DIR HIGH?
GOTO CCW ;DIR=0 SO STEP IS CCW
GOTO CW ;DIR=1 SO STEP IS CW


CW ;INCREMENT 24 BIT COUNTER
;/TEST
LED2_PULSE ;PULSES LED2
LED1_ON
;/END TEST
INCFSZ CTR0,1
RETURN
INCF CTR1,1
RETURN

CCW ;DECREMENT 24 BIT COUNTER
;/TEST
LED2_PULSE ;PULSES LED2
LED1_OFF
;/END TEST

DECF CTR0,1
INCFSZ CTR0,0 ;TEST FOR FF 'UNDERFLOW'
RETURN ;NO UNDERFLOW
DECF CTR1,1
RETURN ;NO UNDERFLOW

;##########################################################################
INIT_INTERRUPTS
;##########################################################################
GOBANK1 ;CONFIG RB0 EDGE ETC
BCF OPTION_REG,INTEDG ;CLEARED FOR FALLING EDGE INTERRUPT
GOBANK0
BCF INTCON,INTF ;ENSURE EXT INT FLAG IS CLEAR
BSF INTCON,INTE ;ENABLE RB0 INT
CLRF PIR1 ;CLEAR INT FLAGS
BSF INTCON,GIE ;ENABLE GIE <7>,
RETURN
;##########################################################################
 
A

Anthony Fremont

Jan 1, 1970
0
Mark said:
No, it's just that I avoid ISR's because of all the inherent trouble

What inherent trouble with PIC ISR's are you referring to, or are you
talking about ISR's in general? I happen to love using interrupt
handlers, they are quite beneficial to have IMO. ;-) I like to use
them for serial i/o, button press sensing, or virtually anything that
involves some kind of unpredictable input. They are also good for
performing repetitive tasks on a precise interval (like switch
debouncing, or even polling ;-) The whole key is in saving and
restoring context properly for the main level and setting up the correct
context for the ISR. Also, it's good to keep in mind when writing your
code that an interrupt can (and eventually will ;-) occur between any
two instructions of your program.

You should try to stop avoiding them. Once you get the hang of it, you
will begin to see more and more applications for them vs. polling at
mainlevel. I tend to use allot of circular queues for my projects,
usually stuffing data in them in an ISR and then pulling it out at
liesure during main level processing.
with them. It's been so long, I'd forgotten GIE was automatically
cleared. It's just as easy most of the time to check for things in the
main program loop anyways.

Polling is ok I suppose, unless you need absolute precision. And
polling introduces it's own set of problems anyway, which is best all
depends upon the specific application. Interrupts are also nice in that
they let you seperate your program flow into asynchronous pieces (in a
sorta threadish way). The 18F pics have two priority levels, letting
you receive a high priority interrupt while a lower one is already
running. :)))
Also check out JAL, it's a friendly pascal-like language for PICs
(which will do assembler also if you like.)
http://groups.yahoo.com/group/jallist/ - binary from
sourceforge.net/projects/jal/

Thanks anyway, but I'm already aware of JAL. It's pretty neat, but it's
just not my thing. I've used high level languages on micros before, but
I tend to stick with assembler for my PIC projects. Although I am
thinking about trying one of the free BASIC compilers to see how I like
it. I will say though that the most trouble I've had out of using
interrupts is when using them from a high level language. But even
then, I only really needed to figure out the rules for the particular
compiler and then things worked as expected.
 
R

Rob

Jan 1, 1970
0
Snipped original post content
~ Your LED2_PULSE and LED1_OFF, what do they do?

~ The optical encoder, is it measuring something fast? Precise? Or is
it just being used as a knob? (100ms sample rate is much easier to
deal with than 2kHz.)

~ The general rule of thumb regarding ISR's & PICs is A) 95% of the
time they are not needed, B) they should be very short, and C) all
port activity has to be double-buffered. (Think, what happens if an
interrupt occurs while a port is being modified?)

~ That said, if the encoder is suppling a very fast interrupt, what
happens if another interrupt occurs before the line
~ BCF INTCON,INTE ;DISABLE RBO INTERRUPT
~ ?

- -- The answer is fractals. http://www.lilavois.com/nick/fractals/.
Chaos theory. http://www.softology.com.au/voc.htm. Infinite
complexity. Everything that we see around us is somehow related to an
infnitely complex, unbalanced dynamic. But what could that be?
~ Mark Jones / Helios Studios
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.0 (MingW32)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFB34RJUoq45ERxHvoRAvgKAJ42I9VHGwHJRixBgPiCbjE+B9TERwCfc2Wr
fWQ28XAUMRvoPfzP8utT3qk=
=YUFk
-----END PGP SIGNATURE-----



Thanks for the reply Mark.

The LEDs are just being turned on/off for test purposes. The encoder runs
very slowly - it is to be used to measure a slowly changing mechanical
position, << 500 Hz encoder pulse rate (500 slot encoder wheel). The
application is battery powered hence the need for the extra glue logic to
power up and sample the encoder periodically.

Re point C, can you please elaborate on "double buffering" port
read/writes - I don't understand what you mean by this.


I was of the understanding that as soon as an interrupt occurs the GIE bit
was cleared - preventing futher interrupts being registered, so the problem
of an interrupt occuring before the line "BCF INTCON,INTE" would not be an
issue - am I incorrect on this?

regards
rob
 
R

Rob

Jan 1, 1970
0
snipped all.........

Thanks for your advice Mark, Anthony & others.
Anthony's context save/restore method seems to have fixed the problem. I
added the changes and have beat up on it for a day without seeing any
misbehavior.

regards
rob
 
Top