PDA

View Full Version : 16F676 Interupt on Change pin determination



BGreen
- 4th May 2006, 00:18
Melanie helped with some example code in this thread http://www.picbasic.co.uk/forum/showthread.php?t=3589
and I figure I'm still confused somewhere in the setup. I defines both "OldPorta" and "NewPorta" which I THINK was the way to start it but when I get the interupt it just defaults to the operation for Porta.5. The following program was supposed to do the following.

If pin Porta.5 is interupted an led would blink 4 times. Porta.4, 3 times, Porta.3, 2 times. Every time it is interupted it blinks 4 times no matter which pin. I can't seem to figure out, probibly from inexperience, what I'm doing wrong. If someone could help I'd appriciate it.


@ DEVICE PIC16F676,INTRC_OSC_NOCLKOUT,WDT_ON,PWRT_OFF,BOD_O FF,PROTECT_OFF,CPD_OFF,MCLR_OFF
ANSEL = 0 ' DISABLE THE ANALOG INPUT
CMCON = 7 ' DISABLE COMPARATOR ON PORTA
VRCON = 0 ' A/D Voltage reference disabled
TRISA = %00111110 ' SETS ALL PORTA PINS TO INPUT ' Set PORTAA to all input
WPUA = %00000110
SYMBOL LED = PORTA.0
I VAR WORD
OLDPORTA VAR WORD
NEWPORTA VAR WORD
INTCON = %10000000
IOCA = %00111000
OPTION_REG = %00001000
OUTPUT PORTA.0
LOW LED
PAUSE 1000
HIGH LED
GOTO BASE
MYINT:
NEWPORTA = PORTA
INTCON = %10000000
IF NEWPORTA.5 <> OLDPORTA.5 THEN
ILOOP:
I = I + 1
IF I = 5 THEN
resume BASE
ENDIF
LOW LED
PAUSE 1000
HIGH LED
PAUSE 1000
GOTO ILOOP
ENDIF
IF NEWPORTA.4 <> OLDPORTA.5 THEN
I = I + 1
IF I = 4 THEN
resume BASE
ENDIF
LOW LED
PAUSE 1000
HIGH LED
PAUSE 1000
GOTO ILOOP
ENDIF
IF NEWPORTA.3 <> OLDPORTA.5 THEN
I = I + 1
IF I = 3 THEN
resume BASE
ENDIF
LOW LED
PAUSE 1000
HIGH LED
PAUSE 1000
GOTO ILOOP
ENDIF
BASE:
OLDPORTA = PORTA
I = 0
ON INTERRUPT GOTO MYINT:
INTCON = %10001000
LOOP:
SLEEP 240
GOTO LOOP

Archilochus
- 4th May 2006, 00:45
Hi HH,
Just at a quick glance, it looks like these two lines are off a bit:


IF NEWPORTA.4 <> OLDPORTA.5 THEN < compares A.4 to A.5

IF NEWPORTA.3 <> OLDPORTA.5 THEN < compares A.3 to A.5


I think they should be:

IF NEWPORTA.4 <> OLDPORTA.4 THEN

IF NEWPORTA.3 <> OLDPORTA.3 THEN

Arch

BGreen
- 4th May 2006, 00:47
Sorry arch, I edited it to simplify it and may have messed that up. Let me check it and I'll post again.

BGreen
- 4th May 2006, 00:50
I fixed it and it still does the same thing. In addition, it seems that if I even get close to it sometimes it goes off. Is there a pin I should ground or have I really screwed something up?

Archilochus
- 4th May 2006, 01:23
I tossed the code into MicroCode Studio to get a better look - think I see the problem...



IF NEWPORTA.5 <> OLDPORTA.5 THEN
ILOOP:
I = I + 1
IF I = 5 THEN
resume BASE
ENDIF
LOW LED : PAUSE 1000 : HIGH LED
PAUSE 1000
GOTO ILOOP
ENDIF

