CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug Reports on this forum. Send them to support@ccsinfo.com

18f6722 interrupt handling

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
pmuldoon



Joined: 26 Sep 2003
Posts: 189
Location: Northern Indiana

View user's profile Send private message

18f6722 interrupt handling
PostPosted: Tue Apr 17, 2018 7:42 am     Reply with quote

Compiler V5.074
PIC 18F6722

Does anybody know how the CCS compiler handles multiple interrupts?
I have a TMR3 and a CCP2 capture from an external input. Both setup as interrupts.

I am suspecting that the compiler is processing the TMR3 interrupt, then processing the CCP2 interrupt before clearing the TMR3 interrupt flag. Essentially servicing all pending interrupts then clearing all of the flags instead of clearing each flag at the end of each interrupt function.

I am checking that flag and looking at CCP2 to determine if I should adjust the upper word or not (the pending TMR2 interrupt may have occured just before or just after the CCP2 capture.)

Adding #priority CCP2,TIMER3 seems to have cured it by insuring that the pending TMR3 interrupt is never serviced before the CCP2 and my test is always valid.

Am I missing something?


Is that possibly the case?
PCM programmer



Joined: 06 Sep 2003
Posts: 20385

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 8:39 am     Reply with quote

You can see what's happening if you make a simple test program as
shown below:
Code:
#include <18F6722.h>
#fuses INTRC_IO, NOWDT, BROWNOUT, PUT
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)

#int_timer3
void t3_isr(void)
{
int8 temp;

temp = 0x55;
}

//--------------------------------------
#int_ccp2
void ccp2_isr(void)
{
int8 temp;

temp = 0xAA;
}

//======================================
void main()
{                                                                     

while(TRUE);
}


Then look at the .LST file. In the interrupt dispatcher code, you'll see
that it tests for the Timer3 interrupt and handles it first. That's because
in the source code, the Timer3 isr occurs first and there is no #priority
statement to override this.
Code:

0005C:  BTFSS  PIE2.TMR3IE
0005E:  GOTO   0068
00062:  BTFSC  PIR2.TMR3IF
00064:  GOTO   00CA
00068:  BTFSS  PIE2.CCP2IE
0006A:  GOTO   0074
0006E:  BTFSC  PIR2.CCP2IF
00070:  GOTO   00D4


Look at the .LST file for the individual isr routines. You can see that each
one clears the interrupt flag before leaving.
Code:
.................... #int_timer3
.................... void t3_isr(void)
.................... {
.................... int8 temp;
.................... 
.................... temp = 0x55;
000CA:  MOVLW  55
000CC:  MOVWF  temp
000CE:  BCF    PIR2.TMR3IF
000D0:  GOTO   0074
.................... }



.................... #int_ccp2
.................... void ccp2_isr(void)
.................... {
.................... int8 temp;
.................... 
.................... temp = 0xAA;
000D4:  MOVLW  AA
000D6:  MOVWF  temp
000D8:  BCF    PIR2.CCP2IF
000DA:  GOTO   0074
.................... }
.................... 
pmuldoon



Joined: 26 Sep 2003
Posts: 189
Location: Northern Indiana

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 9:03 am     Reply with quote

interesting...
how did you find the interrupt dispatcher code?

my interrupt function looked different:
Code:

.................... #INT_TIMER3
.................... void    Tmr3_Handler()
.................... {
....................     ++ulTmr3Rollover;
0054C:  MOVLB  6
0054E:  INCF   x18,F
00550:  BTFSC  FD8.2
00552:  INCF   x19,F
.................... }
PCM programmer



Joined: 06 Sep 2003
Posts: 20385

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 9:16 am     Reply with quote

The interrupt dispatcher code is at the start of the .LST file.

To see a better .LST file with register names, go to compiler options and
turn on Symbolic mode for the .LST file.
pmuldoon



Joined: 26 Sep 2003
Posts: 189
Location: Northern Indiana

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 9:22 am     Reply with quote