IF NEWPORTA.4 <> OLDPORTA.4 THEN
I = I + 1
IF I = 4 THEN
resume BASE
ENDIF
LOW LED : PAUSE 1000 : HIGH LED
PAUSE 1000
GOTO ILOOP ; <<<<<<<<<< JUMPS BACK TO Iloop
ENDIF



No matter which pin is interrupted, the code jumps back to "Iloop", which is in the middle of the "IF NEWPORTA.5 <> OLDPORTA.5 THEN" test. It loops there until done, blinking the LED as if A.5 were interrupted, no matter which pin actually caused it.

Arch

PS - on the erratic triggering, make sure your programmer is actually setting MCLR off, and check for 'floating' inputs.
What value pullup/pulldowns are you using?

BGreen
- 4th May 2006, 01:42
I wasn't using anything but internals on this Arch. I'll check out what you posted and give it a whirl. Thanks for the help. I think my brain began to die after I passed 50.

Archilochus
- 4th May 2006, 02:08
Had a few extra minutes and rewrote the code - I think it should work - I don't have any 16F676's to try it on.
(yer way ahead of me... my brain died off around 5 or so)...



@ DEVICE PIC16F676,INTRC_OSC_NOCLKOUT,WDT_ON,PWRT_OFF,BOD_O FF,PROTECT_OFF,CPD_OFF,MCLR_OFF
LED var PORTA.0
BlinkCount var byte
CounterA var byte
OLDPORTA VAR byte
NEWPORTA VAR byte
; DISABLE ANALOG INPUT, COMPARATOR ON PORTA, A/D reference...
ANSEL = 0 : CMCON = 7 : VRCON = 0
TRISA = %00111110 ; PortA In/Out states
WPUA = %00000110
INTCON = %10000000 : IOCA = %00111000 : OPTION_REG = %00001000
LED = 0 : PAUSE 1000 : LED = 1
GOTO BASE
;
; SUBROUTINES
MYINT:
NEWPORTA = PORTA ; read current PortA state
INTCON = %10000000
;
IF NEWPORTA.5 <> OLDPORTA.5 THEN
BlinkCount = 3 ; Set up for 4 blinks
ENDIF
;
IF NEWPORTA.4 <> OLDPORTA.4 THEN
BlinkCount = 2 ; Set up for 3 blinks
ENDIF
;
IF NEWPORTA.3 <> OLDPORTA.3 THEN
BlinkCount = 1 ; Set up for 2 blinks
ENDIF
;
for CounterA = 0 to BlinkCount
LED = 0 : PAUSE 1000 : LED = 1 : PAUSE 1000
next CounterA
RESUME Base
; The above section could have an error trap in case none of the IF's are true for some reason.
; A "Select Case" might use less code space.
;
; Start main program loop here
;
BASE:
BlinkCount=0 : OLDPORTA = PORTA
;
ON INTERRUPT GOTO MYINT
INTCON = %10001000
;
LOOP:
SLEEP 240 : GOTO LOOP
end




Arch

BGreen
- 4th May 2006, 02:23
Arch, once again I'd like to thank you. That was it. At one time programing was one of my strong points, but it seems I get lost pretty easy these days. Thanks for helping me out again.

Darrel Taylor
- 4th May 2006, 02:52
Well, that definately looks better, and it may work better for now, but it still needs a couple things.

The Interrupt "Handler" should be enclosed within DISABLE/ENABLE statements to keep the Interrupt Handler itself from being interrupted again.

The clearing of the Flag should be done in the handler, not the point that execution resumes. If you RESUME without clearing the flag first, you can end up right back in the Handler again.

And, "resume BASE" should only be "RESUME".
When specifying a location to RESUME to, it essentially does a GOSUB to that routine when the Handler is finished. However, in this program, the PIC is told to go to Sleep, without ever returning from that GOSUB. So on each interrupt, it pushes another address on the stack, which overflows after 8 interrupts. For a 16F, the Stack will wrap around to the beginning again without causing a reset, so you may never see the problem right now, but if there are any other subroutines involved, they will never get a chance to RETURN, or will return to the wrong place.