more info...
my lst threw a comment between the int function I wrote and the exit code it added. I missed it. Now that I found the exit code I can explore further.

Now I'm puzzled as to why my #priority seems to have fixed the problem. It's been running all morning now and not screwed up. I'll look deeper...

Code:

.................... #INT_TIMER3
.................... void    Tmr3_Handler()
.................... {
....................     ++ulTmr3Rollover;
0054C:  MOVLB  6
0054E:  INCF   x18,F
00550:  BTFSC  FD8.2
00552:  INCF   x19,F
.................... }
.................... /****************** TBE_Handler() ************************************
.................... Interrupts whenever UART buffer is ready to send another character.
....................  * disables itself when finished.
.................... *********************************************************************/
00554:  BCF    FA1.1
00556:  MOVLB  0
00558:  GOTO   009C
PCM programmer



Joined: 06 Sep 2003
Posts: 20385

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 10:13 am     Reply with quote

You may be working on the same problem as in my previously posted
CCP capture code. This routine has comments that try to explain
what's happening. Also note that INT_TIMER1 is checked inside this
CCP1 routine.
Code:

// The following variable is incremented in the Timer1 isr, and extends
// Timer1 to 24 bits.  (The lower 16 bits come from the Timer1 hardware).
char gc_timer1_extension;

#priority CCP1, TIMER1, TIMER0  // Set interrupt priority

#int_ccp1
void ccp1_isr(void)
{
char timer_ext_copy;
int32 current_ccp;
static int32 old_ccp = 0;

gc_capture_flag = TRUE;        // Set flag to indicate that we did a capture

current_ccp = (int32)CCP_1;    // Read the current CCP

timer_ext_copy = gc_timer1_extension;  // Get local copy of the timer ext.

// Check if a Timer1 interrupt is pending.  If so, check if the CCP
// capture occurred before or after the Timer rolled over.
// We can tell if it occurred after it rolled over, if the CCP's MSB
// is zero.  ie., if the CCP is somewhere between 0x0000 and 0x00FF.
// We know that checking if the MSB = 0x00 will work, because it
// takes about 30 us to get into this ISR.   The timer increments at
// 1 us per count, so 0xFF = 255 us.
// Actually, to be safer, I'll give it 2 MSB counts, which is 511 us.
// That way, if I lengthen any of the other ISR's, we'll still be
// able to detect the roll-over OK.
// If the timer did roll over after we got a CCP interrupt, then we
// need to increment the timer extension byte, that we save.  We have
// to do that because the CCP interrupt has priority, and so it executes
// before the Timer isr can execute and increment the extension.
// (Designing the code with the priority switched doesn't help.  You
// still have the same type of problem.  With CCP first, the fix is easier).
//
if(interrupt_active(INT_TIMER1)) 
  {
   if(make8(current_ccp, 1) < 2) // Was CCP captured after Timer1 wrapped?
      timer_ext_copy++; // If so, increment the copy of the timer ext.

   // Since we know a timer interrupt is pending, let's just handle it
   // here and now.  That saves a little load off the processor.
   gc_timer1_extension++;            // Increment the real timer extension
   clear_interrupt(INT_TIMER1);     
  }

// Insert the timer extension into the proper place in the 32-bit CCP value.
// ie.,  Insert it into location "EE" as follows: 0x00EEnnnn  (nnnn = the CCP).
make8(current_ccp, 2) = timer_ext_copy;

// Because we're using unsigned math, we don't have to worry if the current
// value is less than the old.   The result is always the absolute value
// of the difference.  The only way there could be a problem is if the new
// CCP value had rolled over twice.  But with a 24-bit value, and a Timer
// pre-scalar of 1, that's 16.7 seconds.  That's way beyond any practical value.

g32_ccp_delta = (current_ccp > old_ccp) ? current_ccp - old_ccp : current_ccp + (0x1000000 - old_ccp);

// Save the current ccp value for next time.
old_ccp = current_ccp;

}