Because of the "Spaghetti" nature of this program, these problems may not be evident because it just falls back into the program and continues on, but when you start adding things later, you will lose a large portion of whatever hair you may have left trying to figure it out.
<br>

BGreen
- 4th May 2006, 03:51
Thank you Darrel. I appriciate the help.

Archilochus
- 5th May 2006, 02:40
Hi Darrel,
I'm a bit confused by the idea that a "RESUME (label)" is like a GOSUB. From the following quote in the PBP manual, it sounds more like RESUME (label) acts like a GOTO:



When the RESUME statement is encountered at the end of the BASIC
interrupt handler, it sets the GIE bit to re-enable interrupts and returns to
where the program was before the interrupt occurred. If RESUME is
given a label to jump to, execution will continue at that location instead.
All previous return addresses will be lost in this case.


I've always used RESMUE with a label, as I want to RESUME at a known location in the code, and have never had any issues, even in large programs with lots of other sub-routines.

Any further info would be great. Thanks,

Arch

mister_e
- 5th May 2006, 03:04
BUT as it erase all previous return address... it just clear the stack at the same time imho.

Let's say you're limited to a 4 level deep stack, everytime you'll RESUME you will eat one in place in the stack. Since your code don't do any Gosubs, it never give you problem, but one day, if you're at the stack limit, and then you add a RESUME LABEL, you may get this problem. Something to be tested i guess.

Darrel Taylor
- 5th May 2006, 03:12
Yes, the manual is a bit vague. But if we take a look at the macro's themself it should be a little clearer.

These are from the PBPPIC14.mac file.
;************************************************* ***************
;* RESUME? : Macro - Resume *
;* *
;* Input : None *
;* Output : None *
;* *
;* Notes : *
;************************************************* ***************

RESUME? macro
RST?RP
retfie
endm
endmod

;************************************************* ***************
;* RESUME?L : Macro - Resume Label *
;* *
;* Input : Label *
;* Output : None *
;* *
;* Notes : *
;************************************************* ***************

RESUME?L macro Label
bsf INTCON, GIE
L?GOTO Label
endm
endmod
With only the RESUME it does a retfie (Return from Interrupt) which causes the hardware to turn GIE back on and pops the last address off the stack to return to.

But, if you include a label with the resume, all it does is set the GIE bit manually, then GOTO's the label. At this point, the address from the interrupt is still on the stack. Once the Subroutine has finished and does a RETURN. It will return to the point where the interrupt first happened.

If the subroutine doesn't do a RETURN, the stack keeps building up.

Also, that subroutine should be enclosed in DISABLE/ENABLE statments to keep it from being interrupted before it returns.

If you're OK with the idea of the stack wrapping around and Discarding all previous gosubs after an interrupt, then it'll still work that way on a 16F. But don't try it with an 18F.

HTH,
&nbsp; Darrel

Archilochus
- 5th May 2006, 03:14
Since your code don't do any Gosubs, it never give you problem, but one day, if you're at the stack limit, and then you add a RESUME LABEL, you may get this problem. Something to be tested i guess.

Hi Steve,
Well, that's what got me wondering. In other code with lots of gosubs, using a RESUME (label) has not caused any problems.
If the stack gets cleared with the RESUME (label), how would the stack overflow occur?

Thanks,
Arch

EDIT
Interesting info Darrel. Can't figure how any other code I've written with ON INTERRUPT continues to work - by the look of what you posted, the code should crash or go off in odd directions - but for some reason it does work OK even after a RESUME (label) followed by several subroutines. I'll have to try and figure that one out one of these days.

On that little sample above - I don't think the DISABLE / ENABLE are needed, as the interrupt handler comes before the ON INTERRUPT statement (at least that's what the manual says).
EndEDIT

mister_e
- 5th May 2006, 03:16
Forget me... see Darrel's explanation above. Sorry i didn't write a single code line and look the compiled file OR, like Darrel, look the According Macro.

Once again i failed :)

But BTW, if you never had any failure, it's probably because you don't use much nested gosub AND/OR you're a lucky guy!

Thanks Darrel, i just see some interesting stuff to do with that RST?RP now ;) I think i'll go deeper in the .LIB when i'll be back in Canada.

Archilochus
- 5th May 2006, 03:37
I'm not sure how to interpret this bit from the same file that Darrel mentioned above:



;************************************************* ***************
;* ONINT?LL : Macro - On Interrupt Goto *
;* *
;* Input : L interrupt handler label *
;* : L interrupt checker label *
;* Output : None *
;* *
;* Notes : *
;************************************************* ***************

ONINT?LL macro Inthand, Intchk
local label
bsf INTCON, GIE
L?GOTO label
Intchk btfsc INTCON, GIE
return
L?GOTO Inthand
label
endm
ONINT_USED = 1
endmod


From this, it looks like PBP does a GOTO to get to the interrupt handler (inthand?) - so no address would be placed on the stack since the handler is not a subroutine, is that right?

Arch

Darrel Taylor
- 5th May 2006, 04:31
OK, so let's look at how the whole process of ON INTERRUPT works.

When an Interrupt occurs, the hardware turns off the GIE bit, pushes the current address on the stack and then vectors to address 4.

At address 4, PBP only puts 1 opcode. RETURN

This pops the hardware generated address from the stack and immediately returns execution to where it was before the interrupt. But leaves the GIE bit turned off, so that no other interrupts can occur until the last one is handled. Because, it can only be Handled, after PBP is done with whatever it was doing at the time.

Then in-between each and every line of code (that's not DISABLED) it does a CALL to the interrupt "Checker" which simply checks to see if GIE is a 0 or not. If it is 0, it means an interrupt occurred and it does the GOTO that you see in the ONINT?LL macro;

It's that CALL to the checker that puts the address on the stack, so that it can return back to the point in-between lines that it jumped from.

So again, if a return never happens, the stack builds up.

An interesting side note is that many people think you can turn OFF all interrupts by clearing the GIE bit (myself included previously). But what that really does with ON INTERRUPT is to make PBP think that an interrupt has happened, it calls the handler (which shouldn't do anything since no Flags are set) and then turns GIE back on again. In order to turn off interrupts globally, that section of code must be DISABLED.


Can't figure how any other code I've written with ON INTERRUPT continues to work.

It all boils down to the one statement in the manual that seems so innocuous.
All previous return addresses will be lost in this case.

As long as you never want to go back to the sections of code that got interrupted, then the stack simply wraps around, and only NEW gosubs past that point will be returned to. That's not something that any of my programs would be able to tolerate.


On that little sample above - I don't think the DISABLE / ENABLE are needed, as the interrupt handler comes before the ON INTERRUPT statement (at least that's what the manual says).


Yup, you're right, anything before the ON INTERRUPT statement is considered DISABLED. My Bad.

HTH,
&nbsp; Darrel

Archilochus
- 5th May 2006, 17:04
Hi Darrel,
Thanks for explaining further - I think the idea has finally got through my thick skull.



It all boils down to the one statement in the manual that seems so innocuous.
All previous return addresses will be lost in this case.

As long as you never want to go back to the sections of code that got interrupted, then the stack simply wraps around, and only NEW gosubs past that point will be returned to. That's not something that any of my programs would be able to tolerate.


This must be why my programs keep working - once interrupted, everything the progam was doing is dropped, and the code never has to return back to before the interrupt, so the stray return addresses piling up on the stack just get overwritten when there are new GOSUBs or CALLs.

I'll have to keep this issue in mind if my code ever gets more complex! The PBP manual really should mention something about the potential stack problems with RESUME(label).

Thanks again,
Arch