//---------------------------------------------------------------------
// The Timer1 interrupt merely increments an 8-bit variable which
// extends the timer to 24 bits.  We need this so we can avoid having
// to switch the Timer pre-scaler between "low" and "high" rpm ranges.
// With a 24 bit timer, we can

#int_timer1
void timer1_isr(void)
{
gc_timer1_extension++;
}
pmuldoon



Joined: 26 Sep 2003
Posts: 189
Location: Northern Indiana

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 10:35 am     Reply with quote

That looks very similar to what i'm trying to do, but I'm not seeing what your timing relationships are.

I have TMR3 rolling over roughly every 12ms and my CCP fires roughly every 83us with maybe 5us variance at most, so I simply compare my capture to 0x8000 because it should be very high if the interrupt fired after, and very low if it fired before.

If the normal handler didn't have a chance to update the rollover yet, then I must still be in interrupt code and can't be more than a few 10's of instructions away from the rollover. My interrupts are probably mostly in-out overhead rather than code I execute.

Seems logical and safe, but maybe I'm missing something. I will look at your example more closely. Right now I just gutted and am re-writing yet another function and have to finish before I can continue experimenting.
Ttelmah



Joined: 11 Mar 2010
Posts: 13538

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 11:19 am     Reply with quote

Using a single hardware interrupt level, all #PRIORITY does, is determine the order that the interrupt flags are polled at the start of the interrupt dispatcher.
When an interrupt triggers, the registers are saved, then the interrupt flags are tested in turn, in the order the interrupt handlers have been declared, or in the order #PRIORITY says if it is used. The first one found 'set', calls it's handler. Having done this, the code exits the handler, and it is immediately called again, if there is another interrupt also triggered.

The way to get a interrupt to always trigger first is to use hardware interrupt priority.
#DEVICE HIGH_INTS=TRUE

Then declare #INT_CCP2 HIGH, which will make this have hardware priority over the timer interrupt. This handler will be called even if the code is in the middle of a standard interrupt when the event triggers.
pmuldoon



Joined: 26 Sep 2003
Posts: 189
Location: Northern Indiana

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 11:41 am     Reply with quote

That's helpful.
I am using the high priority interrupt feature with TMR0 and it works fine.

What I'm learning is that the interrupt code exits completely between interrupts. I guess that means a lot of extra saving and restoring of registers if two interrupts happen at about the same time or when another int is being handled. (Seems like potential for a great compiler option to select whether to service one interrupt at a time, or loop until all flags are clear before RETFIE).
Ttelmah



Joined: 11 Mar 2010
Posts: 13538

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 1:15 pm     Reply with quote

Yes. As I said, it exists the handler.
Problem is if you don't do this you can remain deadlocked inside the interrupt handler. Though only a single instruction is executed when the code exits, at least something does get executed.
pmuldoon



Joined: 26 Sep 2003
Posts: 189
Location: Northern Indiana

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 1:44 pm     Reply with quote

I've totally rewritten my Priority interrupt TMR0. Not related to the problem, just needed to get it done to test this issue again.

Interesting thing. I repeated the test. When I set #priority TIMER3,CCP2 it seemed to take much longer to fail. #priority CCP2,TIMER3 hasn't caused a faiilure yet.

CCP2 is using TMR3 for its timer. Don't know how that could be an issue. The other thing is that my HIGH PRIORITY interrupt TMR0 is firing only 1 or 2 times per 125ms now instead of 400 times.

This is hard to pin down. It's almost like CCP2 is also a HIGH PRIORITY interrupt and interrupting TMR3 after it incremented the rollover but before it cleared the flag. CCS only allows 1 high priority interrupt, but the PIC datasheet looks like you can make more than one high. With the caveat that they can only interrupt low priority interrupts.
newguy



Joined: 24 Jun 2004
Posts: 1517
Location: Edmonton, Alberta

View user's profile Send private message

PostPosted: Tue Apr 17, 2018 2:21 pm     Reply with quote

This might be a compiler issue because it sounds suspiciously like something I found last August.

From the email I sent to support at the time:
Code:
When 5.074 was released, I installed it, compiled a project I had been developing using 5.073, and....it hung my 18F25K40. I didn't have time to find out what was happening/why, so I put it aside and reverted to 5.073 in order to complete the project I had started.

Now I have a (little) time, and I revisited 5.074. Here's the issue: in 5.074, all you have to do is declare #device HIGH_INTS=TRUE, and then declare (but not enable) a single interrupt as being HIGH priority. If you enable a different (low priority) interrupt, and the global interrupt, the processor hangs because the compiler enables the high priority interrupt without you actually enabling it. Result: hung processor as the wrongly enabled high priority interrupt continually fires.


I also sent a zip file containing a project which demonstrated the problem. They quickly traced the issue to a dll and sent me an updated file which fixed the problem. I cannot post the file which demonstrates the issue as it contains proprietary things, sorry.
pmuldoon



Joined: 26 Sep 2003
Posts: 189
Location: Northern Indiana

View user's profile Send private message

PostPosted: Wed Apr 18, 2018 5:59 am     Reply with quote

Thanks for that.
I'll dig into it more today, but if I wanted to create the problem I would make CCP2 a high priority and I think the symptoms would be identical. So maybe it's part of the same issue.

I'll have my program read the IPR regs and see if that tells me anything.
pmuldoon



Joined: 26 Sep 2003
Posts: 189
Location: Northern Indiana

View user's profile Send private message

PostPosted: Wed Apr 18, 2018 7:04 am     Reply with quote

The IPR bits are all correct. And I followed the HI and LO interrupt vectors and they are right. I don't see how it can be a CCS issue. It must either be silicon or my logic. I will have to write a simple version and see if I can learn anything more.

Either way, I think my rearranging the #PRIORITY order fixed it.
pmuldoon



Joined: 26 Sep 2003
Posts: 189
Location: Northern Indiana

View user's profile Send private message

RESOLVED!
PostPosted: Wed Apr 25, 2018 1:06 pm     Reply with quote

It was all me (big surprise).
I was trying to determine if a pending rollover happened just before or just after the capture:
Code:
     // get TICS
   ulSensorTicsHigh = ulTmr3Rollover;
   ulSensorTicsLow = get_capture_time();
   // determine if rollover interrupt occurred before or after capture event
   if(interrupt_active(INT_TIMER3)){
       // ulSensorTicsLow will be a VERY LOW  number if the rollover occurred BEFORE the capture
       // ulSensorTicsLow will be a VERY HIGH number if the rollover occurred AFTER  the capture
       if(ulSensorTicsLow < 0x8000)
           // adjust our copy of the rollover value
           // the true value will be updated by it's pending interrupt when we exit this handler
           ulSensorTicsHigh++;
   }

My flaw was not realizing the TMR3 interrupt could occur AFTER the CCP and still be serviced BEFORE the CCP interrupt. The rollover would happen and the interrupt flag would be cleared and I would have no clue.

I kind of thought about that in the first go-round of writing this, but it required a much tighter compare value and that could be hard glitch to sort out if the timing of the capture signal was changed in the future.
I thought I outsmarted it by setting it up knowing it had to be a really big or really small number then just compared it to 0x8000.

This way works fine with the caveat that I keep a big comment not to change the order of the #PRIORITY for CCP, TMR3 in the future.

Actually I was very lucky the glitch happened frequently enough to be noticeable and repeatable enough to figure it all out. TMR3 had to roll over AFTER a capture yet before the 25 or so instructions when the code finally vectored off to the interrupt handlers.

CCP interrupt was armed to fire once every 125ms
Rollover interrupt was happening every 13.1ms (2^16 * 200ns = 13107us)
window of opportunity was about 5us (25 * 200ns = 5us)

Yet I can't even get a win on a lottery ticket that covers the cost of the ticket, LOL.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group